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:
parent
d0657ebb0d
commit
6dbbc59ae0
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 :=")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue