go/typesadonovan@google.comContents
Introduction
go/typesgo getThe type checker complements several existing standard packages for analyzing Go programs. We've listed them below.
→   go/types
    go/constant
    go/parser
    go/ast
    go/scanner
    go/token
go/tokengo/scannergo/astgo/parsergo/constantgolang.org/x/tools/go/loaderx/toolsThe Go type checker does three main things. First, for every name in the program, it determines which declaration the name refers to; this is known as identifier resolution. Second, for every expression in the program, it determines what type that expression has, or reports an error if the expression has no type, or has an inappropriate type for its context; this is known as type deduction. Third, for every constant expression in the program, it determines the value of that constant; this is known as constant evaluation.
unsafe.SizeofkT{k: 0}TkTkNonetheless, the three processes of identifier resolution, type deduction, and constant evaluation can be separated for the purpose of explanation.
An Example
go get github.com/golang/example/gotypes/...// go get github.com/golang/example/gotypes/pkginfo
package main
import (
	"fmt"
	"go/ast"
	"go/importer"
	"go/parser"
	"go/token"
	"go/types"
	"log"
)
const hello = `package main
import "fmt"
func main() {
        fmt.Println("Hello, world")
}`
func main() {
	fset := token.NewFileSet()
	// Parse the input string, []byte, or io.Reader,
	// recording position information in fset.
	// ParseFile returns an *ast.File, a syntax tree.
	f, err := parser.ParseFile(fset, "hello.go", hello, 0)
	if err != nil {
		log.Fatal(err) // parse error
	}
	// A Config controls various options of the type checker.
	// The defaults work fine except for one setting:
	// we must specify how to deal with imports.
	conf := types.Config{Importer: importer.Default()}
	// Type-check the package containing only file f.
	// Check returns a *types.Package.
	pkg, err := conf.Check("cmd/hello", fset, []*ast.File{f}, nil)
	if err != nil {
		log.Fatal(err) // type error
	}
	fmt.Printf("Package  %q\n", pkg.Path())
	fmt.Printf("Name:    %s\n", pkg.Name())
	fmt.Printf("Imports: %s\n", pkg.Imports())
	fmt.Printf("Scope:   %s\n", pkg.Scope())
}
token.FileSetgo/tokenFileSetFileSettoken.PosFileSettoken.PosFileSetConfigimporter.Default()CheckPackage"cmd/hello"InfoCheckPackagePackagetype Package struct{ ... }
func (*Package) Path() string
func (*Package) Name() string
func (*Package) Scope() *Scope
func (*Package) Imports() []*Package
Finally, the program prints the attributes of the package, shown below. (The hexadecimal number may vary from one run to the next.)
$ go build github.com/golang/example/gotypes/pkginfo
$ ./pkginfo
Package  "cmd/hello"
Name:    main
Imports: [package fmt ("fmt")]
Scope:   package "cmd/hello" scope 0x820533590 {
.  func cmd/hello.main()
}
Path"encoding/json"$GOPATHNamepackagejsonScopeImportsObjects
ast.IdentvartypefuncObjecttype Object interface {
    Name() string   // package-local object name
    Exported() bool // reports whether the name starts with a capital letter
    Type() Type     // object type
    Pos() token.Pos // position of object identifier in declaration
    Parent() *Scope // scope in which this object is declared
    Pkg() *Package  // nil for objects in the Universe scope and labels
    Id() string     // object id (see Ids section below)
}
NameExportedNameast.IsExported(obj.Name())TypePostoken.Pos(*token.FileSet).PositionStringfmt.Println(fset.Position(obj.Pos())) // "hello.go:10:6"
Postoken.NoPosjsonimport "encoding/json"appendlennilObjectObjectObject = *Func         // function, concrete method, or abstract method
       | *Var          // variable, parameter, result, or struct field
       | *Const        // constant
       | *TypeName     // type name
       | *Label        // statement label
       | *PkgName      // package name, e.g. json after import "encoding/json"
       | *Builtin      // predeclared function such as append or len
       | *Nil          // predeclared nil
