diff --git a/cmd/guru/describe.go b/cmd/guru/describe.go index 16f37c6d86..dd32911de5 100644 --- a/cmd/guru/describe.go +++ b/cmd/guru/describe.go @@ -327,9 +327,9 @@ func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error return nil, fmt.Errorf("unexpected AST for expr: %T", n) } - t := qpos.info.TypeOf(expr) - if t == nil { - t = types.Typ[types.Invalid] + typ := qpos.info.TypeOf(expr) + if typ == nil { + typ = types.Typ[types.Invalid] } constVal := qpos.info.Types[expr].Value if c, ok := obj.(*types.Const); ok { @@ -339,11 +339,11 @@ func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error return &describeValueResult{ qpos: qpos, expr: expr, - typ: t, + typ: typ, constVal: constVal, obj: obj, - methods: accessibleMethods(t, qpos.info.Pkg), - fields: accessibleFields(t, qpos.info.Pkg), + methods: accessibleMethods(typ, qpos.info.Pkg), + fields: accessibleFields(typ, qpos.info.Pkg), }, nil } @@ -425,48 +425,46 @@ func (r *describeValueResult) JSON(fset *token.FileSet) []byte { func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) { var description string - var t types.Type + var typ types.Type switch n := path[0].(type) { case *ast.Ident: - t = qpos.info.TypeOf(n) - switch t := t.(type) { - case *types.Basic: + obj := qpos.info.ObjectOf(n).(*types.TypeName) + typ = obj.Type() + if isAlias(obj) { + description = "alias of " + } else if obj.Pos() == n.Pos() { + description = "definition of " // (Named type) + } else if _, ok := typ.(*types.Basic); ok { description = "reference to built-in " - - case *types.Named: - isDef := t.Obj().Pos() == n.Pos() // see caveats at isDef above - if isDef { - description = "definition of " - } else { - description = "reference to " - } + } else { + description = "reference to " // (Named type) } case ast.Expr: - t = qpos.info.TypeOf(n) + typ = qpos.info.TypeOf(n) default: // Unreachable? return nil, fmt.Errorf("unexpected AST for type: %T", n) } - description = description + "type " + qpos.typeString(t) + description = description + "type " + qpos.typeString(typ) // Show sizes for structs and named types (it's fairly obvious for others). - switch t.(type) { + switch typ.(type) { case *types.Named, *types.Struct: szs := types.StdSizes{WordSize: 8, MaxAlign: 8} // assume amd64 description = fmt.Sprintf("%s (size %d, align %d)", description, - szs.Sizeof(t), szs.Alignof(t)) + szs.Sizeof(typ), szs.Alignof(typ)) } return &describeTypeResult{ qpos: qpos, node: path[0], description: description, - typ: t, - methods: accessibleMethods(t, qpos.info.Pkg), - fields: accessibleFields(t, qpos.info.Pkg), + typ: typ, + methods: accessibleMethods(typ, qpos.info.Pkg), + fields: accessibleFields(typ, qpos.info.Pkg), }, nil } @@ -672,23 +670,29 @@ func formatMember(obj types.Object, maxname int) string { fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) case *types.TypeName: + typ := obj.Type() + if isAlias(obj) { + buf.WriteString(" = ") + } else { + buf.WriteByte(' ') + typ = typ.Underlying() + } + var typestr string // Abbreviate long aggregate type names. - var abbrev string - switch t := obj.Type().Underlying().(type) { + switch typ := typ.(type) { case *types.Interface: - if t.NumMethods() > 1 { - abbrev = "interface{...}" + if typ.NumMethods() > 1 { + typestr = "interface{...}" } case *types.Struct: - if t.NumFields() > 1 { - abbrev = "struct{...}" + if typ.NumFields() > 1 { + typestr = "struct{...}" } } - if abbrev == "" { - fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type().Underlying(), qualifier)) - } else { - fmt.Fprintf(&buf, " %s", abbrev) + if typestr == "" { + typestr = types.TypeString(typ, qualifier) } + buf.WriteString(typestr) case *types.Var: fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) @@ -699,20 +703,26 @@ func formatMember(obj types.Object, maxname int) string { func (r *describePackageResult) JSON(fset *token.FileSet) []byte { var members []*serial.DescribeMember for _, mem := range r.members { - typ := mem.obj.Type() + obj := mem.obj + typ := obj.Type() var val string - switch mem := mem.obj.(type) { + var alias string + switch obj := obj.(type) { case *types.Const: - val = mem.Val().String() + val = obj.Val().String() case *types.TypeName: - typ = typ.Underlying() + if isAlias(obj) { + alias = "= " // kludgy + } else { + typ = typ.Underlying() + } } members = append(members, &serial.DescribeMember{ - Name: mem.obj.Name(), - Type: typ.String(), + Name: obj.Name(), + Type: alias + typ.String(), Value: val, - Pos: fset.Position(mem.obj.Pos()).String(), - Kind: tokenOf(mem.obj), + Pos: fset.Position(obj.Pos()).String(), + Kind: tokenOf(obj), Methods: methodsToSerial(r.pkg, mem.methods, fset), }) } diff --git a/cmd/guru/guru_test.go b/cmd/guru/guru_test.go index 003183a6f1..ac185fdbca 100644 --- a/cmd/guru/guru_test.go +++ b/cmd/guru/guru_test.go @@ -218,6 +218,7 @@ func TestGuru(t *testing.T) { } for _, filename := range []string{ + "testdata/src/alias/alias.go", // iff guru.HasAlias (go1.9) "testdata/src/calls/main.go", "testdata/src/describe/main.go", "testdata/src/freevars/main.go", @@ -248,6 +249,9 @@ func TestGuru(t *testing.T) { // wording for a "no such file or directory" error. continue } + if filename == "testdata/src/alias/alias.go" && !guru.HasAlias { + continue + } json := strings.Contains(filename, "-json/") queries := parseQueries(t, filename) diff --git a/cmd/guru/implements.go b/cmd/guru/implements.go index b7c29626ab..2d89b2d7e7 100644 --- a/cmd/guru/implements.go +++ b/cmd/guru/implements.go @@ -102,16 +102,20 @@ func implements(q *Query) error { } // Find all named types, even local types (which can have - // methods via promotion) and the built-in "error". - var allNamed []types.Type + // methods due to promotion) and the built-in "error". + // We ignore aliases 'type M = N' to avoid duplicate + // reporting of the Named type N. + var allNamed []*types.Named for _, info := range lprog.AllPackages { for _, obj := range info.Defs { - if obj, ok := obj.(*types.TypeName); ok { - allNamed = append(allNamed, obj.Type()) + if obj, ok := obj.(*types.TypeName); ok && !isAlias(obj) { + if named, ok := obj.Type().(*types.Named); ok { + allNamed = append(allNamed, named) + } } } } - allNamed = append(allNamed, types.Universe.Lookup("error").Type()) + allNamed = append(allNamed, types.Universe.Lookup("error").Type().(*types.Named)) var msets typeutil.MethodSetCache diff --git a/cmd/guru/isAlias18.go b/cmd/guru/isAlias18.go new file mode 100644 index 0000000000..6a2a4c0d23 --- /dev/null +++ b/cmd/guru/isAlias18.go @@ -0,0 +1,15 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !go1.9 + +package main + +import "go/types" + +func isAlias(obj *types.TypeName) bool { + return false // there are no type aliases before Go 1.9 +} + +const HasAlias = false diff --git a/cmd/guru/isAlias19.go b/cmd/guru/isAlias19.go new file mode 100644 index 0000000000..25096ab884 --- /dev/null +++ b/cmd/guru/isAlias19.go @@ -0,0 +1,15 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build go1.9 + +package main + +import "go/types" + +func isAlias(obj *types.TypeName) bool { + return obj.IsAlias() +} + +const HasAlias = true diff --git a/cmd/guru/testdata/src/alias/alias.go b/cmd/guru/testdata/src/alias/alias.go new file mode 100644 index 0000000000..18487c0c2d --- /dev/null +++ b/cmd/guru/testdata/src/alias/alias.go @@ -0,0 +1,20 @@ +// Tests of Go 1.9 type aliases. +// See go.tools/guru/guru_test.go for explanation. +// See alias.golden for expected query results. + +package alias // @describe describe-pkg "alias" + +type I interface{ f() } // @implements implements-I "I" + +type N int +func (N) f() {} + +type M = N // @describe describe-def-M "M" +var m M // @describe describe-ref-M "M" + +type O N // @describe describe-O "O" + +type P = struct{N} // @describe describe-P "N" + +type U = undefined // @describe describe-U "U" +type _ = undefined // @describe describe-undefined "undefined" diff --git a/cmd/guru/testdata/src/alias/alias.golden b/cmd/guru/testdata/src/alias/alias.golden new file mode 100644 index 0000000000..b5ba46e542 --- /dev/null +++ b/cmd/guru/testdata/src/alias/alias.golden @@ -0,0 +1,47 @@ +-------- @describe describe-pkg -------- +definition of package "alias" + type I interface{f()} + method (I) f() + type M = N + method (N) f() + type N int + method (N) f() + type O int + type P = struct{N} + method (struct{N}) f() + type U = invalid type + var m N + +-------- @implements implements-I -------- +interface type I + is implemented by basic type N + +-------- @describe describe-def-M -------- +alias of type N (size 8, align 8) +defined as int +Methods: + method (N) f() + +-------- @describe describe-ref-M -------- +alias of type N (size 8, align 8) +defined as int +Methods: + method (N) f() + +-------- @describe describe-O -------- +definition of type O (size 8, align 8) +No methods. + +-------- @describe describe-P -------- +type struct{N} (size 8, align 8) +Methods: + method (struct{N}) f() +Fields: + N N + +-------- @describe describe-U -------- +alias of type invalid type + +-------- @describe describe-undefined -------- +identifier + diff --git a/cmd/guru/testdata/src/alias/main.golden b/cmd/guru/testdata/src/alias/main.golden deleted file mode 100644 index fcd7c39eef..0000000000 --- a/cmd/guru/testdata/src/alias/main.golden +++ /dev/null @@ -1,54 +0,0 @@ --------- @describe pkg -------- -definition of package "alias" - type S1 struct{aliaslib.T} - method (S1) Method(x *int) *int - type S2 struct{aliaslib.T} - method (S2) Method(x *int) *int - alias bad1 => ? - alias bad2 => ? - const c_ => aliaslib.C - func f_ => aliaslib.F - type t_ => aliaslib.T - var v_ => aliaslib.V - var x aliaslib.T - --------- @describe bad1 -------- -identifier - --------- @describe bad2 -------- -identifier - --------- @describe v -------- -definition of var alias v_ int - --------- @describe t -------- -alias of type aliaslib.T (size 8, align 8) -defined as int -Methods: - method (T) Method(x *int) *int - --------- @describe c -------- -definition of const alias c_ untyped int of value 3 - --------- @describe f -------- -definition of func alias f_ func() - --------- @describe s1-field -------- -reference to field T aliaslib.T -defined here -Methods: - method (T) Method(x *int) *int - --------- @describe s2-field -------- -type struct{aliaslib.T} (size 8, align 8) -Methods: - method (struct{T}) Method(x *int) *int -Fields: - t_ aliaslib.T - --------- @describe var-x -------- -alias of type aliaslib.T (size 8, align 8) -defined as int -Methods: - method (T) Method(x *int) *int - diff --git a/cmd/guru/testdata/src/aliaslib/aliaslib.go b/cmd/guru/testdata/src/aliaslib/aliaslib.go deleted file mode 100644 index 4f07a42706..0000000000 --- a/cmd/guru/testdata/src/aliaslib/aliaslib.go +++ /dev/null @@ -1,11 +0,0 @@ -package aliaslib - -type T int - -func (T) Method(x *int) *int - -func F() - -const C = 3 - -var V = 0 diff --git a/cmd/guru/whicherrs.go b/cmd/guru/whicherrs.go index 37073c1d22..3a81bf56a1 100644 --- a/cmd/guru/whicherrs.go +++ b/cmd/guru/whicherrs.go @@ -181,11 +181,9 @@ func whicherrs(q *Query) error { // typeswitch or assert to. This means finding out // if the type pointed to can be seen by us. // - // For the purposes of this analysis, the type is always - // either a Named type or a pointer to one. - // There are cases where error can be implemented - // by unnamed types, but in that case, we can't assert to - // it, so we don't care about it for this analysis. + // For the purposes of this analysis, we care only about + // TypeNames of Named or pointer-to-Named types. + // We ignore other types (e.g. structs) that implement error. var name *types.TypeName switch t := conc.(type) { case *types.Pointer: diff --git a/go/ssa/create.go b/go/ssa/create.go index 1d56d1c2a7..049396e4bd 100644 --- a/go/ssa/create.go +++ b/go/ssa/create.go @@ -195,9 +195,10 @@ func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info * obj := scope.Lookup(name) memberFromObject(p, obj, nil) if obj, ok := obj.(*types.TypeName); ok { - named := obj.Type().(*types.Named) - for i, n := 0, named.NumMethods(); i < n; i++ { - memberFromObject(p, named.Method(i), nil) + if named, ok := obj.Type().(*types.Named); ok { + for i, n := 0, named.NumMethods(); i < n; i++ { + memberFromObject(p, named.Method(i), nil) + } } } } diff --git a/go/ssa/interp/external.go b/go/ssa/interp/external.go index 3af0d22ec3..03229430e3 100644 --- a/go/ssa/interp/external.go +++ b/go/ssa/interp/external.go @@ -432,7 +432,7 @@ func ext۰runtime۰Func۰Entry(fr *frame, args []value) value { // This is a workaround for a bug in go/ssa/testmain.go: it creates // InternalExamples even for Example functions with no Output comment. -// TODO(adonovan): fix (and redesign) testmain.go after Go 1.6. +// TODO(adonovan): fix (and redesign) testmain.go.. func ext۰testing۰runExample(fr *frame, args []value) value { // This is a stripped down runExample that simply calls the function. // It does not capture and compare output nor recover from panic. @@ -448,7 +448,7 @@ func ext۰testing۰runExample(fr *frame, args []value) value { func ext۰time۰now(fr *frame, args []value) value { nano := time.Now().UnixNano() - return tuple{int64(nano / 1e9), int32(nano % 1e9), 0} + return tuple{int64(nano / 1e9), int32(nano % 1e9), int64(0)} } func ext۰time۰Sleep(fr *frame, args []value) value { diff --git a/go/ssa/ssa.go b/go/ssa/ssa.go index a5a36f787a..a8bcbb60f6 100644 --- a/go/ssa/ssa.go +++ b/go/ssa/ssa.go @@ -75,9 +75,6 @@ type Member interface { } // A Type is a Member of a Package representing a package-level named type. -// -// Type() returns a *types.Named. -// type Type struct { object *types.TypeName pkg *Package diff --git a/godoc/analysis/typeinfo.go b/godoc/analysis/typeinfo.go index ad945dcf9c..e57683f471 100644 --- a/godoc/analysis/typeinfo.go +++ b/godoc/analysis/typeinfo.go @@ -101,14 +101,16 @@ func (a *analysis) doTypeInfo(info *loader.PackageInfo, implements map[*types.Na // IMPLEMENTS & METHOD SETS for _, obj := range info.Defs { if obj, ok := obj.(*types.TypeName); ok { - a.namedType(obj, implements) + if named, ok := obj.Type().(*types.Named); ok { + a.namedType(named, implements) + } } } } -func (a *analysis) namedType(obj *types.TypeName, implements map[*types.Named]implementsFacts) { +func (a *analysis) namedType(T *types.Named, implements map[*types.Named]implementsFacts) { + obj := T.Obj() qualifier := types.RelativeTo(obj.Pkg()) - T := obj.Type().(*types.Named) v := &TypeInfoJSON{ Name: obj.Name(), Size: sizes.Sizeof(T),