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