ObjectObjectxyx==yParentScopeParentPkgPackageIdTypeObjectfunc (*Func) Scope() *Scope
func (*Var) Anonymous() bool
func (*Var) IsField() bool
func (*Const) Val() constant.Value
func (*TypeName) IsAlias() bool
func (*PkgName) Imported() *Package
(*Func).Scope(*Var).IsField(*Var).Anonymousstruct{T T}struct{T}(*Const).Val(*TypeName).IsAliastype I = intNamedtype Celsius float64(*PkgName).Importedencoding/jsonjsonPkgNamePackagePkgNamePackageast.NodeObjectTypego/astObjectObjectast.ObjectT{k: 0}ast.ObjectIdentifier Resolution
InfoChecktype Info struct {
	Defs       map[*ast.Ident]Object
	Uses       map[*ast.Ident]Object
	Implicits  map[ast.Node]Object
	Selections map[*ast.SelectorExpr]*Selection
	Scopes     map[ast.Node]*Scope
	...
}
Checkmap[*ast.Ident]ObjectDefsUsesvar x int        // def of x, use of int
fmt.Println(x)   // uses of fmt, Println, and x
type T struct{U} // def of T, use of U (type), def of U (field)
DefsUsesstruct{U}UUTypeNameVarThe function below prints the location of each referring and defining identifier in the input program, and the object it refers to.
// go get github.com/golang/example/gotypes/defsuses
func PrintDefsUses(fset *token.FileSet, files ...*ast.File) error {
	conf := types.Config{Importer: importer.Default()}
	info := &types.Info{
		Defs: make(map[*ast.Ident]types.Object),
		Uses: make(map[*ast.Ident]types.Object),
	}
	_, err := conf.Check("hello", fset, files, info)
	if err != nil {
		return err // type error
	}
	for id, obj := range info.Defs {
		fmt.Printf("%s: %q defines %v\n",
			fset.Position(id.Pos()), id.Name, obj)
	}
	for id, obj := range info.Uses {
		fmt.Printf("%s: %q uses %v\n",
			fset.Position(id.Pos()), id.Name, obj)
	}
	return nil
}
Let's use the hello, world program again as the input:
// go get github.com/golang/example/gotypes/hello
package main
import "fmt"
func main() {
	fmt.Println("Hello, 世界")
}
This is what it prints:
$ go build github.com/golang/example/gotypes/defsuses
$ ./defsuses
hello.go:1:9: "main" defines <nil>
hello.go:5:6: "main" defines func hello.main()
hello.go:6:9: "fmt" uses package fmt
hello.go:6:13: "Println" uses func fmt.Println(a ...interface{}) (n int, err error)
DefsmainDefsImplicitsObjectast.Identyyswitch y := x.(type) {
case int:
	fmt.Printf("%d", y)
case string:
	fmt.Printf("%q", y)
default:
	fmt.Print(y)
}
VaryImplicitsast.CaseClauseVardefaultnilVaryDefsjsonast.Identimport "encoding/json"
Implicitsast.ImportSpecPkgNamejsonSelectionsmap[*ast.SelectorExpr]*Selectionexpr.fexprfast.SelectorExprSelectionast.SelectorExprfmt.PrintlnSelectionsfmtPrintlnUsesast.SelectorExprScopes
Scopetype Scope struct{ ... }
func (s *Scope) Names() []string
func (s *Scope) Lookup(name string) Object
NamesLookup for _, name := range scope.Names() {
	fmt.Println(scope.Lookup(name))
}
go/typesScopepackage main
import "fmt"
func main() {
	const message = "hello, world"
	fmt.Println(message)
}
inttrueappendTypeNameConstBuiltinUniverse*Scope"main"main"fmt"PkgNamefmtmainmessageConstmainifforswitchcaseselectScopefunc (s *Scope) Parent() *Scope
func (s *Scope) NumChildren() int
func (s *Scope) Child(i int) *Scope
ParentChildParentScopeUniverseUniverseUniverseUniversePackageScope*ast.File*ast.IfStmtScopesInfo(*Func).ScopeScopeLookupParentScopefunc (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object)
posScopefunc (s *Scope) Pos() token.Pos
func (s *Scope) End() token.Pos
func (s *Scope) Contains(pos token.Pos) bool
func (s *Scope) Innermost(pos token.Pos) *Scope
PosEndScopeContainsInnermostParseComments// go get github.com/golang/example/gotypes/lookup
func main() {
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "hello.go", hello, parser.ParseComments)
	if err != nil {
		log.Fatal(err) // parse error
	}
	conf := types.Config{Importer: importer.Default()}
	pkg, err := conf.Check("cmd/hello", fset, []*ast.File{f}, nil)
	if err != nil {
		log.Fatal(err) // type error
	}
	// Each comment contains a name.
	// Look up that name in the innermost scope enclosing the comment.
	for _, comment := range f.Comments {
		pos := comment.Pos()
		name := strings.TrimSpace(comment.Text())
		fmt.Printf("At %s,\t%q = ", fset.Position(pos), name)
		inner := pkg.Scope().Innermost(pos)
		if _, obj := inner.LookupParent(name, pos); obj != nil {
			fmt.Println(obj)
		} else {
			fmt.Println("not found")
		}
	}
}
pkg.Scope().Innermost(pos)ScopeLookupParent(name, pos)"append""fmt"mainconst hello = `
package main
import "fmt"
// append
func main() {
        // fmt
        fmt.Println("Hello, world")
        // main
        main, x := 1, 2
        // main
        print(main, x)
        // x
}
// x
`
Here's the output:
$ go build github.com/golang/example/gotypes/lookup
$ ./lookup
At hello.go:6:1,        "append" = builtin append
At hello.go:8:9,        "fmt" = package fmt
At hello.go:10:9,       "main" = func cmd/hello.main()
At hello.go:12:9,       "main" = var main int
At hello.go:14:9,       "x" = var x int
At hello.go:16:1,       "x" = not found
mainmainxDownload the program and modify both the input program and the set of comments to get a better feel for how name resolution works.
The table below summarizes which kinds of objects may be declared at each level of the tree of lexical blocks.
            Universe File Package Local
