Black Lives Matter. Support the Equal Justice Initiative.

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