github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/go/types/example_test.go (about) 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 !arm,!arm64,!nacl 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 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. 104 func ExampleMethodSet() { 105 // Parse a single source file. 106 const input = ` 107 package temperature 108 import "fmt" 109 type Celsius float64 110 func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) } 111 func (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. 151 func ExampleInfo() { 152 // Parse a single source file. 153 const input = ` 154 package fib 155 156 type S string 157 158 var a, b, c = len(b), S(c), "hello" 159 160 func 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 int: 243 // defined at - 244 // used at 8:12, 8:17 245 // type string 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 286 func 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 308 func exprString(fset *token.FileSet, expr ast.Expr) string { 309 var buf bytes.Buffer 310 format.Node(&buf, fset, expr) 311 return buf.String() 312 }