Source file src/cmd/compile/internal/types2/example_test.go
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 // +build !arm,!arm64 10 11 package types2_test 12 13 // This file shows examples of basic usage of the go/types API. 14 // 15 // To locate a Go package, use (*go/build.Context).Import. 16 // To load, parse, and type-check a complete Go program 17 // from source, use golang.org/x/tools/go/loader. 18 19 import ( 20 "bytes" 21 "cmd/compile/internal/syntax" 22 "cmd/compile/internal/types2" 23 "fmt" 24 "log" 25 "regexp" 26 "sort" 27 "strings" 28 ) 29 30 // ExampleScope prints the tree of Scopes of a package created from a 31 // set of parsed files. 32 func ExampleScope() { 33 // Parse the source files for a package. 34 var files []*syntax.File 35 for _, file := range []struct{ name, input string }{ 36 {"main.go", ` 37 package main 38 import "fmt" 39 func main() { 40 freezing := FToC(-18) 41 fmt.Println(freezing, Boiling) } 42 `}, 43 {"celsius.go", ` 44 package main 45 import "fmt" 46 type Celsius float64 47 func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) } 48 func FToC(f float64) Celsius { return Celsius(f - 32 / 9 * 5) } 49 const Boiling Celsius = 100 50 func Unused() { {}; {{ var x int; _ = x }} } // make sure empty block scopes get printed 51 `}, 52 } { 53 f, err := parseSrc(file.name, file.input) 54 if err != nil { 55 log.Fatal(err) 56 } 57 files = append(files, f) 58 } 59 60 // Type-check a package consisting of these files. 61 // Type information for the imported "fmt" package 62 // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a. 63 conf := types2.Config{Importer: defaultImporter()} 64 pkg, err := conf.Check("temperature", files, nil) 65 if err != nil { 66 log.Fatal(err) 67 } 68 69 // Print the tree of scopes. 70 // For determinism, we redact addresses. 71 var buf bytes.Buffer 72 pkg.Scope().WriteTo(&buf, 0, true) 73 rx := regexp.MustCompile(` 0x[a-fA-F0-9]*`) 74 fmt.Println(rx.ReplaceAllString(buf.String(), "")) 75 76 // Output: 77 // package "temperature" scope { 78 // . const temperature.Boiling temperature.Celsius 79 // . type temperature.Celsius float64 80 // . func temperature.FToC(f float64) temperature.Celsius 81 // . func temperature.Unused() 82 // . func temperature.main() 83 // . main.go scope { 84 // . . package fmt 85 // . . function scope { 86 // . . . var freezing temperature.Celsius 87 // . . } 88 // . } 89 // . celsius.go scope { 90 // . . package fmt 91 // . . function scope { 92 // . . . var c temperature.Celsius 93 // . . } 94 // . . function scope { 95 // . . . var f float64 96 // . . } 97 // . . function scope { 98 // . . . block scope { 99 // . . . } 100 // . . . block scope { 101 // . . . . block scope { 102 // . . . . . var x int 103 // . . . . } 104 // . . . } 105 // . . } 106 // . } 107 // } 108 } 109 110 // ExampleInfo prints various facts recorded by the type checker in a 111 // types2.Info struct: definitions of and references to each named object, 112 // and the type, value, and mode of every expression in the package. 113 func ExampleInfo() { 114 // Parse a single source file. 115 const input = ` 116 package fib 117 118 type S string 119 120 var a, b, c = len(b), S(c), "hello" 121 122 func fib(x int) int { 123 if x < 2 { 124 return x 125 } 126 return fib(x-1) - fib(x-2) 127 }` 128 f, err := parseSrc("fib.go", input) 129 if err != nil { 130 log.Fatal(err) 131 } 132 133 // Type-check the package. 134 // We create an empty map for each kind of input 135 // we're interested in, and Check populates them. 136 info := types2.Info{ 137 Types: make(map[syntax.Expr]types2.TypeAndValue), 138 Defs: make(map[*syntax.Name]types2.Object), 139 Uses: make(map[*syntax.Name]types2.Object), 140 } 141 var conf types2.Config 142 pkg, err := conf.Check("fib", []*syntax.File{f}, &info) 143 if err != nil { 144 log.Fatal(err) 145 } 146 147 // Print package-level variables in initialization order. 148 fmt.Printf("InitOrder: %v\n\n", info.InitOrder) 149 150 // For each named object, print the line and 151 // column of its definition and each of its uses. 152 fmt.Println("Defs and Uses of each named object:") 153 usesByObj := make(map[types2.Object][]string) 154 for id, obj := range info.Uses { 155 posn := id.Pos() 156 lineCol := fmt.Sprintf("%d:%d", posn.Line(), posn.Col()) 157 usesByObj[obj] = append(usesByObj[obj], lineCol) 158 } 159 var items []string 160 for obj, uses := range usesByObj { 161 sort.Strings(uses) 162 item := fmt.Sprintf("%s:\n defined at %s\n used at %s", 163 types2.ObjectString(obj, types2.RelativeTo(pkg)), 164 obj.Pos(), 165 strings.Join(uses, ", ")) 166 items = append(items, item) 167 } 168 sort.Strings(items) // sort by line:col, in effect 169 fmt.Println(strings.Join(items, "\n")) 170 fmt.Println() 171 172 // TODO(gri) Enable once positions are updated/verified 173 // fmt.Println("Types and Values of each expression:") 174 // items = nil 175 // for expr, tv := range info.Types { 176 // var buf bytes.Buffer 177 // posn := expr.Pos() 178 // tvstr := tv.Type.String() 179 // if tv.Value != nil { 180 // tvstr += " = " + tv.Value.String() 181 // } 182 // // line:col | expr | mode : type = value 183 // fmt.Fprintf(&buf, "%2d:%2d | %-19s | %-7s : %s", 184 // posn.Line(), posn.Col(), types2.ExprString(expr), 185 // mode(tv), tvstr) 186 // items = append(items, buf.String()) 187 // } 188 // sort.Strings(items) 189 // fmt.Println(strings.Join(items, "\n")) 190 191 // Output: 192 // InitOrder: [c = "hello" b = S(c) a = len(b)] 193 // 194 // Defs and Uses of each named object: 195 // builtin len: 196 // defined at <unknown position> 197 // used at 6:15 198 // func fib(x int) int: 199 // defined at fib.go:8:6 200 // used at 12:20, 12:9 201 // type S string: 202 // defined at fib.go:4:6 203 // used at 6:23 204 // type int: 205 // defined at <unknown position> 206 // used at 8:12, 8:17 207 // type string: 208 // defined at <unknown position> 209 // used at 4:8 210 // var b S: 211 // defined at fib.go:6:8 212 // used at 6:19 213 // var c string: 214 // defined at fib.go:6:11 215 // used at 6:25 216 // var x int: 217 // defined at fib.go:8:10 218 // used at 10:10, 12:13, 12:24, 9:5 219 } 220 221 // TODO(gri) Enable once positions are updated/verified 222 // Types and Values of each expression: 223 // 4: 8 | string | type : string 224 // 6:15 | len | builtin : func(string) int 225 // 6:15 | len(b) | value : int 226 // 6:19 | b | var : fib.S 227 // 6:23 | S | type : fib.S 228 // 6:23 | S(c) | value : fib.S 229 // 6:25 | c | var : string 230 // 6:29 | "hello" | value : string = "hello" 231 // 8:12 | int | type : int 232 // 8:17 | int | type : int 233 // 9: 5 | x | var : int 234 // 9: 5 | x < 2 | value : untyped bool 235 // 9: 9 | 2 | value : int = 2 236 // 10:10 | x | var : int 237 // 12: 9 | fib | value : func(x int) int 238 // 12: 9 | fib(x - 1) | value : int 239 // 12: 9 | fib(x - 1) - fib(x - 2) | value : int 240 // 12:13 | x | var : int 241 // 12:13 | x - 1 | value : int 242 // 12:15 | 1 | value : int = 1 243 // 12:20 | fib | value : func(x int) int 244 // 12:20 | fib(x - 2) | value : int 245 // 12:24 | x | var : int 246 // 12:24 | x - 2 | value : int 247 // 12:26 | 2 | value : int = 2 248 249 func mode(tv types2.TypeAndValue) string { 250 switch { 251 case tv.IsVoid(): 252 return "void" 253 case tv.IsType(): 254 return "type" 255 case tv.IsBuiltin(): 256 return "builtin" 257 case tv.IsNil(): 258 return "nil" 259 case tv.Assignable(): 260 if tv.Addressable() { 261 return "var" 262 } 263 return "mapindex" 264 case tv.IsValue(): 265 return "value" 266 default: 267 return "unknown" 268 } 269 } 270