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