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.
|
// Info holds result type information for a package.
|
||||||
type Info struct {
|
type Info struct {
|
||||||
// If Types != nil, it records the type for each expression that is
|
// If Types != nil, it records the expression and corresponding type
|
||||||
// type-checked. Expressions corresponding to identifiers on the lhs
|
// for each expression that is type-checked. Identifiers declaring a
|
||||||
// of a declaration are recorded in Objects, not Types.
|
// variable are recorded in Objects, not Types.
|
||||||
Types map[ast.Expr]Type
|
Types map[ast.Expr]Type
|
||||||
|
|
||||||
// If Values != nil, it records the value of each constant expression
|
// If Values != nil, it records the expression and corresponding value
|
||||||
// that is type-checked.
|
// for each constant expression that is type-checked.
|
||||||
Values map[ast.Expr]exact.Value
|
Values map[ast.Expr]exact.Value
|
||||||
|
|
||||||
// If Objects != nil, it records the object denoted by each identifier
|
// If Objects != nil, it records the identifier and corresponding object
|
||||||
// that is type-checked (including package names, dots "." of dot-imports,
|
// for each identifier that is type-checked (including package names,
|
||||||
// and blank "_" identifiers). For identifiers that were not declared,
|
// dots "." of dot-imports, and blank "_" identifiers). For identifiers
|
||||||
// the corresponding object is nil.
|
// that were not declared due to an error, the corresponding object is nil.
|
||||||
// BUG(gri) Label identifiers in break, continue, or goto statements
|
// BUG(gri) Label identifiers in break, continue, or goto statements
|
||||||
// are not recorded.
|
// are not recorded.
|
||||||
Objects map[*ast.Ident]Object
|
Objects map[*ast.Ident]Object
|
||||||
|
|
||||||
// If Implicits != nil, it records the object for each node that implicitly
|
// If Implicits != nil, it records the node and corresponding object for
|
||||||
// declares objects. The following node and object types may appear:
|
// each node that is type-checked and that implicitly declared an object.
|
||||||
|
// The following node and object types may appear:
|
||||||
//
|
//
|
||||||
// node declared object
|
// node declared object
|
||||||
//
|
//
|
||||||
// *ast.ImportSpec *Package (imports w/o renames), or imported objects (dot-imports)
|
// *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
|
// *ast.Field anonymous struct field or parameter *Var
|
||||||
//
|
//
|
||||||
Implicits map[ast.Node]Object
|
Implicits map[ast.Node]Object
|
||||||
|
|
|
||||||
|
|
@ -288,17 +288,11 @@ func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) {
|
||||||
check.initVars(vars, rhs, true)
|
check.initVars(vars, rhs, true)
|
||||||
|
|
||||||
// declare variables
|
// declare variables
|
||||||
n := 0 // number of new variables
|
n := scope.NumEntries()
|
||||||
for _, obj := range vars {
|
for _, obj := range vars {
|
||||||
if obj.name == "_" {
|
scope.Insert(obj)
|
||||||
obj.setParent(scope)
|
|
||||||
continue // blank identifiers are not visible
|
|
||||||
}
|
|
||||||
if scope.Insert(obj) == nil {
|
|
||||||
n++ // new declaration
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if n == 0 {
|
if n == scope.NumEntries() {
|
||||||
check.errorf(vars[0].Pos(), "no new variables on left side of :=")
|
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.expectKeyword("struct")
|
||||||
p.expect('{')
|
p.expect('{')
|
||||||
scope := NewScope(nil)
|
var fset objset
|
||||||
for i := 0; p.tok != '}'; i++ {
|
for i := 0; p.tok != '}'; i++ {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
p.expect(';')
|
p.expect(';')
|
||||||
|
|
@ -506,15 +506,13 @@ func (p *gcParser) parseStructType() Type {
|
||||||
if tags != nil {
|
if tags != nil {
|
||||||
tags = append(tags, tag)
|
tags = append(tags, tag)
|
||||||
}
|
}
|
||||||
if fld.name == "_" {
|
if alt := fset.insert(fld); alt != nil {
|
||||||
// blank identifiers are not declared
|
|
||||||
fld.setParent(scope)
|
|
||||||
} else if alt := scope.Insert(fld); alt != nil {
|
|
||||||
pname := "<no pkg name>"
|
pname := "<no pkg name>"
|
||||||
if pkg := alt.Pkg(); pkg != nil {
|
if pkg := alt.Pkg(); pkg != nil {
|
||||||
pname = pkg.name
|
pname = pkg.name
|
||||||
}
|
}
|
||||||
p.errorf("multiple fields named %s.%s", pname, alt.Name())
|
p.errorf("multiple fields named %s.%s", pname, alt.Name())
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
fields = append(fields, fld)
|
fields = append(fields, fld)
|
||||||
}
|
}
|
||||||
|
|
@ -598,12 +596,12 @@ func (p *gcParser) parseSignature() *Signature {
|
||||||
// visible in the export data.
|
// visible in the export data.
|
||||||
//
|
//
|
||||||
func (p *gcParser) parseInterfaceType() Type {
|
func (p *gcParser) parseInterfaceType() Type {
|
||||||
var scope *Scope // lazily allocated (empty interfaces are not uncommon)
|
typ := new(Interface)
|
||||||
var methods []*Func
|
var methods []*Func
|
||||||
|
|
||||||
typ := new(Interface)
|
|
||||||
p.expectKeyword("interface")
|
p.expectKeyword("interface")
|
||||||
p.expect('{')
|
p.expect('{')
|
||||||
|
var mset objset
|
||||||
for i := 0; p.tok != '}'; i++ {
|
for i := 0; p.tok != '}'; i++ {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
p.expect(';')
|
p.expect(';')
|
||||||
|
|
@ -614,17 +612,15 @@ func (p *gcParser) parseInterfaceType() Type {
|
||||||
// typ, for less verbose printing of interface method signatures.
|
// typ, for less verbose printing of interface method signatures.
|
||||||
sig.recv = NewVar(token.NoPos, pkg, "", typ)
|
sig.recv = NewVar(token.NoPos, pkg, "", typ)
|
||||||
m := NewFunc(token.NoPos, pkg, name, sig)
|
m := NewFunc(token.NoPos, pkg, name, sig)
|
||||||
if scope == nil {
|
if alt := mset.insert(m); alt != nil {
|
||||||
scope = NewScope(nil)
|
|
||||||
}
|
|
||||||
if alt := scope.Insert(m); alt != nil {
|
|
||||||
p.errorf("multiple methods named %s.%s", alt.Pkg().name, alt.Name())
|
p.errorf("multiple methods named %s.%s", alt.Pkg().name, alt.Name())
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
methods = append(methods, m)
|
methods = append(methods, m)
|
||||||
}
|
}
|
||||||
p.expect('}')
|
p.expect('}')
|
||||||
typ.methods = methods
|
|
||||||
|
|
||||||
|
typ.methods = methods
|
||||||
return typ
|
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.
|
// 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 {
|
func fieldIndex(fields []*Var, pkg *Package, name string) int {
|
||||||
if name == "_" {
|
if name != "_" {
|
||||||
return -1 // blank identifiers are never found
|
for i, f := range fields {
|
||||||
}
|
if f.sameId(pkg, name) {
|
||||||
for i, f := range fields {
|
return i
|
||||||
if f.sameId(pkg, name) {
|
}
|
||||||
return i
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1
|
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).
|
// 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) {
|
func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
|
||||||
assert(name != "_")
|
if name != "_" {
|
||||||
for i, m := range methods {
|
for i, m := range methods {
|
||||||
if m.sameId(pkg, name) {
|
if m.sameId(pkg, name) {
|
||||||
return i, m
|
return i, m
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1, nil
|
return -1, nil
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,13 @@ type objset struct {
|
||||||
// insert attempts to insert an object obj into objset s.
|
// insert attempts to insert an object obj into objset s.
|
||||||
// If s already contains an alternative object alt with
|
// If s already contains an alternative object alt with
|
||||||
// the same name, insert leaves s unchanged and returns alt.
|
// the same name, insert leaves s unchanged and returns alt.
|
||||||
// Otherwise it inserts obj and returns nil.
|
// Otherwise it inserts obj and returns nil. Objects with
|
||||||
// The object name must not be blank _.
|
// blank "_" names are ignored.
|
||||||
func (s *objset) insert(obj Object) (alt Object) {
|
func (s *objset) insert(obj Object) Object {
|
||||||
name := obj.Name()
|
name := obj.Name()
|
||||||
assert(name != "_")
|
if name == "_" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
id := Id(obj.Pkg(), name)
|
id := Id(obj.Pkg(), name)
|
||||||
if alt := s.objmap[id]; alt != nil {
|
if alt := s.objmap[id]; alt != nil {
|
||||||
return alt
|
return alt
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,7 @@ func (check *checker) reportAltDecl(obj Object) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (check *checker) declareObj(scope *Scope, id *ast.Ident, obj Object) {
|
func (check *checker) declareObj(scope *Scope, id *ast.Ident, obj Object) {
|
||||||
if obj.Name() == "_" {
|
if alt := scope.Insert(obj); alt != nil {
|
||||||
// blank identifiers are not declared
|
|
||||||
obj.setParent(scope)
|
|
||||||
} else if alt := scope.Insert(obj); alt != nil {
|
|
||||||
check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
|
check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
|
||||||
check.reportAltDecl(alt)
|
check.reportAltDecl(alt)
|
||||||
obj = nil
|
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) {
|
func (check *checker) declareFld(oset *objset, id *ast.Ident, obj Object) {
|
||||||
if obj.Name() == "_" {
|
if alt := oset.insert(obj); alt != nil {
|
||||||
// blank identifiers are not declared
|
|
||||||
} else if alt := oset.insert(obj); alt != nil {
|
|
||||||
check.errorf(obj.Pos(), "%s redeclared", obj.Name())
|
check.errorf(obj.Pos(), "%s redeclared", obj.Name())
|
||||||
check.reportAltDecl(alt)
|
check.reportAltDecl(alt)
|
||||||
obj = nil
|
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
|
// TODO(gri) consider keeping the objset with the struct instead
|
||||||
if t, _ := named.underlying.(*Struct); t != nil {
|
if t, _ := named.underlying.(*Struct); t != nil {
|
||||||
for _, fld := range t.fields {
|
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
|
// If s already contains an alternative object alt with
|
||||||
// the same name, Insert leaves s unchanged and returns alt.
|
// the same name, Insert leaves s unchanged and returns alt.
|
||||||
// Otherwise it inserts obj, sets the object's scope to
|
// 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 {
|
func (s *Scope) Insert(obj Object) Object {
|
||||||
name := obj.Name()
|
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 {
|
if alt := s.Lookup(name); alt != nil {
|
||||||
return alt
|
return alt
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue