...

Source file src/go/types/example_test.go

Documentation: go/types

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

View as plain text