mirror of https://github.com/golang/go.git
go/types: add Example of API usage
Change-Id: I9d3253b80508d733053789d6cb9645e029bf52e4 Reviewed-on: https://go-review.googlesource.com/10927 Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
5acba80aa2
commit
368f0ee6c4
|
|
@ -0,0 +1,307 @@
|
|||
// 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.
|
||||
|
||||
package types_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"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/importer"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"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.
|
||||
fset := token.NewFileSet()
|
||||
var files []*ast.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
|
||||
`},
|
||||
} {
|
||||
f, err := parser.ParseFile(fset, file.name, file.input, 0)
|
||||
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 := types.Config{Importer: importer.Default()}
|
||||
pkg, err := conf.Check("temperature", fset, 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.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
|
||||
// . . }. }}
|
||||
}
|
||||
|
||||
// ExampleMethodSet prints the method sets of various types.
|
||||
func ExampleMethodSet() {
|
||||
// Parse a single source file.
|
||||
const input = `
|
||||
package temperature
|
||||
import "fmt"
|
||||
type Celsius float64
|
||||
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
|
||||
func (c *Celsius) SetF(f float64) { *c = Celsius(f - 32 / 9 * 5) }
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, "celsius.go", input, 0)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Type-check a package consisting of this file.
|
||||
// Type information for the imported packages
|
||||
// comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a.
|
||||
conf := types.Config{Importer: importer.Default()}
|
||||
pkg, err := conf.Check("temperature", fset, []*ast.File{f}, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Print the method sets of Celsius and *Celsius.
|
||||
celsius := pkg.Scope().Lookup("Celsius").Type()
|
||||
for _, t := range []types.Type{celsius, types.NewPointer(celsius)} {
|
||||
fmt.Printf("Method set of %s:\n", t)
|
||||
mset := types.NewMethodSet(t)
|
||||
for i := 0; i < mset.Len(); i++ {
|
||||
fmt.Println(mset.At(i))
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
// Output:
|
||||
// Method set of temperature.Celsius:
|
||||
// method (temperature.Celsius) String() string
|
||||
//
|
||||
// Method set of *temperature.Celsius:
|
||||
// method (*temperature.Celsius) SetF(f float64)
|
||||
// method (*temperature.Celsius) String() string
|
||||
}
|
||||
|
||||
// ExampleInfo prints various facts recorded by the type checker in a
|
||||
// types.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)
|
||||
}`
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, "fib.go", input, 0)
|
||||
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 := types.Info{
|
||||
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||
Defs: make(map[*ast.Ident]types.Object),
|
||||
Uses: make(map[*ast.Ident]types.Object),
|
||||
}
|
||||
var conf types.Config
|
||||
pkg, err := conf.Check("fib", fset, []*ast.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[types.Object][]string)
|
||||
for id, obj := range info.Uses {
|
||||
posn := fset.Position(id.Pos())
|
||||
lineCol := fmt.Sprintf("%d:%d", posn.Line, posn.Column)
|
||||
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",
|
||||
types.ObjectString(pkg, obj),
|
||||
fset.Position(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()
|
||||
|
||||
fmt.Println("Types and Values of each expression:")
|
||||
items = nil
|
||||
for expr, tv := range info.Types {
|
||||
var buf bytes.Buffer
|
||||
posn := fset.Position(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.Column, exprString(fset, 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 int:
|
||||
// defined at -
|
||||
// used at 8:12, 8:17
|
||||
// type string 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
|
||||
//
|
||||
// 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 types.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"
|
||||
}
|
||||
}
|
||||
|
||||
func exprString(fset *token.FileSet, expr ast.Expr) string {
|
||||
var buf bytes.Buffer
|
||||
format.Node(&buf, fset, expr)
|
||||
return buf.String()
|
||||
}
|
||||
Loading…
Reference in New Issue