aboutsummaryrefslogtreecommitdiff
path: root/src/go/types/decl.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/go/types/decl.go')
-rw-r--r--src/go/types/decl.go266
1 files changed, 154 insertions, 112 deletions
diff --git a/src/go/types/decl.go b/src/go/types/decl.go
index 5c0e611c51..a022ec5678 100644
--- a/src/go/types/decl.go
+++ b/src/go/types/decl.go
@@ -381,6 +381,76 @@ func firstInSrc(path []Object) int {
return fst
}
+type (
+ decl interface {
+ node() ast.Node
+ }
+
+ importDecl struct{ spec *ast.ImportSpec }
+ constDecl struct {
+ spec *ast.ValueSpec
+ iota int
+ typ ast.Expr
+ init []ast.Expr
+ }
+ varDecl struct{ spec *ast.ValueSpec }
+ typeDecl struct{ spec *ast.TypeSpec }
+ funcDecl struct{ decl *ast.FuncDecl }
+)
+
+func (d importDecl) node() ast.Node { return d.spec }
+func (d constDecl) node() ast.Node { return d.spec }
+func (d varDecl) node() ast.Node { return d.spec }
+func (d typeDecl) node() ast.Node { return d.spec }
+func (d funcDecl) node() ast.Node { return d.decl }
+
+func (check *Checker) walkDecls(decls []ast.Decl, f func(decl)) {
+ for _, d := range decls {
+ check.walkDecl(d, f)
+ }
+}
+
+func (check *Checker) walkDecl(d ast.Decl, f func(decl)) {
+ switch d := d.(type) {
+ case *ast.BadDecl:
+ // ignore
+ case *ast.GenDecl:
+ var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
+ for iota, s := range d.Specs {
+ switch s := s.(type) {
+ case *ast.ImportSpec:
+ f(importDecl{s})
+ case *ast.ValueSpec:
+ switch d.Tok {
+ case token.CONST:
+ // determine which initialization expressions to use
+ switch {
+ case s.Type != nil || len(s.Values) > 0:
+ last = s
+ case last == nil:
+ last = new(ast.ValueSpec) // make sure last exists
+ }
+ check.arityMatch(s, last)
+ f(constDecl{spec: s, iota: iota, init: last.Values, typ: last.Type})
+ case token.VAR:
+ check.arityMatch(s, nil)
+ f(varDecl{s})
+ default:
+ check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
+ }
+ case *ast.TypeSpec:
+ f(typeDecl{s})
+ default:
+ check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
+ }
+ }
+ case *ast.FuncDecl:
+ f(funcDecl{d})
+ default:
+ check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
+ }
+}
+
func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) {
assert(obj.typ == nil)
@@ -664,133 +734,105 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
}
}
-func (check *Checker) declStmt(decl ast.Decl) {
+func (check *Checker) declStmt(d ast.Decl) {
pkg := check.pkg
- switch d := decl.(type) {
- case *ast.BadDecl:
- // ignore
+ check.walkDecl(d, func(d decl) {
+ switch d := d.(type) {
+ case constDecl:
+ top := len(check.delayed)
- case *ast.GenDecl:
- var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
- for iota, spec := range d.Specs {
- switch s := spec.(type) {
- case *ast.ValueSpec:
- switch d.Tok {
- case token.CONST:
- top := len(check.delayed)
+ // declare all constants
+ lhs := make([]*Const, len(d.spec.Names))
+ for i, name := range d.spec.Names {
+ obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(d.iota)))
+ lhs[i] = obj
- // determine which init exprs to use
- switch {
- case s.Type != nil || len(s.Values) > 0:
- last = s
- case last == nil:
- last = new(ast.ValueSpec) // make sure last exists
- }
-
- // declare all constants
- lhs := make([]*Const, len(s.Names))
- for i, name := range s.Names {
- obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(iota)))
- lhs[i] = obj
-
- var init ast.Expr
- if i < len(last.Values) {
- init = last.Values[i]
- }
+ var init ast.Expr
+ if i < len(d.init) {
+ init = d.init[i]
+ }
- check.constDecl(obj, last.Type, init)
- }
+ check.constDecl(obj, d.typ, init)
+ }
- check.arityMatch(s, last)
+ // process function literals in init expressions before scope changes
+ check.processDelayed(top)
- // process function literals in init expressions before scope changes
- check.processDelayed(top)
+ // spec: "The scope of a constant or variable identifier declared
+ // inside a function begins at the end of the ConstSpec or VarSpec
+ // (ShortVarDecl for short variable declarations) and ends at the
+ // end of the innermost containing block."
+ scopePos := d.spec.End()
+ for i, name := range d.spec.Names {
+ check.declare(check.scope, name, lhs[i], scopePos)
+ }
- // spec: "The scope of a constant or variable identifier declared
- // inside a function begins at the end of the ConstSpec or VarSpec
- // (ShortVarDecl for short variable declarations) and ends at the
- // end of the innermost containing block."
- scopePos := s.End()
- for i, name := range s.Names {
- check.declare(check.scope, name, lhs[i], scopePos)
- }
+ case varDecl:
+ top := len(check.delayed)
- case token.VAR:
- top := len(check.delayed)
+ lhs0 := make([]*Var, len(d.spec.Names))
+ for i, name := range d.spec.Names {
+ lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
+ }
- lhs0 := make([]*Var, len(s.Names))
- for i, name := range s.Names {
- lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
+ // initialize all variables
+ for i, obj := range lhs0 {
+ var lhs []*Var
+ var init ast.Expr
+ switch len(d.spec.Values) {
+ case len(d.spec.Names):
+ // lhs and rhs match
+ init = d.spec.Values[i]
+ case 1:
+ // rhs is expected to be a multi-valued expression
+ lhs = lhs0
+ init = d.spec.Values[0]
+ default:
+ if i < len(d.spec.Values) {
+ init = d.spec.Values[i]
}
-
- // initialize all variables
- for i, obj := range lhs0 {
- var lhs []*Var
- var init ast.Expr
- switch len(s.Values) {
- case len(s.Names):
- // lhs and rhs match
- init = s.Values[i]
- case 1:
- // rhs is expected to be a multi-valued expression
- lhs = lhs0
- init = s.Values[0]
- default:
- if i < len(s.Values) {
- init = s.Values[i]
- }
- }
- check.varDecl(obj, lhs, s.Type, init)
- if len(s.Values) == 1 {
- // If we have a single lhs variable we are done either way.
- // If we have a single rhs expression, it must be a multi-
- // valued expression, in which case handling the first lhs
- // variable will cause all lhs variables to have a type
- // assigned, and we are done as well.
- if debug {
- for _, obj := range lhs0 {
- assert(obj.typ != nil)
- }
- }
- break
+ }
+ check.varDecl(obj, lhs, d.spec.Type, init)
+ if len(d.spec.Values) == 1 {
+ // If we have a single lhs variable we are done either way.
+ // If we have a single rhs expression, it must be a multi-
+ // valued expression, in which case handling the first lhs
+ // variable will cause all lhs variables to have a type
+ // assigned, and we are done as well.
+ if debug {
+ for _, obj := range lhs0 {
+ assert(obj.typ != nil)
}
}
-
- check.arityMatch(s, nil)
-
- // process function literals in init expressions before scope changes
- check.processDelayed(top)
-
- // declare all variables
- // (only at this point are the variable scopes (parents) set)
- scopePos := s.End() // see constant declarations
- for i, name := range s.Names {
- // see constant declarations
- check.declare(check.scope, name, lhs0[i], scopePos)
- }
-
- default:
- check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
+ break
}
+ }
- case *ast.TypeSpec:
- obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
- // spec: "The scope of a type identifier declared inside a function
- // begins at the identifier in the TypeSpec and ends at the end of
- // the innermost containing block."
- scopePos := s.Name.Pos()
- check.declare(check.scope, s.Name, obj, scopePos)
- // mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
- obj.setColor(grey + color(check.push(obj)))
- check.typeDecl(obj, s.Type, nil, s.Assign.IsValid())
- check.pop().setColor(black)
- default:
- check.invalidAST(s.Pos(), "const, type, or var declaration expected")
+ // process function literals in init expressions before scope changes
+ check.processDelayed(top)
+
+ // declare all variables
+ // (only at this point are the variable scopes (parents) set)
+ scopePos := d.spec.End() // see constant declarations
+ for i, name := range d.spec.Names {
+ // see constant declarations
+ check.declare(check.scope, name, lhs0[i], scopePos)
}
- }
- default:
- check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
- }
+ case typeDecl:
+ obj := NewTypeName(d.spec.Name.Pos(), pkg, d.spec.Name.Name, nil)
+ // spec: "The scope of a type identifier declared inside a function
+ // begins at the identifier in the TypeSpec and ends at the end of
+ // the innermost containing block."
+ scopePos := d.spec.Name.Pos()
+ check.declare(check.scope, d.spec.Name, obj, scopePos)
+ // mark and unmark type before calling typeDecl; its type is still nil (see Checker.objDecl)
+ obj.setColor(grey + color(check.push(obj)))
+ check.typeDecl(obj, d.spec.Type, nil, d.spec.Assign.IsValid())
+ check.pop().setColor(black)
+ default:
+ check.invalidAST(d.node().Pos(), "unknown ast.Decl node %T", d.node())
+ }
+ })
}