Skip to content

Commit 763daec

Browse files
authored
Emit issues based on expression types (#90)
1 parent 70051da commit 763daec

File tree

2 files changed

+119
-51
lines changed

2 files changed

+119
-51
lines changed
Lines changed: 47 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package rules
22

33
import (
4+
"strings"
5+
46
"github.com/hashicorp/hcl/v2"
57
"github.com/hashicorp/hcl/v2/hclsyntax"
68
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
@@ -53,61 +55,60 @@ func (r *TerraformDeprecatedIndexRule) Check(runner tflint.Runner) error {
5355
return err
5456
}
5557

56-
diags := runner.WalkExpressions(tflint.ExprWalkFunc(func(expr hcl.Expression) hcl.Diagnostics {
57-
for _, variable := range expr.Variables() {
58-
filename := expr.Range().Filename
59-
file := files[filename]
60-
61-
bytes := expr.Range().SliceBytes(file.Bytes)
62-
63-
tokens, diags := hclsyntax.LexExpression(bytes, filename, variable.SourceRange().Start)
64-
if diags.HasErrors() {
65-
// HACK: If the expression cannot be lexed, try to lex it as a template.
66-
// If it still cannot be lexed, return the original error.
67-
tTokens, tDiags := hclsyntax.LexTemplate(bytes, filename, variable.SourceRange().Start)
68-
if tDiags.HasErrors() {
69-
return diags
70-
}
71-
72-
tokens = tTokens
73-
}
74-
75-
tokens = tokens[1:]
76-
77-
for i, token := range tokens {
78-
if token.Type == hclsyntax.TokenDot {
79-
if len(tokens) == i+1 {
80-
return nil
81-
}
82-
83-
next := tokens[i+1].Type
84-
if next == hclsyntax.TokenNumberLit || next == hclsyntax.TokenStar {
85-
if tokens[0].Type == hclsyntax.TokenDot {
86-
if err := runner.EmitIssue(
87-
r,
88-
"List items should be accessed using square brackets",
89-
expr.Range(),
90-
); err != nil {
91-
return hcl.Diagnostics{
92-
{
93-
Severity: hcl.DiagError,
94-
Summary: "failed to call EmitIssue()",
95-
Detail: err.Error(),
96-
},
97-
}
98-
}
99-
}
58+
diags := runner.WalkExpressions(tflint.ExprWalkFunc(func(e hcl.Expression) hcl.Diagnostics {
59+
filename := e.Range().Filename
60+
file := files[filename]
61+
62+
switch expr := e.(type) {
63+
case *hclsyntax.ScopeTraversalExpr:
64+
r.checkLegacyTraversalIndex(runner, expr.Traversal, file.Bytes)
65+
case *hclsyntax.RelativeTraversalExpr:
66+
r.checkLegacyTraversalIndex(runner, expr.Traversal, file.Bytes)
67+
case *hclsyntax.SplatExpr:
68+
if strings.HasPrefix(string(expr.MarkerRange.SliceBytes(file.Bytes)), ".") {
69+
if err := runner.EmitIssue(
70+
r,
71+
"List items should be accessed using square brackets",
72+
expr.MarkerRange,
73+
); err != nil {
74+
return hcl.Diagnostics{
75+
{
76+
Severity: hcl.DiagError,
77+
Summary: "failed to call EmitIssue()",
78+
Detail: err.Error(),
79+
},
10080
}
10181
}
10282
}
10383
}
104-
10584
return nil
10685
}))
107-
10886
if diags.HasErrors() {
10987
return diags
11088
}
11189

11290
return nil
11391
}
92+
93+
func (r *TerraformDeprecatedIndexRule) checkLegacyTraversalIndex(runner tflint.Runner, traversal hcl.Traversal, file []byte) hcl.Diagnostics {
94+
for _, t := range traversal {
95+
if _, ok := t.(hcl.TraverseIndex); ok {
96+
if strings.HasPrefix(string(t.SourceRange().SliceBytes(file)), ".") {
97+
if err := runner.EmitIssue(
98+
r,
99+
"List items should be accessed using square brackets",
100+
t.SourceRange(),
101+
); err != nil {
102+
return hcl.Diagnostics{
103+
{
104+
Severity: hcl.DiagError,
105+
Summary: "failed to call EmitIssue()",
106+
Detail: err.Error(),
107+
},
108+
}
109+
}
110+
}
111+
}
112+
}
113+
return nil
114+
}

rules/terraform_deprecated_index_test.go

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ locals {
2929
Filename: "config.tf",
3030
Start: hcl.Pos{
3131
Line: 4,
32-
Column: 11,
32+
Column: 15,
3333
},
3434
End: hcl.Pos{
3535
Line: 4,
@@ -55,11 +55,11 @@ locals {
5555
Filename: "config.tf",
5656
Start: hcl.Pos{
5757
Line: 4,
58-
Column: 12,
58+
Column: 19,
5959
},
6060
End: hcl.Pos{
6161
Line: 4,
62-
Column: 23,
62+
Column: 21,
6363
},
6464
},
6565
},
@@ -116,11 +116,78 @@ EOF
116116
Filename: "config.tf",
117117
Start: hcl.Pos{
118118
Line: 4,
119-
Column: 16,
119+
Column: 36,
120120
},
121121
End: hcl.Pos{
122122
Line: 4,
123-
Column: 49,
123+
Column: 38,
124+
},
125+
},
126+
},
127+
},
128+
},
129+
{
130+
Name: "legacy splat and legacy index",
131+
Content: `
132+
locals {
133+
nested_list = [["a"]]
134+
value = nested_list.*.0
135+
}
136+
`,
137+
Expected: helper.Issues{
138+
{
139+
Rule: NewTerraformDeprecatedIndexRule(),
140+
Message: "List items should be accessed using square brackets",
141+
Range: hcl.Range{
142+
Filename: "config.tf",
143+
Start: hcl.Pos{
144+
Line: 4,
145+
Column: 22,
146+
},
147+
End: hcl.Pos{
148+
Line: 4,
149+
Column: 24,
150+
},
151+
},
152+
},
153+
{
154+
Rule: NewTerraformDeprecatedIndexRule(),
155+
Message: "List items should be accessed using square brackets",
156+
Range: hcl.Range{
157+
Filename: "config.tf",
158+
Start: hcl.Pos{
159+
Line: 4,
160+
Column: 24,
161+
},
162+
End: hcl.Pos{
163+
Line: 4,
164+
Column: 26,
165+
},
166+
},
167+
},
168+
},
169+
},
170+
{
171+
Name: "complex expression",
172+
Content: `
173+
locals {
174+
create_namespace = true
175+
kubernetes_namespace = local.create_namespace ? join("", kubernetes_namespace.default.*.id) : var.kubernetes_namespace
176+
}
177+
`,
178+
Expected: helper.Issues{
179+
{
180+
Rule: NewTerraformDeprecatedIndexRule(),
181+
Message: "List items should be accessed using square brackets",
182+
Range: hcl.Range{
183+
Filename: "config.tf",
184+
Start: hcl.Pos{
185+
Line: 4,
186+
Column: 88,
187+
},
188+
End: hcl.Pos{
189+
Line: 4,
190+
Column: 90,
124191
},
125192
},
126193
},

0 commit comments

Comments
 (0)