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
5package types
6
7import (
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.
24func 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//
56func 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