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