Builtin        ✔
Nil            ✔
Const          ✔            ✔      ✔
TypeName       ✔            ✔      ✔
Func                        ✔
Var                         ✔      ✔
PkgName               ✔
Label                              ✔
Initialization Order
In the course of identifier resolution, the type checker constructs a graph of references among declarations of package-level variables and functions. The type checker reports an error if the initializer expression for a variable refers to that variable, whether directly or indirectly.
The reference graph determines the initialization order of the package-level variables, as required by the Go spec, using a breadth-first algorithm. First, variables in the graph with no successors are removed, sorted into the order in which they appear in the source code, then added to a list. This creates more variables that have no successors. The process repeats until they have all been removed.
InitOrderInfo[]Initializertype Info struct {
	...
	InitOrder []Initializer
	...
}
type Initializer struct {
	Lhs []*Var // var Lhs = Rhs
	Rhs ast.Expr
}
Each element of the list represents a single initializer expression that must be executed, and the variables to which it is assigned. The variables may number zero, one, or more, as in these examples:
var _ io.Writer = new(bytes.Buffer)
var rx = regexp.MustCompile("^b(an)*a$")
var cwd, cwdErr = os.Getwd()
(*Package).ImportsTypes
ObjectTypeObjectTypetype Type interface {
	Underlying() Type
}
And here are the eleven concrete types that satisfy it:
Type = *Basic
     | *Pointer
     | *Array
     | *Slice
     | *Map
     | *Chan
     | *Struct
     | *Tuple
     | *Signature
     | *Named
     | *Interface
