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