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 position
39 // pos of package pkg. [Type] information about the expression is recorded in
40 // info. The expression may be an identifier denoting an uninstantiated generic
41 // function or type.
42 //
43 // If pkg == nil, the [Universe] scope is used and the provided
44 // position pos is ignored. If pkg != nil, and pos is invalid,
45 // the package scope is used. Otherwise, pos must belong to the
46 // package.
47 //
48 // An error is returned if pos is not within the package or
49 // if the node cannot be type-checked.
50 //
51 // Note: [Eval] and CheckExpr should not be used instead of running Check
52 // to compute types and values, but in addition to Check, as these
53 // functions ignore the context in which an expression is used (e.g., an
54 // assignment). Thus, top-level untyped constants will return an
55 // untyped type rather than the respective context-specific type.
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 = 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.exprPos = pos
90 defer check.handleBailout(&err)
91 92 // evaluate node
93 var x operand
94 check.rawExpr(nil, &x, expr, nil, true) // allow generic expressions
95 check.processDelayed(0) // incl. all functions
96 check.recordUntyped()
97 98 return nil
99 }
100