Body.MissingItemRange method

When producing diagnostics about missing attributes or bodies it's
necessary to have a range representing the place where the missing thing
might be inserted.

There's not always a single reasonable value for this, so some liberty
can be taken about what exactly is returned as long as it's somewhere
the user can relate back to the construct producing the error.
This commit is contained in:
Martin Atkins 2017-05-21 11:46:58 -07:00
parent 5fa767a43a
commit 8654cf0361
8 changed files with 105 additions and 6 deletions

View File

@ -12,9 +12,10 @@ type node interface {
}
type objectVal struct {
Attrs map[string]*objectAttr
SrcRange zcl.Range // range of the entire object, brace-to-brace
OpenRange zcl.Range // range of the opening brace
Attrs map[string]*objectAttr
SrcRange zcl.Range // range of the entire object, brace-to-brace
OpenRange zcl.Range // range of the opening brace
CloseRange zcl.Range // range of the closing brace
}
func (n *objectVal) Range() zcl.Range {

View File

@ -256,9 +256,10 @@ Token:
close := p.Read()
return &objectVal{
Attrs: attrs,
SrcRange: zcl.RangeBetween(open.Range, close.Range),
OpenRange: open.Range,
Attrs: attrs,
SrcRange: zcl.RangeBetween(open.Range, close.Range),
OpenRange: open.Range,
CloseRange: close.Range,
}, diags
}

View File

@ -246,6 +246,10 @@ func TestParse(t *testing.T) {
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
End: zcl.Pos{Line: 1, Column: 2, Byte: 1},
},
CloseRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 15, Byte: 14},
End: zcl.Pos{Line: 1, Column: 16, Byte: 15},
},
},
0,
},
@ -290,6 +294,10 @@ func TestParse(t *testing.T) {
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
End: zcl.Pos{Line: 1, Column: 2, Byte: 1},
},
CloseRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 29, Byte: 28},
End: zcl.Pos{Line: 1, Column: 30, Byte: 29},
},
},
0,
},
@ -305,6 +313,10 @@ func TestParse(t *testing.T) {
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
End: zcl.Pos{Line: 1, Column: 2, Byte: 1},
},
CloseRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 2, Byte: 1},
End: zcl.Pos{Line: 1, Column: 3, Byte: 2},
},
},
0,
},
@ -367,6 +379,10 @@ func TestParse(t *testing.T) {
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
End: zcl.Pos{Line: 1, Column: 2, Byte: 1},
},
CloseRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 30, Byte: 29},
End: zcl.Pos{Line: 1, Column: 31, Byte: 30},
},
},
1,
},

View File

@ -144,6 +144,10 @@ func (b *body) JustAttributes() (map[string]*zcl.Attribute, zcl.Diagnostics) {
return attrs, nil
}
func (b *body) MissingItemRange() zcl.Range {
return b.obj.CloseRange
}
func (b *body) unpackBlock(v node, typeName string, typeRange *zcl.Range, labelsLeft []string, labelsUsed []string, labelRanges []zcl.Range, blocks *zcl.Blocks) (diags zcl.Diagnostics) {
if len(labelsLeft) > 0 {
labelName := labelsLeft[0]

View File

@ -197,6 +197,19 @@ func TestBodyPartialContent(t *testing.T) {
Column: 14,
},
},
CloseRange: zcl.Range{
Filename: "test.json",
Start: zcl.Pos{
Byte: 13,
Line: 1,
Column: 14,
},
End: zcl.Pos{
Byte: 14,
Line: 1,
Column: 15,
},
},
},
},
@ -276,6 +289,19 @@ func TestBodyPartialContent(t *testing.T) {
Column: 15,
},
},
CloseRange: zcl.Range{
Filename: "test.json",
Start: zcl.Pos{
Byte: 14,
Line: 1,
Column: 15,
},
End: zcl.Pos{
Byte: 15,
Line: 1,
Column: 16,
},
},
},
},
@ -339,6 +365,19 @@ func TestBodyPartialContent(t *testing.T) {
Column: 18,
},
},
CloseRange: zcl.Range{
Filename: "test.json",
Start: zcl.Pos{
Byte: 17,
Line: 1,
Column: 18,
},
End: zcl.Pos{
Byte: 18,
Line: 1,
Column: 19,
},
},
},
},
@ -419,6 +458,19 @@ func TestBodyPartialContent(t *testing.T) {
Column: 37,
},
},
CloseRange: zcl.Range{
Filename: "test.json",
Start: zcl.Pos{
Byte: 36,
Line: 1,
Column: 37,
},
End: zcl.Pos{
Byte: 37,
Line: 1,
Column: 38,
},
},
},
},

View File

@ -127,6 +127,18 @@ func (mb mergedBodies) JustAttributes() (map[string]*Attribute, Diagnostics) {
return attrs, diags
}
func (mb mergedBodies) MissingItemRange() Range {
if len(mb) == 0 {
// Nothing useful to return here, so we'll return some garbage.
return Range{
Filename: "<empty>",
}
}
// arbitrarily use the first body's missing item range
return mb[0].MissingItemRange()
}
func (mb mergedBodies) mergedContent(schema *BodySchema, partial bool) (*BodyContent, Body, Diagnostics) {
// We need to produce a new schema with none of the attributes marked as
// required, since _any one_ of our bodies can contribute an attribute value.

View File

@ -438,3 +438,9 @@ func (v *testMergedBodiesVictim) JustAttributes() (map[string]*Attribute, Diagno
return attrs, diags
}
func (v *testMergedBodiesVictim) MissingItemRange() Range {
return Range{
Filename: v.Name,
}
}

View File

@ -64,6 +64,13 @@ type Body interface {
// Diagnostics may be produced for other reasons too, such as duplicate
// declarations of the same attribute.
JustAttributes() (map[string]*Attribute, Diagnostics)
// MissingItemRange returns a range that represents where a missing item
// might hypothetically be inserted. This is used when producing
// diagnostics about missing required attributes or blocks. Not all bodies
// will have an obvious single insertion point, so the result here may
// be rather arbitrary.
MissingItemRange() Range
}
// BodyContent is the result of applying a BodySchema to a Body.