NamedTypet1==t2func Identical(t1, t2 Type) bool
Typegolang.org/x/tools/go/types/typeutilx==yT(v)func AssignableTo(V, T Type) bool
func Comparable(T Type) bool
func ConvertibleTo(V, T Type) bool
Let's take a look at each kind of type.
Basic types
Basicunsafe.Pointertype Basic struct{...}
func (*Basic) Kind() BasicKind
func (*Basic) Name() string
func (*Basic) Info() BasicInfo
KindBoolStringInt16ByteUint8RuneInt32UnsafePointerunsafe.PointerUntypedBoolUntypedIntUntypedNilnilInvalidLabelBuiltinPkgNameName"float64"InfoTypTyp[String]*BasicstringUniverseTypintNamedunsafe.PointerPointerx==yboolSimple Composite Types
PointerArraySliceMapChanElemT*T[n]T[]Tmap[K]Tchan Treflect.Value*Map*Chan*Arrayfunc (*Map) Key() Type
func (*Chan) Dir() ChanDir      // = Send | Recv | SendRecv
func (*Array) Len() int64
Struct Types
A struct type has an ordered list of fields and a corresponding ordered list of field tags.
type Struct struct{ ... } 
func (*Struct) NumFields() int
func (*Struct) Field(i int) *Var
func (*Struct) Tag(i int) string
VarIsFieldParentnew(S).fnew(S).d.e.fStructSfAnonymousOne subtlety is relevant to tools that generate documentation. When analyzing a declaration such as this,
type T struct{x int}
Varx"T.x""T"Varxtype T struct{x int}
type U T
VarxTUxTtype T U
type U struct{x int}
A similar issue applies to the methods of named interface types.
Tuple Types
Like a struct, a tuple type has an ordered list of fields, and fields may be named.
type Tuple struct{ ... }
func (*Tuple) Len() int
func (*Tuple) At(i int) *Var
Although tuples are not the type of any variable in Go, they are the type of some expressions, such as the right-hand sides of these assignments:
v, ok = m[key]
v, ok = <-ch
v, ok = x.(T)
f, err = os.Open(filename)
Tuples also represent the types of the parameter list and the result list of a function, as we will see.
*TupleFunction and Method Types
Signaturetype Signature struct{ ... }
func (*Signature) Recv() *Var
func (*Signature) Params() *Tuple
func (*Signature) Results() *Tuple
func (*Signature) Variadic() bool
fmt.PrintlnVariadicappendSignatureRecvFuncIdenticalBuiltinappendSignatureBuiltinInvalidNamed Types
Dictionarymap[string]stringtype Dictionary = map[string]string
TypeNameDictionaryIsAliasTypeMapmap[string]stringThe second form of type declaration, and the only kind prior to Go 1.9, does not use an equals sign:
type Celsius float64
Namedfloat64Namedfloat64TypeNameNamedSince Go 1.9, the Go language specification has used the term defined types instead of named types; the essential property of a defined type is not that it has a name, but that it is a distinct type with its own method set. However, the type checker API predates that change and instead calls defined types "named" types.
type Named struct{ ... }
func (*Named) NumMethods() int
func (*Named) Method(i int) *Func
func (*Named) Obj() *TypeName
func (*Named) Underlying() Type
NamedObjTypeNameTypeNameTypeNamedNamedNamedTypeNameNamedNumMethodsMethodNamedTypeUnderlying*NamedUnderlyingUnderlyingintTUtype T int
type U T
TypeThis is a common pattern:
// handle types of composite literal
switch u := t.Underlying().(type) {
case *Struct:        // ...
case *Map:           // ...
case *Array, *Slice: // ...
default:
	panic("impossible")
}
Interface Types
Interfacetype Interface struct{ ... }
func (*Interface) Empty() bool
func (*Interface) NumMethods() int
func (*Interface) Method(i int) *Func
func (*Interface) NumEmbeddeds() int
func (*Interface) Embedded(i int) *Named
func (*Interface) NumExplicitMethods() int
func (*Interface) ExplicitMethod(i int) *Func
ExplicitMethodEmbeddedMethodEmptyNumMethods() == 0FuncfIJtype I interface { f() }
type J I
IsInterfacefunc IsInterface(Type) bool
The type checker provides three utility methods relating to interface satisfaction:
func Implements(V Type, T *Interface) bool
func AssertableTo(V *Interface, T Type) bool
func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool)
ImplementsMissingMethodImplementsAssertableTov.(T)Tv// error: io.Writer is not assertible to int
func f(w io.Writer) int { return w.(int) } 
TypeAndValue
InfoTypestype Info struct {
	...
	Types map[ast.Expr]TypeAndValue
}
DefsUses*ast.KeyValuePair*ast.EllipsisTypesTypeAndValuetype TypeAndValue struct {
	Type  Type
	Value constant.Value // for constant expressions only
	...
}
func (TypeAndValue) IsVoid() bool      // e.g. "main()"
func (TypeAndValue) IsType() bool      // e.g. "*os.File"
func (TypeAndValue) IsBuiltin() bool   // e.g. "len(x)"
func (TypeAndValue) IsValue() bool     // e.g. "*os.Stdout"
func (TypeAndValue) IsNil() bool       // e.g. "nil"
func (TypeAndValue) Addressable() bool // e.g. "a[i]" but not "f()", "m[key]"
func (TypeAndValue) Assignable() bool  // e.g. "a[i]", "m[key]"
func (TypeAndValue) HasOk() bool       // e.g. "<-ch", "m[key]"
The statement below inspects every expression within the AST of a single type-checked file and prints its type, value, and mode:
// go get github.com/golang/example/gotypes/typeandvalue
// f is a parsed, type-checked *ast.File.
ast.Inspect(f, func(n ast.Node) bool {
	if expr, ok := n.(ast.Expr); ok {
		if tv, ok := info.Types[expr]; ok {
			fmt.Printf("%-24s\tmode:  %s\n", nodeString(expr), mode(tv))
			fmt.Printf("\t\t\t\ttype:  %v\n", tv.Type)
			if tv.Value != nil {
				fmt.Printf("\t\t\t\tvalue: %v\n", tv.Value)
			}
		}
	}
	return true
})
It makes use of these two helper functions, which are not shown:
// nodeString formats a syntax tree in the style of gofmt.
func nodeString(n ast.Node) string
// mode returns a string describing the mode of an expression.
func mode(tv types.TypeAndValue) string
Given this input:
const input = `
package main
var m = make(map[string]int)
func main() {
	v, ok := m["hello, " + "world"]
	print(rune(v), ok)
}
`
the program prints:
$ go build github.com/golang/example/gotypes/typeandvalue
$ ./typeandvalue
make(map[string]int)            mode:  value
                                type:  map[string]int
