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