go.tools/go/types: simplify handling of blank _ identifiers

Also: Fixes a couple of places where scopes instead
      of objsets were used (missed in previous CL).

R=adonovan
CC=golang-dev
https://golang.org/cl/11419047
This commit is contained in:
Robert Griesemer 2013-07-24 12:39:11 -07:00
parent d0657ebb0d
commit 6dbbc59ae0
7 changed files with 55 additions and 59 deletions

View File

@ -87,30 +87,31 @@ type Config struct {
// Info holds result type information for a package.
type Info struct {
// If Types != nil, it records the type for each expression that is
// type-checked. Expressions corresponding to identifiers on the lhs
// of a declaration are recorded in Objects, not Types.
// If Types != nil, it records the expression and corresponding type
// for each expression that is type-checked. Identifiers declaring a
// variable are recorded in Objects, not Types.
Types map[ast.Expr]Type
// If Values != nil, it records the value of each constant expression
// that is type-checked.
// If Values != nil, it records the expression and corresponding value
// for each constant expression that is type-checked.
Values map[ast.Expr]exact.Value
// If Objects != nil, it records the object denoted by each identifier
// that is type-checked (including package names, dots "." of dot-imports,
// and blank "_" identifiers). For identifiers that were not declared,
// the corresponding object is nil.
// If Objects != nil, it records the identifier and corresponding object
// for each identifier that is type-checked (including package names,
// dots "." of dot-imports, and blank "_" identifiers). For identifiers
// that were not declared due to an error, the corresponding object is nil.
// BUG(gri) Label identifiers in break, continue, or goto statements
// are not recorded.
Objects map[*ast.Ident]Object
// If Implicits != nil, it records the object for each node that implicitly
// declares objects. The following node and object types may appear:
// If Implicits != nil, it records the node and corresponding object for
// each node that is type-checked and that implicitly declared an object.
// The following node and object types may appear:
//
// node declared object
//
// *ast.ImportSpec *Package (imports w/o renames), or imported objects (dot-imports)
// *ast.CaseClause type-specific *Var introduced for each single-type type switch clause
// *ast.CaseClause type-specific *Var for each type switch case clause (incl. default)
// *ast.Field anonymous struct field or parameter *Var
//
Implicits map[ast.Node]Object

View File

@ -288,17 +288,11 @@ func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) {
check.initVars(vars, rhs, true)
// declare variables
n := 0 // number of new variables
n := scope.NumEntries()
for _, obj := range vars {
if obj.name == "_" {
obj.setParent(scope)
continue // blank identifiers are not visible
}
if scope.Insert(obj) == nil {
n++ // new declaration
}
scope.Insert(obj)
}
if n == 0 {
if n == scope.NumEntries() {
check.errorf(vars[0].Pos(), "no new variables on left side of :=")
}
}

View File

@ -494,7 +494,7 @@ func (p *gcParser) parseStructType() Type {
p.expectKeyword("struct")
p.expect('{')
scope := NewScope(nil)
var fset objset
for i := 0; p.tok != '}'; i++ {
if i > 0 {
p.expect(';')
@ -506,15 +506,13 @@ func (p *gcParser) parseStructType() Type {
if tags != nil {
tags = append(tags, tag)
}
if fld.name == "_" {
// blank identifiers are not declared
fld.setParent(scope)
} else if alt := scope.Insert(fld); alt != nil {
if alt := fset.insert(fld); alt != nil {
pname := "<no pkg name>"
if pkg := alt.Pkg(); pkg != nil {
pname = pkg.name
}
p.errorf("multiple fields named %s.%s", pname, alt.Name())
continue
}
fields = append(fields, fld)
}
@ -598,12 +596,12 @@ func (p *gcParser) parseSignature() *Signature {
// visible in the export data.
//
func (p *gcParser) parseInterfaceType() Type {
var scope *Scope // lazily allocated (empty interfaces are not uncommon)
typ := new(Interface)
var methods []*Func
typ := new(Interface)
p.expectKeyword("interface")
p.expect('{')
var mset objset
for i := 0; p.tok != '}'; i++ {
if i > 0 {
p.expect(';')
@ -614,17 +612,15 @@ func (p *gcParser) parseInterfaceType() Type {
// typ, for less verbose printing of interface method signatures.
sig.recv = NewVar(token.NoPos, pkg, "", typ)
m := NewFunc(token.NoPos, pkg, name, sig)
if scope == nil {
scope = NewScope(nil)
}
if alt := scope.Insert(m); alt != nil {
if alt := mset.insert(m); alt != nil {
p.errorf("multiple methods named %s.%s", alt.Pkg().name, alt.Name())
continue
}
methods = append(methods, m)
}
p.expect('}')
typ.methods = methods
typ.methods = methods
return typ
}

View File

@ -314,12 +314,11 @@ func concat(list []int, i int) []int {
// fieldIndex returns the index for the field with matching package and name, or a value < 0.
func fieldIndex(fields []*Var, pkg *Package, name string) int {
if name == "_" {
return -1 // blank identifiers are never found
}
for i, f := range fields {
if f.sameId(pkg, name) {
return i
if name != "_" {
for i, f := range fields {
if f.sameId(pkg, name) {
return i
}
}
}
return -1
@ -327,10 +326,11 @@ func fieldIndex(fields []*Var, pkg *Package, name string) int {
// lookupMethod returns the index of and method with matching package and name, or (-1, nil).
func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
assert(name != "_")
for i, m := range methods {
if m.sameId(pkg, name) {
return i, m
if name != "_" {
for i, m := range methods {
if m.sameId(pkg, name) {
return i, m
}
}
}
return -1, nil

View File

@ -19,11 +19,13 @@ type objset struct {
// insert attempts to insert an object obj into objset s.
// If s already contains an alternative object alt with
// the same name, insert leaves s unchanged and returns alt.
// Otherwise it inserts obj and returns nil.
// The object name must not be blank _.
func (s *objset) insert(obj Object) (alt Object) {
// Otherwise it inserts obj and returns nil. Objects with
// blank "_" names are ignored.
func (s *objset) insert(obj Object) Object {
name := obj.Name()
assert(name != "_")
if name == "_" {
return nil
}
id := Id(obj.Pkg(), name)
if alt := s.objmap[id]; alt != nil {
return alt

View File

@ -24,10 +24,7 @@ func (check *checker) reportAltDecl(obj Object) {
}
func (check *checker) declareObj(scope *Scope, id *ast.Ident, obj Object) {
if obj.Name() == "_" {
// blank identifiers are not declared
obj.setParent(scope)
} else if alt := scope.Insert(obj); alt != nil {
if alt := scope.Insert(obj); alt != nil {
check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
check.reportAltDecl(alt)
obj = nil
@ -38,9 +35,7 @@ func (check *checker) declareObj(scope *Scope, id *ast.Ident, obj Object) {
}
func (check *checker) declareFld(oset *objset, id *ast.Ident, obj Object) {
if obj.Name() == "_" {
// blank identifiers are not declared
} else if alt := oset.insert(obj); alt != nil {
if alt := oset.insert(obj); alt != nil {
check.errorf(obj.Pos(), "%s redeclared", obj.Name())
check.reportAltDecl(alt)
obj = nil
@ -525,9 +520,7 @@ func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, cycleOk
// TODO(gri) consider keeping the objset with the struct instead
if t, _ := named.underlying.(*Struct); t != nil {
for _, fld := range t.fields {
if fld.name != "_" {
assert(mset.insert(fld) == nil)
}
assert(mset.insert(fld) == nil)
}
}

View File

@ -106,10 +106,20 @@ func (s *Scope) LookupParent(name string) Object {
// If s already contains an alternative object alt with
// the same name, Insert leaves s unchanged and returns alt.
// Otherwise it inserts obj, sets the object's scope to
// s, and returns nil. The object name must not be blank _.
// s, and returns nil. Objects with blank "_" names are
// not inserted, but have their parent field set to s.
func (s *Scope) Insert(obj Object) Object {
name := obj.Name()
assert(name != "_")
// spec: "The blank identifier, represented by the underscore
// character _, may be used in a declaration like any other
// identifier but the declaration does not introduce a new
// binding."
if name == "_" {
obj.setParent(s)
return nil
}
if alt := s.Lookup(name); alt != nil {
return alt
}