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

View as plain text