예제 #1
0
파일: scope.go 프로젝트: tcard/sgo
// LookupParent follows the parent chain of scopes starting with s until
// it finds a scope where Lookup(name) returns a non-nil object, and then
// returns that scope and object. If a valid position pos is provided,
// only objects that were declared at or before pos are considered.
// If no such scope and object exists, the result is (nil, nil).
//
// Note that obj.Parent() may be different from the returned scope if the
// object was inserted into the scope and already had a parent at that
// time (see Insert, below). This can only happen for dot-imported objects
// whose scope is the scope of the package that exported them.
func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) {
	for ; s != nil; s = s.parent {
		if obj := s.elems[name]; obj != nil && (!pos.IsValid() || obj.scopePos() <= pos) {
			return s, obj
		}
	}
	return nil, nil
}
예제 #2
0
파일: nodes.go 프로젝트: tcard/sgo
// distanceFrom returns the column difference between from and p.pos (the current
// estimated position) if both are on the same line; if they are on different lines
// (or unknown) the result is infinity.
func (p *printer) distanceFrom(from token.Pos) int {
	if from.IsValid() && p.pos.IsValid() {
		if f := p.posFor(from); f.Line == p.pos.Line {
			return p.pos.Column - f.Column
		}
	}
	return infinity
}
예제 #3
0
파일: eval.go 프로젝트: tcard/sgo
// Eval returns the type and, if constant, the value for the
// expression expr, evaluated at position pos of package pkg,
// which must have been derived from type-checking an AST with
// complete position information relative to the provided file
// set.
//
// If the expression contains function literals, their bodies
// are ignored (i.e., the bodies are not type-checked).
//
// If pkg == nil, the Universe scope is used and the provided
// position pos is ignored. If pkg != nil, and pos is invalid,
// the package scope is used. Otherwise, pos must belong to the
// package.
//
// An error is returned if pos is not within the package or
// if the node cannot be evaluated.
//
// Note: Eval should not be used instead of running Check to compute
// types and values, but in addition to Check. Eval will re-evaluate
// its argument each time, and it also does not know about the context
// in which an expression is used (e.g., an assignment). Thus, top-
// level untyped constants will return an untyped type rather then the
// respective context-specific type.
//
func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (TypeAndValue, error) {
	// determine scope
	var scope *Scope
	if pkg == nil {
		scope = Universe
		pos = token.NoPos
	} else if !pos.IsValid() {
		scope = pkg.scope
	} else {
		// The package scope extent (position information) may be
		// incorrect (files spread across a wide range of fset
		// positions) - ignore it and just consider its children
		// (file scopes).
		for _, fscope := range pkg.scope.children {
			if scope = fscope.Innermost(pos); scope != nil {
				break
			}
		}
		if scope == nil || debug {
			s := scope
			for s != nil && s != pkg.scope {
				s = s.parent
			}
			// s == nil || s == pkg.scope
			if s == nil {
				return TypeAndValue{}, fmt.Errorf("no position %s found in package %s", fset.Position(pos), pkg.name)
			}
		}
	}

	// parse expressions
	node, err := parser.ParseExprFrom(fset, "eval", expr, 0)
	if err != nil {
		return TypeAndValue{}, err
	}

	// initialize checker
	check := NewChecker(nil, fset, pkg, nil)
	check.scope = scope
	check.pos = pos
	defer check.handleBailout(&err)

	// evaluate node
	var x operand
	check.rawExpr(&x, node, nil)
	return TypeAndValue{x.mode, x.typ, x.val}, err
}
예제 #4
0
파일: call.go 프로젝트: tcard/sgo
// argument checks passing of argument x to the i'th parameter of the given signature.
// If ellipsis is valid, the argument is followed by ... at that position in the call.
func (check *Checker) argument(fun ast.Expr, sig *Signature, i int, x *operand, ellipsis token.Pos) {
	check.singleValue(x)
	if x.mode == invalid {
		return
	}

	n := sig.params.Len()

	// determine parameter type
	var typ Type
	switch {
	case i < n:
		typ = sig.params.vars[i].typ
	case sig.variadic:
		typ = sig.params.vars[n-1].typ
		if debug {
			if _, ok := typ.(*Slice); !ok {
				check.dump("%s: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), typ)
			}
		}
	default:
		check.errorf(x.pos(), "too many arguments")
		return
	}

	if ellipsis.IsValid() {
		// argument is of the form x... and x is single-valued
		if i != n-1 {
			check.errorf(ellipsis, "can only use ... with matching parameter")
			return
		}
		if _, ok := x.typ.Underlying().(*Slice); !ok {
			check.errorf(x.pos(), "cannot use %s as parameter of type %s", x, typ)
			return
		}
	} else if sig.variadic && i >= n-1 {
		// use the variadic parameter slice's element type
		typ = typ.(*Slice).elem
	}

	check.assignment(x, typ, check.sprintf("argument to %s", fun))
}
예제 #5
0
파일: labels.go 프로젝트: tcard/sgo
// blockBranches processes a block's statement list and returns the set of outgoing forward jumps.
// all is the scope of all declared labels, parent the set of labels declared in the immediately
// enclosing block, and lstmt is the labeled statement this block is associated with (or nil).
func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *ast.LabeledStmt, list []ast.Stmt) []*ast.BranchStmt {
	b := &block{parent: parent, lstmt: lstmt}

	var (
		varDeclPos         token.Pos
		fwdJumps, badJumps []*ast.BranchStmt
	)

	// All forward jumps jumping over a variable declaration are possibly
	// invalid (they may still jump out of the block and be ok).
	// recordVarDecl records them for the given position.
	recordVarDecl := func(pos token.Pos) {
		varDeclPos = pos
		badJumps = append(badJumps[:0], fwdJumps...) // copy fwdJumps to badJumps
	}

	jumpsOverVarDecl := func(jmp *ast.BranchStmt) bool {
		if varDeclPos.IsValid() {
			for _, bad := range badJumps {
				if jmp == bad {
					return true
				}
			}
		}
		return false
	}

	blockBranches := func(lstmt *ast.LabeledStmt, list []ast.Stmt) {
		// Unresolved forward jumps inside the nested block
		// become forward jumps in the current block.
		fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, list)...)
	}

	var stmtBranches func(ast.Stmt)
	stmtBranches = func(s ast.Stmt) {
		switch s := s.(type) {
		case *ast.DeclStmt:
			if d, _ := s.Decl.(*ast.GenDecl); d != nil && d.Tok == token.VAR {
				recordVarDecl(d.Pos())
			}

		case *ast.LabeledStmt:
			// declare non-blank label
			if name := s.Label.Name; name != "_" {
				lbl := NewLabel(s.Label.Pos(), check.pkg, name)
				if alt := all.Insert(lbl); alt != nil {
					check.softErrorf(lbl.pos, "label %s already declared", name)
					check.reportAltDecl(alt)
					// ok to continue
				} else {
					b.insert(s)
					check.recordDef(s.Label, lbl)
				}
				// resolve matching forward jumps and remove them from fwdJumps
				i := 0
				for _, jmp := range fwdJumps {
					if jmp.Label.Name == name {
						// match
						lbl.used = true
						check.recordUse(jmp.Label, lbl)
						if jumpsOverVarDecl(jmp) {
							check.softErrorf(
								jmp.Label.Pos(),
								"goto %s jumps over variable declaration at line %d",
								name,
								check.fset.Position(varDeclPos).Line,
							)
							// ok to continue
						}
					} else {
						// no match - record new forward jump
						fwdJumps[i] = jmp
						i++
					}
				}
				fwdJumps = fwdJumps[:i]
				lstmt = s
			}
			stmtBranches(s.Stmt)

		case *ast.BranchStmt:
			if s.Label == nil {
				return // checked in 1st pass (check.stmt)
			}

			// determine and validate target
			name := s.Label.Name
			switch s.Tok {
			case token.BREAK:
				// spec: "If there is a label, it must be that of an enclosing
				// "for", "switch", or "select" statement, and that is the one
				// whose execution terminates."
				valid := false
				if t := b.enclosingTarget(name); t != nil {
					switch t.Stmt.(type) {
					case *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt:
						valid = true
					}
				}
				if !valid {
					check.errorf(s.Label.Pos(), "invalid break label %s", name)
					return
				}

			case token.CONTINUE:
				// spec: "If there is a label, it must be that of an enclosing
				// "for" statement, and that is the one whose execution advances."
				valid := false
				if t := b.enclosingTarget(name); t != nil {
					switch t.Stmt.(type) {
					case *ast.ForStmt, *ast.RangeStmt:
						valid = true
					}
				}
				if !valid {
					check.errorf(s.Label.Pos(), "invalid continue label %s", name)
					return
				}

			case token.GOTO:
				if b.gotoTarget(name) == nil {
					// label may be declared later - add branch to forward jumps
					fwdJumps = append(fwdJumps, s)
					return
				}

			default:
				check.invalidAST(s.Pos(), "branch statement: %s %s", s.Tok, name)
				return
			}

			// record label use
			obj := all.Lookup(name)
			obj.(*Label).used = true
			check.recordUse(s.Label, obj)

		case *ast.AssignStmt:
			if s.Tok == token.DEFINE {
				recordVarDecl(s.Pos())
			}

		case *ast.BlockStmt:
			blockBranches(lstmt, s.List)

		case *ast.IfStmt:
			stmtBranches(s.Body)
			if s.Else != nil {
				stmtBranches(s.Else)
			}

		case *ast.CaseClause:
			blockBranches(nil, s.Body)

		case *ast.SwitchStmt:
			stmtBranches(s.Body)

		case *ast.TypeSwitchStmt:
			stmtBranches(s.Body)

		case *ast.CommClause:
			blockBranches(nil, s.Body)

		case *ast.SelectStmt:
			stmtBranches(s.Body)

		case *ast.ForStmt:
			stmtBranches(s.Body)

		case *ast.RangeStmt:
			stmtBranches(s.Body)
		}
	}

	for _, s := range list {
		stmtBranches(s)
	}

	return fwdJumps
}
예제 #6
0
파일: assignments.go 프로젝트: tcard/sgo
// If returnPos is valid, initVars is called to type-check the assignment of
// return expressions, and returnPos is the position of the return statement.
func (check *Checker) initVars(lhs []*Var, rhs *ast.ExprList, returnPos token.Pos, entangledLhs *Var) {
	l := len(lhs)

	rhsIsEntangled := false
	if rhs.EntangledPos == 0 && len(rhs.List) > 0 {
		var x operand
		check.rhsMultiExpr(&x, rhs.List[0])
		if t, ok := x.typ.(*Tuple); ok {
			if t.entangled != nil {
				// a, b \ c := f()
				l = len(lhs) + 1
				rhsIsEntangled = true
			} else {
				// a, b, c := f()
				l = len(lhs)
			}
		} else {
			// a, b, c := x, y, z
			l = len(lhs)
			if entangledLhs != nil {
				// v \ ok := m[123]
				l += 1
			}
		}
	} else if rhs.EntangledPos == 1 {
		// a, b \ c := \ z
		rhsIsEntangled = true
		l = 1
	} else if rhs.EntangledPos == len(rhs.List)+1 {
		// a, b \ c := x, y \
		rhsIsEntangled = true
		l = len(lhs)
	} else if len(rhs.List) > 0 {
		rhsIsEntangled = true
		check.error(rhs.List[0].Pos(), "must have values at either side of \\, not both")
	}

	if rhsIsEntangled && entangledLhs == nil {
		check.error(lhs[0].Pos(), "expected entangled assignment, but left-hand side is not entangled")
	}

	allowCommaOk := l == 2 && entangledLhs != nil && !returnPos.IsValid()

	get, r, commaOk := unpack(func(x *operand, i int) {
		if allowCommaOk {
			check.rhsMultiExpr(x, rhs.List[i])
		} else {
			check.multiExpr(x, rhs.List[i])
		}
		if rhsIsEntangled && isBoolean(x.typ) && (!isBooleanConst(*x) || constant.BoolVal(x.val) != false) {
			check.error(rhs.List[i].Pos(), "entangled bool must be the false constant")
		}
	}, len(rhs.List), allowCommaOk)
	if !commaOk && (!rhsIsEntangled && entangledLhs != nil) {
		check.error(rhs.List[0].Pos(), "expected entangled assignment, but right-hand side is not entangled")
	}
	if get == nil || l != r {
		// invalidate lhs and use rhs
		for _, obj := range lhs {
			if obj.typ == nil {
				obj.typ = Typ[Invalid]
			}
		}
		if get == nil {
			return // error reported by unpack
		}
		check.useGetter(get, r)
		// TODO: Error reporting for entangled could be better.
		if returnPos.IsValid() {
			check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r)
			return
		}
		check.errorf(rhs.List[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
		return
	}

	context := "assignment"
	if returnPos.IsValid() {
		context = "return statement"
	}

	var x operand
	if commaOk {
		var a [2]Type
		lhs := []*Var{lhs[0], entangledLhs}
		for i := range a {
			get(&x, i)
			a[i] = check.initVar(lhs[i], &x, context)
		}
		check.recordCommaOkTypes(rhs.List[0], a)
		return
	}

	for i, v := range append(lhs, entangledLhs) {
		if v == nil {
			continue
		}
		if rhs.EntangledPos == 1 && i != len(lhs) {
			continue
		} else if rhs.EntangledPos == len(lhs)+1 && i == len(lhs) {
			continue
		}
		get(&x, i)
		check.initVar(v, &x, context)
	}
}