make                            mode:  builtin
                                type:  func(map[string]int) map[string]int
map[string]int                  mode:  type
                                type:  map[string]int
string                          mode:  type
                                type:  string
int                             mode:  type
                                type:  int
m["hello, "+"world"]            mode:  value,assignable,ok
                                type:  (int, bool)
m                               mode:  value,addressable,assignable
                                type:  map[string]int
"hello, " + "world"             mode:  value
                                type:  string
                                value: "hello, world"
"hello, "                       mode:  value
                                type:  untyped string
                                value: "hello, "
"world"                         mode:  value
                                type:  untyped string
                                value: "world"
print(rune(v), ok)              mode:  void
                                type:  ()
print                           mode:  builtin
                                type:  func(rune, bool)
rune(v)                         mode:  value
                                type:  rune
rune                            mode:  type
                                type:  rune
...more not shown...
makeprintm["hello"](int, bool)mDownload the example and vary the inputs and see what the program prints.
govetx.fx.f()x.f// go get github.com/golang/example/gotypes/nilfunc
// CheckNilFuncComparison reports unintended comparisons
// of functions against nil, e.g., "if x.Method == nil {".
func CheckNilFuncComparison(info *types.Info, n ast.Node) {
	e, ok := n.(*ast.BinaryExpr)
	if !ok {
		return // not a binary operation
	}
	if e.Op != token.EQL && e.Op != token.NEQ {
		return // not a comparison
	}
	// If this is a comparison against nil, find the other operand.
	var other ast.Expr
	if info.Types[e.X].IsNil() {
		other = e.Y
	} else if info.Types[e.Y].IsNil() {
		other = e.X
	} else {
		return // not a comparison against nil
	}
	// Find the object.
	var obj types.Object
	switch v := other.(type) {
	case *ast.Ident:
		obj = info.Uses[v]
	case *ast.SelectorExpr:
		obj = info.Uses[v.Sel]
	default:
		return // not an identifier or selection
	}
	if _, ok := obj.(*types.Func); !ok {
		return // not a function or method
	}
	fmt.Printf("%s: comparison of function %v %v nil is always %v\n",
		fset.Position(e.Pos()), obj.Name(), e.Op, e.Op == token.NEQ)
}
Given this input,
const input = `package main
import "bytes"
func main() {
	var buf bytes.Buffer
	if buf.Bytes == nil && bytes.Repeat != nil && main == nil {
		// ...
	}
}
`
the program reports these errors:
$ go build github.com/golang/example/gotypes/nilfunc
$ ./nilfunc
input.go:7:5: comparison of function Bytes == nil is always false
input.go:7:25: comparison of function Repeat != nil is always true
input.go:7:48: comparison of function main == nil is always false
Selections
expr.ffSelectionsInfoSelectiontype Selection struct{ ... }
func (s *Selection) Kind() SelectionKind // = FieldVal | MethodVal | MethodExpr
func (s *Selection) Recv() Type
func (s *Selection) Obj() Object
func (s *Selection) Type() Type
func (s *Selection) Index() []int
func (s *Selection) Indirect() bool
Kindtype T struct{Field int}
func (T) Method() {}
var v T
                     // Kind            Type
    var _ = v.Field  // FieldVal        int
    var _ = v.Method // MethodVal       func()
    var _ = T.Method // MethodExpr      func(T)
SelectionObjObject*Var*FuncexprTypeMethodValMethodExprIndexIndirectexpr.fexpr.d.e.fIndexIndirectLookupFieldOrMethodTypeSelectionfunc LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) \
    (obj Object, index []int, indirect bool)
