blob: 714bf77821399683925a986183c984869edc93b2 [file] [log] [blame]
// 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 <unknown position>
// 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 <unknown position>
// used at 8:12, 8:17
// type string:
// defined at <unknown position>
// 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"
}
}