1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Only run where builders (build.golang.org) have
6// access to compiled packages for import.
7//
8// +build ignore,!arm,!arm64,!nacl
9
10package types_test
11
12// This file shows examples of basic usage of the go/types API.
13//
14// To locate a Go package, use (*go/build.Context).Import.
15// To load, parse, and type-check a complete Go program
16// from source, use golang.org/x/tools/go/loader.
17
18import (
19	"bytes"
20	"fmt"
21	"go/ast"
22	"go/format"
23	"go/importer"
24	"go/parser"
25	"go/token"
26	"go/types"
27	"log"
28	"regexp"
29	"sort"
30	"strings"
31)
32
33// ExampleScope prints the tree of Scopes of a package created from a
34// set of parsed files.
35func ExampleScope() {
36	// Parse the source files for a package.
37	fset := token.NewFileSet()
38	var files []*ast.File
39	for _, file := range []struct{ name, input string }{
40		{"main.go", `
41package main
42import "fmt"
43func main() {
44	freezing := FToC(-18)
45	fmt.Println(freezing, Boiling) }
46`},
47		{"celsius.go", `
48package main
49import "fmt"
50type Celsius float64
51func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
52func FToC(f float64) Celsius { return Celsius(f - 32 / 9 * 5) }
53const Boiling Celsius = 100
54`},
55	} {
56		f, err := parser.ParseFile(fset, file.name, file.input, 0)
57		if err != nil {
58			log.Fatal(err)
59		}
60		files = append(files, f)
61	}
62
63	// Type-check a package consisting of these files.
64	// Type information for the imported "fmt" package
65	// comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a.
66	conf := types.Config{Importer: importer.Default()}
67	pkg, err := conf.Check("temperature", fset, files, nil)
68	if err != nil {
69		log.Fatal(err)
70	}
71
72	// Print the tree of scopes.
73	// For determinism, we redact addresses.
74	var buf bytes.Buffer
75	pkg.Scope().WriteTo(&buf, 0, true)
76	rx := regexp.MustCompile(` 0x[a-fA-F0-9]*`)
77	fmt.Println(rx.ReplaceAllString(buf.String(), ""))
78
79	// Output:
80	// package "temperature" scope {
81	// .  const temperature.Boiling temperature.Celsius
82	// .  type temperature.Celsius float64
83	// .  func temperature.FToC(f float64) temperature.Celsius
84	// .  func temperature.main()
85	//
86	// .  main.go scope {
87	// .  .  package fmt
88	//
89	// .  .  function scope {
90	// .  .  .  var freezing temperature.Celsius
91	// .  .  }.  }
92	// .  celsius.go scope {
93	// .  .  package fmt
94	//
95	// .  .  function scope {
96	// .  .  .  var c temperature.Celsius
97	// .  .  }
98	// .  .  function scope {
99	// .  .  .  var f float64
100	// .  .  }.  }}
101}
102
103// ExampleMethodSet prints the method sets of various types.
104func ExampleMethodSet() {
105	// Parse a single source file.
106	const input = `
107package temperature
108import "fmt"
109type Celsius float64
110func (c Celsius) String() string  { return fmt.Sprintf("%g°C", c) }
111func (c *Celsius) SetF(f float64) { *c = Celsius(f - 32 / 9 * 5) }
112`
113	fset := token.NewFileSet()
114	f, err := parser.ParseFile(fset, "celsius.go", input, 0)
115	if err != nil {
116		log.Fatal(err)
117	}
118
119	// Type-check a package consisting of this file.
120	// Type information for the imported packages
121	// comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a.
122	conf := types.Config{Importer: importer.Default()}
123	pkg, err := conf.Check("temperature", fset, []*ast.File{f}, nil)
124	if err != nil {
125		log.Fatal(err)
126	}
127
128	// Print the method sets of Celsius and *Celsius.
129	celsius := pkg.Scope().Lookup("Celsius").Type()
130	for _, t := range []types.Type{celsius, types.NewPointer(celsius)} {
131		fmt.Printf("Method set of %s:\n", t)
132		mset := types.NewMethodSet(t)
133		for i := 0; i < mset.Len(); i++ {
134			fmt.Println(mset.At(i))
135		}
136		fmt.Println()
137	}
138
139	// Output:
140	// Method set of temperature.Celsius:
141	// method (temperature.Celsius) String() string
142	//
143	// Method set of *temperature.Celsius:
144	// method (*temperature.Celsius) SetF(f float64)
145	// method (*temperature.Celsius) String() string
146}
147
148// ExampleInfo prints various facts recorded by the type checker in a
149// types.Info struct: definitions of and references to each named object,
150// and the type, value, and mode of every expression in the package.
151func ExampleInfo() {
152	// Parse a single source file.
153	const input = `
154package fib
155
156type S string
157
158var a, b, c = len(b), S(c), "hello"
159
160func fib(x int) int {
161	if x < 2 {
162		return x
163	}
164	return fib(x-1) - fib(x-2)
165}`
166	fset := token.NewFileSet()
167	f, err := parser.ParseFile(fset, "fib.go", input, 0)
168	if err != nil {
169		log.Fatal(err)
170	}
171
172	// Type-check the package.
173	// We create an empty map for each kind of input
174	// we're interested in, and Check populates them.
175	info := types.Info{
176		Types: make(map[ast.Expr]types.TypeAndValue),
177		Defs:  make(map[*ast.Ident]types.Object),
178		Uses:  make(map[*ast.Ident]types.Object),
179	}
180	var conf types.Config
181	pkg, err := conf.Check("fib", fset, []*ast.File{f}, &info)
182	if err != nil {
183		log.Fatal(err)
184	}
185
186	// Print package-level variables in initialization order.
187	fmt.Printf("InitOrder: %v\n\n", info.InitOrder)
188
189	// For each named object, print the line and
190	// column of its definition and each of its uses.
191	fmt.Println("Defs and Uses of each named object:")
192	usesByObj := make(map[types.Object][]string)
193	for id, obj := range info.Uses {
194		posn := fset.Position(id.Pos())
195		lineCol := fmt.Sprintf("%d:%d", posn.Line, posn.Column)
196		usesByObj[obj] = append(usesByObj[obj], lineCol)
197	}
198	var items []string
199	for obj, uses := range usesByObj {
200		sort.Strings(uses)
201		item := fmt.Sprintf("%s:\n  defined at %s\n  used at %s",
202			types.ObjectString(obj, types.RelativeTo(pkg)),
203			fset.Position(obj.Pos()),
204			strings.Join(uses, ", "))
205		items = append(items, item)
206	}
207	sort.Strings(items) // sort by line:col, in effect
208	fmt.Println(strings.Join(items, "\n"))
209	fmt.Println()
210
211	fmt.Println("Types and Values of each expression:")
212	items = nil
213	for expr, tv := range info.Types {
214		var buf bytes.Buffer
215		posn := fset.Position(expr.Pos())
216		tvstr := tv.Type.String()
217		if tv.Value != nil {
218			tvstr += " = " + tv.Value.String()
219		}
220		// line:col | expr | mode : type = value
221		fmt.Fprintf(&buf, "%2d:%2d | %-19s | %-7s : %s",
222			posn.Line, posn.Column, exprString(fset, expr),
223			mode(tv), tvstr)
224		items = append(items, buf.String())
225	}
226	sort.Strings(items)
227	fmt.Println(strings.Join(items, "\n"))
228
229	// Output:
230	// InitOrder: [c = "hello" b = S(c) a = len(b)]
231	//
232	// Defs and Uses of each named object:
233	// builtin len:
234	//   defined at -
235	//   used at 6:15
236	// func fib(x int) int:
237	//   defined at fib.go:8:6
238	//   used at 12:20, 12:9
239	// type S string:
240	//   defined at fib.go:4:6
241	//   used at 6:23
242	// type int:
243	//   defined at -
244	//   used at 8:12, 8:17
245	// type string:
246	//   defined at -
247	//   used at 4:8
248	// var b S:
249	//   defined at fib.go:6:8
250	//   used at 6:19
251	// var c string:
252	//   defined at fib.go:6:11
253	//   used at 6:25
254	// var x int:
255	//   defined at fib.go:8:10
256	//   used at 10:10, 12:13, 12:24, 9:5
257	//
258	// Types and Values of each expression:
259	//  4: 8 | string              | type    : string
260	//  6:15 | len                 | builtin : func(string) int
261	//  6:15 | len(b)              | value   : int
262	//  6:19 | b                   | var     : fib.S
263	//  6:23 | S                   | type    : fib.S
264	//  6:23 | S(c)                | value   : fib.S
265	//  6:25 | c                   | var     : string
266	//  6:29 | "hello"             | value   : string = "hello"
267	//  8:12 | int                 | type    : int
268	//  8:17 | int                 | type    : int
269	//  9: 5 | x                   | var     : int
270	//  9: 5 | x < 2               | value   : untyped bool
271	//  9: 9 | 2                   | value   : int = 2
272	// 10:10 | x                   | var     : int
273	// 12: 9 | fib                 | value   : func(x int) int
274	// 12: 9 | fib(x - 1)          | value   : int
275	// 12: 9 | fib(x-1) - fib(x-2) | value   : int
276	// 12:13 | x                   | var     : int
277	// 12:13 | x - 1               | value   : int
278	// 12:15 | 1                   | value   : int = 1
279	// 12:20 | fib                 | value   : func(x int) int
280	// 12:20 | fib(x - 2)          | value   : int
281	// 12:24 | x                   | var     : int
282	// 12:24 | x - 2               | value   : int
283	// 12:26 | 2                   | value   : int = 2
284}
285
286func mode(tv types.TypeAndValue) string {
287	switch {
288	case tv.IsVoid():
289		return "void"
290	case tv.IsType():
291		return "type"
292	case tv.IsBuiltin():
293		return "builtin"
294	case tv.IsNil():
295		return "nil"
296	case tv.Assignable():
297		if tv.Addressable() {
298			return "var"
299		}
300		return "mapindex"
301	case tv.IsValue():
302		return "value"
303	default:
304		return "unknown"
305	}
306}
307
308func exprString(fset *token.FileSet, expr ast.Expr) string {
309	var buf bytes.Buffer
310	format.Node(&buf, fset, expr)
311	return buf.String()
312}
313