github.com/arvindram03/terraform@v0.3.7-0.20150212015210-408f838db36d/config/lang/check_types.go (about) 1 package lang 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/hashicorp/terraform/config/lang/ast" 8 ) 9 10 // TypeCheck implements ast.Visitor for type checking an AST tree. 11 // It requires some configuration to look up the type of nodes. 12 // 13 // It also optionally will not type error and will insert an implicit 14 // type conversions for specific types if specified by the Implicit 15 // field. Note that this is kind of organizationally weird to put into 16 // this structure but we'd rather do that than duplicate the type checking 17 // logic multiple times. 18 type TypeCheck struct { 19 Scope ast.Scope 20 21 // Implicit is a map of implicit type conversions that we can do, 22 // and that shouldn't error. The key of the first map is the from type, 23 // the key of the second map is the to type, and the final string 24 // value is the function to call (which must be registered in the Scope). 25 Implicit map[ast.Type]map[ast.Type]string 26 27 // Stack of types. This shouldn't be used directly except by implementations 28 // of TypeCheckNode. 29 Stack []ast.Type 30 31 err error 32 lock sync.Mutex 33 } 34 35 // TypeCheckNode is the interface that must be implemented by any 36 // ast.Node that wants to support type-checking. If the type checker 37 // encounters a node that doesn't implement this, it will error. 38 type TypeCheckNode interface { 39 TypeCheck(*TypeCheck) (ast.Node, error) 40 } 41 42 func (v *TypeCheck) Visit(root ast.Node) error { 43 v.lock.Lock() 44 defer v.lock.Unlock() 45 defer v.reset() 46 root.Accept(v.visit) 47 return v.err 48 } 49 50 func (v *TypeCheck) visit(raw ast.Node) ast.Node { 51 if v.err != nil { 52 return raw 53 } 54 55 var result ast.Node 56 var err error 57 switch n := raw.(type) { 58 case *ast.Call: 59 tc := &typeCheckCall{n} 60 result, err = tc.TypeCheck(v) 61 case *ast.Concat: 62 tc := &typeCheckConcat{n} 63 result, err = tc.TypeCheck(v) 64 case *ast.LiteralNode: 65 tc := &typeCheckLiteral{n} 66 result, err = tc.TypeCheck(v) 67 case *ast.VariableAccess: 68 tc := &typeCheckVariableAccess{n} 69 result, err = tc.TypeCheck(v) 70 default: 71 tc, ok := raw.(TypeCheckNode) 72 if !ok { 73 err = fmt.Errorf("unknown node: %#v", raw) 74 break 75 } 76 77 result, err = tc.TypeCheck(v) 78 } 79 80 if err != nil { 81 pos := raw.Pos() 82 v.err = fmt.Errorf("At column %d, line %d: %s", 83 pos.Column, pos.Line, err) 84 } 85 86 return result 87 } 88 89 type typeCheckCall struct { 90 n *ast.Call 91 } 92 93 func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) { 94 // Look up the function in the map 95 function, ok := v.Scope.LookupFunc(tc.n.Func) 96 if !ok { 97 return nil, fmt.Errorf("unknown function called: %s", tc.n.Func) 98 } 99 100 // The arguments are on the stack in reverse order, so pop them off. 101 args := make([]ast.Type, len(tc.n.Args)) 102 for i, _ := range tc.n.Args { 103 args[len(tc.n.Args)-1-i] = v.StackPop() 104 } 105 106 // Verify the args 107 for i, expected := range function.ArgTypes { 108 if args[i] != expected { 109 cn := v.ImplicitConversion(args[i], expected, tc.n.Args[i]) 110 if cn != nil { 111 tc.n.Args[i] = cn 112 continue 113 } 114 115 return nil, fmt.Errorf( 116 "%s: argument %d should be %s, got %s", 117 tc.n.Func, i+1, expected, args[i]) 118 } 119 } 120 121 // If we're variadic, then verify the types there 122 if function.Variadic { 123 args = args[len(function.ArgTypes):] 124 for i, t := range args { 125 if t != function.VariadicType { 126 realI := i + len(function.ArgTypes) 127 cn := v.ImplicitConversion( 128 t, function.VariadicType, tc.n.Args[realI]) 129 if cn != nil { 130 tc.n.Args[realI] = cn 131 continue 132 } 133 134 return nil, fmt.Errorf( 135 "%s: argument %d should be %s, got %s", 136 tc.n.Func, realI, 137 function.VariadicType, t) 138 } 139 } 140 } 141 142 // Return type 143 v.StackPush(function.ReturnType) 144 145 return tc.n, nil 146 } 147 148 type typeCheckConcat struct { 149 n *ast.Concat 150 } 151 152 func (tc *typeCheckConcat) TypeCheck(v *TypeCheck) (ast.Node, error) { 153 n := tc.n 154 types := make([]ast.Type, len(n.Exprs)) 155 for i, _ := range n.Exprs { 156 types[len(n.Exprs)-1-i] = v.StackPop() 157 } 158 159 // All concat args must be strings, so validate that 160 for i, t := range types { 161 if t != ast.TypeString { 162 cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i]) 163 if cn != nil { 164 n.Exprs[i] = cn 165 continue 166 } 167 168 return nil, fmt.Errorf( 169 "argument %d must be a string", i+1) 170 } 171 } 172 173 // This always results in type string 174 v.StackPush(ast.TypeString) 175 176 return n, nil 177 } 178 179 type typeCheckLiteral struct { 180 n *ast.LiteralNode 181 } 182 183 func (tc *typeCheckLiteral) TypeCheck(v *TypeCheck) (ast.Node, error) { 184 v.StackPush(tc.n.Typex) 185 return tc.n, nil 186 } 187 188 type typeCheckVariableAccess struct { 189 n *ast.VariableAccess 190 } 191 192 func (tc *typeCheckVariableAccess) TypeCheck(v *TypeCheck) (ast.Node, error) { 193 // Look up the variable in the map 194 variable, ok := v.Scope.LookupVar(tc.n.Name) 195 if !ok { 196 return nil, fmt.Errorf( 197 "unknown variable accessed: %s", tc.n.Name) 198 } 199 200 // Add the type to the stack 201 v.StackPush(variable.Type) 202 203 return tc.n, nil 204 } 205 206 func (v *TypeCheck) ImplicitConversion( 207 actual ast.Type, expected ast.Type, n ast.Node) ast.Node { 208 if v.Implicit == nil { 209 return nil 210 } 211 212 fromMap, ok := v.Implicit[actual] 213 if !ok { 214 return nil 215 } 216 217 toFunc, ok := fromMap[expected] 218 if !ok { 219 return nil 220 } 221 222 return &ast.Call{ 223 Func: toFunc, 224 Args: []ast.Node{n}, 225 Posx: n.Pos(), 226 } 227 } 228 229 func (v *TypeCheck) reset() { 230 v.Stack = nil 231 v.err = nil 232 } 233 234 func (v *TypeCheck) StackPush(t ast.Type) { 235 v.Stack = append(v.Stack, t) 236 } 237 238 func (v *TypeCheck) StackPop() ast.Type { 239 var x ast.Type 240 x, v.Stack = v.Stack[len(v.Stack)-1], v.Stack[:len(v.Stack)-1] 241 return x 242 }