SelectionObjIndexIndirectaddressableT&*TLookupFieldOrMethod*TTT*TLookupFieldOrMethod(pkg *Package, name string)IdIds
LookupFieldOrMethodPackagefpackage a
type A int
func (A) f()
package b
type B int
func (B) f()
package c
import ( "a"; "b" )
type C struct{a.A; b.B} // C has two methods called f
c.CfffᵃfᵇFᵃFᵇFfcccffᶜCCaffᵃObject.IdIdfunc Id(pkg *Package, name string) string
expr.fxreflect.StructFieldNamePkgPathFieldByNamereflect.Valuereflect.TypeMethod Sets
T*TTTNewMethodSet(T)type MethodSet struct{ ... }
func NewMethodSet(T Type) *MethodSet
func (s *MethodSet) Len() int
func (s *MethodSet) At(i int) *Selection
func (s *MethodSet) Lookup(pkg *Package, name string) *Selection
LenAtSelectionsMethodValIdLookupNewMethodSetgolang.org/x/tools/go/types/typeutilMethodSetCacheMethodSetLookupFieldOrMethodThe next program generates a boilerplate declaration of a new concrete type that satisfies an existing interface. Here's an example:
$ ./skeleton io ReadWriteCloser buffer
// *buffer implements io.ReadWriteCloser.
type buffer struct{}
func (b *buffer) Close() error {
	panic("unimplemented")
}
func (b *buffer) Read(p []byte) (n int, err error) {
	panic("unimplemented")
}
func (b *buffer) Write(p []byte) (n int, err error) {
	panic("unimplemented")
}
mainPrintSkeleton// go get github.com/golang/example/gotypes/skeleton
func PrintSkeleton(pkg *types.Package, ifacename, concname string) error {
	obj := pkg.Scope().Lookup(ifacename)
	if obj == nil {
		return fmt.Errorf("%s.%s not found", pkg.Path(), ifacename)
	}
	if _, ok := obj.(*types.TypeName); !ok {
		return fmt.Errorf("%v is not a named type", obj)
	}
	iface, ok := obj.Type().Underlying().(*types.Interface)
	if !ok {
		return fmt.Errorf("type %v is a %T, not an interface",
			obj, obj.Type().Underlying())
	}
	// Use first letter of type name as receiver parameter.
	if !isValidIdentifier(concname) {
		return fmt.Errorf("invalid concrete type name: %q", concname)
	}
	r, _ := utf8.DecodeRuneInString(concname)
	fmt.Printf("// *%s implements %s.%s.\n", concname, pkg.Path(), ifacename)
	fmt.Printf("type %s struct{}\n", concname)
	mset := types.NewMethodSet(iface)
	for i := 0; i < mset.Len(); i++ {
		meth := mset.At(i).Obj()
		sig := types.TypeString(meth.Type(), (*types.Package).Name)
		fmt.Printf("func (%c *%s) %s%s {\n\tpanic(\"unimplemented\")\n}\n",
			r, concname, meth.Name(),
			strings.TrimPrefix(sig, "func"))
	}
	return nil
}
PrintSkeletonsigmeth.Type().String()net/http.ResponseWriterTypeString(*types.Package).Namehttp$ ./skeleton net/http Handler myHandler
// *myHandler implements net/http.Handler.
type myHandler struct{}
func (m *myHandler) ServeHTTP(http.ResponseWriter, *http.Request) {
	panic("unimplemented")
}
pkg// go get github.com/golang/example/gotypes/implements
// Find all named types at package level.
var allNamed []*types.Named
for _, name := range pkg.Scope().Names() {
	if obj, ok := pkg.Scope().Lookup(name).(*types.TypeName); ok {
		allNamed = append(allNamed, obj.Type().(*types.Named))
	}
}
// Test assignability of all distinct pairs of
// named types (T, U) where U is an interface.
for _, T := range allNamed {
	for _, U := range allNamed {
		if T == U || !types.IsInterface(U) {
			continue
		}
		if types.AssignableTo(T, U) {
			fmt.Printf("%s satisfies %s\n", T, U)
		} else if !types.IsInterface(T) &&
			types.AssignableTo(types.NewPointer(T), U) {
			fmt.Printf("%s satisfies %s\n", types.NewPointer(T), U)
		}
	}
}
Given this input,
// go get github.com/golang/example/gotypes/implements
const input = `package main
type A struct{}
func (*A) f()
type B int
func (B) f()
func (*B) g()
type I interface { f() }
type J interface { g() }
`
the program prints:
$ go build github.com/golang/example/gotypes/implements
$ ./implements
*hello.A satisfies hello.I
hello.B satisfies hello.I
*hello.B satisfies hello.J
Bg*Btypes.NewPointer(T)Constants
[16]bytetypeandvalue"Hello, " + "world"ValueTypeAndValueValuego/constantpackage constant // go/constant
type Value interface {
	Kind() Kind 
}
type Kind int // one of Unknown, Bool, String, Int, Float, Complex
The interface has only one method, for discriminating the various kinds of constants, but the package provides many functions for inspecting a value of a known kind,
// Accessors
func BoolVal(x Value) bool
func Float32Val(x Value) (float32, bool)
func Float64Val(x Value) (float64, bool)
func Int64Val(x Value) (int64, bool)
func StringVal(x Value) string
func Uint64Val(x Value) (uint64, bool)
func Bytes(x Value) []byte
func BitLen(x Value) int
func Sign(x Value) int
for performing arithmetic on values,
// Operations
func Compare(x Value, op token.Token, y Value) bool
func UnaryOp(op token.Token, y Value, prec uint) Value
func BinaryOp(x Value, op token.Token, y Value) Value
func Shift(x Value, op token.Token, s uint) Value
func Denom(x Value) Value
func Num(x Value) Value
func Real(x Value) Value
func Imag(x Value) Value
and for constructing new values:
// Constructors
func MakeBool(b bool) Value
func MakeFloat64(x float64) Value
func MakeFromBytes(bytes []byte) Value
func MakeFromLiteral(lit string, tok token.Token, prec uint) Value
func MakeImag(x Value) Value
func MakeInt64(x int64) Value
func MakeString(s string) Value
func MakeUint64(x uint64) Value
func MakeUnknown() Value
Valueint64float64go/constantIntRatFloatmath/bigValuesSize and Alignment
unsafe.Sizeof(v)unsafe.Alignof(v)unsafe.Offsetof(v.f)vgcamd64types.Sizestypes.Configpackage types
type Sizes interface {
	Alignof(T Type) int64
	Offsetsof(fields []*Var) []int64
	Sizeof(T Type) int64
}
StdSizestype StdSizes struct {
	WordSize int64
	MaxAlign int64
}
StdSizes{8, 8}Sizeshugeparam-bytes// go get github.com/golang/example/gotypes/hugeparam
var bytesFlag = flag.Int("bytes", 48, "maximum parameter size in bytes")
var sizeof = (&types.StdSizes{8, 8}).Sizeof // the sizeof function
func PrintHugeParams(fset *token.FileSet, info *types.Info, files []*ast.File) {
	checkTuple := func(descr string, tuple *types.Tuple) {
		for i := 0; i < tuple.Len(); i++ {
			v := tuple.At(i)
			if sz := sizeof(v.Type()); sz > int64(*bytesFlag) {
				fmt.Printf("%s: %q %s: %s = %d bytes\n",
					fset.Position(v.Pos()),
					v.Name(), descr, v.Type(), sz)
			}
		}
	}
	checkSig := func(sig *types.Signature) {
		checkTuple("parameter", sig.Params())
		checkTuple("result", sig.Results())
	}
	for _, file := range files {
		ast.Inspect(file, func(n ast.Node) bool {
			switch n := n.(type) {
			case *ast.FuncDecl:
				checkSig(info.Defs[n.Name].Type().(*types.Signature))
			case *ast.FuncLit:
				checkSig(info.Types[n.Type].Type.(*types.Signature))
			}
			return true
		})
	}
}
Inspect*ast.FuncDecl*ast.FuncLitencoding/xmlStartElement% ./hugeparam encoding/xml
/go/src/encoding/xml/marshal.go:167:50: "start" parameter: encoding/xml.StartElement = 56 bytes
/go/src/encoding/xml/marshal.go:734:97: "" result: encoding/xml.StartElement = 56 bytes
/go/src/encoding/xml/marshal.go:761:51: "start" parameter: encoding/xml.StartElement = 56 bytes
/go/src/encoding/xml/marshal.go:781:68: "start" parameter: encoding/xml.StartElement = 56 bytes
/go/src/encoding/xml/xml.go:72:30: "" result: encoding/xml.StartElement = 56 bytes
Imports
Check[]*ast.FileImportImporterConfigGOPATHtype Importer interface {
	Import(path string) (*Package, error)
}
Importerimporter.Default()go/importer$GOROOT$GOPATH.agcgccgogo installgo build -igolang.org/tools/x/go/loaderImportercgotypes.Packageast.Filetypes.Infogo/loaderdocgo doc$ ./doc net/http File
type net/http.File interface{Readdir(count int) ([]os.FileInfo, error); Seek(offset int64, whence int) (int64, error); Stat() (os.FileInfo, error); io.Closer; io.Reader}
/go/src/io/io.go:92:2: method (net/http.File) Close() error
/go/src/io/io.go:71:2: method (net/http.File) Read(p []byte) (n int, err error)
/go/src/net/http/fs.go:65:2: method (net/http.File) Readdir(count int) ([]os.FileInfo, error)
/go/src/net/http/fs.go:66:2: method (net/http.File) Seek(offset int64, whence int) (int64, error)
/go/src/net/http/fs.go:67:2: method (net/http.File) Stat() (os.FileInfo, error)
 A File is returned by a FileSystem's Open method and can be
