@@ -5,8 +5,10 @@ import (
5
5
6
6
"github.com/hashicorp/hcl/v2"
7
7
"github.com/hashicorp/hcl/v2/hclsyntax"
8
+ "github.com/hashicorp/hcl/v2/json"
8
9
"github.com/terraform-linters/tflint-plugin-sdk/hclext"
9
10
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
11
+ "github.com/zclconf/go-cty/cty"
10
12
)
11
13
12
14
// Runner is a custom runner that provides helper functions for this ruleset.
@@ -243,17 +245,28 @@ func (r *Runner) GetProviderRefs() (map[string]*ProviderRef, hcl.Diagnostics) {
243
245
}
244
246
245
247
walkDiags := r .WalkExpressions (tflint .ExprWalkFunc (func (expr hcl.Expression ) hcl.Diagnostics {
246
- if fce , ok := expr .(* hclsyntax.FunctionCallExpr ); ok {
247
- parts := strings .Split (fce .Name , "::" )
248
- if len (parts ) < 2 || parts [0 ] != "provider" || parts [1 ] == "" {
248
+ // For JSON syntax, walker is not implemented,
249
+ // so extract the hclsyntax.Node that we can walk on.
250
+ // See https://github.com/hashicorp/hcl/issues/543
251
+ nodes , diags := r .walkableNodesInExpr (expr )
252
+
253
+ for _ , node := range nodes {
254
+ visitDiags := hclsyntax .VisitAll (node , func (n hclsyntax.Node ) hcl.Diagnostics {
255
+ if funcCallExpr , ok := n .(* hclsyntax.FunctionCallExpr ); ok {
256
+ parts := strings .Split (funcCallExpr .Name , "::" )
257
+ if len (parts ) < 2 || parts [0 ] != "provider" || parts [1 ] == "" {
258
+ return nil
259
+ }
260
+ providerRefs [parts [1 ]] = & ProviderRef {
261
+ Name : parts [1 ],
262
+ DefRange : funcCallExpr .Range (),
263
+ }
264
+ }
249
265
return nil
250
- }
251
- providerRefs [parts [1 ]] = & ProviderRef {
252
- Name : parts [1 ],
253
- DefRange : expr .Range (),
254
- }
266
+ })
267
+ diags = diags .Extend (visitDiags )
255
268
}
256
- return nil
269
+ return diags
257
270
}))
258
271
diags = diags .Extend (walkDiags )
259
272
if walkDiags .HasErrors () {
@@ -262,3 +275,62 @@ func (r *Runner) GetProviderRefs() (map[string]*ProviderRef, hcl.Diagnostics) {
262
275
263
276
return providerRefs , diags
264
277
}
278
+
279
+ // walkableNodesInExpr returns hclsyntax.Node from the given expression.
280
+ // If the expression is an hclsyntax expression, it is returned as is.
281
+ // If the expression is a JSON expression, it is parsed and
282
+ // hclsyntax.Node it contains is returned.
283
+ func (r * Runner ) walkableNodesInExpr (expr hcl.Expression ) ([]hclsyntax.Node , hcl.Diagnostics ) {
284
+ nodes := []hclsyntax.Node {}
285
+
286
+ expr = hcl .UnwrapExpressionUntil (expr , func (expr hcl.Expression ) bool {
287
+ _ , native := expr .(hclsyntax.Expression )
288
+ return native || json .IsJSONExpression (expr )
289
+ })
290
+ if expr == nil {
291
+ return nil , nil
292
+ }
293
+
294
+ if json .IsJSONExpression (expr ) {
295
+ // HACK: For JSON expressions, we can get the JSON value as a literal
296
+ // without any prior HCL parsing by evaluating it in a nil context.
297
+ // We can take advantage of this property to walk through cty.Value
298
+ // that may contain HCL expressions instead of walking through
299
+ // expression nodes directly.
300
+ // See https://github.com/hashicorp/hcl/issues/642
301
+ val , diags := expr .Value (nil )
302
+ if diags .HasErrors () {
303
+ return nodes , diags
304
+ }
305
+
306
+ err := cty .Walk (val , func (path cty.Path , v cty.Value ) (bool , error ) {
307
+ if v .Type () != cty .String || v .IsNull () || ! v .IsKnown () {
308
+ return true , nil
309
+ }
310
+
311
+ node , parseDiags := hclsyntax .ParseTemplate ([]byte (v .AsString ()), expr .Range ().Filename , expr .Range ().Start )
312
+ if diags .HasErrors () {
313
+ diags = diags .Extend (parseDiags )
314
+ return true , nil
315
+ }
316
+
317
+ nodes = append (nodes , node )
318
+ return true , nil
319
+ })
320
+ if err != nil {
321
+ return nodes , hcl.Diagnostics {{
322
+ Severity : hcl .DiagError ,
323
+ Summary : "Failed to walk the expression value" ,
324
+ Detail : err .Error (),
325
+ Subject : expr .Range ().Ptr (),
326
+ }}
327
+ }
328
+
329
+ return nodes , diags
330
+ }
331
+
332
+ // The JSON syntax is already processed, so it's guaranteed to be native syntax.
333
+ nodes = append (nodes , expr .(hclsyntax.Expression ))
334
+
335
+ return nodes , nil
336
+ }
0 commit comments