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 "sort" 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-F0-9]*`) 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 sort.Strings(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 sort.Strings(items) // sort by line:col, in effect 152 fmt.Println(strings.Join(items, "\n")) 153 fmt.Println() 154 155 // TODO(gri) Enable once positions are updated/verified 156 // fmt.Println("Types and Values of each expression:") 157 // items = nil 158 // for expr, tv := range info.Types { 159 // var buf strings.Builder 160 // posn := expr.Pos() 161 // tvstr := tv.Type.String() 162 // if tv.Value != nil { 163 // tvstr += " = " + tv.Value.String() 164 // } 165 // // line:col | expr | mode : type = value 166 // fmt.Fprintf(&buf, "%2d:%2d | %-19s | %-7s : %s", 167 // posn.Line(), posn.Col(), types2.ExprString(expr), 168 // mode(tv), tvstr) 169 // items = append(items, buf.String()) 170 // } 171 // sort.Strings(items) 172 // fmt.Println(strings.Join(items, "\n")) 173 174 // Output: 175 // InitOrder: [c = "hello" b = S(c) a = len(b)] 176 // 177 // Defs and Uses of each named object: 178 // builtin len: 179 // defined at <unknown position> 180 // used at 6:15 181 // func fib(x int) int: 182 // defined at fib:8:6 183 // used at 12:20, 12:9 184 // type S string: 185 // defined at fib:4:6 186 // used at 6:23 187 // type int: 188 // defined at <unknown position> 189 // used at 8:12, 8:17 190 // type string: 191 // defined at <unknown position> 192 // used at 4:8 193 // var b S: 194 // defined at fib:6:8 195 // used at 6:19 196 // var c string: 197 // defined at fib:6:11 198 // used at 6:25 199 // var x int: 200 // defined at fib:8:10 201 // used at 10:10, 12:13, 12:24, 9:5 202 } 203 204 // TODO(gri) Enable once positions are updated/verified 205 // Types and Values of each expression: 206 // 4: 8 | string | type : string 207 // 6:15 | len | builtin : func(string) int 208 // 6:15 | len(b) | value : int 209 // 6:19 | b | var : fib.S 210 // 6:23 | S | type : fib.S 211 // 6:23 | S(c) | value : fib.S 212 // 6:25 | c | var : string 213 // 6:29 | "hello" | value : string = "hello" 214 // 8:12 | int | type : int 215 // 8:17 | int | type : int 216 // 9: 5 | x | var : int 217 // 9: 5 | x < 2 | value : untyped bool 218 // 9: 9 | 2 | value : int = 2 219 // 10:10 | x | var : int 220 // 12: 9 | fib | value : func(x int) int 221 // 12: 9 | fib(x - 1) | value : int 222 // 12: 9 | fib(x - 1) - fib(x - 2) | value : int 223 // 12:13 | x | var : int 224 // 12:13 | x - 1 | value : int 225 // 12:15 | 1 | value : int = 1 226 // 12:20 | fib | value : func(x int) int 227 // 12:20 | fib(x - 2) | value : int 228 // 12:24 | x | var : int 229 // 12:24 | x - 2 | value : int 230 // 12:26 | 2 | value : int = 2 231 232 func mode(tv types2.TypeAndValue) string { 233 switch { 234 case tv.IsVoid(): 235 return "void" 236 case tv.IsType(): 237 return "type" 238 case tv.IsBuiltin(): 239 return "builtin" 240 case tv.IsNil(): 241 return "nil" 242 case tv.Assignable(): 243 if tv.Addressable() { 244 return "var" 245 } 246 return "mapindex" 247 case tv.IsValue(): 248 return "value" 249 default: 250 return "unknown" 251 } 252 } 253