// Copyright 2015 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. // Only run where builders (build.golang.org) have // access to compiled packages for import. // //go:build !arm && !arm64 // +build !arm,!arm64 package types2_test // This file shows examples of basic usage of the go/types API. // // To locate a Go package, use (*go/build.Context).Import. // To load, parse, and type-check a complete Go program // from source, use golang.org/x/tools/go/loader. import ( "bytes" "cmd/compile/internal/syntax" "cmd/compile/internal/types2" "fmt" "log" "regexp" "sort" "strings" ) // ExampleScope prints the tree of Scopes of a package created from a // set of parsed files. func ExampleScope() { // Parse the source files for a package. var files []*syntax.File for _, file := range []struct{ name, input string }{ {"main.go", ` package main import "fmt" func main() { freezing := FToC(-18) fmt.Println(freezing, Boiling) } `}, {"celsius.go", ` package main import "fmt" type Celsius float64 func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) } func FToC(f float64) Celsius { return Celsius(f - 32 / 9 * 5) } const Boiling Celsius = 100 func Unused() { {}; {{ var x int; _ = x }} } // make sure empty block scopes get printed `}, } { f, err := parseSrc(file.name, file.input) if err != nil { log.Fatal(err) } files = append(files, f) } // Type-check a package consisting of these files. // Type information for the imported "fmt" package // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a. conf := types2.Config{Importer: defaultImporter()} pkg, err := conf.Check("temperature", files, nil) if err != nil { log.Fatal(err) } // Print the tree of scopes. // For determinism, we redact addresses. var buf bytes.Buffer pkg.Scope().WriteTo(&buf, 0, true) rx := regexp.MustCompile(` 0x[a-fA-F0-9]*`) fmt.Println(rx.ReplaceAllString(buf.String(), "")) // Output: // package "temperature" scope { // . const temperature.Boiling temperature.Celsius // . type temperature.Celsius float64 // . func temperature.FToC(f float64) temperature.Celsius // . func temperature.Unused() // . func temperature.main() // . main.go scope { // . . package fmt // . . function scope { // . . . var freezing temperature.Celsius // . . } // . } // . celsius.go scope { // . . package fmt // . . function scope { // . . . var c temperature.Celsius // . . } // . . function scope { // . . . var f float64 // . . } // . . function scope { // . . . block scope { // . . . } // . . . block scope { // . . . . block scope { // . . . . . var x int // . . . . } // . . . } // . . } // . } // } } // ExampleInfo prints various facts recorded by the type checker in a // types2.Info struct: definitions of and references to each named object, // and the type, value, and mode of every expression in the package. func ExampleInfo() { // Parse a single source file. const input = ` package fib type S string var a, b, c = len(b), S(c), "hello" func fib(x int) int { if x < 2 { return x } return fib(x-1) - fib(x-2) }` f, err := parseSrc("fib.go", input) if err != nil { log.Fatal(err) } // Type-check the package. // We create an empty map for each kind of input // we're interested in, and Check populates them. info := types2.Info{ Types: make(map[syntax.Expr]types2.TypeAndValue), Defs: make(map[*syntax.Name]types2.Object), Uses: make(map[*syntax.Name]types2.Object), } var conf types2.Config pkg, err := conf.Check("fib", []*syntax.File{f}, &info) if err != nil { log.Fatal(err) } // Print package-level variables in initialization order. fmt.Printf("InitOrder: %v\n\n", info.InitOrder) // For each named object, print the line and // column of its definition and each of its uses. fmt.Println("Defs and Uses of each named object:") usesByObj := make(map[types2.Object][]string) for id, obj := range info.Uses { posn := id.Pos() lineCol := fmt.Sprintf("%d:%d", posn.Line(), posn.Col()) usesByObj[obj] = append(usesByObj[obj], lineCol) } var items []string for obj, uses := range usesByObj { sort.Strings(uses) item := fmt.Sprintf("%s:\n defined at %s\n used at %s", types2.ObjectString(obj, types2.RelativeTo(pkg)), obj.Pos(), strings.Join(uses, ", ")) items = append(items, item) } sort.Strings(items) // sort by line:col, in effect fmt.Println(strings.Join(items, "\n")) fmt.Println() // TODO(gri) Enable once positions are updated/verified // fmt.Println("Types and Values of each expression:") // items = nil // for expr, tv := range info.Types { // var buf bytes.Buffer // posn := expr.Pos() // tvstr := tv.Type.String() // if tv.Value != nil { // tvstr += " = " + tv.Value.String() // } // // line:col | expr | mode : type = value // fmt.Fprintf(&buf, "%2d:%2d | %-19s | %-7s : %s", // posn.Line(), posn.Col(), types2.ExprString(expr), // mode(tv), tvstr) // items = append(items, buf.String()) // } // sort.Strings(items) // fmt.Println(strings.Join(items, "\n")) // Output: // InitOrder: [c = "hello" b = S(c) a = len(b)] // // Defs and Uses of each named object: // builtin len: // defined at // used at 6:15 // func fib(x int) int: // defined at fib.go:8:6 // used at 12:20, 12:9 // type S string: // defined at fib.go:4:6 // used at 6:23 // type int: // defined at // used at 8:12, 8:17 // type string: // defined at // used at 4:8 // var b S: // defined at fib.go:6:8 // used at 6:19 // var c string: // defined at fib.go:6:11 // used at 6:25 // var x int: // defined at fib.go:8:10 // used at 10:10, 12:13, 12:24, 9:5 } // TODO(gri) Enable once positions are updated/verified // Types and Values of each expression: // 4: 8 | string | type : string // 6:15 | len | builtin : func(string) int // 6:15 | len(b) | value : int // 6:19 | b | var : fib.S // 6:23 | S | type : fib.S // 6:23 | S(c) | value : fib.S // 6:25 | c | var : string // 6:29 | "hello" | value : string = "hello" // 8:12 | int | type : int // 8:17 | int | type : int // 9: 5 | x | var : int // 9: 5 | x < 2 | value : untyped bool // 9: 9 | 2 | value : int = 2 // 10:10 | x | var : int // 12: 9 | fib | value : func(x int) int // 12: 9 | fib(x - 1) | value : int // 12: 9 | fib(x - 1) - fib(x - 2) | value : int // 12:13 | x | var : int // 12:13 | x - 1 | value : int // 12:15 | 1 | value : int = 1 // 12:20 | fib | value : func(x int) int // 12:20 | fib(x - 2) | value : int // 12:24 | x | var : int // 12:24 | x - 2 | value : int // 12:26 | 2 | value : int = 2 func mode(tv types2.TypeAndValue) string { switch { case tv.IsVoid(): return "void" case tv.IsType(): return "type" case tv.IsBuiltin(): return "builtin" case tv.IsNil(): return "nil" case tv.Assignable(): if tv.Addressable() { return "var" } return "mapindex" case tv.IsValue(): return "value" default: return "unknown" } }