mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
lint: Include more source context in unused attribute diagnostics
From some more practical testing of this I realized that usually the first thing I want to know after seeing this warning is what the object literal was being assigned to and what else was also defined inside it, and so this sets the diagnostic's "context" to include the whole containing object literal so that the source snippet in the diagnostic message is more immediately useful, without having to cross-reference to the source code in a separate text editor. Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
This commit is contained in:
@@ -265,6 +265,7 @@ func lintVariableDefaultValue(expr hcl.Expression, targetTy cty.Type) hcl.Diagno
|
||||
nounPhrase, attrName, suggestion,
|
||||
),
|
||||
Subject: unused.NameRange.ToHCL().Ptr(),
|
||||
Context: unused.ContextRange.ToHCL().Ptr(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,13 @@ type DiscardedObjectConstructorAttr struct {
|
||||
// definition that is being reported.
|
||||
NameRange tfdiags.SourceRange
|
||||
|
||||
// ContextRange is the source range of relevant context surrounding the
|
||||
// problematic attribute defition, intended to be used for the "Context"
|
||||
// field of a generated diagnostic so that its included source code
|
||||
// snippet will also include some surrounding lines that should indicate
|
||||
// what else was set in the relevant object constructor.
|
||||
ContextRange tfdiags.SourceRange
|
||||
|
||||
// TargetType is the leaf object type that the affected object constructor
|
||||
// was building a value for. This is the type that the last element of
|
||||
// Path would traverse into, and which (by definition) does not have an
|
||||
@@ -133,6 +140,7 @@ func yieldDiscardedObjectConstructorAttrsObject(expr hcl.Expression, targetTy ct
|
||||
result := DiscardedObjectConstructorAttr{
|
||||
Path: retPath,
|
||||
NameRange: tfdiags.SourceRangeFromHCL(item.KeyExpr.Range()),
|
||||
ContextRange: tfdiags.SourceRangeFromHCL(expr.SrcRange), // the entire containing object literal
|
||||
TargetType: targetTy,
|
||||
}
|
||||
if !yield(result) {
|
||||
|
||||
@@ -38,6 +38,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 2, Column: 5, Byte: 6},
|
||||
End: tfdiags.SourcePos{Line: 2, Column: 8, Byte: 9},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
||||
End: tfdiags.SourcePos{Line: 3, Column: 5, Byte: 22},
|
||||
},
|
||||
TargetType: cty.EmptyObject,
|
||||
},
|
||||
},
|
||||
@@ -56,6 +60,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 2, Column: 5, Byte: 6},
|
||||
End: tfdiags.SourcePos{Line: 2, Column: 8, Byte: 9},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
||||
End: tfdiags.SourcePos{Line: 3, Column: 5, Byte: 22},
|
||||
},
|
||||
TargetType: cty.Object(map[string]cty.Type{
|
||||
"bar": cty.String,
|
||||
}),
|
||||
@@ -90,6 +98,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 2, Column: 6, Byte: 7},
|
||||
End: tfdiags.SourcePos{Line: 2, Column: 9, Byte: 10},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 2, Column: 5, Byte: 6},
|
||||
End: tfdiags.SourcePos{Line: 2, Column: 18, Byte: 19},
|
||||
},
|
||||
TargetType: cty.EmptyObject,
|
||||
},
|
||||
{
|
||||
@@ -98,6 +110,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 3, Column: 6, Byte: 26},
|
||||
End: tfdiags.SourcePos{Line: 3, Column: 9, Byte: 29},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 3, Column: 5, Byte: 25},
|
||||
End: tfdiags.SourcePos{Line: 3, Column: 19, Byte: 39},
|
||||
},
|
||||
TargetType: cty.EmptyObject,
|
||||
},
|
||||
},
|
||||
@@ -116,6 +132,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 3, Column: 6, Byte: 27},
|
||||
End: tfdiags.SourcePos{Line: 3, Column: 9, Byte: 30},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 2, Column: 19, Byte: 20},
|
||||
End: tfdiags.SourcePos{Line: 4, Column: 6, Byte: 44},
|
||||
},
|
||||
TargetType: cty.EmptyObject,
|
||||
},
|
||||
},
|
||||
@@ -145,6 +165,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 2, Column: 6, Byte: 7},
|
||||
End: tfdiags.SourcePos{Line: 2, Column: 9, Byte: 10},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 2, Column: 5, Byte: 6},
|
||||
End: tfdiags.SourcePos{Line: 2, Column: 18, Byte: 19},
|
||||
},
|
||||
TargetType: cty.EmptyObject,
|
||||
},
|
||||
{
|
||||
@@ -153,6 +177,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 3, Column: 6, Byte: 26},
|
||||
End: tfdiags.SourcePos{Line: 3, Column: 9, Byte: 29},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 3, Column: 5, Byte: 25},
|
||||
End: tfdiags.SourcePos{Line: 3, Column: 19, Byte: 39},
|
||||
},
|
||||
TargetType: cty.EmptyObject,
|
||||
},
|
||||
},
|
||||
@@ -171,6 +199,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 3, Column: 6, Byte: 27},
|
||||
End: tfdiags.SourcePos{Line: 3, Column: 9, Byte: 30},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 2, Column: 19, Byte: 20},
|
||||
End: tfdiags.SourcePos{Line: 4, Column: 6, Byte: 44},
|
||||
},
|
||||
TargetType: cty.EmptyObject,
|
||||
},
|
||||
},
|
||||
@@ -200,6 +232,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 2, Column: 10, Byte: 11},
|
||||
End: tfdiags.SourcePos{Line: 2, Column: 13, Byte: 14},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 2, Column: 9, Byte: 10},
|
||||
End: tfdiags.SourcePos{Line: 2, Column: 22, Byte: 23},
|
||||
},
|
||||
TargetType: cty.EmptyObject,
|
||||
},
|
||||
{
|
||||
@@ -208,6 +244,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 3, Column: 10, Byte: 34},
|
||||
End: tfdiags.SourcePos{Line: 3, Column: 13, Byte: 37},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 3, Column: 9, Byte: 33},
|
||||
End: tfdiags.SourcePos{Line: 3, Column: 23, Byte: 47},
|
||||
},
|
||||
TargetType: cty.EmptyObject,
|
||||
},
|
||||
},
|
||||
@@ -226,6 +266,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 3, Column: 6, Byte: 32},
|
||||
End: tfdiags.SourcePos{Line: 3, Column: 9, Byte: 35},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 2, Column: 24, Byte: 25},
|
||||
End: tfdiags.SourcePos{Line: 4, Column: 6, Byte: 49},
|
||||
},
|
||||
TargetType: cty.EmptyObject,
|
||||
},
|
||||
},
|
||||
@@ -258,6 +302,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 2, Column: 6, Byte: 7},
|
||||
End: tfdiags.SourcePos{Line: 2, Column: 9, Byte: 10},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 2, Column: 5, Byte: 6},
|
||||
End: tfdiags.SourcePos{Line: 2, Column: 18, Byte: 19},
|
||||
},
|
||||
TargetType: cty.EmptyObject,
|
||||
},
|
||||
{
|
||||
@@ -266,6 +314,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 3, Column: 6, Byte: 26},
|
||||
End: tfdiags.SourcePos{Line: 3, Column: 9, Byte: 29},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 3, Column: 5, Byte: 25},
|
||||
End: tfdiags.SourcePos{Line: 3, Column: 19, Byte: 39},
|
||||
},
|
||||
TargetType: cty.Object(map[string]cty.Type{"not_baz": cty.String}),
|
||||
},
|
||||
},
|
||||
@@ -311,6 +363,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 3, Column: 6, Byte: 17},
|
||||
End: tfdiags.SourcePos{Line: 3, Column: 9, Byte: 20},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 2, Column: 9, Byte: 10},
|
||||
End: tfdiags.SourcePos{Line: 4, Column: 6, Byte: 34},
|
||||
},
|
||||
TargetType: cty.EmptyObject,
|
||||
},
|
||||
{
|
||||
@@ -319,6 +375,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 6, Column: 6, Byte: 50},
|
||||
End: tfdiags.SourcePos{Line: 6, Column: 9, Byte: 53},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 5, Column: 9, Byte: 43},
|
||||
End: tfdiags.SourcePos{Line: 7, Column: 6, Byte: 68},
|
||||
},
|
||||
TargetType: cty.Object(map[string]cty.Type{"not_baz": cty.String}),
|
||||
},
|
||||
{
|
||||
@@ -327,6 +387,10 @@ func TestDiscardedObjectConstructorAttrs(t *testing.T) {
|
||||
Start: tfdiags.SourcePos{Line: 8, Column: 5, Byte: 73},
|
||||
End: tfdiags.SourcePos{Line: 8, Column: 6, Byte: 74},
|
||||
},
|
||||
ContextRange: tfdiags.SourceRange{
|
||||
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
||||
End: tfdiags.SourcePos{Line: 9, Column: 5, Byte: 84},
|
||||
},
|
||||
TargetType: cty.Object(map[string]cty.Type{
|
||||
"a": cty.EmptyObject,
|
||||
"b": cty.Object(map[string]cty.Type{"not_baz": cty.String}),
|
||||
|
||||
@@ -292,6 +292,7 @@ func (n *nodeModuleVariable) warningDiags() tfdiags.Diagnostics {
|
||||
n.Addr.Variable.Name, extraPathClause, attrName, suggestion,
|
||||
),
|
||||
Subject: unused.NameRange.ToHCL().Ptr(),
|
||||
Context: unused.ContextRange.ToHCL().Ptr(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -322,12 +322,13 @@ func TestNodeModuleVariable_warningDiags(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
Expr: &hclsyntax.ObjectConsExpr{
|
||||
SrcRange: hcl.Range{Filename: "context1.tofu"},
|
||||
Items: []hclsyntax.ObjectConsItem{
|
||||
{
|
||||
KeyExpr: &hclsyntax.LiteralValueExpr{
|
||||
Val: cty.StringVal("baz"),
|
||||
SrcRange: hcl.Range{
|
||||
Filename: "test.tofu",
|
||||
Filename: "test1.tofu",
|
||||
},
|
||||
},
|
||||
ValueExpr: &hclsyntax.LiteralValueExpr{
|
||||
@@ -342,12 +343,13 @@ func TestNodeModuleVariable_warningDiags(t *testing.T) {
|
||||
},
|
||||
},
|
||||
ValueExpr: &hclsyntax.ObjectConsExpr{
|
||||
SrcRange: hcl.Range{Filename: "context2.tofu"},
|
||||
Items: []hclsyntax.ObjectConsItem{
|
||||
{
|
||||
KeyExpr: &hclsyntax.LiteralValueExpr{
|
||||
Val: cty.StringVal("beep"),
|
||||
SrcRange: hcl.Range{
|
||||
Filename: "test.tofu",
|
||||
Filename: "test2.tofu",
|
||||
},
|
||||
},
|
||||
ValueExpr: &hclsyntax.LiteralValueExpr{
|
||||
@@ -371,7 +373,10 @@ func TestNodeModuleVariable_warningDiags(t *testing.T) {
|
||||
Summary: "Object attribute is ignored",
|
||||
Detail: `The object type for input variable "foo" does not include an attribute named "baz", so this definition is unused. Did you mean to set attribute "bar" instead?`,
|
||||
Subject: &hcl.Range{
|
||||
Filename: "test.tofu",
|
||||
Filename: "test1.tofu", // from synthetic source range in constructed expression above
|
||||
},
|
||||
Context: &hcl.Range{
|
||||
Filename: "context1.tofu", // from synthetic source range in constructed expression above
|
||||
},
|
||||
})
|
||||
wantDiags = wantDiags.Append(&hcl.Diagnostic{
|
||||
@@ -379,7 +384,10 @@ func TestNodeModuleVariable_warningDiags(t *testing.T) {
|
||||
Summary: "Object attribute is ignored",
|
||||
Detail: `The object type for input variable "foo" nested value .bar does not include an attribute named "beep", so this definition is unused.`,
|
||||
Subject: &hcl.Range{
|
||||
Filename: "test.tofu",
|
||||
Filename: "test2.tofu", // from synthetic source range in constructed expression above
|
||||
},
|
||||
Context: &hcl.Range{
|
||||
Filename: "context2.tofu", // from synthetic source range in constructed expression above
|
||||
},
|
||||
})
|
||||
wantDiags = wantDiags.ForRPC()
|
||||
|
||||
Reference in New Issue
Block a user