]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: add Example of API usage
authorAlan Donovan <adonovan@google.com>
Thu, 11 Jun 2015 20:44:08 +0000 (16:44 -0400)
committerAlan Donovan <adonovan@google.com>
Fri, 12 Jun 2015 22:34:24 +0000 (22:34 +0000)
Change-Id: I9d3253b80508d733053789d6cb9645e029bf52e4
Reviewed-on: https://go-review.googlesource.com/10927
Reviewed-by: Robert Griesemer <gri@golang.org>
src/go/types/example_test.go [new file with mode: 0644]

diff --git a/src/go/types/example_test.go b/src/go/types/example_test.go
new file mode 100644 (file)
index 0000000..eb64e49
--- /dev/null
@@ -0,0 +1,307 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types_test
+
+// This file shows examples of basic usage of the go/types API.
+//
+// To locate a Go package, use (*go/build.Context).Import.
+// To load, parse, and type-check a complete Go program
+// from source, use golang.org/x/tools/go/loader.
+
+import (
+       "bytes"
+       "fmt"
+       "go/ast"
+       "go/format"
+       "go/importer"
+       "go/parser"
+       "go/token"
+       "go/types"
+       "log"
+       "regexp"
+       "sort"
+       "strings"
+)
+
+// ExampleScope prints the tree of Scopes of a package created from a
+// set of parsed files.
+func ExampleScope() {
+       // Parse the source files for a package.
+       fset := token.NewFileSet()
+       var files []*ast.File
+       for _, file := range []struct{ name, input string }{
+               {"main.go", `
+package main
+import "fmt"
+func main() {
+       freezing := FToC(-18)
+       fmt.Println(freezing, Boiling) }
+`},
+               {"celsius.go", `
+package main
+import "fmt"
+type Celsius float64
+func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
+func FToC(f float64) Celsius { return Celsius(f - 32 / 9 * 5) }
+const Boiling Celsius = 100
+`},
+       } {
+               f, err := parser.ParseFile(fset, file.name, file.input, 0)
+               if err != nil {
+                       log.Fatal(err)
+               }
+               files = append(files, f)
+       }
+
+       // Type-check a package consisting of these files.
+       // Type information for the imported "fmt" package
+       // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a.
+       conf := types.Config{Importer: importer.Default()}
+       pkg, err := conf.Check("temperature", fset, files, nil)
+       if err != nil {
+               log.Fatal(err)
+       }
+
+       // Print the tree of scopes.
+       // For determinism, we redact addresses.
+       var buf bytes.Buffer
+       pkg.Scope().WriteTo(&buf, 0, true)
+       rx := regexp.MustCompile(` 0x[a-fA-F0-9]*`)
+       fmt.Println(rx.ReplaceAllString(buf.String(), ""))
+
+       // Output:
+       // package "temperature" scope {
+       // .  const temperature.Boiling temperature.Celsius
+       // .  type temperature.Celsius float64
+       // .  func temperature.FToC(f float64) temperature.Celsius
+       // .  func temperature.main()
+       //
+       // .  main.go scope {
+       // .  .  package fmt
+       //
+       // .  .  function scope {
+       // .  .  .  var freezing temperature.Celsius
+       // .  .  }.  }
+       // .  celsius.go scope {
+       // .  .  package fmt
+       //
+       // .  .  function scope {
+       // .  .  .  var c temperature.Celsius
+       // .  .  }
+       // .  .  function scope {
+       // .  .  .  var f float64
+       // .  .  }.  }}
+}
+
+// ExampleMethodSet prints the method sets of various types.
+func ExampleMethodSet() {
+       // Parse a single source file.
+       const input = `
+package temperature
+import "fmt"
+type Celsius float64
+func (c Celsius) String() string  { return fmt.Sprintf("%g°C", c) }
+func (c *Celsius) SetF(f float64) { *c = Celsius(f - 32 / 9 * 5) }
+`
+       fset := token.NewFileSet()
+       f, err := parser.ParseFile(fset, "celsius.go", input, 0)
+       if err != nil {
+               log.Fatal(err)
+       }
+
+       // Type-check a package consisting of this file.
+       // Type information for the imported packages
+       // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a.
+       conf := types.Config{Importer: importer.Default()}
+       pkg, err := conf.Check("temperature", fset, []*ast.File{f}, nil)
+       if err != nil {
+               log.Fatal(err)
+       }
+
+       // Print the method sets of Celsius and *Celsius.
+       celsius := pkg.Scope().Lookup("Celsius").Type()
+       for _, t := range []types.Type{celsius, types.NewPointer(celsius)} {
+               fmt.Printf("Method set of %s:\n", t)
+               mset := types.NewMethodSet(t)
+               for i := 0; i < mset.Len(); i++ {
+                       fmt.Println(mset.At(i))
+               }
+               fmt.Println()
+       }
+
+       // Output:
+       // Method set of temperature.Celsius:
+       // method (temperature.Celsius) String() string
+       //
+       // Method set of *temperature.Celsius:
+       // method (*temperature.Celsius) SetF(f float64)
+       // method (*temperature.Celsius) String() string
+}
+
+// ExampleInfo prints various facts recorded by the type checker in a
+// types.Info struct: definitions of and references to each named object,
+// and the type, value, and mode of every expression in the package.
+func ExampleInfo() {
+       // Parse a single source file.
+       const input = `
+package fib
+
+type S string
+
+var a, b, c = len(b), S(c), "hello"
+
+func fib(x int) int {
+       if x < 2 {
+               return x
+       }
+       return fib(x-1) - fib(x-2)
+}`
+       fset := token.NewFileSet()
+       f, err := parser.ParseFile(fset, "fib.go", input, 0)
+       if err != nil {
+               log.Fatal(err)
+       }
+
+       // Type-check the package.
+       // We create an empty map for each kind of input
+       // we're interested in, and Check populates them.
+       info := types.Info{
+               Types: make(map[ast.Expr]types.TypeAndValue),
+               Defs:  make(map[*ast.Ident]types.Object),
+               Uses:  make(map[*ast.Ident]types.Object),
+       }
+       var conf types.Config
+       pkg, err := conf.Check("fib", fset, []*ast.File{f}, &info)
+       if err != nil {
+               log.Fatal(err)
+       }
+
+       // Print package-level variables in initialization order.
+       fmt.Printf("InitOrder: %v\n\n", info.InitOrder)
+
+       // For each named object, print the line and
+       // column of its definition and each of its uses.
+       fmt.Println("Defs and Uses of each named object:")
+       usesByObj := make(map[types.Object][]string)
+       for id, obj := range info.Uses {
+               posn := fset.Position(id.Pos())
+               lineCol := fmt.Sprintf("%d:%d", posn.Line, posn.Column)
+               usesByObj[obj] = append(usesByObj[obj], lineCol)
+       }
+       var items []string
+       for obj, uses := range usesByObj {
+               sort.Strings(uses)
+               item := fmt.Sprintf("%s:\n  defined at %s\n  used at %s",
+                       types.ObjectString(pkg, obj),
+                       fset.Position(obj.Pos()),
+                       strings.Join(uses, ", "))
+               items = append(items, item)
+       }
+       sort.Strings(items) // sort by line:col, in effect
+       fmt.Println(strings.Join(items, "\n"))
+       fmt.Println()
+
+       fmt.Println("Types and Values of each expression:")
+       items = nil
+       for expr, tv := range info.Types {
+               var buf bytes.Buffer
+               posn := fset.Position(expr.Pos())
+               tvstr := tv.Type.String()
+               if tv.Value != nil {
+                       tvstr += " = " + tv.Value.String()
+               }
+               // line:col | expr | mode : type = value
+               fmt.Fprintf(&buf, "%2d:%2d | %-19s | %-7s : %s",
+                       posn.Line, posn.Column, exprString(fset, expr),
+                       mode(tv), tvstr)
+               items = append(items, buf.String())
+       }
+       sort.Strings(items)
+       fmt.Println(strings.Join(items, "\n"))
+
+       // Output:
+       // InitOrder: [c = "hello" b = S(c) a = len(b)]
+       //
+       // Defs and Uses of each named object:
+       // builtin len:
+       //   defined at -
+       //   used at 6:15
+       // func fib(x int) int:
+       //   defined at fib.go:8:6
+       //   used at 12:20, 12:9
+       // type S string:
+       //   defined at fib.go:4:6
+       //   used at 6:23
+       // type int int:
+       //   defined at -
+       //   used at 8:12, 8:17
+       // type string string:
+       //   defined at -
+       //   used at 4:8
+       // var b S:
+       //   defined at fib.go:6:8
+       //   used at 6:19
+       // var c string:
+       //   defined at fib.go:6:11
+       //   used at 6:25
+       // var x int:
+       //   defined at fib.go:8:10
+       //   used at 10:10, 12:13, 12:24, 9:5
+       //
+       // Types and Values of each expression:
+       //  4: 8 | string              | type    : string
+       //  6:15 | len                 | builtin : func(string) int
+       //  6:15 | len(b)              | value   : int
+       //  6:19 | b                   | var     : fib.S
+       //  6:23 | S                   | type    : fib.S
+       //  6:23 | S(c)                | value   : fib.S
+       //  6:25 | c                   | var     : string
+       //  6:29 | "hello"             | value   : string = "hello"
+       //  8:12 | int                 | type    : int
+       //  8:17 | int                 | type    : int
+       //  9: 5 | x                   | var     : int
+       //  9: 5 | x < 2               | value   : untyped bool
+       //  9: 9 | 2                   | value   : int = 2
+       // 10:10 | x                   | var     : int
+       // 12: 9 | fib                 | value   : func(x int) int
+       // 12: 9 | fib(x - 1)          | value   : int
+       // 12: 9 | fib(x-1) - fib(x-2) | value   : int
+       // 12:13 | x                   | var     : int
+       // 12:13 | x - 1               | value   : int
+       // 12:15 | 1                   | value   : int = 1
+       // 12:20 | fib                 | value   : func(x int) int
+       // 12:20 | fib(x - 2)          | value   : int
+       // 12:24 | x                   | var     : int
+       // 12:24 | x - 2               | value   : int
+       // 12:26 | 2                   | value   : int = 2
+}
+
+func mode(tv types.TypeAndValue) string {
+       switch {
+       case tv.IsVoid():
+               return "void"
+       case tv.IsType():
+               return "type"
+       case tv.IsBuiltin():
+               return "builtin"
+       case tv.IsNil():
+               return "nil"
+       case tv.Assignable():
+               if tv.Addressable() {
+                       return "var"
+               }
+               return "mapindex"
+       case tv.IsValue():
+               return "value"
+       default:
+               return "unknown"
+       }
+}
+
+func exprString(fset *token.FileSet, expr ast.Expr) string {
+       var buf bytes.Buffer
+       format.Node(&buf, fset, expr)
+       return buf.String()
+}