// ArrayType = "[" int_lit "]" Type .
//
-func (p *parser) parseArrayType() types.Type {
+func (p *parser) parseArrayType(parent *types.Package) types.Type {
// "[" already consumed and lookahead known not to be "]"
lit := p.expect(scanner.Int)
p.expect(']')
- elem := p.parseType()
+ elem := p.parseType(parent)
n, err := strconv.ParseInt(lit, 10, 64)
if err != nil {
p.error(err)
// MapType = "map" "[" Type "]" Type .
//
-func (p *parser) parseMapType() types.Type {
+func (p *parser) parseMapType(parent *types.Package) types.Type {
p.expectKeyword("map")
p.expect('[')
- key := p.parseType()
+ key := p.parseType(parent)
p.expect(']')
- elem := p.parseType()
+ elem := p.parseType(parent)
return types.NewMap(key, elem)
}
// Name = identifier | "?" | QualifiedName .
//
-// For unqualified names, the returned package is the imported package.
+// For unqualified and anonymous names, the returned package is the parent
+// package unless parent == nil, in which case the returned package is the
+// package being imported. (The parent package is not nil if the the name
+// is an unqualified struct field or interface method name belonging to a
+// type declared in another package.)
+//
// For qualified names, the returned package is nil (and not created if
// it doesn't exist yet) unless materializePkg is set (which creates an
-// unnamed package). In the latter case, a subequent import clause is
-// expected to provide a name for the package.
+// unnamed package with valid package path). In the latter case, a
+// subequent import clause is expected to provide a name for the package.
//
-func (p *parser) parseName(materializePkg bool) (pkg *types.Package, name string) {
+func (p *parser) parseName(parent *types.Package, materializePkg bool) (pkg *types.Package, name string) {
+ pkg = parent
+ if pkg == nil {
+ pkg = p.sharedPkgs[p.id]
+ }
switch p.tok {
case scanner.Ident:
- pkg = p.sharedPkgs[p.id]
name = p.lit
p.next()
case '?':
// anonymous
- pkg = p.sharedPkgs[p.id]
p.next()
case '@':
// exported name prefixed with package path
+ pkg = nil
var id string
id, name = p.parseQualifiedName()
if materializePkg {
// Field = Name Type [ string_lit ] .
//
-func (p *parser) parseField() (*types.Var, string) {
- pkg, name := p.parseName(true)
- typ := p.parseType()
+func (p *parser) parseField(parent *types.Package) (*types.Var, string) {
+ pkg, name := p.parseName(parent, true)
+ typ := p.parseType(parent)
anonymous := false
if name == "" {
// anonymous field - typ must be T or *T and T must be a type name
pkg = nil
name = typ.Name()
case *types.Named:
- name = typ.Obj().Name()
+ obj := typ.Obj()
+ pkg = obj.Pkg()
+ name = obj.Name()
default:
p.errorf("anonymous field expected")
}
// StructType = "struct" "{" [ FieldList ] "}" .
// FieldList = Field { ";" Field } .
//
-func (p *parser) parseStructType() types.Type {
+func (p *parser) parseStructType(parent *types.Package) types.Type {
var fields []*types.Var
var tags []string
if i > 0 {
p.expect(';')
}
- fld, tag := p.parseField()
+ fld, tag := p.parseField(parent)
if tag != "" && tags == nil {
tags = make([]string, i)
}
// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
//
func (p *parser) parseParameter() (par *types.Var, isVariadic bool) {
- _, name := p.parseName(false)
+ _, name := p.parseName(nil, false)
// remove gc-specific parameter numbering
if i := strings.Index(name, "ยท"); i >= 0 {
name = name[:i]
p.expectSpecial("...")
isVariadic = true
}
- typ := p.parseType()
+ typ := p.parseType(nil)
if isVariadic {
typ = types.NewSlice(typ)
}
// by the compiler and thus embedded interfaces are never
// visible in the export data.
//
-func (p *parser) parseInterfaceType() types.Type {
+func (p *parser) parseInterfaceType(parent *types.Package) types.Type {
var methods []*types.Func
p.expectKeyword("interface")
if i > 0 {
p.expect(';')
}
- pkg, name := p.parseName(true)
+ pkg, name := p.parseName(parent, true)
sig := p.parseSignature(nil)
methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig))
}
// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
//
-func (p *parser) parseChanType() types.Type {
+func (p *parser) parseChanType(parent *types.Package) types.Type {
dir := types.SendRecv
if p.tok == scanner.Ident {
p.expectKeyword("chan")
p.expectKeyword("chan")
dir = types.RecvOnly
}
- elem := p.parseType()
+ elem := p.parseType(parent)
return types.NewChan(dir, elem)
}
// PointerType = "*" Type .
// FuncType = "func" Signature .
//
-func (p *parser) parseType() types.Type {
+func (p *parser) parseType(parent *types.Package) types.Type {
switch p.tok {
case scanner.Ident:
switch p.lit {
default:
return p.parseBasicType()
case "struct":
- return p.parseStructType()
+ return p.parseStructType(parent)
case "func":
// FuncType
p.next()
return p.parseSignature(nil)
case "interface":
- return p.parseInterfaceType()
+ return p.parseInterfaceType(parent)
case "map":
- return p.parseMapType()
+ return p.parseMapType(parent)
case "chan":
- return p.parseChanType()
+ return p.parseChanType(parent)
}
case '@':
// TypeName
if p.tok == ']' {
// SliceType
p.next()
- return types.NewSlice(p.parseType())
+ return types.NewSlice(p.parseType(parent))
}
- return p.parseArrayType()
+ return p.parseArrayType(parent)
case '*':
// PointerType
p.next()
- return types.NewPointer(p.parseType())
+ return types.NewPointer(p.parseType(parent))
case '<':
- return p.parseChanType()
+ return p.parseChanType(parent)
case '(':
// "(" Type ")"
p.next()
- typ := p.parseType()
+ typ := p.parseType(parent)
p.expect(')')
return typ
}
var typ0 types.Type
if p.tok != '=' {
- typ0 = p.parseType()
+ // constant types are never structured - no need for parent type
+ typ0 = p.parseType(nil)
}
p.expect('=')
// structure, but throw it away if the object already has a type.
// This ensures that all imports refer to the same type object for
// a given type declaration.
- typ := p.parseType()
+ typ := p.parseType(pkg)
if name := obj.Type().(*types.Named); name.Underlying() == nil {
name.SetUnderlying(typ)
func (p *parser) parseVarDecl() {
p.expectKeyword("var")
pkg, name := p.parseExportedName()
- typ := p.parseType()
+ typ := p.parseType(pkg)
pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ))
}
base := deref(recv.Type()).(*types.Named)
// parse method name, signature, and possibly inlined body
- _, name := p.parseName(false)
+ _, name := p.parseName(nil, false)
sig := p.parseFunc(recv)
// methods always belong to the same package as the base type object
}
}
}
+
+func TestIssue13898(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ // import go/internal/gcimporter which imports go/types partially
+ imports := make(map[string]*types.Package)
+ _, err := Import(imports, "go/internal/gcimporter", ".")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // look for go/types package
+ var goTypesPkg *types.Package
+ for path, pkg := range imports {
+ if path == "go/types" {
+ goTypesPkg = pkg
+ break
+ }
+ }
+ if goTypesPkg == nil {
+ t.Fatal("go/types not found")
+ }
+
+ // look for go/types.Object type
+ obj := goTypesPkg.Scope().Lookup("Object")
+ if obj == nil {
+ t.Fatal("go/types.Object not found")
+ }
+ typ, ok := obj.Type().(*types.Named)
+ if !ok {
+ t.Fatalf("go/types.Object type is %v; wanted named type", typ)
+ }
+
+ // lookup go/types.Object.Pkg method
+ m, _, _ := types.LookupFieldOrMethod(typ, false, nil, "Pkg")
+ if m == nil {
+ t.Fatal("go/types.Object.Pkg not found")
+ }
+
+ // the method must belong to go/types
+ if m.Pkg().Path() != "go/types" {
+ t.Fatalf("found %v; want go/types", m.Pkg())
+ }
+}
"go/ast"
"go/importer"
"go/parser"
+ "internal/testenv"
"sort"
"strings"
"testing"
t.Errorf("Unexpected defs/uses\ngot:\n%s\nwant:\n%s", got, want)
}
}
+
+// This tests that the package associated with the types.Object.Pkg method
+// is the type's package independent of the order in which the imports are
+// listed in the sources src1, src2 below.
+// The actual issue is in go/internal/gcimporter which has a corresponding
+// test; we leave this test here to verify correct behavior at the go/types
+// level.
+func TestIssue13898(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ const src0 = `
+package main
+
+import "go/types"
+
+func main() {
+ var info types.Info
+ for _, obj := range info.Uses {
+ _ = obj.Pkg()
+ }
+}
+`
+ // like src0, but also imports go/importer
+ const src1 = `
+package main
+
+import (
+ "go/types"
+ _ "go/importer"
+)
+
+func main() {
+ var info types.Info
+ for _, obj := range info.Uses {
+ _ = obj.Pkg()
+ }
+}
+`
+ // like src1 but with different import order
+ // (used to fail with this issue)
+ const src2 = `
+package main
+
+import (
+ _ "go/importer"
+ "go/types"
+)
+
+func main() {
+ var info types.Info
+ for _, obj := range info.Uses {
+ _ = obj.Pkg()
+ }
+}
+`
+ f := func(test, src string) {
+ f, err := parser.ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ cfg := Config{Importer: importer.Default()}
+ info := Info{Uses: make(map[*ast.Ident]Object)}
+ _, err = cfg.Check("main", fset, []*ast.File{f}, &info)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var pkg *Package
+ count := 0
+ for id, obj := range info.Uses {
+ if id.Name == "Pkg" {
+ pkg = obj.Pkg()
+ count++
+ }
+ }
+ if count != 1 {
+ t.Fatalf("%s: got %d entries named Pkg; want 1", test, count)
+ }
+ if pkg.Name() != "types" {
+ t.Fatalf("%s: got %v; want package types", test, pkg)
+ }
+ }
+
+ f("src0", src0)
+ f("src1", src1)
+ f("src2", src2)
+}