1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package types 6 7 import ( 8 "fmt" 9 "go/ast" 10 "go/parser" 11 "go/token" 12 ) 13 14 // Eval returns the type and, if constant, the value for the 15 // expression expr, evaluated at position pos of package pkg, 16 // which must have been derived from type-checking an AST with 17 // complete position information relative to the provided file 18 // set. 19 // 20 // The meaning of the parameters fset, pkg, and pos is the 21 // same as in CheckExpr. An error is returned if expr cannot 22 // be parsed successfully, or the resulting expr AST cannot be 23 // type-checked. 24 func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (_ TypeAndValue, err error) { 25 // parse expressions 26 node, err := parser.ParseExprFrom(fset, "eval", expr, 0) 27 if err != nil { 28 return TypeAndValue{}, err 29 } 30 31 info := &Info{ 32 Types: make(map[ast.Expr]TypeAndValue), 33 } 34 err = CheckExpr(fset, pkg, pos, node, info) 35 return info.Types[node], err 36 } 37 38 // CheckExpr type checks the expression expr as if it had appeared at 39 // position pos of package pkg. Type information about the expression 40 // is recorded in info. 41 // 42 // If pkg == nil, the Universe scope is used and the provided 43 // position pos is ignored. If pkg != nil, and pos is invalid, 44 // the package scope is used. Otherwise, pos must belong to the 45 // package. 46 // 47 // An error is returned if pos is not within the package or 48 // if the node cannot be type-checked. 49 // 50 // Note: Eval and CheckExpr should not be used instead of running Check 51 // to compute types and values, but in addition to Check, as these 52 // functions ignore the context in which an expression is used (e.g., an 53 // assignment). Thus, top-level untyped constants will return an 54 // untyped type rather then the respective context-specific type. 55 // 56 func CheckExpr(fset *token.FileSet, pkg *Package, pos token.Pos, expr ast.Expr, info *Info) (err error) { 57 // determine scope 58 var scope *Scope 59 if pkg == nil { 60 scope = Universe 61 pos = token.NoPos 62 } else if !pos.IsValid() { 63 scope = pkg.scope 64 } else { 65 // The package scope extent (position information) may be 66 // incorrect (files spread across a wide range of fset 67 // positions) - ignore it and just consider its children 68 // (file scopes). 69 for _, fscope := range pkg.scope.children { 70 if scope = fscope.Innermost(pos); scope != nil { 71 break 72 } 73 } 74 if scope == nil || debug { 75 s := scope 76 for s != nil && s != pkg.scope { 77 s = s.parent 78 } 79 // s == nil || s == pkg.scope 80 if s == nil { 81 return fmt.Errorf("no position %s found in package %s", fset.Position(pos), pkg.name) 82 } 83 } 84 } 85 86 // initialize checker 87 check := NewChecker(nil, fset, pkg, info) 88 check.scope = scope 89 check.pos = pos 90 defer check.handleBailout(&err) 91 92 // evaluate node 93 var x operand 94 check.rawExpr(&x, expr, nil) 95 check.processDelayed(0) // incl. all functions 96 check.recordUntyped() 97 98 return nil 99 } 100