served by the FileServer implementation.
The methods should behave the same as those on an *os.File.
http.Filepkgpath// go get github.com/golang/example/gotypes/doc
pkgpath, name := os.Args[1], os.Args[2]
// The loader loads a complete Go program from source code.
conf := loader.Config{ParserMode: parser.ParseComments}
conf.Import(pkgpath)
lprog, err := conf.Load()
if err != nil {
	log.Fatal(err) // load error
}
// Find the package and package-level object.
pkg := lprog.Package(pkgpath).Pkg
obj := pkg.Scope().Lookup(name)
if obj == nil {
	log.Fatalf("%s.%s not found", pkg.Path(), name)
}
Notice that we instructed the parser to retain comments during parsing. The rest of the program prints the output:
// go get github.com/golang/example/gotypes/doc
// Print the object and its methods (incl. location of definition).
fmt.Println(obj)
for _, sel := range typeutil.IntuitiveMethodSet(obj.Type(), nil) {
	fmt.Printf("%s: %s\n", lprog.Fset.Position(sel.Obj().Pos()), sel)
}
// Find the path from the root of the AST to the object's position.
// Walk up to the enclosing ast.Decl for the doc comment.
_, path, _ := lprog.PathEnclosingInterval(obj.Pos(), obj.Pos())
for _, n := range path {
	switch n := n.(type) {
	case *ast.GenDecl:
		fmt.Println("\n", n.Doc.Text())
		return
	case *ast.FuncDecl:
		fmt.Println("\n", n.Doc.Text())
		return
	}
}
IntuitiveMethodSetNewMethodSet*TThttp.FilePathEnclosingIntervalFormatting support
TypeObjectStringSelectionString[]encoding/json.Marshaler                                     // a *Slice type
encoding/json.Marshal                                         // a *Func object
(*encoding/json.Encoder).Encode                               // a *Func object (method)
func (enc *encoding/json.Encoder) Encode(v interface{}) error // a method *Signature
func NewEncoder(w io.Writer) *encoding/json.Encoder           // a function *Signature
hugeparamskeletongo/typesStringfunc ObjectString(obj Object, qf Qualifier) string
func TypeString(typ Type, qf Qualifier) string
func SelectionString(s *Selection, qf Qualifier) string
type Qualifier func(*Package) string
TypeStringObjectStringSelectionStringStringQualifierQualifierString(*Package).NameName[]json.Marshaler
json.Marshal
(*json.Encoder).Encode
func (enc *json.Encoder) Encode(v interface{}) error
func NewEncoder(w io.Writer) *json.Encoder
RelativeTo(pkg)func RelativeTo(pkg *Package) Qualifier
json.NewEncoder// RelativeTo "encoding/json":
func NewEncoder(w io.Writer) *Encoder
// RelativeTo "io":
func NewEncoder(w Writer) *encoding/json.Encoder
// RelativeTo any other package:
func NewEncoder(w io.Writer) *encoding/json.Encoder
Another qualifier that may be relevant to refactoring tools (but is not currently provided by the type checker) is one that renders each package name using the locally appropriate name within a given source file. Its behavior would depend on the set of import declarations, including renaming imports, within that source file.
Getting from A to B
Objectast.Identast.Nodetoken.Postoken.PosIn this section, we'll list solutions to a number of common problems of the form "I have an A; I need the corresponding B".
token.Posast.Nodeastutil.PathEnclosingIntervalast.Node*ast.Filetoken.Posloader(*loader.Program).PathEnclosingIntervalObjectPosPathEnclosingIntervalast.IdentObjectUsesDefsObjectDocParseCommentsdoc