1
0
mirror of https://github.com/golang/go synced 2024-11-18 10:14:45 -07:00

x/tools: support Go 1.9 type aliases

For #18130.

Change-Id: Ice695602619dbbf851af970e790f07ff2ac2c141
Reviewed-on: https://go-review.googlesource.com/36623
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Alan Donovan 2017-02-09 14:54:08 -05:00
parent 8524ce5143
commit 6e7ee5a9ec
14 changed files with 177 additions and 129 deletions

View File

@ -327,9 +327,9 @@ func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error
return nil, fmt.Errorf("unexpected AST for expr: %T", n) return nil, fmt.Errorf("unexpected AST for expr: %T", n)
} }
t := qpos.info.TypeOf(expr) typ := qpos.info.TypeOf(expr)
if t == nil { if typ == nil {
t = types.Typ[types.Invalid] typ = types.Typ[types.Invalid]
} }
constVal := qpos.info.Types[expr].Value constVal := qpos.info.Types[expr].Value
if c, ok := obj.(*types.Const); ok { if c, ok := obj.(*types.Const); ok {
@ -339,11 +339,11 @@ func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error
return &describeValueResult{ return &describeValueResult{
qpos: qpos, qpos: qpos,
expr: expr, expr: expr,
typ: t, typ: typ,
constVal: constVal, constVal: constVal,
obj: obj, obj: obj,
methods: accessibleMethods(t, qpos.info.Pkg), methods: accessibleMethods(typ, qpos.info.Pkg),
fields: accessibleFields(t, qpos.info.Pkg), fields: accessibleFields(typ, qpos.info.Pkg),
}, nil }, nil
} }
@ -425,48 +425,46 @@ func (r *describeValueResult) JSON(fset *token.FileSet) []byte {
func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) { func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) {
var description string var description string
var t types.Type var typ types.Type
switch n := path[0].(type) { switch n := path[0].(type) {
case *ast.Ident: case *ast.Ident:
t = qpos.info.TypeOf(n) obj := qpos.info.ObjectOf(n).(*types.TypeName)
switch t := t.(type) { typ = obj.Type()
case *types.Basic: 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 " description = "reference to built-in "
} else {
case *types.Named: description = "reference to " // (Named type)
isDef := t.Obj().Pos() == n.Pos() // see caveats at isDef above
if isDef {
description = "definition of "
} else {
description = "reference to "
}
} }
case ast.Expr: case ast.Expr:
t = qpos.info.TypeOf(n) typ = qpos.info.TypeOf(n)
default: default:
// Unreachable? // Unreachable?
return nil, fmt.Errorf("unexpected AST for type: %T", n) 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). // Show sizes for structs and named types (it's fairly obvious for others).
switch t.(type) { switch typ.(type) {
case *types.Named, *types.Struct: case *types.Named, *types.Struct:
szs := types.StdSizes{WordSize: 8, MaxAlign: 8} // assume amd64 szs := types.StdSizes{WordSize: 8, MaxAlign: 8} // assume amd64
description = fmt.Sprintf("%s (size %d, align %d)", description, description = fmt.Sprintf("%s (size %d, align %d)", description,
szs.Sizeof(t), szs.Alignof(t)) szs.Sizeof(typ), szs.Alignof(typ))
} }
return &describeTypeResult{ return &describeTypeResult{
qpos: qpos, qpos: qpos,
node: path[0], node: path[0],
description: description, description: description,
typ: t, typ: typ,
methods: accessibleMethods(t, qpos.info.Pkg), methods: accessibleMethods(typ, qpos.info.Pkg),
fields: accessibleFields(t, qpos.info.Pkg), fields: accessibleFields(typ, qpos.info.Pkg),
}, nil }, nil
} }
@ -672,23 +670,29 @@ func formatMember(obj types.Object, maxname int) string {
fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier))
case *types.TypeName: 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. // Abbreviate long aggregate type names.
var abbrev string switch typ := typ.(type) {
switch t := obj.Type().Underlying().(type) {
case *types.Interface: case *types.Interface:
if t.NumMethods() > 1 { if typ.NumMethods() > 1 {
abbrev = "interface{...}" typestr = "interface{...}"
} }
case *types.Struct: case *types.Struct:
if t.NumFields() > 1 { if typ.NumFields() > 1 {
abbrev = "struct{...}" typestr = "struct{...}"
} }
} }
if abbrev == "" { if typestr == "" {
fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type().Underlying(), qualifier)) typestr = types.TypeString(typ, qualifier)
} else {
fmt.Fprintf(&buf, " %s", abbrev)
} }
buf.WriteString(typestr)
case *types.Var: case *types.Var:
fmt.Fprintf(&buf, " %s", types.TypeString(obj.Type(), qualifier)) 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 { func (r *describePackageResult) JSON(fset *token.FileSet) []byte {
var members []*serial.DescribeMember var members []*serial.DescribeMember
for _, mem := range r.members { for _, mem := range r.members {
typ := mem.obj.Type() obj := mem.obj
typ := obj.Type()
var val string var val string
switch mem := mem.obj.(type) { var alias string
switch obj := obj.(type) {
case *types.Const: case *types.Const:
val = mem.Val().String() val = obj.Val().String()
case *types.TypeName: case *types.TypeName:
typ = typ.Underlying() if isAlias(obj) {
alias = "= " // kludgy
} else {
typ = typ.Underlying()
}
} }
members = append(members, &serial.DescribeMember{ members = append(members, &serial.DescribeMember{
Name: mem.obj.Name(), Name: obj.Name(),
Type: typ.String(), Type: alias + typ.String(),
Value: val, Value: val,
Pos: fset.Position(mem.obj.Pos()).String(), Pos: fset.Position(obj.Pos()).String(),
Kind: tokenOf(mem.obj), Kind: tokenOf(obj),
Methods: methodsToSerial(r.pkg, mem.methods, fset), Methods: methodsToSerial(r.pkg, mem.methods, fset),
}) })
} }

View File

@ -218,6 +218,7 @@ func TestGuru(t *testing.T) {
} }
for _, filename := range []string{ for _, filename := range []string{
"testdata/src/alias/alias.go", // iff guru.HasAlias (go1.9)
"testdata/src/calls/main.go", "testdata/src/calls/main.go",
"testdata/src/describe/main.go", "testdata/src/describe/main.go",
"testdata/src/freevars/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. // wording for a "no such file or directory" error.
continue continue
} }
if filename == "testdata/src/alias/alias.go" && !guru.HasAlias {
continue
}
json := strings.Contains(filename, "-json/") json := strings.Contains(filename, "-json/")
queries := parseQueries(t, filename) queries := parseQueries(t, filename)

View File

@ -102,16 +102,20 @@ func implements(q *Query) error {
} }
// Find all named types, even local types (which can have // Find all named types, even local types (which can have
// methods via promotion) and the built-in "error". // methods due to promotion) and the built-in "error".
var allNamed []types.Type // 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 _, info := range lprog.AllPackages {
for _, obj := range info.Defs { for _, obj := range info.Defs {
if obj, ok := obj.(*types.TypeName); ok { if obj, ok := obj.(*types.TypeName); ok && !isAlias(obj) {
allNamed = append(allNamed, obj.Type()) 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 var msets typeutil.MethodSetCache

15
cmd/guru/isAlias18.go Normal file
View File

@ -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

15
cmd/guru/isAlias19.go Normal file
View File

@ -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

20
cmd/guru/testdata/src/alias/alias.go vendored Normal file
View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -1,11 +0,0 @@
package aliaslib
type T int
func (T) Method(x *int) *int
func F()
const C = 3
var V = 0

View File

@ -181,11 +181,9 @@ func whicherrs(q *Query) error {
// typeswitch or assert to. This means finding out // typeswitch or assert to. This means finding out
// if the type pointed to can be seen by us. // if the type pointed to can be seen by us.
// //
// For the purposes of this analysis, the type is always // For the purposes of this analysis, we care only about
// either a Named type or a pointer to one. // TypeNames of Named or pointer-to-Named types.
// There are cases where error can be implemented // We ignore other types (e.g. structs) that implement error.
// by unnamed types, but in that case, we can't assert to
// it, so we don't care about it for this analysis.
var name *types.TypeName var name *types.TypeName
switch t := conc.(type) { switch t := conc.(type) {
case *types.Pointer: case *types.Pointer:

View File

@ -195,9 +195,10 @@ func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *
obj := scope.Lookup(name) obj := scope.Lookup(name)
memberFromObject(p, obj, nil) memberFromObject(p, obj, nil)
if obj, ok := obj.(*types.TypeName); ok { if obj, ok := obj.(*types.TypeName); ok {
named := obj.Type().(*types.Named) if named, ok := obj.Type().(*types.Named); ok {
for i, n := 0, named.NumMethods(); i < n; i++ { for i, n := 0, named.NumMethods(); i < n; i++ {
memberFromObject(p, named.Method(i), nil) memberFromObject(p, named.Method(i), nil)
}
} }
} }
} }

View File

@ -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 // This is a workaround for a bug in go/ssa/testmain.go: it creates
// InternalExamples even for Example functions with no Output comment. // 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 { func ext۰testing۰runExample(fr *frame, args []value) value {
// This is a stripped down runExample that simply calls the function. // This is a stripped down runExample that simply calls the function.
// It does not capture and compare output nor recover from panic. // 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 { func ext۰time۰now(fr *frame, args []value) value {
nano := time.Now().UnixNano() 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 { func ext۰time۰Sleep(fr *frame, args []value) value {

View File

@ -75,9 +75,6 @@ type Member interface {
} }
// A Type is a Member of a Package representing a package-level named type. // A Type is a Member of a Package representing a package-level named type.
//
// Type() returns a *types.Named.
//
type Type struct { type Type struct {
object *types.TypeName object *types.TypeName
pkg *Package pkg *Package

View File

@ -101,14 +101,16 @@ func (a *analysis) doTypeInfo(info *loader.PackageInfo, implements map[*types.Na
// IMPLEMENTS & METHOD SETS // IMPLEMENTS & METHOD SETS
for _, obj := range info.Defs { for _, obj := range info.Defs {
if obj, ok := obj.(*types.TypeName); ok { 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()) qualifier := types.RelativeTo(obj.Pkg())
T := obj.Type().(*types.Named)
v := &TypeInfoJSON{ v := &TypeInfoJSON{
Name: obj.Name(), Name: obj.Name(),
Size: sizes.Sizeof(T), Size: sizes.Sizeof(T),