-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Change-Id: I9d3253b80508d733053789d6cb9645e029bf52e4 Reviewed-on: https://go-review.googlesource.com/10927 Reviewed-by: Robert Griesemer <[email protected]>
- Loading branch information
Showing
1 changed file
with
307 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() | ||
} |