@@ -5,8 +5,10 @@ import (
55
66 "github.com/hashicorp/hcl/v2"
77 "github.com/hashicorp/hcl/v2/hclsyntax"
8+ "github.com/hashicorp/hcl/v2/json"
89 "github.com/terraform-linters/tflint-plugin-sdk/hclext"
910 "github.com/terraform-linters/tflint-plugin-sdk/tflint"
11+ "github.com/zclconf/go-cty/cty"
1012)
1113
1214// 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) {
243245 }
244246
245247 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+ }
249265 return nil
250- }
251- providerRefs [parts [1 ]] = & ProviderRef {
252- Name : parts [1 ],
253- DefRange : expr .Range (),
254- }
266+ })
267+ diags = diags .Extend (visitDiags )
255268 }
256- return nil
269+ return diags
257270 }))
258271 diags = diags .Extend (walkDiags )
259272 if walkDiags .HasErrors () {
@@ -262,3 +275,62 @@ func (r *Runner) GetProviderRefs() (map[string]*ProviderRef, hcl.Diagnostics) {
262275
263276 return providerRefs , diags
264277}
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