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  }