github.com/hashicorp/hcl/v2@v2.20.0/hclsyntax/walk_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package hclsyntax 5 6 import ( 7 "fmt" 8 "reflect" 9 "testing" 10 11 "github.com/davecgh/go-spew/spew" 12 "github.com/go-test/deep" 13 14 "github.com/hashicorp/hcl/v2" 15 ) 16 17 func TestWalk(t *testing.T) { 18 19 tests := []struct { 20 src string 21 want []testWalkCall 22 }{ 23 { 24 `1`, 25 []testWalkCall{ 26 {testWalkEnter, "*hclsyntax.LiteralValueExpr"}, 27 {testWalkExit, "*hclsyntax.LiteralValueExpr"}, 28 }, 29 }, 30 { 31 `foo`, 32 []testWalkCall{ 33 {testWalkEnter, "*hclsyntax.ScopeTraversalExpr"}, 34 {testWalkExit, "*hclsyntax.ScopeTraversalExpr"}, 35 }, 36 }, 37 { 38 `1 + 1`, 39 []testWalkCall{ 40 {testWalkEnter, "*hclsyntax.BinaryOpExpr"}, 41 {testWalkEnter, "*hclsyntax.LiteralValueExpr"}, 42 {testWalkExit, "*hclsyntax.LiteralValueExpr"}, 43 {testWalkEnter, "*hclsyntax.LiteralValueExpr"}, 44 {testWalkExit, "*hclsyntax.LiteralValueExpr"}, 45 {testWalkExit, "*hclsyntax.BinaryOpExpr"}, 46 }, 47 }, 48 { 49 `(1 + 1)`, 50 []testWalkCall{ 51 {testWalkEnter, "*hclsyntax.ParenthesesExpr"}, 52 {testWalkEnter, "*hclsyntax.BinaryOpExpr"}, 53 {testWalkEnter, "*hclsyntax.LiteralValueExpr"}, 54 {testWalkExit, "*hclsyntax.LiteralValueExpr"}, 55 {testWalkEnter, "*hclsyntax.LiteralValueExpr"}, 56 {testWalkExit, "*hclsyntax.LiteralValueExpr"}, 57 {testWalkExit, "*hclsyntax.BinaryOpExpr"}, 58 {testWalkExit, "*hclsyntax.ParenthesesExpr"}, 59 }, 60 }, 61 { 62 `a[0]`, 63 []testWalkCall{ 64 // because the index is constant here, the index is absorbed into the traversal 65 {testWalkEnter, "*hclsyntax.ScopeTraversalExpr"}, 66 {testWalkExit, "*hclsyntax.ScopeTraversalExpr"}, 67 }, 68 }, 69 { 70 `0[foo]`, // semantically incorrect, but should still parse and be walkable 71 []testWalkCall{ 72 {testWalkEnter, "*hclsyntax.IndexExpr"}, 73 {testWalkEnter, "*hclsyntax.LiteralValueExpr"}, 74 {testWalkExit, "*hclsyntax.LiteralValueExpr"}, 75 {testWalkEnter, "*hclsyntax.ScopeTraversalExpr"}, 76 {testWalkExit, "*hclsyntax.ScopeTraversalExpr"}, 77 {testWalkExit, "*hclsyntax.IndexExpr"}, 78 }, 79 }, 80 { 81 `bar()`, 82 []testWalkCall{ 83 {testWalkEnter, "*hclsyntax.FunctionCallExpr"}, 84 {testWalkExit, "*hclsyntax.FunctionCallExpr"}, 85 }, 86 }, 87 { 88 `bar(1, a)`, 89 []testWalkCall{ 90 {testWalkEnter, "*hclsyntax.FunctionCallExpr"}, 91 {testWalkEnter, "*hclsyntax.LiteralValueExpr"}, 92 {testWalkExit, "*hclsyntax.LiteralValueExpr"}, 93 {testWalkEnter, "*hclsyntax.ScopeTraversalExpr"}, 94 {testWalkExit, "*hclsyntax.ScopeTraversalExpr"}, 95 {testWalkExit, "*hclsyntax.FunctionCallExpr"}, 96 }, 97 }, 98 { 99 `bar(1, a)[0]`, 100 []testWalkCall{ 101 {testWalkEnter, "*hclsyntax.RelativeTraversalExpr"}, 102 {testWalkEnter, "*hclsyntax.FunctionCallExpr"}, 103 {testWalkEnter, "*hclsyntax.LiteralValueExpr"}, 104 {testWalkExit, "*hclsyntax.LiteralValueExpr"}, 105 {testWalkEnter, "*hclsyntax.ScopeTraversalExpr"}, 106 {testWalkExit, "*hclsyntax.ScopeTraversalExpr"}, 107 {testWalkExit, "*hclsyntax.FunctionCallExpr"}, 108 {testWalkExit, "*hclsyntax.RelativeTraversalExpr"}, 109 }, 110 }, 111 { 112 `[for x in foo: x + 1 if x < 10]`, 113 []testWalkCall{ 114 {testWalkEnter, "*hclsyntax.ForExpr"}, 115 {testWalkEnter, "*hclsyntax.ScopeTraversalExpr"}, 116 {testWalkExit, "*hclsyntax.ScopeTraversalExpr"}, 117 {testWalkEnter, "hclsyntax.ChildScope"}, 118 {testWalkEnter, "*hclsyntax.BinaryOpExpr"}, 119 {testWalkEnter, "*hclsyntax.ScopeTraversalExpr"}, 120 {testWalkExit, "*hclsyntax.ScopeTraversalExpr"}, 121 {testWalkEnter, "*hclsyntax.LiteralValueExpr"}, 122 {testWalkExit, "*hclsyntax.LiteralValueExpr"}, 123 {testWalkExit, "*hclsyntax.BinaryOpExpr"}, 124 {testWalkExit, "hclsyntax.ChildScope"}, 125 {testWalkEnter, "hclsyntax.ChildScope"}, 126 {testWalkEnter, "*hclsyntax.BinaryOpExpr"}, 127 {testWalkEnter, "*hclsyntax.ScopeTraversalExpr"}, 128 {testWalkExit, "*hclsyntax.ScopeTraversalExpr"}, 129 {testWalkEnter, "*hclsyntax.LiteralValueExpr"}, 130 {testWalkExit, "*hclsyntax.LiteralValueExpr"}, 131 {testWalkExit, "*hclsyntax.BinaryOpExpr"}, 132 {testWalkExit, "hclsyntax.ChildScope"}, 133 {testWalkExit, "*hclsyntax.ForExpr"}, 134 }, 135 }, 136 } 137 138 for _, test := range tests { 139 t.Run(test.src, func(t *testing.T) { 140 expr, diags := ParseExpression([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1}) 141 if diags.HasErrors() { 142 t.Fatalf("failed to parse expression: %s", diags.Error()) 143 } 144 145 w := testWalker{} 146 diags = Walk(expr, &w) 147 if diags.HasErrors() { 148 t.Fatalf("failed to walk: %s", diags.Error()) 149 } 150 151 got := w.Calls 152 if !reflect.DeepEqual(got, test.want) { 153 t.Errorf("wrong calls\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(test.want)) 154 for _, problem := range deep.Equal(got, test.want) { 155 t.Errorf(problem) 156 } 157 } 158 }) 159 } 160 } 161 162 type testWalkMethod int 163 164 const testWalkEnter testWalkMethod = 1 165 const testWalkExit testWalkMethod = 2 166 167 type testWalkCall struct { 168 Method testWalkMethod 169 NodeType string 170 } 171 172 type testWalker struct { 173 Calls []testWalkCall 174 } 175 176 func (w *testWalker) Enter(node Node) hcl.Diagnostics { 177 w.Calls = append(w.Calls, testWalkCall{testWalkEnter, fmt.Sprintf("%T", node)}) 178 return nil 179 } 180 181 func (w *testWalker) Exit(node Node) hcl.Diagnostics { 182 w.Calls = append(w.Calls, testWalkCall{testWalkExit, fmt.Sprintf("%T", node)}) 183 return nil 184 }