🐳 docker build

This commit is contained in:
Liang Ding 2018-03-13 12:32:44 +08:00
parent 1fcc40af30
commit 1d1b548a68
159 changed files with 69223 additions and 1 deletions

View File

@ -7,7 +7,7 @@ RUN apt-get update && apt-get install bzip2 zip unzip && cp -r /usr/local/go /us
ENV GOROOT_BOOTSTRAP=/usr/local/gobt
ADD . /wide/gogogo/src/github.com/b3log/wide
ADD vendor/* /go/src/
ADD vendor/ /go/src/
RUN go install github.com/visualfc/gotools github.com/nsf/gocode github.com/bradfitz/goimports
RUN useradd wide && useradd runner

685
vendor/github.com/visualfc/gotools/astview/astdoc.go generated vendored Normal file
View File

@ -0,0 +1,685 @@
// Copyright 2009 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 doc extracts source code documentation from a Go AST.
package astview
import (
"go/ast"
"go/token"
"regexp"
"sort"
"strconv"
)
// ----------------------------------------------------------------------------
type typeDoc struct {
// len(decl.Specs) == 1, and the element type is *ast.TypeSpec
// if the type declaration hasn't been seen yet, decl is nil
decl *ast.GenDecl
// values, factory functions, and methods associated with the type
values []*ast.GenDecl // consts and vars
factories map[string]*ast.FuncDecl
methods map[string]*ast.FuncDecl
}
// docReader accumulates documentation for a single package.
// It modifies the AST: Comments (declaration documentation)
// that have been collected by the DocReader are set to nil
// in the respective AST nodes so that they are not printed
// twice (once when printing the documentation and once when
// printing the corresponding AST node).
//
type docReader struct {
doc *ast.CommentGroup // package documentation, if any
pkgName string
showAll bool
values []*ast.GenDecl // consts and vars
types map[string]*typeDoc
funcs map[string]*ast.FuncDecl
imports map[string]int
bugs []*ast.CommentGroup
}
func (doc *docReader) init(pkgName string, showAll bool) {
doc.pkgName = pkgName
doc.showAll = showAll
doc.imports = make(map[string]int)
doc.types = make(map[string]*typeDoc)
doc.funcs = make(map[string]*ast.FuncDecl)
}
func (doc *docReader) addDoc(comments *ast.CommentGroup) {
if doc.doc == nil {
// common case: just one package comment
doc.doc = comments
return
}
// More than one package comment: Usually there will be only
// one file with a package comment, but it's better to collect
// all comments than drop them on the floor.
// (This code isn't particularly clever - no amortized doubling is
// used - but this situation occurs rarely and is not time-critical.)
n1 := len(doc.doc.List)
n2 := len(comments.List)
list := make([]*ast.Comment, n1+1+n2) // + 1 for separator line
copy(list, doc.doc.List)
list[n1] = &ast.Comment{token.NoPos, "//"} // separator line
copy(list[n1+1:], comments.List)
doc.doc = &ast.CommentGroup{list}
}
func (doc *docReader) addType(decl *ast.GenDecl) {
spec := decl.Specs[0].(*ast.TypeSpec)
typ := doc.lookupTypeDoc(spec.Name.Name)
// typ should always be != nil since declared types
// are always named - be conservative and check
if typ != nil {
// a type should be added at most once, so typ.decl
// should be nil - if it isn't, simply overwrite it
typ.decl = decl
}
}
func (doc *docReader) lookupTypeDoc(name string) *typeDoc {
if name == "" {
return nil // no type docs for anonymous types
}
if tdoc, found := doc.types[name]; found {
return tdoc
}
// type wasn't found - add one without declaration
tdoc := &typeDoc{nil, nil, make(map[string]*ast.FuncDecl), make(map[string]*ast.FuncDecl)}
doc.types[name] = tdoc
return tdoc
}
func docBaseTypeName(typ ast.Expr, showAll bool) string {
switch t := typ.(type) {
case *ast.Ident:
// if the type is not exported, the effect to
// a client is as if there were no type name
if showAll || t.IsExported() {
return t.Name
}
case *ast.StarExpr:
return docBaseTypeName(t.X, showAll)
}
return ""
}
func (doc *docReader) addValue(decl *ast.GenDecl) {
// determine if decl should be associated with a type
// Heuristic: For each typed entry, determine the type name, if any.
// If there is exactly one type name that is sufficiently
// frequent, associate the decl with the respective type.
domName := ""
domFreq := 0
prev := ""
for _, s := range decl.Specs {
if v, ok := s.(*ast.ValueSpec); ok {
name := ""
switch {
case v.Type != nil:
// a type is present; determine its name
name = docBaseTypeName(v.Type, doc.showAll)
case decl.Tok == token.CONST:
// no type is present but we have a constant declaration;
// use the previous type name (w/o more type information
// we cannot handle the case of unnamed variables with
// initializer expressions except for some trivial cases)
name = prev
}
if name != "" {
// entry has a named type
if domName != "" && domName != name {
// more than one type name - do not associate
// with any type
domName = ""
break
}
domName = name
domFreq++
}
prev = name
}
}
// determine values list
const threshold = 0.75
values := &doc.values
if domName != "" && domFreq >= int(float64(len(decl.Specs))*threshold) {
// typed entries are sufficiently frequent
typ := doc.lookupTypeDoc(domName)
if typ != nil {
values = &typ.values // associate with that type
}
}
*values = append(*values, decl)
}
// Helper function to set the table entry for function f. Makes sure that
// at least one f with associated documentation is stored in table, if there
// are multiple f's with the same name.
func setFunc(table map[string]*ast.FuncDecl, f *ast.FuncDecl) {
name := f.Name.Name
if g, exists := table[name]; exists && g.Doc != nil {
// a function with the same name has already been registered;
// since it has documentation, assume f is simply another
// implementation and ignore it
// TODO(gri) consider collecting all functions, or at least
// all comments
return
}
// function doesn't exist or has no documentation; use f
table[name] = f
}
func (doc *docReader) addFunc(fun *ast.FuncDecl) {
name := fun.Name.Name
// determine if it should be associated with a type
if fun.Recv != nil {
// method
typ := doc.lookupTypeDoc(docBaseTypeName(fun.Recv.List[0].Type, doc.showAll))
if typ != nil {
// exported receiver type
setFunc(typ.methods, fun)
}
// otherwise don't show the method
// TODO(gri): There may be exported methods of non-exported types
// that can be called because of exported values (consts, vars, or
// function results) of that type. Could determine if that is the
// case and then show those methods in an appropriate section.
return
}
// perhaps a factory function
// determine result type, if any
if fun.Type.Results.NumFields() >= 1 {
res := fun.Type.Results.List[0]
if len(res.Names) <= 1 {
// exactly one (named or anonymous) result associated
// with the first type in result signature (there may
// be more than one result)
tname := docBaseTypeName(res.Type, doc.showAll)
typ := doc.lookupTypeDoc(tname)
if typ != nil {
// named and exported result type
// Work-around for failure of heuristic: In package os
// too many functions are considered factory functions
// for the Error type. Eliminate manually for now as
// this appears to be the only important case in the
// current library where the heuristic fails.
if doc.pkgName == "os" && tname == "Error" &&
name != "NewError" && name != "NewSyscallError" {
// not a factory function for os.Error
setFunc(doc.funcs, fun) // treat as ordinary function
return
}
setFunc(typ.factories, fun)
return
}
}
}
// ordinary function
setFunc(doc.funcs, fun)
}
func (doc *docReader) addDecl(decl ast.Decl) {
switch d := decl.(type) {
case *ast.GenDecl:
if len(d.Specs) > 0 {
switch d.Tok {
case token.IMPORT:
// imports are handled individually
for _, spec := range d.Specs {
if s, ok := spec.(*ast.ImportSpec); ok {
if import_, err := strconv.Unquote(s.Path.Value); err == nil {
doc.imports[import_] = 1
}
}
}
case token.CONST, token.VAR:
// constants and variables are always handled as a group
doc.addValue(d)
case token.TYPE:
// types are handled individually
for _, spec := range d.Specs {
// make a (fake) GenDecl node for this TypeSpec
// (we need to do this here - as opposed to just
// for printing - so we don't lose the GenDecl
// documentation)
//
// TODO(gri): Consider just collecting the TypeSpec
// node (and copy in the GenDecl.doc if there is no
// doc in the TypeSpec - this is currently done in
// makeTypeDocs below). Simpler data structures, but
// would lose GenDecl documentation if the TypeSpec
// has documentation as well.
doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos, []ast.Spec{spec}, token.NoPos})
// A new GenDecl node is created, no need to nil out d.Doc.
}
}
}
case *ast.FuncDecl:
doc.addFunc(d)
}
}
func copyCommentList(list []*ast.Comment) []*ast.Comment {
return append([]*ast.Comment(nil), list...)
}
var (
bug_markers = regexp.MustCompile("^/[/*][ \t]*BUG\\(.*\\):[ \t]*") // BUG(uid):
bug_content = regexp.MustCompile("[^ \n\r\t]+") // at least one non-whitespace char
)
// addFile adds the AST for a source file to the docReader.
// Adding the same AST multiple times is a no-op.
//
func (doc *docReader) addFile(src *ast.File) {
// add package documentation
if src.Doc != nil {
doc.addDoc(src.Doc)
src.Doc = nil // doc consumed - remove from ast.File node
}
// add all declarations
for _, decl := range src.Decls {
doc.addDecl(decl)
}
// collect BUG(...) comments
for _, c := range src.Comments {
text := c.List[0].Text
if m := bug_markers.FindStringIndex(text); m != nil {
// found a BUG comment; maybe empty
if btxt := text[m[1]:]; bug_content.MatchString(btxt) {
// non-empty BUG comment; collect comment without BUG prefix
list := copyCommentList(c.List)
list[0].Text = text[m[1]:]
doc.bugs = append(doc.bugs, &ast.CommentGroup{list})
}
}
}
src.Comments = nil // consumed unassociated comments - remove from ast.File node
}
func NewFileDoc(file *ast.File, showAll bool) *PackageDoc {
var r docReader
r.init(file.Name.Name, showAll)
r.addFile(file)
return r.newDoc("", nil)
}
func NewPackageDoc(pkg *ast.Package, importpath string, showAll bool) *PackageDoc {
var r docReader
r.init(pkg.Name, showAll)
filenames := make([]string, len(pkg.Files))
i := 0
for filename, f := range pkg.Files {
r.addFile(f)
filenames[i] = filename
i++
}
return r.newDoc(importpath, filenames)
}
// ----------------------------------------------------------------------------
// Conversion to external representation
// ValueDoc is the documentation for a group of declared
// values, either vars or consts.
//
type ValueDoc struct {
Doc string
Decl *ast.GenDecl
order int
}
type sortValueDoc []*ValueDoc
func (p sortValueDoc) Len() int { return len(p) }
func (p sortValueDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func declName(d *ast.GenDecl) string {
if len(d.Specs) != 1 {
return ""
}
switch v := d.Specs[0].(type) {
case *ast.ValueSpec:
return v.Names[0].Name
case *ast.TypeSpec:
return v.Name.Name
}
return ""
}
func (p sortValueDoc) Less(i, j int) bool {
// sort by name
// pull blocks (name = "") up to top
// in original order
if ni, nj := declName(p[i].Decl), declName(p[j].Decl); ni != nj {
return ni < nj
}
return p[i].order < p[j].order
}
func makeValueDocs(list []*ast.GenDecl, tok token.Token) []*ValueDoc {
d := make([]*ValueDoc, len(list)) // big enough in any case
n := 0
for i, decl := range list {
if decl.Tok == tok {
d[n] = &ValueDoc{decl.Doc.Text(), decl, i}
n++
decl.Doc = nil // doc consumed - removed from AST
}
}
d = d[0:n]
sort.Sort(sortValueDoc(d))
return d
}
// FuncDoc is the documentation for a func declaration,
// either a top-level function or a method function.
//
type FuncDoc struct {
Doc string
Recv ast.Expr // TODO(rsc): Would like string here
Name string
Decl *ast.FuncDecl
}
type sortFuncDoc []*FuncDoc
func (p sortFuncDoc) Len() int { return len(p) }
func (p sortFuncDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p sortFuncDoc) Less(i, j int) bool { return p[i].Name < p[j].Name }
func makeFuncDocs(m map[string]*ast.FuncDecl) []*FuncDoc {
d := make([]*FuncDoc, len(m))
i := 0
for _, f := range m {
doc := new(FuncDoc)
doc.Doc = f.Doc.Text()
f.Doc = nil // doc consumed - remove from ast.FuncDecl node
if f.Recv != nil {
doc.Recv = f.Recv.List[0].Type
}
doc.Name = f.Name.Name
doc.Decl = f
d[i] = doc
i++
}
sort.Sort(sortFuncDoc(d))
return d
}
// TypeDoc is the documentation for a declared type.
// Consts and Vars are sorted lists of constants and variables of (mostly) that type.
// Factories is a sorted list of factory functions that return that type.
// Methods is a sorted list of method functions on that type.
type TypeDoc struct {
Doc string
Type *ast.TypeSpec
Consts []*ValueDoc
Vars []*ValueDoc
Funcs []*FuncDoc
Methods []*FuncDoc
Decl *ast.GenDecl
order int
}
type sortTypeDoc []*TypeDoc
func (p sortTypeDoc) Len() int { return len(p) }
func (p sortTypeDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p sortTypeDoc) Less(i, j int) bool {
// sort by name
// pull blocks (name = "") up to top
// in original order
if ni, nj := p[i].Type.Name.Name, p[j].Type.Name.Name; ni != nj {
return ni < nj
}
return p[i].order < p[j].order
}
// NOTE(rsc): This would appear not to be correct for type ( )
// blocks, but the doc extractor above has split them into
// individual declarations.
func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {
d := make([]*TypeDoc, len(m))
i := 0
for _, old := range m {
// all typeDocs should have a declaration associated with
// them after processing an entire package - be conservative
// and check
if decl := old.decl; decl != nil {
typespec := decl.Specs[0].(*ast.TypeSpec)
t := new(TypeDoc)
doc := typespec.Doc
typespec.Doc = nil // doc consumed - remove from ast.TypeSpec node
if doc == nil {
// no doc associated with the spec, use the declaration doc, if any
doc = decl.Doc
}
decl.Doc = nil // doc consumed - remove from ast.Decl node
t.Doc = doc.Text()
t.Type = typespec
t.Consts = makeValueDocs(old.values, token.CONST)
t.Vars = makeValueDocs(old.values, token.VAR)
t.Funcs = makeFuncDocs(old.factories)
t.Methods = makeFuncDocs(old.methods)
t.Decl = old.decl
t.order = i
d[i] = t
i++
} else {
// no corresponding type declaration found - move any associated
// values, factory functions, and methods back to the top-level
// so that they are not lost (this should only happen if a package
// file containing the explicit type declaration is missing or if
// an unqualified type name was used after a "." import)
// 1) move values
doc.values = append(doc.values, old.values...)
// 2) move factory functions
for name, f := range old.factories {
doc.funcs[name] = f
}
// 3) move methods
for name, f := range old.methods {
// don't overwrite functions with the same name
if _, found := doc.funcs[name]; !found {
doc.funcs[name] = f
}
}
}
}
d = d[0:i] // some types may have been ignored
sort.Sort(sortTypeDoc(d))
return d
}
func makeBugDocs(list []*ast.CommentGroup) []string {
d := make([]string, len(list))
for i, g := range list {
d[i] = g.Text()
}
return d
}
// PackageDoc is the documentation for an entire package.
//
type PackageDoc struct {
PackageName string
ImportPath string
Imports []string
Filenames []string
Doc string
Consts []*ValueDoc
Types []*TypeDoc
Vars []*ValueDoc
Funcs []*FuncDoc
Factorys []*FuncDoc
Bugs []string
}
// newDoc returns the accumulated documentation for the package.
//
func (doc *docReader) newDoc(importpath string, filenames []string) *PackageDoc {
p := new(PackageDoc)
p.PackageName = doc.pkgName
p.ImportPath = importpath
sort.Strings(filenames)
p.Filenames = filenames
p.Doc = doc.doc.Text()
p.Imports = sortedKeys(doc.imports)
// makeTypeDocs may extend the list of doc.values and
// doc.funcs and thus must be called before any other
// function consuming those lists
p.Types = doc.makeTypeDocs(doc.types)
p.Consts = makeValueDocs(doc.values, token.CONST)
p.Vars = makeValueDocs(doc.values, token.VAR)
p.Funcs = makeFuncDocs(doc.funcs)
p.Bugs = makeBugDocs(doc.bugs)
for _, d := range p.Types {
switch d.Type.Type.(type) {
case *ast.StructType:
p.Factorys = append(p.Factorys, d.Funcs...)
d.Funcs = make([]*FuncDoc, 0)
case *ast.InterfaceType:
p.Factorys = append(p.Factorys, d.Funcs...)
d.Funcs = make([]*FuncDoc, 0)
default:
p.Vars = append(p.Vars, d.Vars...)
d.Vars = make([]*ValueDoc, 0)
p.Consts = append(p.Consts, d.Consts...)
d.Consts = make([]*ValueDoc, 0)
}
}
return p
}
func sortedKeys(m map[string]int) []string {
list := make([]string, len(m))
i := 0
for key := range m {
list[i] = key
i++
}
sort.Strings(list)
return list
}
// ----------------------------------------------------------------------------
// Filtering by name
type Filter func(string) bool
func matchFields(fields *ast.FieldList, f Filter) bool {
if fields != nil {
for _, field := range fields.List {
for _, name := range field.Names {
if f(name.Name) {
return true
}
}
}
}
return false
}
func matchDecl(d *ast.GenDecl, f Filter) bool {
for _, d := range d.Specs {
switch v := d.(type) {
case *ast.ValueSpec:
for _, name := range v.Names {
if f(name.Name) {
return true
}
}
case *ast.TypeSpec:
if f(v.Name.Name) {
return true
}
switch t := v.Type.(type) {
case *ast.StructType:
if matchFields(t.Fields, f) {
return true
}
case *ast.InterfaceType:
if matchFields(t.Methods, f) {
return true
}
}
}
}
return false
}
func filterValueDocs(a []*ValueDoc, f Filter) []*ValueDoc {
w := 0
for _, vd := range a {
if matchDecl(vd.Decl, f) {
a[w] = vd
w++
}
}
return a[0:w]
}
func filterFuncDocs(a []*FuncDoc, f Filter) []*FuncDoc {
w := 0
for _, fd := range a {
if f(fd.Name) {
a[w] = fd
w++
}
}
return a[0:w]
}
func filterTypeDocs(a []*TypeDoc, f Filter) []*TypeDoc {
w := 0
for _, td := range a {
n := 0 // number of matches
if matchDecl(td.Decl, f) {
n = 1
} else {
// type name doesn't match, but we may have matching consts, vars, factories or methods
td.Consts = filterValueDocs(td.Consts, f)
td.Vars = filterValueDocs(td.Vars, f)
td.Funcs = filterFuncDocs(td.Funcs, f)
td.Methods = filterFuncDocs(td.Methods, f)
n += len(td.Consts) + len(td.Vars) + len(td.Funcs) + len(td.Methods)
}
if n > 0 {
a[w] = td
w++
}
}
return a[0:w]
}
// Filter eliminates documentation for names that don't pass through the filter f.
// TODO: Recognize "Type.Method" as a name.
//
func (p *PackageDoc) Filter(f Filter) {
p.Consts = filterValueDocs(p.Consts, f)
p.Vars = filterValueDocs(p.Vars, f)
p.Types = filterTypeDocs(p.Types, f)
p.Funcs = filterFuncDocs(p.Funcs, f)
p.Doc = "" // don't show top-level package doc
}

346
vendor/github.com/visualfc/gotools/astview/astview.go generated vendored Normal file
View File

@ -0,0 +1,346 @@
// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package astview
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"io"
"io/ioutil"
"os"
"strings"
"github.com/visualfc/gotools/command"
"golang.org/x/tools/go/types"
)
var Command = &command.Command{
Run: runAstView,
UsageLine: "astview [-stdin] files...",
Short: "print go files astview",
Long: `print go files astview`,
}
var astViewStdin bool
func init() {
Command.Flag.BoolVar(&astViewStdin, "stdin", false, "input from stdin")
}
func runAstView(cmd *command.Command, args []string) error {
if len(args) == 0 {
cmd.Usage()
return os.ErrInvalid
}
if astViewStdin {
view, err := NewFilePackageSource(args[0], os.Stdin, true)
if err != nil {
fmt.Fprintf(os.Stderr, "astview: %s", err)
command.SetExitStatus(3)
command.Exit()
}
view.PrintTree(os.Stdout)
} else {
err := PrintFilesTree(args, os.Stdout, true)
if err != nil {
fmt.Fprintf(os.Stderr, "astview:%s", err)
command.SetExitStatus(3)
command.Exit()
}
}
return nil
}
const (
tag_package = "p"
tag_imports_folder = "+m"
tag_import = "mm"
tag_type = "t"
tag_struct = "s"
tag_interface = "i"
tag_value = "v"
tag_const = "c"
tag_func = "f"
tag_value_folder = "+v"
tag_const_folder = "+c"
tag_func_folder = "+f"
tag_factor_folder = "+tf"
tag_type_method = "tm"
tag_type_factor = "tf"
tag_type_value = "tv"
)
type PackageView struct {
fset *token.FileSet
pdoc *PackageDoc
pkg *ast.Package
expr bool
}
var AllFiles []string
func (p *PackageView) posFileIndex(pos token.Position) int {
var index = -1
for i := 0; i < len(AllFiles); i++ {
if AllFiles[i] == pos.Filename {
index = i
break
}
}
if index == -1 {
AllFiles = append(AllFiles, pos.Filename)
index = len(AllFiles) - 1
}
return index
}
func (p *PackageView) posText(pos token.Position) (s string) {
index := p.posFileIndex(pos)
return fmt.Sprintf("%d:%d:%d", index, pos.Line, pos.Column)
}
func NewFilePackage(filename string) (*PackageView, error) {
p := new(PackageView)
p.fset = token.NewFileSet()
file, err := parser.ParseFile(p.fset, filename, nil, parser.AllErrors)
if file == nil {
return nil, err
}
m := make(map[string]*ast.File)
m[filename] = file
pkg, err := ast.NewPackage(p.fset, m, nil, nil)
if err != nil {
return nil, err
}
p.pkg = pkg
p.pdoc = NewPackageDoc(pkg, pkg.Name, true)
return p, nil
}
func NewPackageView(pkg *ast.Package, fset *token.FileSet, expr bool) (*PackageView, error) {
p := new(PackageView)
p.fset = fset
p.pkg = pkg
p.pdoc = NewPackageDoc(pkg, pkg.Name, true)
p.expr = expr
return p, nil
}
func ParseFiles(fset *token.FileSet, filenames []string, mode parser.Mode) (pkgs map[string]*ast.Package, pkgsfiles []string, first error) {
pkgs = make(map[string]*ast.Package)
for _, filename := range filenames {
if src, err := parser.ParseFile(fset, filename, nil, mode); src != nil {
name := src.Name.Name
pkg, found := pkgs[name]
if !found {
pkg = &ast.Package{
Name: name,
Files: make(map[string]*ast.File),
}
pkgs[name] = pkg
}
pkg.Files[filename] = src
pkgsfiles = append(pkgsfiles, filename)
} else {
first = err
return
}
}
return
}
func PrintFilesTree(filenames []string, w io.Writer, expr bool) error {
fset := token.NewFileSet()
pkgs, pkgsfiles, err := ParseFiles(fset, filenames, parser.AllErrors)
if err != nil {
return err
}
AllFiles = pkgsfiles
for i := 0; i < len(AllFiles); i++ {
fmt.Fprintf(w, "@%s\n", AllFiles[i])
}
for _, pkg := range pkgs {
view, err := NewPackageView(pkg, fset, expr)
if err != nil {
return err
}
view.PrintTree(w)
}
return nil
}
func NewFilePackageSource(filename string, f *os.File, expr bool) (*PackageView, error) {
src, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
p := new(PackageView)
p.fset = token.NewFileSet()
p.expr = expr
file, err := parser.ParseFile(p.fset, filename, src, 0)
if err != nil {
return nil, err
}
m := make(map[string]*ast.File)
m[filename] = file
pkg, err := ast.NewPackage(p.fset, m, nil, nil)
if err != nil {
return nil, err
}
p.pdoc = NewPackageDoc(pkg, pkg.Name, true)
return p, nil
}
func (p *PackageView) printFuncsHelper(w io.Writer, funcs []*FuncDoc, level int, tag string, tag_folder string) {
for _, f := range funcs {
pos := p.fset.Position(f.Decl.Pos())
if p.expr {
fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag, f.Name, p.posText(pos), types.ExprString(f.Decl.Type))
} else {
fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, f.Name, p.posText(pos))
}
}
}
func (p *PackageView) PrintVars(w io.Writer, vars []*ValueDoc, level int, tag string, tag_folder string) {
if len(tag_folder) > 0 && len(vars) > 0 {
if tag_folder == tag_value_folder {
fmt.Fprintf(w, "%d,%s,Variables\n", level, tag_folder)
} else if tag_folder == tag_const_folder {
fmt.Fprintf(w, "%d,%s,Constants\n", level, tag_folder)
}
level++
}
for _, v := range vars {
if v.Decl == nil {
continue
}
for _, s := range v.Decl.Specs {
if m, ok := s.(*ast.ValueSpec); ok {
pos := p.fset.Position(m.Pos())
for i := 0; i < len(m.Names); i++ {
if p.expr && m.Type != nil {
fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag, m.Names[i], p.posText(pos), types.ExprString(m.Type))
} else {
fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, m.Names[i], p.posText(pos))
}
}
}
}
}
}
func (p *PackageView) PrintTypes(w io.Writer, types []*TypeDoc, level int) {
for _, d := range types {
if d.Decl == nil {
continue
}
typespec := d.Decl.Specs[0].(*ast.TypeSpec)
var tag = tag_type
if _, ok := typespec.Type.(*ast.InterfaceType); ok {
tag = tag_interface
} else if _, ok := typespec.Type.(*ast.StructType); ok {
tag = tag_struct
}
pos := p.fset.Position(d.Decl.Pos())
fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, d.Type.Name, p.posText(pos))
p.printFuncsHelper(w, d.Funcs, level+1, tag_type_factor, "")
p.printFuncsHelper(w, d.Methods, level+1, tag_type_method, "")
p.PrintTypeFields(w, d.Decl, level+1)
//p.PrintVars(w, d.Consts, level+1, tag_const, "")
//p.PrintVars(w, d.Vars, level+1, tag_value, "")
}
}
func (p *PackageView) PrintTypeFields(w io.Writer, decl *ast.GenDecl, level int) {
spec, ok := decl.Specs[0].(*ast.TypeSpec)
if ok == false {
return
}
switch d := spec.Type.(type) {
case *ast.StructType:
for _, list := range d.Fields.List {
if list.Names == nil {
continue
}
for _, m := range list.Names {
pos := p.fset.Position(m.Pos())
if list.Type != nil {
fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag_type_value, m.Name, p.posText(pos), types.ExprString(list.Type))
} else {
fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag_type_value, m.Name, p.posText(pos))
}
}
}
case *ast.InterfaceType:
for _, list := range d.Methods.List {
if list.Names == nil {
continue
}
for _, m := range list.Names {
pos := p.fset.Position(m.Pos())
fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag_type_method, m.Name, p.posText(pos))
}
}
}
}
func (p *PackageView) PrintHeader(w io.Writer, level int) {
fmt.Fprintf(w, "%d,%s,%s\n", level, tag_package, p.pdoc.PackageName)
}
func (p *PackageView) PrintImports(w io.Writer, level int, tag, tag_folder string) {
if tag_folder != "" && len(p.pdoc.Imports) > 0 {
fmt.Fprintf(w, "%d,%s,%s\n", level, tag_folder, "Imports")
level++
}
for _, name := range p.pdoc.Imports {
vname := "\"" + name + "\""
var ps []string
for _, file := range p.pkg.Files {
for _, v := range file.Imports {
if v.Path.Value == vname {
pos := p.fset.Position(v.Pos())
ps = append(ps, p.posText(pos))
}
}
}
fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, name, strings.Join(ps, ";"))
}
}
func (p *PackageView) PrintFuncs(w io.Writer, level int, tag_folder string) {
hasFolder := false
if len(p.pdoc.Funcs) > 0 || len(p.pdoc.Factorys) > 0 {
hasFolder = true
}
if !hasFolder {
return
}
if len(tag_folder) > 0 {
fmt.Fprintf(w, "%d,%s,Functions\n", level, tag_folder)
level++
}
p.printFuncsHelper(w, p.pdoc.Factorys, level, tag_type_factor, tag_func_folder)
p.printFuncsHelper(w, p.pdoc.Funcs, level, tag_func, tag_func_folder)
}
func (p *PackageView) PrintPackage(w io.Writer, level int) {
p.PrintHeader(w, level)
level++
p.PrintImports(w, level, tag_import, tag_imports_folder)
p.PrintVars(w, p.pdoc.Vars, level, tag_value, tag_value_folder)
p.PrintVars(w, p.pdoc.Consts, level, tag_const, tag_const_folder)
p.PrintFuncs(w, level, tag_func_folder)
p.PrintTypes(w, p.pdoc.Types, level)
}
// level,tag,pos@info
func (p *PackageView) PrintTree(w io.Writer) {
p.PrintPackage(w, 0)
}

343
vendor/github.com/visualfc/gotools/command/command.go generated vendored Normal file
View File

@ -0,0 +1,343 @@
// Copyright 2011 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.
//modify 2013-2014 visualfc
package command
import (
"bytes"
"flag"
"fmt"
"io"
"log"
"os"
"strings"
"sync"
"text/template"
"unicode"
"unicode/utf8"
)
// A Command is an implementation of a go command
// like go build or go fix.
type Command struct {
// Run runs the command.
// The args are the arguments after the command name.
Run func(cmd *Command, args []string) error
// UsageLine is the one-line usage message.
// The first word in the line is taken to be the command name.
UsageLine string
// Short is the short description shown in the 'go help' output.
Short string
// Long is the long message shown in the 'go help <this-command>' output.
Long string
// Flag is a set of flags specific to this command.
Flag flag.FlagSet
// CustomFlags indicates that the command will do its own
// flag parsing.
CustomFlags bool
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
}
// Name returns the command's name: the first word in the usage line.
func (c *Command) Name() string {
name := c.UsageLine
i := strings.Index(name, " ")
if i >= 0 {
name = name[:i]
}
return name
}
func (c *Command) Usage() {
fmt.Fprintf(os.Stderr, "usage: %s %s\n", AppName, c.UsageLine)
c.Flag.SetOutput(os.Stderr)
c.Flag.PrintDefaults()
//fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long))
os.Exit(2)
}
func (c *Command) PrintUsage() {
fmt.Fprintf(Stderr, "usage: %s %s\n", AppName, c.UsageLine)
c.Flag.SetOutput(Stderr)
c.Flag.PrintDefaults()
}
// Runnable reports whether the command can be run; otherwise
// it is a documentation pseudo-command such as importpath.
func (c *Command) Runnable() bool {
return c.Run != nil
}
func (c *Command) Println(args ...interface{}) {
fmt.Fprintln(c.Stdout, args...)
}
func (c *Command) Printf(format string, args ...interface{}) {
fmt.Fprintf(c.Stdout, format, args...)
}
var commands []*Command
func Register(cmd *Command) {
commands = append(commands, cmd)
}
func CommandList() (cmds []string) {
for _, cmd := range commands {
cmds = append(cmds, cmd.Name())
}
return
}
var exitStatus = 0
var exitMu sync.Mutex
func SetExitStatus(n int) {
exitMu.Lock()
if exitStatus < n {
exitStatus = n
}
exitMu.Unlock()
}
var (
Stdout io.Writer = os.Stdout
Stderr io.Writer = os.Stderr
Stdin io.Reader = os.Stdin
)
func RunArgs(arguments []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error {
flag.CommandLine.Parse(arguments)
args := flag.Args()
if len(args) < 1 {
printUsage(os.Stderr)
return os.ErrInvalid
}
if len(args) == 1 && strings.TrimSpace(args[0]) == "" {
printUsage(os.Stderr)
return os.ErrInvalid
}
if args[0] == "help" {
if !help(args[1:]) {
return os.ErrInvalid
}
return nil
}
for _, cmd := range commands {
if cmd.Name() == args[0] && cmd.Run != nil {
cmd.Flag.Usage = func() { cmd.Usage() }
if cmd.CustomFlags {
args = args[1:]
} else {
cmd.Flag.Parse(args[1:])
args = cmd.Flag.Args()
}
cmd.Stdin = stdin
cmd.Stdout = stdout
cmd.Stderr = stderr
return cmd.Run(cmd, args)
}
}
fmt.Fprintf(os.Stderr, "%s: unknown subcommand %q\nRun '%s help' for usage.\n",
AppName, args[0], AppName)
return os.ErrInvalid
}
func Main() {
flag.Usage = usage
flag.Parse()
log.SetFlags(0)
args := flag.Args()
if len(args) < 1 {
usage()
}
if len(args) == 1 && strings.TrimSpace(args[0]) == "" {
usage()
}
if args[0] == "help" {
if !help(args[1:]) {
os.Exit(2)
}
return
}
for _, cmd := range commands {
if cmd.Name() == args[0] && cmd.Run != nil {
cmd.Flag.Usage = func() { cmd.Usage() }
if cmd.CustomFlags {
args = args[1:]
} else {
cmd.Flag.Parse(args[1:])
args = cmd.Flag.Args()
}
cmd.Stdin = Stdin
cmd.Stdout = Stdout
cmd.Stderr = Stderr
cmd.Run(cmd, args)
Exit()
return
}
}
fmt.Fprintf(os.Stderr, "%s: unknown subcommand %q\nRun '%s help' for usage.\n",
AppName, args[0], AppName)
SetExitStatus(2)
Exit()
}
var AppInfo string = "LiteIDE golang tool."
var AppName string = "tools"
var usageTemplate = `
Usage:
{{AppName}} command [arguments]
The commands are:
{{range .}}{{if .Runnable}}
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
Use "{{AppName}} help [command]" for more information about a command.
Additional help topics:
{{range .}}{{if not .Runnable}}
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
Use "{{AppName}} help [topic]" for more information about that topic.
`
var helpTemplate = `{{if .Runnable}}usage: {{AppName}} {{.UsageLine}}
{{end}}{{.Long | trim}}
`
var documentationTemplate = `//
/*
{{range .}}{{if .Short}}{{.Short | capitalize}}
{{end}}{{if .Runnable}}Usage:
{{AppName}} {{.UsageLine}}
{{end}}{{.Long | trim}}
{{end}}*/
package main
`
// tmpl executes the given template text on data, writing the result to w.
func tmpl(w io.Writer, text string, data interface{}) {
t := template.New("top")
t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize})
template.Must(t.Parse(text))
if err := t.Execute(w, data); err != nil {
panic(err)
}
}
func capitalize(s string) string {
if s == "" {
return s
}
r, n := utf8.DecodeRuneInString(s)
return string(unicode.ToTitle(r)) + s[n:]
}
func printUsage(w io.Writer) {
if len(AppInfo) > 0 {
fmt.Fprintln(w, AppInfo)
}
tmpl(w, strings.Replace(usageTemplate, "{{AppName}}", AppName, -1), commands)
}
func usage() {
printUsage(os.Stderr)
os.Exit(2)
}
// help implements the 'help' command.
func help(args []string) bool {
if len(args) == 0 {
printUsage(os.Stdout)
// not exit 2: succeeded at 'go help'.
return true
}
if len(args) != 1 {
fmt.Fprintf(os.Stderr, "usage: %s help command\n\nToo many arguments given.\n", AppName)
return false
}
arg := args[0]
// 'go help documentation' generates doc.go.
if arg == "documentation" {
buf := new(bytes.Buffer)
printUsage(buf)
usage := &Command{Long: buf.String()}
tmpl(os.Stdout, strings.Replace(documentationTemplate, "{{AppName}}", AppName, -1), append([]*Command{usage}, commands...))
return false
}
for _, cmd := range commands {
if cmd.Name() == arg {
tmpl(os.Stdout, strings.Replace(helpTemplate, "{{AppName}}", AppName, -1), cmd)
// not exit 2: succeeded at 'go help cmd'.
return true
}
}
fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run '%s help'.\n", arg, AppName)
//os.Exit(2) // failed at 'go help cmd'
return false
}
var atexitFuncs []func()
func Atexit(f func()) {
atexitFuncs = append(atexitFuncs, f)
}
func Exit() {
for _, f := range atexitFuncs {
f()
}
os.Exit(exitStatus)
}
func Fatalf(format string, args ...interface{}) {
Errorf(format, args...)
Exit()
}
func Errorf(format string, args ...interface{}) {
log.Printf(format, args...)
SetExitStatus(1)
}
var logf = log.Printf
func ExitIfErrors() {
if exitStatus != 0 {
Exit()
}
}

33
vendor/github.com/visualfc/gotools/command/version.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package command
import (
"os"
"runtime"
)
func init() {
Register(cmdVersion)
}
var AppVersion string = "1.0"
var cmdVersion = &Command{
Run: runVersion,
UsageLine: "version",
Short: "print tool version",
Long: `Version prints the version.`,
}
func runVersion(cmd *Command, args []string) error {
if len(args) != 0 {
cmd.PrintUsage()
return os.ErrInvalid
}
cmd.Printf("%s version %s [%s %s/%s]\n", AppName, AppVersion, runtime.Version(), runtime.GOOS, runtime.GOARCH)
return nil
}

352
vendor/github.com/visualfc/gotools/docview/dirtrees.go generated vendored Normal file
View File

@ -0,0 +1,352 @@
// Copyright 2010 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.
// This file contains the code dealing with package directory trees.
package docview
import (
"bytes"
"go/doc"
"go/parser"
"go/token"
"log"
"os"
"path/filepath"
"strings"
"unicode"
)
type Directory struct {
Depth int
Path string // includes Name
Name string
Text string // package documentation, if any
Dirs []*Directory // subdirectories
}
//func isGoFile(fi os.FileInfo) bool {
// name := fi.Name()
// return !fi.IsDir() &&
// len(name) > 0 && name[0] != '.' && // ignore .files
// filepath.Ext(name) == ".go"
//}
func isGoFile(f os.FileInfo) bool {
// ignore non-Go files
name := f.Name()
return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}
func isPkgFile(fi os.FileInfo) bool {
return isGoFile(fi) &&
!strings.HasSuffix(fi.Name(), "_test.go") // ignore test files
}
func isPkgDir(fi os.FileInfo) bool {
name := fi.Name()
return fi.IsDir() && len(name) > 0 &&
name[0] != '_' && name[0] != '.' // ignore _files and .files
}
func firstSentence(s string) string {
i := -1 // index+1 of first terminator (punctuation ending a sentence)
j := -1 // index+1 of first terminator followed by white space
prev := 'A'
for k, ch := range s {
k1 := k + 1
if ch == '.' || ch == '!' || ch == '?' {
if i < 0 {
i = k1 // first terminator
}
if k1 < len(s) && s[k1] <= ' ' {
if j < 0 {
j = k1 // first terminator followed by white space
}
if !unicode.IsUpper(prev) {
j = k1
break
}
}
}
prev = ch
}
if j < 0 {
// use the next best terminator
j = i
if j < 0 {
// no terminator at all, use the entire string
j = len(s)
}
}
return s[0:j]
}
type treeBuilder struct {
pathFilter func(string) bool
maxDepth int
}
func (b *treeBuilder) newDirTree(fset *token.FileSet, path, name string, depth int) *Directory {
if b.pathFilter != nil && !b.pathFilter(path) {
return nil
}
if depth >= b.maxDepth {
// return a dummy directory so that the parent directory
// doesn't get discarded just because we reached the max
// directory depth
return &Directory{depth, path, name, "", nil}
}
list, err := fs.ReadDir(path)
if err != nil {
// newDirTree is called with a path that should be a package
// directory; errors here should not happen, but if they do,
// we want to know about them
log.Printf("ReadDir(%s): %s", path, err)
}
// determine number of subdirectories and if there are package files
ndirs := 0
hasPkgFiles := false
var synopses [4]string // prioritized package documentation (0 == highest priority)
for _, d := range list {
switch {
case isPkgDir(d):
ndirs++
case isPkgFile(d):
// looks like a package file, but may just be a file ending in ".go";
// don't just count it yet (otherwise we may end up with hasPkgFiles even
// though the directory doesn't contain any real package files - was bug)
if synopses[0] == "" {
// no "optimal" package synopsis yet; continue to collect synopses
//file, err := parseFile(fset, filepath.Join(path, d.Name()),
//parser.ParseComments|parser.PackageClauseOnly)
file, err := parser.ParseFile(fset, filepath.Join(path, d.Name()), nil,
parser.ParseComments|parser.PackageClauseOnly)
if err == nil {
hasPkgFiles = true
if file.Doc != nil {
// prioritize documentation
i := -1
switch file.Name.Name {
case name:
i = 0 // normal case: directory name matches package name
case fakePkgName:
i = 1 // synopses for commands
case "main":
i = 2 // directory contains a main package
default:
i = 3 // none of the above
}
if 0 <= i && i < len(synopses) && synopses[i] == "" {
synopses[i] = doc.Synopsis(file.Doc.Text())
}
}
}
}
}
}
// create subdirectory tree
var dirs []*Directory
if ndirs > 0 {
dirs = make([]*Directory, ndirs)
i := 0
for _, d := range list {
if isPkgDir(d) {
name := d.Name()
dd := b.newDirTree(fset, filepath.Join(path, name), name, depth+1)
if dd != nil {
dirs[i] = dd
i++
}
}
}
dirs = dirs[0:i]
}
// if there are no package files and no subdirectories
// containing package files, ignore the directory
if !hasPkgFiles && len(dirs) == 0 {
return nil
}
// select the highest-priority synopsis for the directory entry, if any
synopsis := ""
for _, synopsis = range synopses {
if synopsis != "" {
break
}
}
return &Directory{depth, path, name, synopsis, dirs}
}
// newDirectory creates a new package directory tree with at most maxDepth
// levels, anchored at root. The result tree is pruned such that it only
// contains directories that contain package files or that contain
// subdirectories containing package files (transitively). If a non-nil
// pathFilter is provided, directory paths additionally must be accepted
// by the filter (i.e., pathFilter(path) must be true). If a value >= 0 is
// provided for maxDepth, nodes at larger depths are pruned as well; they
// are assumed to contain package files even if their contents are not known
// (i.e., in this case the tree may contain directories w/o any package files).
//
func newDirectory(root string, pathFilter func(string) bool, maxDepth int) *Directory {
// The root could be a symbolic link so use Stat not Lstat.
d, err := fs.Stat(root)
// If we fail here, report detailed error messages; otherwise
// is is hard to see why a directory tree was not built.
switch {
case err != nil:
log.Printf("newDirectory(%s): %s", root, err)
return nil
case !isPkgDir(d):
log.Printf("newDirectory(%s): not a package directory", root)
return nil
}
if maxDepth < 0 {
maxDepth = 1e6 // "infinity"
}
b := treeBuilder{pathFilter, maxDepth}
// the file set provided is only for local parsing, no position
// information escapes and thus we don't need to save the set
return b.newDirTree(token.NewFileSet(), root, d.Name(), 0)
}
func (dir *Directory) writeLeafs(buf *bytes.Buffer) {
if dir != nil {
if len(dir.Dirs) == 0 {
buf.WriteString(dir.Path)
buf.WriteByte('\n')
return
}
for _, d := range dir.Dirs {
d.writeLeafs(buf)
}
}
}
func (dir *Directory) walk(c chan<- *Directory, skipRoot bool) {
if dir != nil {
if !skipRoot {
c <- dir
}
for _, d := range dir.Dirs {
d.walk(c, false)
}
}
}
func (dir *Directory) iter(skipRoot bool) <-chan *Directory {
c := make(chan *Directory)
go func() {
dir.walk(c, skipRoot)
close(c)
}()
return c
}
func (dir *Directory) lookupLocal(name string) *Directory {
for _, d := range dir.Dirs {
if d.Name == name {
return d
}
}
return nil
}
// lookup looks for the *Directory for a given path, relative to dir.
func (dir *Directory) lookup(path string) *Directory {
d := strings.Split(dir.Path, string(filepath.Separator))
p := strings.Split(path, string(filepath.Separator))
i := 0
for i < len(d) {
if i >= len(p) || d[i] != p[i] {
return nil
}
i++
}
for dir != nil && i < len(p) {
dir = dir.lookupLocal(p[i])
i++
}
return dir
}
// DirEntry describes a directory entry. The Depth and Height values
// are useful for presenting an entry in an indented fashion.
//
type DirEntry struct {
Depth int // >= 0
Height int // = DirList.MaxHeight - Depth, > 0
Path string // includes Name, relative to DirList root
Name string
Synopsis string
}
type DirList struct {
MaxHeight int // directory tree height, > 0
List []DirEntry
}
// listing creates a (linear) directory listing from a directory tree.
// If skipRoot is set, the root directory itself is excluded from the list.
//
func (root *Directory) listing(skipRoot bool) *DirList {
if root == nil {
return nil
}
// determine number of entries n and maximum height
n := 0
minDepth := 1 << 30 // infinity
maxDepth := 0
for d := range root.iter(skipRoot) {
n++
if minDepth > d.Depth {
minDepth = d.Depth
}
if maxDepth < d.Depth {
maxDepth = d.Depth
}
}
maxHeight := maxDepth - minDepth + 1
if n == 0 {
return nil
}
// create list
list := make([]DirEntry, n)
i := 0
for d := range root.iter(skipRoot) {
p := &list[i]
p.Depth = d.Depth - minDepth
p.Height = maxHeight - p.Depth
// the path is relative to root.Path - remove the root.Path
// prefix (the prefix should always be present but avoid
// crashes and check)
path := d.Path
if strings.HasPrefix(d.Path, root.Path) {
path = d.Path[len(root.Path):]
}
// remove trailing separator if any - path must be relative
if len(path) > 0 && path[0] == filepath.Separator {
path = path[1:]
}
p.Path = filepath.ToSlash(path)
p.Name = d.Name
p.Synopsis = d.Text
i++
}
return &DirList{maxHeight, list}
}

407
vendor/github.com/visualfc/gotools/docview/docview.go generated vendored Normal file
View File

@ -0,0 +1,407 @@
// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package docview
import (
"bytes"
"fmt"
"go/build"
"io"
"log"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"text/template"
"time"
"github.com/visualfc/gotools/command"
)
var Command = &command.Command{
Run: runDocView,
UsageLine: "docview [-mode] [-list|-find]",
Short: "golang docview util",
Long: `golang docview util`,
}
var goroot = runtime.GOROOT()
var docViewFind string
var docViewList string
var docViewMode string
func init() {
Command.Flag.StringVar(&docViewFind, "find", "", "find package list, :pkg flag is best match")
Command.Flag.StringVar(&docViewList, "list", "", "Print go packages list [pkg|cmd]")
Command.Flag.StringVar(&docViewMode, "mode", "text", "Print mode [text|html|lite]")
}
func runDocView(cmd *command.Command, args []string) error {
if docViewFind == "" && docViewList == "" {
cmd.Usage()
return os.ErrInvalid
}
var template string
var info *Info
if len(docViewList) > 0 {
pkgPath := filepath.Join(goroot, "src", docViewList)
if docViewList == "pkg" {
_, err := os.Stat(pkgPath)
if err != nil {
pkgPath = filepath.Join(goroot, "src")
}
}
info = NewListInfo(pkgPath)
if info != nil {
if docViewList == "pkg" {
var filterList []DirEntry
for _, v := range info.Dirs.List {
if v.Path == "cmd" {
continue
}
if strings.HasPrefix(v.Path, "cmd/") {
continue
}
if strings.Contains(v.Path, "/testdata") {
continue
}
filterList = append(filterList, v)
}
info.Dirs.List = filterList
} else if docViewList == "cmd" {
var filterList []DirEntry
for _, v := range info.Dirs.List {
if strings.Contains(v.Path, "/") {
continue
}
if strings.Contains(v.Path, "internal") {
continue
}
filterList = append(filterList, v)
}
info.Dirs.List = filterList
}
}
switch docViewMode {
case "html":
template = listHTML
case "lite":
template = listLite
case "text":
template = listText
default:
template = listText
}
} else if len(docViewFind) > 0 {
dir := NewSourceDir(goroot)
info = dir.FindInfo(docViewFind)
switch docViewMode {
case "html":
template = findHTML
case "lite":
template = findLite
case "text":
template = findText
default:
template = findText
}
}
if info == nil {
fmt.Fprintf(os.Stderr, "<error>\n")
command.SetExitStatus(3)
command.Exit()
}
contents := info.GetPkgList(docViewMode, template)
fmt.Fprintf(os.Stdout, "%s", contents)
return nil
}
var (
fs FileSystem = OS // the underlying file system
)
// Fake package file and name for commands. Contains the command documentation.
const fakePkgFile = "doc.go"
const fakePkgName = "documentation"
func textFmt(w io.Writer, format string, x ...interface{}) {
var buf bytes.Buffer
fmt.Fprint(&buf, x)
template.HTMLEscape(w, buf.Bytes())
}
func pathEscFmt(w io.Writer, format string, x ...interface{}) {
switch v := x[0].(type) {
case []byte:
template.HTMLEscape(w, v)
case string:
template.HTMLEscape(w, []byte(filepath.ToSlash(v)))
default:
var buf bytes.Buffer
fmt.Fprint(&buf, x)
template.HTMLEscape(w, buf.Bytes())
}
}
func htmlEscFmt(w io.Writer, format string, x ...interface{}) {
switch v := x[0].(type) {
case int:
template.HTMLEscape(w, []byte(strconv.Itoa(v)))
case []byte:
template.HTMLEscape(w, v)
case string:
template.HTMLEscape(w, []byte(v))
default:
var buf bytes.Buffer
fmt.Fprint(&buf, x)
template.HTMLEscape(w, buf.Bytes())
}
}
// Template formatter for "padding" format.
func paddingFmt(w io.Writer, format string, x ...interface{}) {
for i := x[0].(int); i > 0; i-- {
fmt.Fprint(w, `<td width="25"></td>`)
}
}
// Template formatter for "time" format.
func timeFmt(w io.Writer, format string, x ...interface{}) {
template.HTMLEscape(w, []byte(time.Unix(x[0].(int64)/1e9, 0).String()))
}
var fmap = template.FuncMap{
"repeat": strings.Repeat,
}
func readTemplateData(name, data string) *template.Template {
return template.Must(template.New(name).Funcs(fmap).Parse(data))
}
func readTemplateFile(name, path string) *template.Template {
return template.Must(template.New(name).Funcs(fmap).ParseFiles(path))
}
func applyTemplate(t *template.Template, name string, data interface{}) []byte {
var buf bytes.Buffer
if err := t.Execute(&buf, data); err != nil {
log.Printf("%s.Execute: %s", name, err)
}
return buf.Bytes()
}
type Info struct {
Find string
Best *DirEntry
Dirs *DirList
}
type GodocDir struct {
pkg *Directory
cmd *Directory
gopath []*Directory
}
func NewSourceDir(goroot string) *GodocDir {
pkgPath := filepath.Join(goroot, "src/pkg")
_, err := os.Stat(pkgPath)
var cmd *Directory
if err != nil {
pkgPath = filepath.Join(goroot, "src")
} else {
cmd = newDirectory(filepath.Join(goroot, "src", "cmd"), nil, -1)
}
pkg := newDirectory(pkgPath, nil, -1)
ctx := build.Default
ctx.GOROOT = ""
var gopath []*Directory
for _, v := range ctx.SrcDirs() {
gopath = append(gopath, newDirectory(v, nil, -1))
}
return &GodocDir{pkg, cmd, gopath}
}
func (dir *GodocDir) FindInfo(name string) *Info {
max1, best1, list1 := FindDir(dir.pkg, name)
max2, best2, list2 := FindDir(dir.cmd, name)
var maxHeight int
if max1 >= max2 {
maxHeight = max1
} else {
maxHeight = max2
}
var best *DirEntry
if best1 != nil {
best = best1
if best2 != nil {
list2 = append(list2, *best2)
}
} else {
best = best2
}
var list []DirEntry
list = append(list, list1...)
list = append(list, list2...)
for _, v := range dir.gopath {
max3, best3, list3 := FindDir(v, name)
if max3 > maxHeight {
maxHeight = max3
}
if best == nil {
best = best3
}
list = append(list, list3...)
}
return &Info{name, best, &DirList{maxHeight, list}}
}
func FindDir(dir *Directory, pkgname string) (maxHeight int, best *DirEntry, list []DirEntry) {
if dir == nil {
return
}
dirList := dir.listing(true)
max := len(dirList.List)
maxHeight = dirList.MaxHeight
for i := 0; i < max; i++ {
name := dirList.List[i].Name
path := filepath.ToSlash(dirList.List[i].Path)
if name == pkgname || path == pkgname {
best = &dirList.List[i]
} else if strings.Contains(path, pkgname) {
list = append(list, dirList.List[i])
}
}
return
}
func appendList(list1, list2 []DirEntry) []DirEntry {
list := list1
max := len(list2)
for i := 0; i < max; i++ {
list = append(list, list2[i])
}
return list
}
func NewListInfo(root string) *Info {
dir := newDirectory(root, nil, -1)
if dir == nil {
return nil
}
return &Info{"", nil, dir.listing(true)}
}
func FindPkgInfo(root string, pkgname string) *Info {
dir := newDirectory(root, nil, -1)
if dir == nil {
return nil
}
dirList := dir.listing(true)
if pkgname == "*" {
return &Info{pkgname, nil, dirList}
}
var best DirEntry
var list []DirEntry
max := len(dirList.List)
for i := 0; i < max; i++ {
name := dirList.List[i].Name
path := filepath.ToSlash(dirList.List[i].Path)
if name == pkgname || path == pkgname {
best = dirList.List[i]
} else if strings.Contains(path, pkgname) {
list = append(list, dirList.List[i])
}
}
return &Info{pkgname, &best, &DirList{dirList.MaxHeight, list}}
}
func (info *Info) GetPkgList(name, templateData string) []byte {
data := readTemplateData(name, templateData)
return applyTemplate(data, "pkglist", info)
}
var listHTML = `<!-- Golang Package List -->
<p class="detail">
Need more packages? The
<a href="http://godashboard.appspot.com/package">Package Dashboard</a>
provides a list of <a href="/cmd/goinstall/">goinstallable</a> packages.
</p>
<h2 id="Subdirectories">Subdirectories</h2>
<p>
{{with .Dirs}}
<p>
<table class="layout">
<tr>
<th align="left" colspan="{{html .MaxHeight}}">Name</th>
<td width="25">&nbsp;</td>
<th align="left">Synopsis</th>
</tr>
{{range .List}}
<tr>
{{repeat "<td width=\"25\"></td>" .Depth}}
<td align="left" colspan="{{html .Height}}"><a href="{{.Path}}">{{html .Name}}</a></td>
<td></td>
<td align="left">{{html .Synopsis}}</td>
</tr>
{{end}}
</table>
</p>
{{end}}`
var listText = `$list
{{with .Dirs}}
{{range .List}}{{.Path }}
{{end}}
{{end}}`
var listLite = `$list{{with .Dirs}}{{range .List}},{{.Path}}{{end}}{{end}}`
var findHTML = `<!-- Golang Package List -->
<p class="detail">
Need more packages? The
<a href="http://godashboard.appspot.com/package">Package Dashboard</a>
provides a list of <a href="/cmd/goinstall/">goinstallable</a> packages.
</p>
<h2 id="Subdirectories">Subdirectories</h2>
<table class="layout">
<tr>
<th align="left">Best</th>
<td width="25">&nbsp;</td>
<th align="left">Synopsis</th>
{{with .Best}}
<tr>
<td align="left"><a href="{{html .Path}}">{{.Path}}</a></td>
<td></td>
<td align="left">{{html .Synopsis}}</td>
</tr>
{{end}}
{{with .Dirs}}
<tr>
<th align="left">Match</th>
<td width="25">&nbsp;</td>
<th align="left">Synopsis</th>
</tr>
{{range .List}}
<tr>
<td align="left"><a href="{{html .Path}}">{{.Path}}</a></td>
<td></td>
<td align="left">{{html .Synopsis}}</td>
</tr>
{{end}}
</table>
</p>
{{end}}`
var findText = `$best
{{with .Best}}{{.Path}}{{end}}
$list
{{with .Dirs}}{{range .List}}{{.Path}}
{{end}}{{end}}`
var findLite = `$find,{{with .Best}}{{.Path}}{{end}}{{with .Dirs}}{{range .List}},{{.Path}}{{end}}{{end}}`

668
vendor/github.com/visualfc/gotools/docview/docx.go generated vendored Normal file
View File

@ -0,0 +1,668 @@
// Copyright 2009 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 doc extracts source code documentation from a Go AST.
package docview
import (
"go/ast"
"go/token"
"regexp"
"sort"
"strconv"
)
// ----------------------------------------------------------------------------
type typeDoc struct {
// len(decl.Specs) == 1, and the element type is *ast.TypeSpec
// if the type declaration hasn't been seen yet, decl is nil
decl *ast.GenDecl
// values, factory functions, and methods associated with the type
values []*ast.GenDecl // consts and vars
factories map[string]*ast.FuncDecl
methods map[string]*ast.FuncDecl
}
// docReader accumulates documentation for a single package.
// It modifies the AST: Comments (declaration documentation)
// that have been collected by the DocReader are set to nil
// in the respective AST nodes so that they are not printed
// twice (once when printing the documentation and once when
// printing the corresponding AST node).
//
type docReader struct {
doc *ast.CommentGroup // package documentation, if any
pkgName string
showAll bool
values []*ast.GenDecl // consts and vars
types map[string]*typeDoc
funcs map[string]*ast.FuncDecl
imports map[string]int
bugs []*ast.CommentGroup
}
func (doc *docReader) init(pkgName string, showAll bool) {
doc.pkgName = pkgName
doc.showAll = showAll
doc.imports = make(map[string]int)
doc.types = make(map[string]*typeDoc)
doc.funcs = make(map[string]*ast.FuncDecl)
}
func (doc *docReader) addDoc(comments *ast.CommentGroup) {
if doc.doc == nil {
// common case: just one package comment
doc.doc = comments
return
}
// More than one package comment: Usually there will be only
// one file with a package comment, but it's better to collect
// all comments than drop them on the floor.
// (This code isn't particularly clever - no amortized doubling is
// used - but this situation occurs rarely and is not time-critical.)
n1 := len(doc.doc.List)
n2 := len(comments.List)
list := make([]*ast.Comment, n1+1+n2) // + 1 for separator line
copy(list, doc.doc.List)
list[n1] = &ast.Comment{token.NoPos, "//"} // separator line
copy(list[n1+1:], comments.List)
doc.doc = &ast.CommentGroup{list}
}
func (doc *docReader) addType(decl *ast.GenDecl) {
spec := decl.Specs[0].(*ast.TypeSpec)
typ := doc.lookupTypeDoc(spec.Name.Name)
// typ should always be != nil since declared types
// are always named - be conservative and check
if typ != nil {
// a type should be added at most once, so typ.decl
// should be nil - if it isn't, simply overwrite it
typ.decl = decl
}
}
func (doc *docReader) lookupTypeDoc(name string) *typeDoc {
if name == "" {
return nil // no type docs for anonymous types
}
if tdoc, found := doc.types[name]; found {
return tdoc
}
// type wasn't found - add one without declaration
tdoc := &typeDoc{nil, nil, make(map[string]*ast.FuncDecl), make(map[string]*ast.FuncDecl)}
doc.types[name] = tdoc
return tdoc
}
func docBaseTypeName(typ ast.Expr, showAll bool) string {
switch t := typ.(type) {
case *ast.Ident:
// if the type is not exported, the effect to
// a client is as if there were no type name
if showAll || t.IsExported() {
return t.Name
}
case *ast.StarExpr:
return docBaseTypeName(t.X, showAll)
}
return ""
}
func (doc *docReader) addValue(decl *ast.GenDecl) {
// determine if decl should be associated with a type
// Heuristic: For each typed entry, determine the type name, if any.
// If there is exactly one type name that is sufficiently
// frequent, associate the decl with the respective type.
domName := ""
domFreq := 0
prev := ""
for _, s := range decl.Specs {
if v, ok := s.(*ast.ValueSpec); ok {
name := ""
switch {
case v.Type != nil:
// a type is present; determine its name
name = docBaseTypeName(v.Type, doc.showAll)
case decl.Tok == token.CONST:
// no type is present but we have a constant declaration;
// use the previous type name (w/o more type information
// we cannot handle the case of unnamed variables with
// initializer expressions except for some trivial cases)
name = prev
}
if name != "" {
// entry has a named type
if domName != "" && domName != name {
// more than one type name - do not associate
// with any type
domName = ""
break
}
domName = name
domFreq++
}
prev = name
}
}
// determine values list
const threshold = 0.75
values := &doc.values
if domName != "" && domFreq >= int(float64(len(decl.Specs))*threshold) {
// typed entries are sufficiently frequent
typ := doc.lookupTypeDoc(domName)
if typ != nil {
values = &typ.values // associate with that type
}
}
*values = append(*values, decl)
}
// Helper function to set the table entry for function f. Makes sure that
// at least one f with associated documentation is stored in table, if there
// are multiple f's with the same name.
func setFunc(table map[string]*ast.FuncDecl, f *ast.FuncDecl) {
name := f.Name.Name
if g, exists := table[name]; exists && g.Doc != nil {
// a function with the same name has already been registered;
// since it has documentation, assume f is simply another
// implementation and ignore it
// TODO(gri) consider collecting all functions, or at least
// all comments
return
}
// function doesn't exist or has no documentation; use f
table[name] = f
}
func (doc *docReader) addFunc(fun *ast.FuncDecl) {
name := fun.Name.Name
// determine if it should be associated with a type
if fun.Recv != nil {
// method
typ := doc.lookupTypeDoc(docBaseTypeName(fun.Recv.List[0].Type, doc.showAll))
if typ != nil {
// exported receiver type
setFunc(typ.methods, fun)
}
// otherwise don't show the method
// TODO(gri): There may be exported methods of non-exported types
// that can be called because of exported values (consts, vars, or
// function results) of that type. Could determine if that is the
// case and then show those methods in an appropriate section.
return
}
// perhaps a factory function
// determine result type, if any
if fun.Type.Results.NumFields() >= 1 {
res := fun.Type.Results.List[0]
if len(res.Names) <= 1 {
// exactly one (named or anonymous) result associated
// with the first type in result signature (there may
// be more than one result)
tname := docBaseTypeName(res.Type, doc.showAll)
typ := doc.lookupTypeDoc(tname)
if typ != nil {
// named and exported result type
// Work-around for failure of heuristic: In package os
// too many functions are considered factory functions
// for the Error type. Eliminate manually for now as
// this appears to be the only important case in the
// current library where the heuristic fails.
if doc.pkgName == "os" && tname == "Error" &&
name != "NewError" && name != "NewSyscallError" {
// not a factory function for os.Error
setFunc(doc.funcs, fun) // treat as ordinary function
return
}
setFunc(typ.factories, fun)
return
}
}
}
// ordinary function
setFunc(doc.funcs, fun)
}
func (doc *docReader) addDecl(decl ast.Decl) {
switch d := decl.(type) {
case *ast.GenDecl:
if len(d.Specs) > 0 {
switch d.Tok {
case token.IMPORT:
// imports are handled individually
for _, spec := range d.Specs {
if s, ok := spec.(*ast.ImportSpec); ok {
if import_, err := strconv.Unquote(s.Path.Value); err == nil {
doc.imports[import_] = 1
}
}
}
case token.CONST, token.VAR:
// constants and variables are always handled as a group
doc.addValue(d)
case token.TYPE:
// types are handled individually
for _, spec := range d.Specs {
// make a (fake) GenDecl node for this TypeSpec
// (we need to do this here - as opposed to just
// for printing - so we don't lose the GenDecl
// documentation)
//
// TODO(gri): Consider just collecting the TypeSpec
// node (and copy in the GenDecl.doc if there is no
// doc in the TypeSpec - this is currently done in
// makeTypeDocs below). Simpler data structures, but
// would lose GenDecl documentation if the TypeSpec
// has documentation as well.
doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos, []ast.Spec{spec}, token.NoPos})
// A new GenDecl node is created, no need to nil out d.Doc.
}
}
}
case *ast.FuncDecl:
doc.addFunc(d)
}
}
func copyCommentList(list []*ast.Comment) []*ast.Comment {
return append([]*ast.Comment(nil), list...)
}
var (
bug_markers = regexp.MustCompile("^/[/*][ \t]*BUG\\(.*\\):[ \t]*") // BUG(uid):
bug_content = regexp.MustCompile("[^ \n\r\t]+") // at least one non-whitespace char
)
// addFile adds the AST for a source file to the docReader.
// Adding the same AST multiple times is a no-op.
//
func (doc *docReader) addFile(src *ast.File) {
// add package documentation
if src.Doc != nil {
doc.addDoc(src.Doc)
src.Doc = nil // doc consumed - remove from ast.File node
}
// add all declarations
for _, decl := range src.Decls {
doc.addDecl(decl)
}
// collect BUG(...) comments
for _, c := range src.Comments {
text := c.List[0].Text
if m := bug_markers.FindStringIndex(text); m != nil {
// found a BUG comment; maybe empty
if btxt := text[m[1]:]; bug_content.MatchString(btxt) {
// non-empty BUG comment; collect comment without BUG prefix
list := copyCommentList(c.List)
list[0].Text = text[m[1]:]
doc.bugs = append(doc.bugs, &ast.CommentGroup{list})
}
}
}
src.Comments = nil // consumed unassociated comments - remove from ast.File node
}
func NewFileDoc(file *ast.File, showAll bool) *PackageDoc {
var r docReader
r.init(file.Name.Name, showAll)
r.addFile(file)
return r.newDoc("", nil)
}
func NewPackageDoc(pkg *ast.Package, importpath string, showAll bool) *PackageDoc {
var r docReader
r.init(pkg.Name, showAll)
filenames := make([]string, len(pkg.Files))
i := 0
for filename, f := range pkg.Files {
r.addFile(f)
filenames[i] = filename
i++
}
return r.newDoc(importpath, filenames)
}
// ----------------------------------------------------------------------------
// Conversion to external representation
// ValueDoc is the documentation for a group of declared
// values, either vars or consts.
//
type ValueDoc struct {
Doc string
Decl *ast.GenDecl
order int
}
type sortValueDoc []*ValueDoc
func (p sortValueDoc) Len() int { return len(p) }
func (p sortValueDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func declName(d *ast.GenDecl) string {
if len(d.Specs) != 1 {
return ""
}
switch v := d.Specs[0].(type) {
case *ast.ValueSpec:
return v.Names[0].Name
case *ast.TypeSpec:
return v.Name.Name
}
return ""
}
func (p sortValueDoc) Less(i, j int) bool {
// sort by name
// pull blocks (name = "") up to top
// in original order
if ni, nj := declName(p[i].Decl), declName(p[j].Decl); ni != nj {
return ni < nj
}
return p[i].order < p[j].order
}
func makeValueDocs(list []*ast.GenDecl, tok token.Token) []*ValueDoc {
d := make([]*ValueDoc, len(list)) // big enough in any case
n := 0
for i, decl := range list {
if decl.Tok == tok {
d[n] = &ValueDoc{decl.Doc.Text(), decl, i}
n++
decl.Doc = nil // doc consumed - removed from AST
}
}
d = d[0:n]
sort.Sort(sortValueDoc(d))
return d
}
// FuncDoc is the documentation for a func declaration,
// either a top-level function or a method function.
//
type FuncDoc struct {
Doc string
Recv ast.Expr // TODO(rsc): Would like string here
Name string
Decl *ast.FuncDecl
}
type sortFuncDoc []*FuncDoc
func (p sortFuncDoc) Len() int { return len(p) }
func (p sortFuncDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p sortFuncDoc) Less(i, j int) bool { return p[i].Name < p[j].Name }
func makeFuncDocs(m map[string]*ast.FuncDecl) []*FuncDoc {
d := make([]*FuncDoc, len(m))
i := 0
for _, f := range m {
doc := new(FuncDoc)
doc.Doc = f.Doc.Text()
f.Doc = nil // doc consumed - remove from ast.FuncDecl node
if f.Recv != nil {
doc.Recv = f.Recv.List[0].Type
}
doc.Name = f.Name.Name
doc.Decl = f
d[i] = doc
i++
}
sort.Sort(sortFuncDoc(d))
return d
}
// TypeDoc is the documentation for a declared type.
// Consts and Vars are sorted lists of constants and variables of (mostly) that type.
// Factories is a sorted list of factory functions that return that type.
// Methods is a sorted list of method functions on that type.
type TypeDoc struct {
Doc string
Type *ast.TypeSpec
Consts []*ValueDoc
Vars []*ValueDoc
Funcs []*FuncDoc
Methods []*FuncDoc
Decl *ast.GenDecl
order int
}
type sortTypeDoc []*TypeDoc
func (p sortTypeDoc) Len() int { return len(p) }
func (p sortTypeDoc) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p sortTypeDoc) Less(i, j int) bool {
// sort by name
// pull blocks (name = "") up to top
// in original order
if ni, nj := p[i].Type.Name.Name, p[j].Type.Name.Name; ni != nj {
return ni < nj
}
return p[i].order < p[j].order
}
// NOTE(rsc): This would appear not to be correct for type ( )
// blocks, but the doc extractor above has split them into
// individual declarations.
func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {
d := make([]*TypeDoc, len(m))
i := 0
for _, old := range m {
// all typeDocs should have a declaration associated with
// them after processing an entire package - be conservative
// and check
if decl := old.decl; decl != nil {
typespec := decl.Specs[0].(*ast.TypeSpec)
t := new(TypeDoc)
doc := typespec.Doc
typespec.Doc = nil // doc consumed - remove from ast.TypeSpec node
if doc == nil {
// no doc associated with the spec, use the declaration doc, if any
doc = decl.Doc
}
decl.Doc = nil // doc consumed - remove from ast.Decl node
t.Doc = doc.Text()
t.Type = typespec
t.Consts = makeValueDocs(old.values, token.CONST)
t.Vars = makeValueDocs(old.values, token.VAR)
t.Funcs = makeFuncDocs(old.factories)
t.Methods = makeFuncDocs(old.methods)
t.Decl = old.decl
t.order = i
d[i] = t
i++
} else {
// no corresponding type declaration found - move any associated
// values, factory functions, and methods back to the top-level
// so that they are not lost (this should only happen if a package
// file containing the explicit type declaration is missing or if
// an unqualified type name was used after a "." import)
// 1) move values
doc.values = append(doc.values, old.values...)
// 2) move factory functions
for name, f := range old.factories {
doc.funcs[name] = f
}
// 3) move methods
for name, f := range old.methods {
// don't overwrite functions with the same name
if _, found := doc.funcs[name]; !found {
doc.funcs[name] = f
}
}
}
}
d = d[0:i] // some types may have been ignored
sort.Sort(sortTypeDoc(d))
return d
}
func makeBugDocs(list []*ast.CommentGroup) []string {
d := make([]string, len(list))
for i, g := range list {
d[i] = g.Text()
}
return d
}
// PackageDoc is the documentation for an entire package.
//
type PackageDoc struct {
PackageName string
ImportPath string
Imports []string
Filenames []string
Doc string
Consts []*ValueDoc
Types []*TypeDoc
Vars []*ValueDoc
Funcs []*FuncDoc
Bugs []string
}
// newDoc returns the accumulated documentation for the package.
//
func (doc *docReader) newDoc(importpath string, filenames []string) *PackageDoc {
p := new(PackageDoc)
p.PackageName = doc.pkgName
p.ImportPath = importpath
sort.Strings(filenames)
p.Filenames = filenames
p.Doc = doc.doc.Text()
p.Imports = sortedKeys(doc.imports)
// makeTypeDocs may extend the list of doc.values and
// doc.funcs and thus must be called before any other
// function consuming those lists
p.Types = doc.makeTypeDocs(doc.types)
p.Consts = makeValueDocs(doc.values, token.CONST)
p.Vars = makeValueDocs(doc.values, token.VAR)
p.Funcs = makeFuncDocs(doc.funcs)
p.Bugs = makeBugDocs(doc.bugs)
return p
}
func sortedKeys(m map[string]int) []string {
list := make([]string, len(m))
i := 0
for key := range m {
list[i] = key
i++
}
sort.Strings(list)
return list
}
// ----------------------------------------------------------------------------
// Filtering by name
type Filter func(string) bool
func matchFields(fields *ast.FieldList, f Filter) bool {
if fields != nil {
for _, field := range fields.List {
for _, name := range field.Names {
if f(name.Name) {
return true
}
}
}
}
return false
}
func matchDecl(d *ast.GenDecl, f Filter) bool {
for _, d := range d.Specs {
switch v := d.(type) {
case *ast.ValueSpec:
for _, name := range v.Names {
if f(name.Name) {
return true
}
}
case *ast.TypeSpec:
if f(v.Name.Name) {
return true
}
switch t := v.Type.(type) {
case *ast.StructType:
if matchFields(t.Fields, f) {
return true
}
case *ast.InterfaceType:
if matchFields(t.Methods, f) {
return true
}
}
}
}
return false
}
func filterValueDocs(a []*ValueDoc, f Filter) []*ValueDoc {
w := 0
for _, vd := range a {
if matchDecl(vd.Decl, f) {
a[w] = vd
w++
}
}
return a[0:w]
}
func filterFuncDocs(a []*FuncDoc, f Filter) []*FuncDoc {
w := 0
for _, fd := range a {
if f(fd.Name) {
a[w] = fd
w++
}
}
return a[0:w]
}
func filterTypeDocs(a []*TypeDoc, f Filter) []*TypeDoc {
w := 0
for _, td := range a {
n := 0 // number of matches
if matchDecl(td.Decl, f) {
n = 1
} else {
// type name doesn't match, but we may have matching consts, vars, factories or methods
td.Consts = filterValueDocs(td.Consts, f)
td.Vars = filterValueDocs(td.Vars, f)
td.Funcs = filterFuncDocs(td.Funcs, f)
td.Methods = filterFuncDocs(td.Methods, f)
n += len(td.Consts) + len(td.Vars) + len(td.Funcs) + len(td.Methods)
}
if n > 0 {
a[w] = td
w++
}
}
return a[0:w]
}
// Filter eliminates documentation for names that don't pass through the filter f.
// TODO: Recognize "Type.Method" as a name.
//
func (p *PackageDoc) Filter(f Filter) {
p.Consts = filterValueDocs(p.Consts, f)
p.Vars = filterValueDocs(p.Vars, f)
p.Types = filterTypeDocs(p.Types, f)
p.Funcs = filterFuncDocs(p.Funcs, f)
p.Doc = "" // don't show top-level package doc
}

View File

@ -0,0 +1,70 @@
// Copyright 2011 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.
// This file defines types for abstract file system access and
// provides an implementation accessing the file system of the
// underlying OS.
package docview
import (
"fmt"
"io"
"io/ioutil"
"os"
)
// The FileSystem interface specifies the methods godoc is using
// to access the file system for which it serves documentation.
type FileSystem interface {
Open(path string) (io.ReadCloser, error)
Lstat(path string) (os.FileInfo, error)
Stat(path string) (os.FileInfo, error)
ReadDir(path string) ([]os.FileInfo, error)
}
// ReadFile reads the file named by path from fs and returns the contents.
func ReadFile(fs FileSystem, path string) ([]byte, error) {
rc, err := fs.Open(path)
if err != nil {
return nil, err
}
defer rc.Close()
return ioutil.ReadAll(rc)
}
// ----------------------------------------------------------------------------
// OS-specific FileSystem implementation
var OS FileSystem = osFS{}
// osFS is the OS-specific implementation of FileSystem
type osFS struct{}
func (osFS) Open(path string) (io.ReadCloser, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
fi, err := f.Stat()
if err != nil {
return nil, err
}
if fi.IsDir() {
return nil, fmt.Errorf("Open: %s is a directory", path)
}
return f, nil
}
func (osFS) Lstat(path string) (os.FileInfo, error) {
return os.Lstat(path)
}
func (osFS) Stat(path string) (os.FileInfo, error) {
return os.Stat(path)
}
func (osFS) ReadDir(path string) ([]os.FileInfo, error) {
return ioutil.ReadDir(path) // is sorted
}

609
vendor/github.com/visualfc/gotools/finddoc/finddoc.go generated vendored Normal file
View File

@ -0,0 +1,609 @@
// Copyright 2013 The rspace Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Doc is a simple document printer that produces the doc comments for its
// argument symbols, plus a link to the full documentation and a pointer to
// the source. It has a more Go-like UI than godoc. It can also search for
// symbols by looking in all packages, and case is ignored. For instance:
// doc isupper
// will find unicode.IsUpper.
//
// The -pkg flag retrieves package-level doc comments only.
//
// Usage:
// doc pkg.name # "doc io.Writer"
// doc pkg name # "doc fmt Printf"
// doc name # "doc isupper" (finds unicode.IsUpper)
// doc -pkg pkg # "doc fmt"
//
// The pkg is the last element of the package path;
// no slashes (ast.Node not go/ast.Node).
//
// Flags
// -c(onst) -f(unc) -i(nterface) -m(ethod) -s(truct) -t(ype) -v(ar)
// restrict hits to declarations of the corresponding kind.
// Flags
// -doc -src -url
// restrict printing to the documentation, source path, or godoc URL.
package finddoc
import (
"bytes"
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"os"
"path"
"path/filepath"
"regexp"
"runtime"
"strings"
"github.com/visualfc/gotools/command"
_ "golang.org/x/tools/go/gcimporter"
"golang.org/x/tools/go/types"
)
const usageDoc = `Find documentation for names.
usage:
doc pkg.name # "doc io.Writer"
doc pkg name # "doc fmt Printf"
doc name # "doc isupper" finds unicode.IsUpper
doc -pkg pkg # "doc fmt"
doc -r expr # "doc -r '.*exported'"
pkg is the last component of any package, e.g. fmt, parser
name is the name of an exported symbol; case is ignored in matches.
The name may also be a regular expression to select which names
to match. In regular expression searches, case is ignored and
the pattern must match the entire name, so ".?print" will match
Print, Fprint and Sprint but not Fprintf.
Flags
-c(onst) -f(unc) -i(nterface) -m(ethod) -s(truct) -t(ype) -v(ar)
restrict hits to declarations of the corresponding kind.
Flags
-doc -src -url
restrict printing to the documentation, source path, or godoc URL.
Flag
-r
takes a single argument (no package), a name or regular expression
to search for in all packages.
`
var Command = &command.Command{
Run: runDoc,
UsageLine: "finddoc [pkg.name|pkg name|-pkg name]",
Short: "golang doc lookup",
Long: usageDoc,
}
var (
// If none is set, all are set.
docFlag bool
srcFlag bool
urlFlag bool
regexpFlag bool
matchWordFlag bool
matchCaseFlag bool
constantFlag bool
functionFlag bool
interfaceFlag bool
methodFlag bool
packageFlag bool
structFlag bool
typeFlag bool
variableFlag bool
urlHeadTag string
)
func init() {
Command.Flag.BoolVar(&docFlag, "doc", false, "restrict output to documentation only")
Command.Flag.BoolVar(&srcFlag, "src", false, "restrict output to source file only")
Command.Flag.BoolVar(&urlFlag, "url", false, "restrict output to godoc URL only")
Command.Flag.BoolVar(&regexpFlag, "r", false, "single argument is a regular expression for a name")
Command.Flag.BoolVar(&matchWordFlag, "word", false, "search match whole word")
Command.Flag.BoolVar(&matchCaseFlag, "case", false, "search match case")
Command.Flag.BoolVar(&constantFlag, "const", false, "show doc for consts only")
Command.Flag.BoolVar(&functionFlag, "func", false, "show doc for funcs only")
Command.Flag.BoolVar(&interfaceFlag, "interface", false, "show doc for interfaces only")
Command.Flag.BoolVar(&methodFlag, "method", false, "show doc for methods only")
Command.Flag.BoolVar(&packageFlag, "package", false, "show top-level package doc only")
Command.Flag.BoolVar(&structFlag, "struct", false, "show doc for structs only")
Command.Flag.BoolVar(&typeFlag, "type", false, "show doc for types only")
Command.Flag.BoolVar(&variableFlag, "var", false, "show doc for vars only")
Command.Flag.BoolVar(&constantFlag, "c", false, "alias for -const")
Command.Flag.BoolVar(&functionFlag, "f", false, "alias for -func")
Command.Flag.BoolVar(&interfaceFlag, "i", false, "alias for -interface")
Command.Flag.BoolVar(&methodFlag, "m", false, "alias for -method")
Command.Flag.BoolVar(&packageFlag, "pkg", false, "alias for -package")
Command.Flag.BoolVar(&structFlag, "s", false, "alias for -struct")
Command.Flag.BoolVar(&typeFlag, "t", false, "alias for -type")
Command.Flag.BoolVar(&variableFlag, "v", false, "alias for -var")
Command.Flag.StringVar(&urlHeadTag, "urltag", "", "url head tag, liteide provate")
}
func runDoc(cmd *command.Command, args []string) error {
if !(constantFlag || functionFlag || interfaceFlag || methodFlag || packageFlag || structFlag || typeFlag || variableFlag) { // none set
constantFlag = true
functionFlag = true
methodFlag = true
// Not package! It's special.
typeFlag = true
variableFlag = true
}
if !(docFlag || srcFlag || urlFlag) {
docFlag = true
srcFlag = true
urlFlag = true
}
var pkg, name string
switch len(args) {
case 1:
if packageFlag {
pkg = args[0]
} else if regexpFlag {
name = args[0]
} else if strings.Contains(args[0], ".") {
pkg, name = split(args[0])
} else {
name = args[0]
}
case 2:
if packageFlag {
cmd.Usage()
}
pkg, name = args[0], args[1]
default:
cmd.Usage()
return os.ErrInvalid
}
if strings.Contains(pkg, "/") {
fmt.Fprintf(os.Stderr, "doc: package name cannot contain slash (TODO)\n")
os.Exit(2)
}
for _, path := range Paths(pkg) {
lookInDirectory(path, name)
}
return nil
}
var slash = string(filepath.Separator)
var slashDot = string(filepath.Separator) + "."
var goRootSrcPkg = filepath.Join(runtime.GOROOT(), "src", "pkg")
var goRootSrcCmd = filepath.Join(runtime.GOROOT(), "src", "cmd")
var goPaths = SplitGopath()
func split(arg string) (pkg, name string) {
dot := strings.IndexRune(arg, '.') // We know there's one there.
return arg[0:dot], arg[dot+1:]
}
func Paths(pkg string) []string {
pkgs := pathsFor(runtime.GOROOT(), pkg)
for _, root := range goPaths {
pkgs = append(pkgs, pathsFor(root, pkg)...)
}
return pkgs
}
func SplitGopath() []string {
gopath := os.Getenv("GOPATH")
if gopath == "" {
return nil
}
return strings.Split(gopath, string(os.PathListSeparator))
}
// pathsFor recursively walks the tree looking for possible directories for the package:
// those whose basename is pkg.
func pathsFor(root, pkg string) []string {
root = path.Join(root, "src")
pkgPaths := make([]string, 0, 10)
visit := func(pathName string, f os.FileInfo, err error) error {
if err != nil {
return nil
}
// One package per directory. Ignore the files themselves.
if !f.IsDir() {
return nil
}
// No .hg or other dot nonsense please.
if strings.Contains(pathName, slashDot) {
return filepath.SkipDir
}
// Is the last element of the path correct
if pkg == "" || filepath.Base(pathName) == pkg {
pkgPaths = append(pkgPaths, pathName)
}
return nil
}
filepath.Walk(root, visit)
return pkgPaths
}
// lookInDirectory looks in the package (if any) in the directory for the named exported identifier.
func lookInDirectory(directory, name string) {
fset := token.NewFileSet()
pkgs, _ := parser.ParseDir(fset, directory, nil, parser.ParseComments) // Ignore the error.
for _, pkg := range pkgs {
if pkg.Name == "main" || strings.HasSuffix(pkg.Name, "_test") {
continue
}
doPackage(pkg, fset, name)
}
}
// prefixDirectory places the directory name on the beginning of each name in the list.
func prefixDirectory(directory string, names []string) {
if directory != "." {
for i, name := range names {
names[i] = filepath.Join(directory, name)
}
}
}
// File is a wrapper for the state of a file used in the parser.
// The parse tree walkers are all methods of this type.
type File struct {
fset *token.FileSet
name string // Name of file.
ident string // Identifier we are searching for.
lowerIdent string // lower ident
regexp *regexp.Regexp
pathPrefix string // Prefix from GOROOT/GOPATH.
urlPrefix string // Start of corresponding URL for golang.org or godoc.org.
file *ast.File
comments ast.CommentMap
defs map[*ast.Ident]types.Object
doPrint bool
found bool
allFiles []*File // All files in the package.
}
// doPackage analyzes the single package constructed from the named files, looking for
// the definition of ident.
func doPackage(pkg *ast.Package, fset *token.FileSet, ident string) {
var files []*File
found := false
for name, astFile := range pkg.Files {
if packageFlag && astFile.Doc == nil {
continue
}
file := &File{
fset: fset,
name: name,
ident: ident,
lowerIdent: strings.ToLower(ident),
file: astFile,
comments: ast.NewCommentMap(fset, astFile, astFile.Comments),
}
if regexpFlag && regexp.QuoteMeta(ident) != ident {
// It's a regular expression.
var err error
file.regexp, err = regexp.Compile("^(?i:" + ident + ")$")
if err != nil {
fmt.Fprintf(os.Stderr, "regular expression `%s`:", err)
os.Exit(2)
}
}
switch {
case strings.HasPrefix(name, goRootSrcPkg):
file.urlPrefix = "http://golang.org/pkg"
file.pathPrefix = goRootSrcPkg
case strings.HasPrefix(name, goRootSrcCmd):
file.urlPrefix = "http://golang.org/cmd"
file.pathPrefix = goRootSrcCmd
default:
file.urlPrefix = "http://godoc.org"
for _, path := range goPaths {
p := filepath.Join(path, "src")
if strings.HasPrefix(name, p) {
file.pathPrefix = p
break
}
}
}
file.urlPrefix = urlHeadTag + file.urlPrefix
files = append(files, file)
if found {
continue
}
file.doPrint = false
if packageFlag {
file.pkgComments()
} else {
ast.Walk(file, file.file)
if file.found {
found = true
}
}
}
if !found {
return
}
// By providing the Context with our own error function, it will continue
// past the first error. There is no need for that function to do anything.
config := types.Config{
Error: func(error) {},
}
info := &types.Info{
Defs: make(map[*ast.Ident]types.Object),
}
path := ""
var astFiles []*ast.File
for name, astFile := range pkg.Files {
if path == "" {
path = name
}
astFiles = append(astFiles, astFile)
}
config.Check(path, fset, astFiles, info) // Ignore errors.
// We need to search all files for methods, so record the full list in each file.
for _, file := range files {
file.allFiles = files
}
for _, file := range files {
file.doPrint = true
file.defs = info.Defs
if packageFlag {
file.pkgComments()
} else {
ast.Walk(file, file.file)
}
}
}
// Visit implements the ast.Visitor interface.
func (f *File) Visit(node ast.Node) ast.Visitor {
switch n := node.(type) {
case *ast.GenDecl:
// Variables, constants, types.
for _, spec := range n.Specs {
switch spec := spec.(type) {
case *ast.ValueSpec:
if constantFlag && n.Tok == token.CONST || variableFlag && n.Tok == token.VAR {
for _, ident := range spec.Names {
if f.match(ident.Name) {
f.printNode(n, ident, f.nameURL(ident.Name))
break
}
}
}
case *ast.TypeSpec:
// If there is only one Spec, there are probably no parens and the
// comment we want appears before the type keyword, bound to
// the GenDecl. If the Specs are parenthesized, the comment we want
// is bound to the Spec. Hence we dig into the GenDecl to the Spec,
// but only if there are no parens.
node := ast.Node(n)
if n.Lparen.IsValid() {
node = spec
}
if f.match(spec.Name.Name) {
if typeFlag {
f.printNode(node, spec.Name, f.nameURL(spec.Name.Name))
} else {
switch spec.Type.(type) {
case *ast.InterfaceType:
if interfaceFlag {
f.printNode(node, spec.Name, f.nameURL(spec.Name.Name))
}
case *ast.StructType:
if structFlag {
f.printNode(node, spec.Name, f.nameURL(spec.Name.Name))
}
}
}
if f.doPrint && f.defs[spec.Name] != nil && f.defs[spec.Name].Type() != nil {
ms := types.NewMethodSet(f.defs[spec.Name].Type()) //.Type().MethodSet()
if ms.Len() == 0 {
ms = types.NewMethodSet(types.NewPointer(f.defs[spec.Name].Type())) //.MethodSet()
}
f.methodSet(ms)
}
}
case *ast.ImportSpec:
continue // Don't care.
}
}
case *ast.FuncDecl:
// Methods, top-level functions.
if f.match(n.Name.Name) {
n.Body = nil // Do not print the function body.
if methodFlag && n.Recv != nil {
f.printNode(n, n.Name, f.methodURL(n.Recv.List[0].Type, n.Name.Name))
} else if functionFlag && n.Recv == nil {
f.printNode(n, n.Name, f.nameURL(n.Name.Name))
}
}
}
return f
}
func (f *File) match(name string) bool {
// name must be exported.
if !ast.IsExported(name) {
return false
}
if f.regexp == nil {
if matchWordFlag {
if matchCaseFlag {
return name == f.ident
}
return strings.ToLower(name) == f.lowerIdent
} else {
if matchCaseFlag {
return strings.Contains(name, f.ident)
}
return strings.Contains(strings.ToLower(name), f.lowerIdent)
}
}
return f.regexp.MatchString(name)
}
func (f *File) printNode(node, ident ast.Node, url string) {
if !f.doPrint {
f.found = true
return
}
fmt.Printf("%s%s%s", url, f.sourcePos(f.fset.Position(ident.Pos())), f.docs(node))
}
func (f *File) docs(node ast.Node) []byte {
if !docFlag {
return nil
}
commentedNode := printer.CommentedNode{Node: node}
if comments := f.comments.Filter(node).Comments(); comments != nil {
commentedNode.Comments = comments
}
var b bytes.Buffer
printer.Fprint(&b, f.fset, &commentedNode)
b.Write([]byte("\n\n")) // Add a blank line between entries if we print documentation.
return b.Bytes()
}
func (f *File) pkgComments() {
doc := f.file.Doc
if doc == nil {
return
}
url := ""
if urlFlag {
url = f.packageURL() + "\n"
}
docText := ""
if docFlag {
docText = fmt.Sprintf("package %s\n%s\n\n", f.file.Name.Name, doc.Text())
}
fmt.Printf("%s%s%s", url, f.sourcePos(f.fset.Position(doc.Pos())), docText)
}
func (f *File) packageURL() string {
s := strings.TrimPrefix(f.name, f.pathPrefix)
// Now we have a path with a final file name. Drop it.
if i := strings.LastIndex(s, slash); i > 0 {
s = s[:i+1]
}
return f.urlPrefix + s
}
func (f *File) packageName() string {
s := strings.TrimPrefix(f.name, f.pathPrefix)
// Now we have a path with a final file name. Drop it.
if i := strings.LastIndex(s, slash); i > 0 {
s = s[:i+1]
}
s = strings.Trim(s, slash)
return filepath.ToSlash(s)
}
func (f *File) sourcePos(posn token.Position) string {
if !srcFlag {
return ""
}
return fmt.Sprintf("%s:%d:\n", posn.Filename, posn.Line)
}
func (f *File) nameURL(name string) string {
if !urlFlag {
return ""
}
return fmt.Sprintf("%s#%s\n", f.packageURL(), name)
}
func (f *File) methodURL(typ ast.Expr, name string) string {
if !urlFlag {
return ""
}
var b bytes.Buffer
printer.Fprint(&b, f.fset, typ)
typeName := b.Bytes()
if len(typeName) > 0 && typeName[0] == '*' {
typeName = typeName[1:]
}
return fmt.Sprintf("%s#%s.%s\n", f.packageURL(), typeName, name)
}
// Here follows the code to find and print a method (actually a method set, because
// we want to do only one redundant tree walk, not one per method).
// It should be much easier than walking the whole tree again, but that's what we must do.
// TODO.
type method struct {
index int // Which doc to write. (Keeps the results sorted)
*types.Selection
}
type methodVisitor struct {
*File
methods []method
docs []string
}
func (f *File) methodSet(set *types.MethodSet) {
// Build the set of things we're looking for.
methods := make([]method, 0, set.Len())
docs := make([]string, set.Len())
for i := 0; i < set.Len(); i++ {
if ast.IsExported(set.At(i).Obj().Name()) {
m := method{
i,
set.At(i),
}
methods = append(methods, m)
}
}
if len(methods) == 0 {
return
}
// Collect the docs.
for _, file := range f.allFiles {
visitor := &methodVisitor{
File: file,
methods: methods,
docs: docs,
}
ast.Walk(visitor, file.file)
methods = visitor.methods
}
// Print them in order. The incoming method set is sorted by name.
for _, doc := range docs {
if doc != "" {
fmt.Print(doc)
}
}
}
// Visit implements the ast.Visitor interface.
func (visitor *methodVisitor) Visit(node ast.Node) ast.Visitor {
switch n := node.(type) {
case *ast.FuncDecl:
for i, method := range visitor.methods {
// If this is the right one, the position of the name of its identifier will match.
if method.Obj().Pos() == n.Name.Pos() {
n.Body = nil // TODO. Ugly - don't print the function body.
visitor.docs[method.index] = fmt.Sprintf("%s", visitor.File.docs(n))
// If this was the last method, we're done.
if len(visitor.methods) == 1 {
return nil
}
// Drop this one from the list.
visitor.methods = append(visitor.methods[:i], visitor.methods[i+1:]...)
return visitor
}
}
}
return visitor
}

3774
vendor/github.com/visualfc/gotools/goapi/goapi.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

394
vendor/github.com/visualfc/gotools/goimports/fix.go generated vendored Normal file
View File

@ -0,0 +1,394 @@
// Copyright 2013 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 goimports
import (
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"os"
"path"
"path/filepath"
"strings"
"sync"
"github.com/visualfc/gotools/stdlib"
"golang.org/x/tools/go/ast/astutil"
)
// importToGroup is a list of functions which map from an import path to
// a group number.
var importToGroup = []func(importPath string) (num int, ok bool){
func(importPath string) (num int, ok bool) {
if strings.HasPrefix(importPath, "appengine") {
return 2, true
}
return
},
func(importPath string) (num int, ok bool) {
if strings.Contains(importPath, ".") {
return 1, true
}
return
},
}
func importGroup(importPath string) int {
for _, fn := range importToGroup {
if n, ok := fn(importPath); ok {
return n
}
}
return 0
}
func fixImports(fset *token.FileSet, f *ast.File) (added []string, err error) {
// refs are a set of possible package references currently unsatisfied by imports.
// first key: either base package (e.g. "fmt") or renamed package
// second key: referenced package symbol (e.g. "Println")
refs := make(map[string]map[string]bool)
// decls are the current package imports. key is base package or renamed package.
decls := make(map[string]*ast.ImportSpec)
// collect potential uses of packages.
var visitor visitFn
visitor = visitFn(func(node ast.Node) ast.Visitor {
if node == nil {
return visitor
}
switch v := node.(type) {
case *ast.ImportSpec:
if v.Name != nil {
decls[v.Name.Name] = v
} else {
local := importPathToName(strings.Trim(v.Path.Value, `\"`))
decls[local] = v
}
case *ast.SelectorExpr:
xident, ok := v.X.(*ast.Ident)
if !ok {
break
}
if xident.Obj != nil {
// if the parser can resolve it, it's not a package ref
break
}
pkgName := xident.Name
if refs[pkgName] == nil {
refs[pkgName] = make(map[string]bool)
}
if decls[pkgName] == nil {
refs[pkgName][v.Sel.Name] = true
}
}
return visitor
})
ast.Walk(visitor, f)
// Search for imports matching potential package references.
searches := 0
type result struct {
ipath string
name string
err error
}
results := make(chan result)
for pkgName, symbols := range refs {
if len(symbols) == 0 {
continue // skip over packages already imported
}
go func(pkgName string, symbols map[string]bool) {
ipath, rename, err := findImport(pkgName, symbols)
r := result{ipath: ipath, err: err}
if rename {
r.name = pkgName
}
results <- r
}(pkgName, symbols)
searches++
}
for i := 0; i < searches; i++ {
result := <-results
if result.err != nil {
return nil, result.err
}
if result.ipath != "" {
if result.name != "" {
astutil.AddNamedImport(fset, f, result.name, result.ipath)
} else {
astutil.AddImport(fset, f, result.ipath)
}
added = append(added, result.ipath)
}
}
// Nil out any unused ImportSpecs, to be removed in following passes
unusedImport := map[string]bool{}
for pkg, is := range decls {
if refs[pkg] == nil && pkg != "_" && pkg != "." {
unusedImport[strings.Trim(is.Path.Value, `"`)] = true
}
}
for ipath := range unusedImport {
if ipath == "C" {
// Don't remove cgo stuff.
continue
}
astutil.DeleteImport(fset, f, ipath)
}
return added, nil
}
// importPathToName returns the package name for the given import path.
var importPathToName = importPathToNameGoPath
// importPathToNameBasic assumes the package name is the base of import path.
func importPathToNameBasic(importPath string) (packageName string) {
return path.Base(importPath)
}
// importPathToNameGoPath finds out the actual package name, as declared in its .go files.
// If there's a problem, it falls back to using importPathToNameBasic.
func importPathToNameGoPath(importPath string) (packageName string) {
if stdlib.IsStdPkg(importPath) {
return path.Base(importPath)
}
if buildPkg, err := build.Import(importPath, "", 0); err == nil {
return buildPkg.Name
} else {
return importPathToNameBasic(importPath)
}
}
type pkg struct {
importpath string // full pkg import path, e.g. "net/http"
dir string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt"
}
var pkgIndexOnce sync.Once
var pkgIndex struct {
sync.Mutex
m map[string][]pkg // shortname => []pkg, e.g "http" => "net/http"
}
// gate is a semaphore for limiting concurrency.
type gate chan struct{}
func (g gate) enter() { g <- struct{}{} }
func (g gate) leave() { <-g }
// fsgate protects the OS & filesystem from too much concurrency.
// Too much disk I/O -> too many threads -> swapping and bad scheduling.
var fsgate = make(gate, 8)
func loadPkgIndex() {
pkgIndex.Lock()
pkgIndex.m = make(map[string][]pkg)
pkgIndex.Unlock()
var wg sync.WaitGroup
for _, path := range build.Default.SrcDirs() {
fsgate.enter()
f, err := os.Open(path)
if err != nil {
fsgate.leave()
fmt.Fprint(os.Stderr, err)
continue
}
children, err := f.Readdir(-1)
f.Close()
fsgate.leave()
if err != nil {
fmt.Fprint(os.Stderr, err)
continue
}
for _, child := range children {
if child.IsDir() {
wg.Add(1)
go func(path, name string) {
defer wg.Done()
loadPkg(&wg, path, name)
}(path, child.Name())
}
}
}
wg.Wait()
}
func loadPkg(wg *sync.WaitGroup, root, pkgrelpath string) {
importpath := filepath.ToSlash(pkgrelpath)
dir := filepath.Join(root, importpath)
fsgate.enter()
defer fsgate.leave()
pkgDir, err := os.Open(dir)
if err != nil {
return
}
children, err := pkgDir.Readdir(-1)
pkgDir.Close()
if err != nil {
return
}
// hasGo tracks whether a directory actually appears to be a
// Go source code directory. If $GOPATH == $HOME, and
// $HOME/src has lots of other large non-Go projects in it,
// then the calls to importPathToName below can be expensive.
hasGo := false
for _, child := range children {
name := child.Name()
if name == "" {
continue
}
if c := name[0]; c == '.' || ('0' <= c && c <= '9') {
continue
}
if strings.HasSuffix(name, ".go") {
hasGo = true
}
if child.IsDir() {
wg.Add(1)
go func(root, name string) {
defer wg.Done()
loadPkg(wg, root, name)
}(root, filepath.Join(importpath, name))
}
}
if hasGo {
shortName := importPathToName(importpath)
pkgIndex.Lock()
pkgIndex.m[shortName] = append(pkgIndex.m[shortName], pkg{
importpath: importpath,
dir: dir,
})
pkgIndex.Unlock()
}
}
// loadExports returns a list exports for a package.
var loadExports = loadExportsGoPath
func loadExportsGoPath(dir string) map[string]bool {
exports := make(map[string]bool)
buildPkg, err := build.ImportDir(dir, 0)
if err != nil {
if strings.Contains(err.Error(), "no buildable Go source files in") {
return nil
}
fmt.Fprintf(os.Stderr, "could not import %q: %v\n", dir, err)
return nil
}
fset := token.NewFileSet()
for _, files := range [...][]string{buildPkg.GoFiles, buildPkg.CgoFiles} {
for _, file := range files {
f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
if err != nil {
fmt.Fprintf(os.Stderr, "could not parse %q: %v\n", file, err)
continue
}
for name := range f.Scope.Objects {
if ast.IsExported(name) {
exports[name] = true
}
}
}
}
return exports
}
// findImport searches for a package with the given symbols.
// If no package is found, findImport returns "".
// Declared as a variable rather than a function so goimports can be easily
// extended by adding a file with an init function.
var findImport = findImportGoPath
func findImportGoPath(pkgName string, symbols map[string]bool) (string, bool, error) {
// Fast path for the standard library.
// In the common case we hopefully never have to scan the GOPATH, which can
// be slow with moving disks.
if pkg, rename, ok := findImportStdlib(pkgName, symbols); ok {
return pkg, rename, nil
}
// TODO(sameer): look at the import lines for other Go files in the
// local directory, since the user is likely to import the same packages
// in the current Go file. Return rename=true when the other Go files
// use a renamed package that's also used in the current file.
pkgIndexOnce.Do(loadPkgIndex)
// Collect exports for packages with matching names.
var wg sync.WaitGroup
var pkgsMu sync.Mutex // guards pkgs
// full importpath => exported symbol => True
// e.g. "net/http" => "Client" => True
pkgs := make(map[string]map[string]bool)
pkgIndex.Lock()
for _, pkg := range pkgIndex.m[pkgName] {
wg.Add(1)
go func(importpath, dir string) {
defer wg.Done()
exports := loadExports(dir)
if exports != nil {
pkgsMu.Lock()
pkgs[importpath] = exports
pkgsMu.Unlock()
}
}(pkg.importpath, pkg.dir)
}
pkgIndex.Unlock()
wg.Wait()
// Filter out packages missing required exported symbols.
for symbol := range symbols {
for importpath, exports := range pkgs {
if !exports[symbol] {
delete(pkgs, importpath)
}
}
}
if len(pkgs) == 0 {
return "", false, nil
}
// If there are multiple candidate packages, the shortest one wins.
// This is a heuristic to prefer the standard library (e.g. "bytes")
// over e.g. "github.com/foo/bar/bytes".
shortest := ""
for importPath := range pkgs {
if shortest == "" || len(importPath) < len(shortest) {
shortest = importPath
}
}
return shortest, false, nil
}
type visitFn func(node ast.Node) ast.Visitor
func (fn visitFn) Visit(node ast.Node) ast.Visitor {
return fn(node)
}
func findImportStdlib(shortPkg string, symbols map[string]bool) (importPath string, rename, ok bool) {
for symbol := range symbols {
path := stdlib.Symbols[shortPkg+"."+symbol]
if path == "" {
return "", false, false
}
if importPath != "" && importPath != path {
// Ambiguous. Symbols pointed to different things.
return "", false, false
}
importPath = path
}
return importPath, false, importPath != ""
}

View File

@ -0,0 +1,206 @@
// Copyright 2013 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 goimports
import (
"bytes"
"fmt"
"go/parser"
"go/printer"
"go/scanner"
"go/token"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
"github.com/visualfc/gotools/command"
)
var Command = &command.Command{
Run: runGoimports,
UsageLine: "goimports [flags] [path ...]",
Short: "updates go import lines",
Long: `goimports updates your Go import lines, adding missing ones and removing unreferenced ones. `,
}
var (
goimportsList bool
goimportsWrite bool
goimportsDiff bool
goimportsAllErrors bool
// layout control
goimportsComments bool
goimportsTabWidth int
goimportsTabIndent bool
)
//func init
func init() {
Command.Flag.BoolVar(&goimportsList, "l", false, "list files whose formatting differs from goimport's")
Command.Flag.BoolVar(&goimportsWrite, "w", false, "write result to (source) file instead of stdout")
Command.Flag.BoolVar(&goimportsDiff, "d", false, "display diffs instead of rewriting files")
Command.Flag.BoolVar(&goimportsAllErrors, "e", false, "report all errors (not just the first 10 on different lines)")
// layout control
Command.Flag.BoolVar(&goimportsComments, "comments", true, "print comments")
Command.Flag.IntVar(&goimportsTabWidth, "tabwidth", 8, "tab width")
Command.Flag.BoolVar(&goimportsTabIndent, "tabs", true, "indent with tabs")
}
var (
fileSet = token.NewFileSet() // per process FileSet
exitCode = 0
initModesOnce sync.Once // guards calling initModes
parserMode parser.Mode
printerMode printer.Mode
options *Options
)
func report(err error) {
scanner.PrintError(os.Stderr, err)
exitCode = 2
}
func runGoimports(cmd *command.Command, args []string) error {
runtime.GOMAXPROCS(runtime.NumCPU())
if goimportsTabWidth < 0 {
fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", goimportsTabWidth)
exitCode = 2
os.Exit(exitCode)
return os.ErrInvalid
}
options = &Options{
TabWidth: goimportsTabWidth,
TabIndent: goimportsTabIndent,
Comments: goimportsComments,
AllErrors: goimportsAllErrors,
Fragment: true,
}
if len(args) == 0 {
if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil {
report(err)
}
} else {
for _, path := range args {
switch dir, err := os.Stat(path); {
case err != nil:
report(err)
case dir.IsDir():
walkDir(path)
default:
if err := processFile(path, nil, os.Stdout, false); err != nil {
report(err)
}
}
}
}
os.Exit(exitCode)
return nil
}
func isGoFile(f os.FileInfo) bool {
// ignore non-Go files
name := f.Name()
return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}
func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error {
if in == nil {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
in = f
}
src, err := ioutil.ReadAll(in)
if err != nil {
return err
}
res, err := Process(filename, src, options)
if err != nil {
return err
}
if !bytes.Equal(src, res) {
// formatting has changed
if goimportsList {
fmt.Fprintln(out, filename)
}
if goimportsWrite {
err = ioutil.WriteFile(filename, res, 0)
if err != nil {
return err
}
}
if goimportsDiff {
data, err := diff(src, res)
if err != nil {
return fmt.Errorf("computing diff: %s", err)
}
fmt.Printf("diff %s gofmt/%s\n", filename, filename)
out.Write(data)
}
}
if !goimportsList && !goimportsWrite && !goimportsDiff {
_, err = out.Write(res)
}
return err
}
func visitFile(path string, f os.FileInfo, err error) error {
if err == nil && isGoFile(f) {
err = processFile(path, nil, os.Stdout, false)
}
if err != nil {
report(err)
}
return nil
}
func walkDir(path string) {
filepath.Walk(path, visitFile)
}
func diff(b1, b2 []byte) (data []byte, err error) {
f1, err := ioutil.TempFile("", "gofmt")
if err != nil {
return
}
defer os.Remove(f1.Name())
defer f1.Close()
f2, err := ioutil.TempFile("", "gofmt")
if err != nil {
return
}
defer os.Remove(f2.Name())
defer f2.Close()
f1.Write(b1)
f2.Write(b2)
data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
if len(data) > 0 {
// diff exits with a non-zero status when the files don't match.
// Ignore that failure as long as we get output.
err = nil
}
return
}

281
vendor/github.com/visualfc/gotools/goimports/imports.go generated vendored Normal file
View File

@ -0,0 +1,281 @@
// Copyright 2013 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 imports implements a Go pretty-printer (like package "go/format")
// that also adds or removes import statements as necessary.
package goimports
import (
"bufio"
"bytes"
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/printer"
"go/token"
"io"
"regexp"
"strconv"
"strings"
"golang.org/x/tools/go/ast/astutil"
)
// Options specifies options for processing files.
type Options struct {
Fragment bool // Accept fragment of a source file (no package statement)
AllErrors bool // Report all errors (not just the first 10 on different lines)
Comments bool // Print comments (true if nil *Options provided)
TabIndent bool // Use tabs for indent (true if nil *Options provided)
Format bool
TabWidth int // Tab width (8 if nil *Options provided)
}
// Process formats and adjusts imports for the provided file.
// If opt is nil the defaults are used.
func Process(filename string, src []byte, opt *Options) ([]byte, error) {
if opt == nil {
opt = &Options{Comments: true, TabIndent: true, TabWidth: 8}
}
fileSet := token.NewFileSet()
file, adjust, err := goImportParse(fileSet, filename, src, opt)
if err != nil {
return nil, err
}
_, err = fixImports(fileSet, file)
if err != nil {
return nil, err
}
sortImports(fileSet, file)
imps := astutil.Imports(fileSet, file)
var spacesBefore []string // import paths we need spaces before
for _, impSection := range imps {
// Within each block of contiguous imports, see if any
// import lines are in different group numbers. If so,
// we'll need to put a space between them so it's
// compatible with gofmt.
lastGroup := -1
for _, importSpec := range impSection {
importPath, _ := strconv.Unquote(importSpec.Path.Value)
groupNum := importGroup(importPath)
if groupNum != lastGroup && lastGroup != -1 {
spacesBefore = append(spacesBefore, importPath)
}
lastGroup = groupNum
}
}
printerMode := printer.UseSpaces
if opt.TabIndent {
printerMode |= printer.TabIndent
}
printConfig := &printer.Config{Mode: printerMode, Tabwidth: opt.TabWidth}
var buf bytes.Buffer
err = printConfig.Fprint(&buf, fileSet, file)
if err != nil {
return nil, err
}
out := buf.Bytes()
if adjust != nil {
out = adjust(src, out)
}
if len(spacesBefore) > 0 {
out = addImportSpaces(bytes.NewReader(out), spacesBefore)
}
if opt.Format {
out, err = format.Source(out)
if err != nil {
return nil, err
}
}
return out, nil
}
// parse parses src, which was read from filename,
// as a Go source file or statement list.
func goImportParse(fset *token.FileSet, filename string, src []byte, opt *Options) (*ast.File, func(orig, src []byte) []byte, error) {
parserMode := parser.Mode(0)
if opt.Comments {
parserMode |= parser.ParseComments
}
if opt.AllErrors {
parserMode |= parser.AllErrors
}
// Try as whole source file.
file, err := parser.ParseFile(fset, filename, src, parserMode)
if err == nil {
return file, nil, nil
}
// If the error is that the source file didn't begin with a
// package line and we accept fragmented input, fall through to
// try as a source fragment. Stop and return on any other error.
if !opt.Fragment || !strings.Contains(err.Error(), "expected 'package'") {
return nil, nil, err
}
// If this is a declaration list, make it a source file
// by inserting a package clause.
// Insert using a ;, not a newline, so that the line numbers
// in psrc match the ones in src.
psrc := append([]byte("package main;"), src...)
file, err = parser.ParseFile(fset, filename, psrc, parserMode)
if err == nil {
// If a main function exists, we will assume this is a main
// package and leave the file.
if containsMainFunc(file) {
return file, nil, nil
}
adjust := func(orig, src []byte) []byte {
// Remove the package clause.
// Gofmt has turned the ; into a \n.
src = src[len("package main\n"):]
return matchSpace(orig, src)
}
return file, adjust, nil
}
// If the error is that the source file didn't begin with a
// declaration, fall through to try as a statement list.
// Stop and return on any other error.
if !strings.Contains(err.Error(), "expected declaration") {
return nil, nil, err
}
// If this is a statement list, make it a source file
// by inserting a package clause and turning the list
// into a function body. This handles expressions too.
// Insert using a ;, not a newline, so that the line numbers
// in fsrc match the ones in src.
fsrc := append(append([]byte("package p; func _() {"), src...), '}')
file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
if err == nil {
adjust := func(orig, src []byte) []byte {
// Remove the wrapping.
// Gofmt has turned the ; into a \n\n.
src = src[len("package p\n\nfunc _() {"):]
src = src[:len(src)-len("}\n")]
// Gofmt has also indented the function body one level.
// Remove that indent.
src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1)
return matchSpace(orig, src)
}
return file, adjust, nil
}
// Failed, and out of options.
return nil, nil, err
}
// containsMainFunc checks if a file contains a function declaration with the
// function signature 'func main()'
func containsMainFunc(file *ast.File) bool {
for _, decl := range file.Decls {
if f, ok := decl.(*ast.FuncDecl); ok {
if f.Name.Name != "main" {
continue
}
if len(f.Type.Params.List) != 0 {
continue
}
if f.Type.Results != nil && len(f.Type.Results.List) != 0 {
continue
}
return true
}
}
return false
}
func cutSpace(b []byte) (before, middle, after []byte) {
i := 0
for i < len(b) && (b[i] == ' ' || b[i] == '\t' || b[i] == '\n') {
i++
}
j := len(b)
for j > 0 && (b[j-1] == ' ' || b[j-1] == '\t' || b[j-1] == '\n') {
j--
}
if i <= j {
return b[:i], b[i:j], b[j:]
}
return nil, nil, b[j:]
}
// matchSpace reformats src to use the same space context as orig.
// 1) If orig begins with blank lines, matchSpace inserts them at the beginning of src.
// 2) matchSpace copies the indentation of the first non-blank line in orig
// to every non-blank line in src.
// 3) matchSpace copies the trailing space from orig and uses it in place
// of src's trailing space.
func matchSpace(orig []byte, src []byte) []byte {
before, _, after := cutSpace(orig)
i := bytes.LastIndex(before, []byte{'\n'})
before, indent := before[:i+1], before[i+1:]
_, src, _ = cutSpace(src)
var b bytes.Buffer
b.Write(before)
for len(src) > 0 {
line := src
if i := bytes.IndexByte(line, '\n'); i >= 0 {
line, src = line[:i+1], line[i+1:]
} else {
src = nil
}
if len(line) > 0 && line[0] != '\n' { // not blank
b.Write(indent)
}
b.Write(line)
}
b.Write(after)
return b.Bytes()
}
var impLine = regexp.MustCompile(`^\s+(?:[\w\.]+\s+)?"(.+)"`)
func addImportSpaces(r io.Reader, breaks []string) []byte {
var out bytes.Buffer
sc := bufio.NewScanner(r)
inImports := false
done := false
for sc.Scan() {
s := sc.Text()
if !inImports && !done && strings.HasPrefix(s, "import") {
inImports = true
}
if inImports && (strings.HasPrefix(s, "var") ||
strings.HasPrefix(s, "func") ||
strings.HasPrefix(s, "const") ||
strings.HasPrefix(s, "type")) {
done = true
inImports = false
}
if inImports && len(breaks) > 0 {
if m := impLine.FindStringSubmatch(s); m != nil {
if m[1] == string(breaks[0]) {
out.WriteByte('\n')
breaks = breaks[1:]
}
}
}
fmt.Fprintln(&out, s)
}
return out.Bytes()
}

View File

@ -0,0 +1,214 @@
// +build go1.2
// Copyright 2013 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.
// Hacked up copy of go/ast/import.go
package goimports
import (
"go/ast"
"go/token"
"sort"
"strconv"
)
// sortImports sorts runs of consecutive import lines in import blocks in f.
// It also removes duplicate imports when it is possible to do so without data loss.
func sortImports(fset *token.FileSet, f *ast.File) {
for i, d := range f.Decls {
d, ok := d.(*ast.GenDecl)
if !ok || d.Tok != token.IMPORT {
// Not an import declaration, so we're done.
// Imports are always first.
break
}
if len(d.Specs) == 0 {
// Empty import block, remove it.
f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
}
if !d.Lparen.IsValid() {
// Not a block: sorted by default.
continue
}
// Identify and sort runs of specs on successive lines.
i := 0
specs := d.Specs[:0]
for j, s := range d.Specs {
if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line {
// j begins a new run. End this one.
specs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...)
i = j
}
}
specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...)
d.Specs = specs
// Deduping can leave a blank line before the rparen; clean that up.
if len(d.Specs) > 0 {
lastSpec := d.Specs[len(d.Specs)-1]
lastLine := fset.Position(lastSpec.Pos()).Line
if rParenLine := fset.Position(d.Rparen).Line; rParenLine > lastLine+1 {
fset.File(d.Rparen).MergeLine(rParenLine - 1)
}
}
}
}
func importPath(s ast.Spec) string {
t, err := strconv.Unquote(s.(*ast.ImportSpec).Path.Value)
if err == nil {
return t
}
return ""
}
func importName(s ast.Spec) string {
n := s.(*ast.ImportSpec).Name
if n == nil {
return ""
}
return n.Name
}
func importComment(s ast.Spec) string {
c := s.(*ast.ImportSpec).Comment
if c == nil {
return ""
}
return c.Text()
}
// collapse indicates whether prev may be removed, leaving only next.
func collapse(prev, next ast.Spec) bool {
if importPath(next) != importPath(prev) || importName(next) != importName(prev) {
return false
}
return prev.(*ast.ImportSpec).Comment == nil
}
type posSpan struct {
Start token.Pos
End token.Pos
}
func sortSpecs(fset *token.FileSet, f *ast.File, specs []ast.Spec) []ast.Spec {
// Can't short-circuit here even if specs are already sorted,
// since they might yet need deduplication.
// A lone import, however, may be safely ignored.
if len(specs) <= 1 {
return specs
}
// Record positions for specs.
pos := make([]posSpan, len(specs))
for i, s := range specs {
pos[i] = posSpan{s.Pos(), s.End()}
}
// Identify comments in this range.
// Any comment from pos[0].Start to the final line counts.
lastLine := fset.Position(pos[len(pos)-1].End).Line
cstart := len(f.Comments)
cend := len(f.Comments)
for i, g := range f.Comments {
if g.Pos() < pos[0].Start {
continue
}
if i < cstart {
cstart = i
}
if fset.Position(g.End()).Line > lastLine {
cend = i
break
}
}
comments := f.Comments[cstart:cend]
// Assign each comment to the import spec preceding it.
importComment := map[*ast.ImportSpec][]*ast.CommentGroup{}
specIndex := 0
for _, g := range comments {
for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() {
specIndex++
}
s := specs[specIndex].(*ast.ImportSpec)
importComment[s] = append(importComment[s], g)
}
// Sort the import specs by import path.
// Remove duplicates, when possible without data loss.
// Reassign the import paths to have the same position sequence.
// Reassign each comment to abut the end of its spec.
// Sort the comments by new position.
sort.Sort(byImportSpec(specs))
// Dedup. Thanks to our sorting, we can just consider
// adjacent pairs of imports.
deduped := specs[:0]
for i, s := range specs {
if i == len(specs)-1 || !collapse(s, specs[i+1]) {
deduped = append(deduped, s)
} else {
p := s.Pos()
fset.File(p).MergeLine(fset.Position(p).Line)
}
}
specs = deduped
// Fix up comment positions
for i, s := range specs {
s := s.(*ast.ImportSpec)
if s.Name != nil {
s.Name.NamePos = pos[i].Start
}
s.Path.ValuePos = pos[i].Start
s.EndPos = pos[i].End
for _, g := range importComment[s] {
for _, c := range g.List {
c.Slash = pos[i].End
}
}
}
sort.Sort(byCommentPos(comments))
return specs
}
type byImportSpec []ast.Spec // slice of *ast.ImportSpec
func (x byImportSpec) Len() int { return len(x) }
func (x byImportSpec) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x byImportSpec) Less(i, j int) bool {
ipath := importPath(x[i])
jpath := importPath(x[j])
igroup := importGroup(ipath)
jgroup := importGroup(jpath)
if igroup != jgroup {
return igroup < jgroup
}
if ipath != jpath {
return ipath < jpath
}
iname := importName(x[i])
jname := importName(x[j])
if iname != jname {
return iname < jname
}
return importComment(x[i]) < importComment(x[j])
}
type byCommentPos []*ast.CommentGroup
func (x byCommentPos) Len() int { return len(x) }
func (x byCommentPos) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x byCommentPos) Less(i, j int) bool { return x[i].Pos() < x[j].Pos() }

View File

@ -0,0 +1,14 @@
// +build !go1.2
// Copyright 2013 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 goimports
import "go/ast"
// Go 1.1 users don't get fancy package grouping.
// But this is still gofmt-compliant:
var sortImports = ast.SortImports

View File

@ -0,0 +1,383 @@
// Copyright 2013 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.
//modify 2013-2014 visualfc
package gopresent
import (
"fmt"
"html/template"
"io"
"os"
"path/filepath"
"github.com/visualfc/gotools/command"
"golang.org/x/tools/present"
)
var Command = &command.Command{
Run: runPresent,
UsageLine: "gopresent",
Short: "golang present util",
Long: `golang present util`,
}
var presentVerifyOnly bool
var presentInput string
var presentStdout bool
var presentOutput string
func init() {
Command.Flag.BoolVar(&presentVerifyOnly, "v", false, "verify present only")
Command.Flag.BoolVar(&presentStdout, "stdout", false, "output use std output")
Command.Flag.StringVar(&presentInput, "i", "", "input golang present file")
Command.Flag.StringVar(&presentOutput, "o", "", "output html file name")
}
func runPresent(cmd *command.Command, args []string) error {
if presentInput == "" || !isDoc(presentInput) {
cmd.Usage()
return os.ErrInvalid
}
if presentVerifyOnly {
err := VerifyDoc(presentInput)
if err != nil {
fmt.Fprintf(os.Stderr, "present:%s", err)
command.SetExitStatus(3)
command.Exit()
}
return nil
}
w := os.Stdout
if !presentStdout {
if presentOutput == "" {
presentOutput = presentInput + ".html"
}
ext := filepath.Ext(presentOutput)
if ext != ".htm" && ext != ".html" {
presentOutput += ".html"
}
var err error
w, err = os.Create(presentOutput)
if err != nil {
fmt.Fprintf(os.Stderr, "present:%s", err)
command.SetExitStatus(3)
command.Exit()
}
}
err := RenderDoc(w, presentInput)
if err != nil {
fmt.Fprintf(os.Stderr, "present:%s", err)
command.SetExitStatus(3)
command.Exit()
}
return nil
}
var extensions = map[string]string{
".slide": "slides.tmpl",
".article": "article.tmpl",
}
var extensions_tmpl = map[string]string{
".slide": slides_tmpl,
".article": article_tmpl,
}
func isDoc(path string) bool {
_, ok := extensions[filepath.Ext(path)]
return ok
}
func VerifyDoc(docFile string) error {
doc, err := parse(docFile, 0)
if err != nil {
return err
}
dir := filepath.Dir(docFile)
return verify_doc(dir, doc)
}
// renderDoc reads the present file, builds its template representation,
// and executes the template, sending output to w.
func renderDoc(w io.Writer, base, docFile string) error {
// Read the input and build the doc structure.
doc, err := parse(docFile, 0)
if err != nil {
return err
}
// Find which template should be executed.
ext := filepath.Ext(docFile)
contentTmpl, ok := extensions[ext]
if !ok {
return fmt.Errorf("no template for extension %v", ext)
}
// Locate the template file.
actionTmpl := filepath.Join(base, "templates/action.tmpl")
contentTmpl = filepath.Join(base, "templates", contentTmpl)
// Read and parse the input.
tmpl := present.Template()
tmpl = tmpl.Funcs(template.FuncMap{"playable": playable})
if _, err := tmpl.ParseFiles(actionTmpl, contentTmpl); err != nil {
return err
}
// Execute the template.
return doc.Render(w, tmpl)
}
func RenderDoc(w io.Writer, docFile string) error {
// Read the input and build the doc structure.
doc, err := parse(docFile, 0)
if err != nil {
return err
}
// Find which template should be executed.
ext := filepath.Ext(docFile)
contentTmpl, ok := extensions_tmpl[ext]
if !ok {
return fmt.Errorf("no template for extension %v", ext)
}
// Locate the template file.
actionTmpl := action_tmpl //filepath.Join(base, "templates/action.tmpl")
// Read and parse the input.
tmpl := present.Template()
tmpl = tmpl.Funcs(template.FuncMap{"playable": playable})
if tmpl, err = tmpl.New("action").Parse(actionTmpl); err != nil {
return err
}
if tmpl, err = tmpl.New("content").Parse(contentTmpl); err != nil {
return err
}
// Execute the template.
return doc.Render(w, tmpl)
}
func parse(name string, mode present.ParseMode) (*present.Doc, error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
defer f.Close()
return present.Parse(f, name, 0)
}
func playable(c present.Code) bool {
return present.PlayEnabled && c.Play
}
func isSkipURL(url string) bool {
if filepath.HasPrefix(url, "http://") {
return true
}
if filepath.HasPrefix(url, "https://") {
return true
}
return false
}
func verify_path(root string, url string) error {
if isSkipURL(url) {
return nil
}
path := url
if !filepath.IsAbs(url) {
path = filepath.Join(root, path)
}
_, err := os.Stat(path)
if err != nil {
return err
}
return nil
}
func verify_doc(root string, doc *present.Doc) error {
for _, section := range doc.Sections {
for _, elem := range section.Elem {
switch i := elem.(type) {
case present.Image:
if err := verify_path(root, i.URL); err != nil {
return fmt.Errorf("! .image %s not exist", i.URL)
}
}
}
}
return nil
}
var action_tmpl = `
{/*
This is the action template.
It determines how the formatting actions are rendered.
*/}
{{define "section"}}
<h{{len .Number}} id="TOC_{{.FormattedNumber}}">{{.FormattedNumber}} {{.Title}}</h{{len .Number}}>
{{range .Elem}}{{elem $.Template .}}{{end}}
{{end}}
{{define "list"}}
<ul>
{{range .Bullet}}
<li>{{style .}}</li>
{{end}}
</ul>
{{end}}
{{define "text"}}
{{if .Pre}}
<div class="code"><pre>{{range .Lines}}{{.}}{{end}}</pre></div>
{{else}}
<p>
{{range $i, $l := .Lines}}{{if $i}}{{template "newline"}}
{{end}}{{style $l}}{{end}}
</p>
{{end}}
{{end}}
{{define "code"}}
<div class="code{{if playable .}} playground{{end}}" contenteditable="true" spellcheck="false">{{.Text}}</div>
{{end}}
{{define "image"}}
<div class="image">
<img src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}}>
</div>
{{end}}
{{define "iframe"}}
<iframe src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}}></iframe>
{{end}}
{{define "link"}}<p class="link"><a href="{{.URL}}" target="_blank">{{style .Label}}</a></p>{{end}}
{{define "html"}}{{.HTML}}{{end}}
`
var article_tmpl = `
{/* This is the article template. It defines how articles are formatted. */}
{{define "root"}}
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}}</title>
<link type="text/css" rel="stylesheet" href="static/article.css">
<meta charset='utf-8'>
</head>
<body>
<div id="topbar" class="wide">
<div class="container">
<div id="heading">{{.Title}}
{{with .Subtitle}}{{.}}{{end}}
</div>
</div>
</div>
<div id="page" class="wide">
<div class="container">
{{with .Sections}}
<div id="toc">
{{template "TOC" .}}
</div>
{{end}}
{{range .Sections}}
{{elem $.Template .}}
{{end}}{{/* of Section block */}}
<h2>Authors</h2>
{{range .Authors}}
<div class="author">
{{range .Elem}}{{elem $.Template .}}{{end}}
</div>
{{end}}
</div>
</div>
<script src='/play.js'></script>
</body>
</html>
{{end}}
{{define "TOC"}}
<ul>
{{range .}}
<li><a href="#TOC_{{.FormattedNumber}}">{{.Title}}</a></li>
{{with .Sections}}{{template "TOC" .}}{{end}}
{{end}}
</ul>
{{end}}
{{define "newline"}}
{{/* No automatic line break. Paragraphs are free-form. */}}
{{end}}
`
var slides_tmpl = `
{/* This is the slide template. It defines how presentations are formatted. */}
{{define "root"}}
<!DOCTYPE html>
<html>
<head>
<title>{{.Title}}</title>
<meta charset='utf-8'>
<script src='static/slides.js'></script>
</head>
<body style='display: none'>
<section class='slides layout-widescreen'>
<article>
<h1>{{.Title}}</h1>
{{with .Subtitle}}<h3>{{.}}</h3>{{end}}
{{if not .Time.IsZero}}<h3>{{.Time.Format "2 January 2006"}}</h3>{{end}}
{{range .Authors}}
<div class="presenter">
{{range .TextElem}}{{elem $.Template .}}{{end}}
</div>
{{end}}
</article>
{{range $i, $s := .Sections}}
<!-- start of slide {{$s.Number}} -->
<article>
{{if $s.Elem}}
<h3>{{$s.Title}}</h3>
{{range $s.Elem}}{{elem $.Template .}}{{end}}
{{else}}
<h2>{{$s.Title}}</h2>
{{end}}
</article>
<!-- end of slide {{$i}} -->
{{end}}{{/* of Slide block */}}
<article>
<h3>Thank you</h1>
{{range .Authors}}
<div class="presenter">
{{range .Elem}}{{elem $.Template .}}{{end}}
</div>
{{end}}
</article>
</body>
{{if .PlayEnabled}}
<script src='/play.js'></script>
{{end}}
</html>
{{end}}
{{define "newline"}}
<br>
{{end}}
`

206
vendor/github.com/visualfc/gotools/jsonfmt/jsonfmt.go generated vendored Normal file
View File

@ -0,0 +1,206 @@
// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package jsonfmt
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/visualfc/gotools/command"
)
var Command = &command.Command{
Run: runJsonFmt,
UsageLine: "jsonfmt",
Short: "json format util",
Long: `json format util.`,
}
var (
jsonFmtList bool
jsonFmtCompact bool
jsonFmtWrite bool
jsonFmtDiff bool
jsonTabWidth int
jsonTabIndent bool
)
func init() {
Command.Flag.BoolVar(&jsonFmtList, "l", false, "list files whose formatting differs")
Command.Flag.BoolVar(&jsonFmtCompact, "c", false, "compact json")
Command.Flag.BoolVar(&jsonFmtWrite, "w", false, "write result to (source) file instead of stdout")
Command.Flag.BoolVar(&jsonFmtDiff, "d", false, "display diffs instead of rewriting files")
Command.Flag.IntVar(&jsonTabWidth, "tabwidth", 4, "tab width")
Command.Flag.BoolVar(&jsonTabIndent, "tabs", false, "indent with tabs")
}
func runJsonFmt(cmd *command.Command, args []string) error {
opt := &JsonFmtOption{}
opt.List = jsonFmtList
opt.Compact = jsonFmtCompact
opt.IndentTab = jsonTabIndent
opt.TabWidth = jsonTabWidth
opt.Write = jsonFmtWrite
opt.Diff = jsonFmtDiff
if len(args) == 0 {
if err := processJsonFile("<standard input>", os.Stdin, os.Stdout, true, opt); err != nil {
reportJsonError(err)
}
} else {
for _, path := range args {
switch dir, err := os.Stat(path); {
case err != nil:
reportJsonError(err)
case dir.IsDir():
filepath.Walk(path, func(path string, f os.FileInfo, err error) error {
if err == nil && isJsonFile(f) {
err = processJsonFile(path, nil, os.Stdout, false, opt)
}
if err != nil {
reportJsonError(err)
}
return nil
})
default:
if err := processJsonFile(path, nil, os.Stdout, false, opt); err != nil {
reportJsonError(err)
}
}
}
}
return nil
}
type JsonFmtOption struct {
List bool
Compact bool
Format bool
Write bool
Diff bool
IndentTab bool
TabWidth int
}
func isJsonFile(f os.FileInfo) bool {
// ignore non-Go files
name := f.Name()
return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".json")
}
func reportJsonError(err error) {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(2)
}
func processJson(filename string, src []byte, opt *JsonFmtOption) ([]byte, error) {
if opt.Compact {
var out bytes.Buffer
err := json.Compact(&out, src)
if err != nil {
return nil, err
}
return out.Bytes(), nil
} else {
var out bytes.Buffer
var err error
if opt.IndentTab {
err = json.Indent(&out, src, "", "\t")
} else {
var indent string
for i := 0; i < opt.TabWidth; i++ {
indent += " "
}
err = json.Indent(&out, src, "", indent)
}
if err != nil {
return nil, err
}
return out.Bytes(), nil
}
return src, nil
}
func processJsonFile(filename string, in io.Reader, out io.Writer, stdin bool, opt *JsonFmtOption) error {
if in == nil {
f, err := os.Open(filename)
if err != nil {
return err
}
defer f.Close()
in = f
}
src, err := ioutil.ReadAll(in)
if err != nil {
return err
}
res, err := processJson(filename, src, opt)
if err != nil {
return err
}
if !bytes.Equal(src, res) {
// formatting has changed
if opt.List {
fmt.Fprintln(out, filename)
}
if opt.Write {
err = ioutil.WriteFile(filename, res, 0)
if err != nil {
return err
}
}
if opt.Diff {
data, err := diffJson(src, res)
if err != nil {
return fmt.Errorf("computing diff: %s", err)
}
fmt.Printf("diff %s json/%s\n", filename, filename)
out.Write(data)
}
}
if !opt.List && !opt.Write && !opt.Diff {
_, err = out.Write(res)
}
return err
}
func diffJson(b1, b2 []byte) (data []byte, err error) {
f1, err := ioutil.TempFile("", "json")
if err != nil {
return
}
defer os.Remove(f1.Name())
defer f1.Close()
f2, err := ioutil.TempFile("", "json")
if err != nil {
return
}
defer os.Remove(f2.Name())
defer f2.Close()
f1.Write(b1)
f2.Write(b2)
data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
if len(data) > 0 {
// diff exits with a non-zero status when the files don't match.
// Ignore that failure as long as we get output.
err = nil
}
return
}

105
vendor/github.com/visualfc/gotools/oracle/oracle.go generated vendored Normal file
View File

@ -0,0 +1,105 @@
// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package oracle
import (
"fmt"
"go/build"
"log"
"os"
"runtime"
"github.com/visualfc/gotools/command"
"golang.org/x/tools/oracle"
)
//The mode argument determines the query to perform:
// callees show possible targets of selected function call
// callers show possible callers of selected function
// callgraph show complete callgraph of program
// callstack show path from callgraph root to selected function
// describe describe selected syntax: definition, methods, etc
// freevars show free variables of selection
// implements show 'implements' relation for selected type
// peers show send/receive corresponding to selected channel op
// referrers show all refs to entity denoted by selected identifier
// what show basic information about the selected syntax node
var Command = &command.Command{
Run: runOracle,
UsageLine: "oracle",
Short: "golang oracle util",
Long: `golang oracle util.`,
}
var (
oraclePos string
oracleReflect bool
)
func init() {
Command.Flag.StringVar(&oraclePos, "pos", "", "filename:#offset")
Command.Flag.BoolVar(&oracleReflect, "reflect", false, "Analyze reflection soundly (slow).")
}
func runOracle(cmd *command.Command, args []string) error {
if len(args) < 2 {
cmd.Usage()
return os.ErrInvalid
}
if os.Getenv("GOMAXPROCS") == "" {
n := runtime.NumCPU()
if n < 4 {
n = 4
}
runtime.GOMAXPROCS(n)
}
mode := args[0]
args = args[1:]
if args[0] == "." {
pkgPath, err := os.Getwd()
if err != nil {
log.Fatalln(err)
}
pkg, err := build.Default.ImportDir(pkgPath, 0)
if err != nil {
log.Fatalln(err)
}
args = pkg.GoFiles
//log.Println(pkg.ImportPath)
if pkg.ImportPath != "." && pkg.ImportPath != "" {
args = []string{pkg.ImportPath}
}
}
query := oracle.Query{
Mode: mode,
Pos: oraclePos,
Build: &build.Default,
Scope: args,
PTALog: nil,
Reflection: oracleReflect,
}
if err := oracle.Run(&query); err != nil {
fmt.Fprintf(os.Stderr, "oracle: %s.\n", err)
return err
}
if mode == "referrers" {
ref := query.Serial().Referrers
if ref != nil {
fmt.Fprintln(os.Stdout, ref.Desc)
fmt.Fprintln(os.Stdout, ref.ObjPos)
for _, v := range ref.Refs {
fmt.Fprintln(os.Stdout, v)
}
}
} else {
query.WriteTo(os.Stdout)
}
return nil
}

379
vendor/github.com/visualfc/gotools/pkgs/pkgs.go generated vendored Normal file
View File

@ -0,0 +1,379 @@
// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package pkgs
import (
"encoding/json"
"fmt"
"go/build"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"sync"
"github.com/visualfc/gotools/command"
"github.com/visualfc/gotools/goapi"
)
var Command = &command.Command{
Run: runPkgs,
UsageLine: "pkgs",
Short: "print liteide_stub version",
Long: `Version prints the liteide_stub version.`,
}
var (
pkgsList bool
pkgsJson bool
pkgsFind string
pkgsStd bool
pkgsPkgOnly bool
pkgsSkipGoroot bool
)
func init() {
Command.Flag.BoolVar(&pkgsList, "list", false, "list all package")
Command.Flag.BoolVar(&pkgsJson, "json", false, "json format")
Command.Flag.BoolVar(&pkgsStd, "std", false, "std library")
Command.Flag.BoolVar(&pkgsPkgOnly, "pkg", false, "pkg only")
Command.Flag.BoolVar(&pkgsSkipGoroot, "skip_goroot", false, "skip goroot")
Command.Flag.StringVar(&pkgsFind, "find", "", "find package by name")
}
func runPkgs(cmd *command.Command, args []string) error {
runtime.GOMAXPROCS(runtime.NumCPU())
if len(args) != 0 {
cmd.Usage()
return os.ErrInvalid
}
//pkgIndexOnce.Do(loadPkgsList)
var pp PathPkgsIndex
pp.LoadIndex()
pp.Sort()
if pkgsList {
for _, pi := range pp.indexs {
for _, pkg := range pi.pkgs {
if pkgsPkgOnly && pkg.IsCommand() {
continue
}
if pkgsJson {
var p GoPackage
p.copyBuild(pkg)
b, err := json.MarshalIndent(&p, "", "\t")
if err == nil {
cmd.Stdout.Write(b)
cmd.Stdout.Write([]byte{'\n'})
}
} else {
cmd.Println(pkg.ImportPath)
}
}
}
} else if pkgsFind != "" {
for _, pi := range pp.indexs {
for _, pkg := range pi.pkgs {
if pkg.Name == pkgsFind {
if pkgsPkgOnly && pkg.IsCommand() {
continue
}
if pkgsJson {
var p GoPackage
p.copyBuild(pkg)
b, err := json.MarshalIndent(p, "", "\t")
if err == nil {
cmd.Stdout.Write(b)
cmd.Stdout.Write([]byte{'\n'})
}
} else {
cmd.Println(pkg.Name)
}
break
}
}
}
}
return nil
}
// A Package describes a single package found in a directory.
type GoPackage struct {
// Note: These fields are part of the go command's public API.
// See list.go. It is okay to add fields, but not to change or
// remove existing ones. Keep in sync with list.go
Dir string `json:",omitempty"` // directory containing package sources
ImportPath string `json:",omitempty"` // import path of package in dir
Name string `json:",omitempty"` // package name
Doc string `json:",omitempty"` // package documentation string
Target string `json:",omitempty"` // install path
Goroot bool `json:",omitempty"` // is this package found in the Go root?
Standard bool `json:",omitempty"` // is this package part of the standard Go library?
Stale bool `json:",omitempty"` // would 'go install' do anything for this package?
Root string `json:",omitempty"` // Go root or Go path dir containing this package
ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory
// Source files
GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string `json:",omitempty"` // .go sources files that import "C"
IgnoredGoFiles []string `json:",omitempty"` // .go sources ignored due to build constraints
CFiles []string `json:",omitempty"` // .c source files
CXXFiles []string `json:",omitempty"` // .cc, .cpp and .cxx source files
MFiles []string `json:",omitempty"` // .m source files
HFiles []string `json:",omitempty"` // .h, .hh, .hpp and .hxx source files
SFiles []string `json:",omitempty"` // .s source files
SwigFiles []string `json:",omitempty"` // .swig files
SwigCXXFiles []string `json:",omitempty"` // .swigcxx files
SysoFiles []string `json:",omitempty"` // .syso system object files added to package
// Cgo directives
CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler
CgoCPPFLAGS []string `json:",omitempty"` // cgo: flags for C preprocessor
CgoCXXFLAGS []string `json:",omitempty"` // cgo: flags for C++ compiler
CgoLDFLAGS []string `json:",omitempty"` // cgo: flags for linker
CgoPkgConfig []string `json:",omitempty"` // cgo: pkg-config names
// Dependency information
Imports []string `json:",omitempty"` // import paths used by this package
Deps []string `json:",omitempty"` // all (recursively) imported dependencies
// Error information
Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies?
// Test information
TestGoFiles []string `json:",omitempty"` // _test.go files in package
TestImports []string `json:",omitempty"` // imports from TestGoFiles
XTestGoFiles []string `json:",omitempty"` // _test.go files outside package
XTestImports []string `json:",omitempty"` // imports from XTestGoFiles
// Unexported fields are not part of the public API.
build *build.Package
pkgdir string // overrides build.PkgDir
imports []*goapi.Package
deps []*goapi.Package
gofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths
sfiles []string
allgofiles []string // gofiles + IgnoredGoFiles, absolute paths
target string // installed file for this package (may be executable)
fake bool // synthesized package
forceBuild bool // this package must be rebuilt
forceLibrary bool // this package is a library (even if named "main")
cmdline bool // defined by files listed on command line
local bool // imported via local path (./ or ../)
localPrefix string // interpret ./ and ../ imports relative to this prefix
exeName string // desired name for temporary executable
coverMode string // preprocess Go source files with the coverage tool in this mode
coverVars map[string]*CoverVar // variables created by coverage analysis
omitDWARF bool // tell linker not to write DWARF information
}
// CoverVar holds the name of the generated coverage variables targeting the named file.
type CoverVar struct {
File string // local file name
Var string // name of count struct
}
func (p *GoPackage) copyBuild(pp *build.Package) {
p.build = pp
p.Dir = pp.Dir
p.ImportPath = pp.ImportPath
p.Name = pp.Name
p.Doc = pp.Doc
p.Root = pp.Root
p.ConflictDir = pp.ConflictDir
// TODO? Target
p.Goroot = pp.Goroot
p.Standard = p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".")
p.GoFiles = pp.GoFiles
p.CgoFiles = pp.CgoFiles
p.IgnoredGoFiles = pp.IgnoredGoFiles
p.CFiles = pp.CFiles
p.CXXFiles = pp.CXXFiles
p.MFiles = pp.MFiles
p.HFiles = pp.HFiles
p.SFiles = pp.SFiles
p.SwigFiles = pp.SwigFiles
p.SwigCXXFiles = pp.SwigCXXFiles
p.SysoFiles = pp.SysoFiles
p.CgoCFLAGS = pp.CgoCFLAGS
p.CgoCPPFLAGS = pp.CgoCPPFLAGS
p.CgoCXXFLAGS = pp.CgoCXXFLAGS
p.CgoLDFLAGS = pp.CgoLDFLAGS
p.CgoPkgConfig = pp.CgoPkgConfig
p.Imports = pp.Imports
p.TestGoFiles = pp.TestGoFiles
p.TestImports = pp.TestImports
p.XTestGoFiles = pp.XTestGoFiles
p.XTestImports = pp.XTestImports
}
type PathPkgsIndex struct {
indexs []*PkgsIndex
}
func (p *PathPkgsIndex) LoadIndex() {
var wg sync.WaitGroup
var context = build.Default
if pkgsStd {
context.GOPATH = ""
}
var srcDirs []string
goroot := context.GOROOT
gopath := context.GOPATH
context.GOPATH = ""
if !pkgsSkipGoroot {
//go1.4 go/src/
//go1.3 go/src/pkg; go/src/cmd
_, err := os.Stat(filepath.Join(goroot, "src/pkg/runtime"))
if err == nil {
for _, v := range context.SrcDirs() {
if strings.HasSuffix(v, "pkg") {
srcDirs = append(srcDirs, v[:len(v)-3]+"cmd")
}
srcDirs = append(srcDirs, v)
}
} else {
srcDirs = append(srcDirs, filepath.Join(goroot, "src"))
}
}
context.GOPATH = gopath
context.GOROOT = ""
for _, v := range context.SrcDirs() {
srcDirs = append(srcDirs, v)
}
context.GOROOT = goroot
for _, path := range srcDirs {
pi := &PkgsIndex{}
p.indexs = append(p.indexs, pi)
pkgsGate.enter()
f, err := os.Open(path)
if err != nil {
pkgsGate.leave()
fmt.Fprint(os.Stderr, err)
continue
}
children, err := f.Readdir(-1)
f.Close()
pkgsGate.leave()
if err != nil {
fmt.Fprint(os.Stderr, err)
continue
}
for _, child := range children {
if child.IsDir() {
wg.Add(1)
go func(path, name string) {
defer wg.Done()
pi.loadPkgsPath(&wg, path, name)
}(path, child.Name())
}
}
}
wg.Wait()
}
func (p *PathPkgsIndex) Sort() {
for _, v := range p.indexs {
v.sort()
}
}
type PkgsIndex struct {
sync.Mutex
pkgs []*build.Package
}
func (p *PkgsIndex) sort() {
sort.Sort(PkgSlice(p.pkgs))
}
type PkgSlice []*build.Package
func (p PkgSlice) Len() int {
return len([]*build.Package(p))
}
func (p PkgSlice) Less(i, j int) bool {
if p[i].IsCommand() && !p[j].IsCommand() {
return true
} else if !p[i].IsCommand() && p[j].IsCommand() {
return false
}
return p[i].ImportPath < p[j].ImportPath
}
func (p PkgSlice) Swap(i, j int) {
p[i], p[j] = p[j], p[i]
}
// pkgsgate protects the OS & filesystem from too much concurrency.
// Too much disk I/O -> too many threads -> swapping and bad scheduling.
// gate is a semaphore for limiting concurrency.
type gate chan struct{}
func (g gate) enter() { g <- struct{}{} }
func (g gate) leave() { <-g }
var pkgsGate = make(gate, 8)
func (p *PkgsIndex) loadPkgsPath(wg *sync.WaitGroup, root, pkgrelpath string) {
importpath := filepath.ToSlash(pkgrelpath)
dir := filepath.Join(root, importpath)
pkgsGate.enter()
defer pkgsGate.leave()
pkgDir, err := os.Open(dir)
if err != nil {
return
}
children, err := pkgDir.Readdir(-1)
pkgDir.Close()
if err != nil {
return
}
// hasGo tracks whether a directory actually appears to be a
// Go source code directory. If $GOPATH == $HOME, and
// $HOME/src has lots of other large non-Go projects in it,
// then the calls to importPathToName below can be expensive.
hasGo := false
for _, child := range children {
name := child.Name()
if name == "" {
continue
}
if c := name[0]; c == '.' || ('0' <= c && c <= '9') {
continue
}
if strings.HasSuffix(name, ".go") {
hasGo = true
}
if child.IsDir() {
if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") || name == "testdata" {
continue
}
wg.Add(1)
go func(root, name string) {
defer wg.Done()
p.loadPkgsPath(wg, root, name)
}(root, filepath.Join(importpath, name))
}
}
if hasGo {
buildPkg, err := build.ImportDir(dir, 0)
if err == nil {
if buildPkg.ImportPath == "." {
buildPkg.ImportPath = filepath.ToSlash(pkgrelpath)
buildPkg.Root = root
buildPkg.Goroot = true
}
p.Lock()
p.pkgs = append(p.pkgs, buildPkg)
p.Unlock()
}
}
}

87
vendor/github.com/visualfc/gotools/runcmd/runcmd.go generated vendored Normal file
View File

@ -0,0 +1,87 @@
// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runcmd
import (
"fmt"
"os"
"os/exec"
"strings"
"github.com/visualfc/gotools/command"
)
var Command = &command.Command{
Run: runCmd,
UsageLine: "runcmd [-w work_path] <program_name> [arguments...]",
Short: "run program",
Long: `run program and arguments`,
}
var execWorkPath string
var execWaitEnter bool
func init() {
Command.Flag.StringVar(&execWorkPath, "w", "", "work path")
Command.Flag.BoolVar(&execWaitEnter, "e", true, "wait enter and continue")
}
func runCmd(cmd *command.Command, args []string) error {
if len(args) == 0 {
cmd.Usage()
return os.ErrInvalid
}
if execWorkPath == "" {
var err error
execWorkPath, err = os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "liteide_stub exec: os.Getwd() false\n")
command.SetExitStatus(3)
command.Exit()
return err
}
}
fileName := args[0]
filePath, err := exec.LookPath(fileName)
if err != nil {
filePath, err = exec.LookPath("./" + fileName)
}
if err != nil {
fmt.Fprintf(os.Stderr, "liteide_stub exec: file %s not found\n", fileName)
command.SetExitStatus(3)
command.Exit()
}
fmt.Println("Starting Process", filePath, strings.Join(args[1:], " "), "...")
command := exec.Command(filePath, args[1:]...)
command.Dir = execWorkPath
command.Stdin = os.Stdin
command.Stdout = os.Stdout
command.Stderr = os.Stderr
err = command.Run()
if err != nil {
fmt.Println("\nEnd Process", err)
} else {
fmt.Println("\nEnd Process", "exit status 0")
}
exitWaitEnter()
return nil
}
func exitWaitEnter() {
if !execWaitEnter {
return
}
fmt.Println("\nPress enter key to continue")
var s = [256]byte{}
os.Stdin.Read(s[:])
command.SetExitStatus(0)
command.Exit()
}

19
vendor/github.com/visualfc/gotools/stdlib/go13.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
// +build !go1.4
package stdlib
import (
"go/build"
"os"
"path/filepath"
)
func ImportStdPkg(context *build.Context, path string, mode build.ImportMode) (*build.Package, error) {
realpath := filepath.Join(context.GOROOT, "src", "pkg", path)
if _, err := os.Stat(realpath); err != nil {
realpath = filepath.Join(context.GOROOT, "src", path)
}
pkg, err := context.ImportDir(realpath, 0)
pkg.ImportPath = path
return pkg, err
}

19
vendor/github.com/visualfc/gotools/stdlib/go14.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
// +build go1.4
package stdlib
import (
"go/build"
"os"
"path/filepath"
)
func ImportStdPkg(context *build.Context, path string, mode build.ImportMode) (*build.Package, error) {
realpath := filepath.Join(context.GOROOT, "src", path)
if _, err := os.Stat(realpath); err != nil {
realpath = filepath.Join(context.GOROOT, "src/pkg", path)
}
pkg, err := context.ImportDir(realpath, 0)
pkg.ImportPath = path
return pkg, err
}

168
vendor/github.com/visualfc/gotools/stdlib/mkpkglist.go generated vendored Normal file
View File

@ -0,0 +1,168 @@
// +build ignore
package main
import (
"fmt"
"strings"
)
var pkgList = `
cmd/cgo
cmd/fix
cmd/go
cmd/gofmt
cmd/yacc
archive/tar
archive/zip
bufio
bytes
compress/bzip2
compress/flate
compress/gzip
compress/lzw
compress/zlib
container/heap
container/list
container/ring
crypto
crypto/aes
crypto/cipher
crypto/des
crypto/dsa
crypto/ecdsa
crypto/elliptic
crypto/hmac
crypto/md5
crypto/rand
crypto/rc4
crypto/rsa
crypto/sha1
crypto/sha256
crypto/sha512
crypto/subtle
crypto/tls
crypto/x509
crypto/x509/pkix
database/sql
database/sql/driver
debug/dwarf
debug/elf
debug/gosym
debug/macho
debug/pe
encoding
encoding/ascii85
encoding/asn1
encoding/base32
encoding/base64
encoding/binary
encoding/csv
encoding/gob
encoding/hex
encoding/json
encoding/pem
encoding/xml
errors
expvar
flag
fmt
go/ast
go/build
go/doc
go/format
go/parser
go/printer
go/scanner
go/token
hash
hash/adler32
hash/crc32
hash/crc64
hash/fnv
html
html/template
image
image/color
image/color/palette
image/draw
image/gif
image/jpeg
image/png
index/suffixarray
io
io/ioutil
log
log/syslog
math
math/big
math/cmplx
math/rand
mime
mime/multipart
net
net/http
net/http/cgi
net/http/cookiejar
net/http/fcgi
net/http/httptest
net/http/httputil
net/http/pprof
net/mail
net/rpc
net/rpc/jsonrpc
net/smtp
net/textproto
net/url
os
os/exec
os/signal
os/user
path
path/filepath
reflect
regexp
regexp/syntax
runtime
runtime/cgo
runtime/debug
runtime/pprof
runtime/race
sort
strconv
strings
sync
sync/atomic
syscall
testing
testing/iotest
testing/quick
text/scanner
text/tabwriter
text/template
text/template/parse
time
unicode
unicode/utf16
unicode/utf8
unsafe
`
func main() {
//fmt.Println(pkgList)
var list []string
index := 0
for _, v := range strings.Split(pkgList, "\n") {
v = strings.TrimSpace(v)
if v == "" {
continue
}
v = "\"" + v + "\""
if index%4 == 0 && index != 0 {
v = "\n" + v
}
list = append(list, v)
index++
}
fmt.Println(strings.Join(list, ","))
}

91
vendor/github.com/visualfc/gotools/stdlib/mkstdlib.go generated vendored Normal file
View File

@ -0,0 +1,91 @@
// +build ignore
// mkstdlib generates the zstdlib.go file, containing the Go standard
// library API symbols. It's baked into the binary to avoid scanning
// GOPATH in the common case.
package main
import (
"bufio"
"bytes"
"fmt"
"go/format"
"io"
"log"
"os"
"path"
"path/filepath"
"regexp"
"sort"
"strings"
)
func mustOpen(name string) io.Reader {
f, err := os.Open(name)
if err != nil {
log.Fatal(err)
}
return f
}
func api(base string) string {
return filepath.Join(os.Getenv("GOROOT"), "api", base)
}
var sym = regexp.MustCompile(`^pkg (\S+).*?, (?:var|func|type|const) ([A-Z]\w*)`)
func main() {
var buf bytes.Buffer
outf := func(format string, args ...interface{}) {
fmt.Fprintf(&buf, format, args...)
}
outf("// AUTO-GENERATED BY mkstdlib.go\n\n")
outf("package imports\n")
outf("var stdlib = map[string]string{\n")
f := io.MultiReader(
mustOpen(api("go1.txt")),
mustOpen(api("go1.1.txt")),
mustOpen(api("go1.2.txt")),
mustOpen(api("go1.3.txt")),
)
sc := bufio.NewScanner(f)
fullImport := map[string]string{} // "zip.NewReader" => "archive/zip"
ambiguous := map[string]bool{}
var keys []string
for sc.Scan() {
l := sc.Text()
has := func(v string) bool { return strings.Contains(l, v) }
if has("struct, ") || has("interface, ") || has(", method (") {
continue
}
if m := sym.FindStringSubmatch(l); m != nil {
full := m[1]
key := path.Base(full) + "." + m[2]
if exist, ok := fullImport[key]; ok {
if exist != full {
ambiguous[key] = true
}
} else {
fullImport[key] = full
keys = append(keys, key)
}
}
}
if err := sc.Err(); err != nil {
log.Fatal(err)
}
sort.Strings(keys)
for _, key := range keys {
if ambiguous[key] {
outf("\t// %q is ambiguous\n", key)
} else {
outf("\t%q: %q,\n", key, fullImport[key])
}
}
outf("}\n")
fmtbuf, err := format.Source(buf.Bytes())
if err != nil {
log.Fatal(err)
}
os.Stdout.Write(fmtbuf)
}

48
vendor/github.com/visualfc/gotools/stdlib/pkglist.go generated vendored Normal file
View File

@ -0,0 +1,48 @@
package stdlib
var Packages = []string{
"cmd/cgo", "cmd/fix", "cmd/go", "cmd/gofmt",
"cmd/yacc", "archive/tar", "archive/zip", "bufio",
"bytes", "compress/bzip2", "compress/flate", "compress/gzip",
"compress/lzw", "compress/zlib", "container/heap", "container/list",
"container/ring", "crypto", "crypto/aes", "crypto/cipher",
"crypto/des", "crypto/dsa", "crypto/ecdsa", "crypto/elliptic",
"crypto/hmac", "crypto/md5", "crypto/rand", "crypto/rc4",
"crypto/rsa", "crypto/sha1", "crypto/sha256", "crypto/sha512",
"crypto/subtle", "crypto/tls", "crypto/x509", "crypto/x509/pkix",
"database/sql", "database/sql/driver", "debug/dwarf", "debug/elf",
"debug/gosym", "debug/macho", "debug/pe", "encoding",
"encoding/ascii85", "encoding/asn1", "encoding/base32", "encoding/base64",
"encoding/binary", "encoding/csv", "encoding/gob", "encoding/hex",
"encoding/json", "encoding/pem", "encoding/xml", "errors",
"expvar", "flag", "fmt", "go/ast",
"go/build", "go/doc", "go/format", "go/parser",
"go/printer", "go/scanner", "go/token", "hash",
"hash/adler32", "hash/crc32", "hash/crc64", "hash/fnv",
"html", "html/template", "image", "image/color",
"image/color/palette", "image/draw", "image/gif", "image/jpeg",
"image/png", "index/suffixarray", "io", "io/ioutil",
"log", "log/syslog", "math", "math/big",
"math/cmplx", "math/rand", "mime", "mime/multipart",
"net", "net/http", "net/http/cgi", "net/http/cookiejar",
"net/http/fcgi", "net/http/httptest", "net/http/httputil", "net/http/pprof",
"net/mail", "net/rpc", "net/rpc/jsonrpc", "net/smtp",
"net/textproto", "net/url", "os", "os/exec",
"os/signal", "os/user", "path", "path/filepath",
"reflect", "regexp", "regexp/syntax", "runtime",
"runtime/cgo", "runtime/debug", "runtime/pprof", "runtime/race",
"sort", "strconv", "strings", "sync",
"sync/atomic", "syscall", "testing", "testing/iotest",
"testing/quick", "text/scanner", "text/tabwriter", "text/template",
"text/template/parse", "time", "unicode", "unicode/utf16",
"unicode/utf8", "unsafe",
}
func IsStdPkg(pkg string) bool {
for _, v := range Packages {
if v == pkg {
return true
}
}
return false
}

8479
vendor/github.com/visualfc/gotools/stdlib/zstdlib.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

1064
vendor/github.com/visualfc/gotools/types/types.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

27
vendor/golang.org/x/tools/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/golang.org/x/tools/PATENTS generated vendored Normal file
View File

@ -0,0 +1,22 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

967
vendor/golang.org/x/tools/container/intsets/sparse.go generated vendored Normal file
View File

@ -0,0 +1,967 @@
// Copyright 2014 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 intsets provides Sparse, a compact and fast representation
// for sparse sets of int values.
//
// The time complexity of the operations Len, Insert, Remove and Has
// is in O(n) but in practice those methods are faster and more
// space-efficient than equivalent operations on sets based on the Go
// map type. The IsEmpty, Min, Max, Clear and TakeMin operations
// require constant time.
//
package intsets // import "golang.org/x/tools/container/intsets"
// TODO(adonovan):
// - Add InsertAll(...int), RemoveAll(...int)
// - Add 'bool changed' results for {Intersection,Difference}With too.
//
// TODO(adonovan): implement Dense, a dense bit vector with a similar API.
// The space usage would be proportional to Max(), not Len(), and the
// implementation would be based upon big.Int.
//
// TODO(adonovan): experiment with making the root block indirect (nil
// iff IsEmpty). This would reduce the memory usage when empty and
// might simplify the aliasing invariants.
//
// TODO(adonovan): opt: make UnionWith and Difference faster.
// These are the hot-spots for go/pointer.
import (
"bytes"
"fmt"
)
// A Sparse is a set of int values.
// Sparse operations (even queries) are not concurrency-safe.
//
// The zero value for Sparse is a valid empty set.
//
// Sparse sets must be copied using the Copy method, not by assigning
// a Sparse value.
//
type Sparse struct {
// An uninitialized Sparse represents an empty set.
// An empty set may also be represented by
// root.next == root.prev == &root.
// In a non-empty set, root.next points to the first block and
// root.prev to the last.
// root.offset and root.bits are unused.
root block
}
type word uintptr
const (
_m = ^word(0)
bitsPerWord = 8 << (_m>>8&1 + _m>>16&1 + _m>>32&1)
bitsPerBlock = 256 // optimal value for go/pointer solver performance
wordsPerBlock = bitsPerBlock / bitsPerWord
)
// Limit values of implementation-specific int type.
const (
MaxInt = int(^uint(0) >> 1)
MinInt = -MaxInt - 1
)
// -- block ------------------------------------------------------------
// A set is represented as a circular doubly-linked list of blocks,
// each containing an offset and a bit array of fixed size
// bitsPerBlock; the blocks are ordered by increasing offset.
//
// The set contains an element x iff the block whose offset is x - (x
// mod bitsPerBlock) has the bit (x mod bitsPerBlock) set, where mod
// is the Euclidean remainder.
//
// A block may only be empty transiently.
//
type block struct {
offset int // offset mod bitsPerBlock == 0
bits [wordsPerBlock]word // contains at least one set bit
next, prev *block // doubly-linked list of blocks
}
// wordMask returns the word index (in block.bits)
// and single-bit mask for the block's ith bit.
func wordMask(i uint) (w uint, mask word) {
w = i / bitsPerWord
mask = 1 << (i % bitsPerWord)
return
}
// insert sets the block b's ith bit and
// returns true if it was not already set.
//
func (b *block) insert(i uint) bool {
w, mask := wordMask(i)
if b.bits[w]&mask == 0 {
b.bits[w] |= mask
return true
}
return false
}
// remove clears the block's ith bit and
// returns true if the bit was previously set.
// NB: may leave the block empty.
//
func (b *block) remove(i uint) bool {
w, mask := wordMask(i)
if b.bits[w]&mask != 0 {
b.bits[w] &^= mask
return true
}
return false
}
// has reports whether the block's ith bit is set.
func (b *block) has(i uint) bool {
w, mask := wordMask(i)
return b.bits[w]&mask != 0
}
// empty reports whether b.len()==0, but more efficiently.
func (b *block) empty() bool {
for _, w := range b.bits {
if w != 0 {
return false
}
}
return true
}
// len returns the number of set bits in block b.
func (b *block) len() int {
var l int
for _, w := range b.bits {
l += popcount(w)
}
return l
}
// max returns the maximum element of the block.
// The block must not be empty.
//
func (b *block) max() int {
bi := b.offset + bitsPerBlock
// Decrement bi by number of high zeros in last.bits.
for i := len(b.bits) - 1; i >= 0; i-- {
if w := b.bits[i]; w != 0 {
return bi - nlz(w) - 1
}
bi -= bitsPerWord
}
panic("BUG: empty block")
}
// min returns the minimum element of the block,
// and also removes it if take is set.
// The block must not be initially empty.
// NB: may leave the block empty.
//
func (b *block) min(take bool) int {
for i, w := range b.bits {
if w != 0 {
tz := ntz(w)
if take {
b.bits[i] = w &^ (1 << uint(tz))
}
return b.offset + int(i*bitsPerWord) + tz
}
}
panic("BUG: empty block")
}
// forEach calls f for each element of block b.
// f must not mutate b's enclosing Sparse.
func (b *block) forEach(f func(int)) {
for i, w := range b.bits {
offset := b.offset + i*bitsPerWord
for bi := 0; w != 0 && bi < bitsPerWord; bi++ {
if w&1 != 0 {
f(offset)
}
offset++
w >>= 1
}
}
}
// offsetAndBitIndex returns the offset of the block that would
// contain x and the bit index of x within that block.
//
func offsetAndBitIndex(x int) (int, uint) {
mod := x % bitsPerBlock
if mod < 0 {
// Euclidean (non-negative) remainder
mod += bitsPerBlock
}
return x - mod, uint(mod)
}
// -- Sparse --------------------------------------------------------------
// start returns the root's next block, which is the root block
// (if s.IsEmpty()) or the first true block otherwise.
// start has the side effect of ensuring that s is properly
// initialized.
//
func (s *Sparse) start() *block {
root := &s.root
if root.next == nil {
root.next = root
root.prev = root
} else if root.next.prev != root {
// Copying a Sparse x leads to pernicious corruption: the
// new Sparse y shares the old linked list, but iteration
// on y will never encounter &y.root so it goes into a
// loop. Fail fast before this occurs.
panic("A Sparse has been copied without (*Sparse).Copy()")
}
return root.next
}
// IsEmpty reports whether the set s is empty.
func (s *Sparse) IsEmpty() bool {
return s.start() == &s.root
}
// Len returns the number of elements in the set s.
func (s *Sparse) Len() int {
var l int
for b := s.start(); b != &s.root; b = b.next {
l += b.len()
}
return l
}
// Max returns the maximum element of the set s, or MinInt if s is empty.
func (s *Sparse) Max() int {
if s.IsEmpty() {
return MinInt
}
return s.root.prev.max()
}
// Min returns the minimum element of the set s, or MaxInt if s is empty.
func (s *Sparse) Min() int {
if s.IsEmpty() {
return MaxInt
}
return s.root.next.min(false)
}
// block returns the block that would contain offset,
// or nil if s contains no such block.
//
func (s *Sparse) block(offset int) *block {
b := s.start()
for b != &s.root && b.offset <= offset {
if b.offset == offset {
return b
}
b = b.next
}
return nil
}
// Insert adds x to the set s, and reports whether the set grew.
func (s *Sparse) Insert(x int) bool {
offset, i := offsetAndBitIndex(x)
b := s.start()
for b != &s.root && b.offset <= offset {
if b.offset == offset {
return b.insert(i)
}
b = b.next
}
// Insert new block before b.
new := &block{offset: offset}
new.next = b
new.prev = b.prev
new.prev.next = new
new.next.prev = new
return new.insert(i)
}
func (s *Sparse) removeBlock(b *block) {
b.prev.next = b.next
b.next.prev = b.prev
}
// Remove removes x from the set s, and reports whether the set shrank.
func (s *Sparse) Remove(x int) bool {
offset, i := offsetAndBitIndex(x)
if b := s.block(offset); b != nil {
if !b.remove(i) {
return false
}
if b.empty() {
s.removeBlock(b)
}
return true
}
return false
}
// Clear removes all elements from the set s.
func (s *Sparse) Clear() {
s.root.next = &s.root
s.root.prev = &s.root
}
// If set s is non-empty, TakeMin sets *p to the minimum element of
// the set s, removes that element from the set and returns true.
// Otherwise, it returns false and *p is undefined.
//
// This method may be used for iteration over a worklist like so:
//
// var x int
// for worklist.TakeMin(&x) { use(x) }
//
func (s *Sparse) TakeMin(p *int) bool {
head := s.start()
if head == &s.root {
return false
}
*p = head.min(true)
if head.empty() {
s.removeBlock(head)
}
return true
}
// Has reports whether x is an element of the set s.
func (s *Sparse) Has(x int) bool {
offset, i := offsetAndBitIndex(x)
if b := s.block(offset); b != nil {
return b.has(i)
}
return false
}
// forEach applies function f to each element of the set s in order.
//
// f must not mutate s. Consequently, forEach is not safe to expose
// to clients. In any case, using "range s.AppendTo()" allows more
// natural control flow with continue/break/return.
//
func (s *Sparse) forEach(f func(int)) {
for b := s.start(); b != &s.root; b = b.next {
b.forEach(f)
}
}
// Copy sets s to the value of x.
func (s *Sparse) Copy(x *Sparse) {
if s == x {
return
}
xb := x.start()
sb := s.start()
for xb != &x.root {
if sb == &s.root {
sb = s.insertBlockBefore(sb)
}
sb.offset = xb.offset
sb.bits = xb.bits
xb = xb.next
sb = sb.next
}
s.discardTail(sb)
}
// insertBlockBefore returns a new block, inserting it before next.
func (s *Sparse) insertBlockBefore(next *block) *block {
b := new(block)
b.next = next
b.prev = next.prev
b.prev.next = b
next.prev = b
return b
}
// discardTail removes block b and all its successors from s.
func (s *Sparse) discardTail(b *block) {
if b != &s.root {
b.prev.next = &s.root
s.root.prev = b.prev
}
}
// IntersectionWith sets s to the intersection s ∩ x.
func (s *Sparse) IntersectionWith(x *Sparse) {
if s == x {
return
}
xb := x.start()
sb := s.start()
for xb != &x.root && sb != &s.root {
switch {
case xb.offset < sb.offset:
xb = xb.next
case xb.offset > sb.offset:
sb = sb.next
s.removeBlock(sb.prev)
default:
var sum word
for i := range sb.bits {
r := xb.bits[i] & sb.bits[i]
sb.bits[i] = r
sum |= r
}
if sum != 0 {
sb = sb.next
} else {
// sb will be overwritten or removed
}
xb = xb.next
}
}
s.discardTail(sb)
}
// Intersection sets s to the intersection x ∩ y.
func (s *Sparse) Intersection(x, y *Sparse) {
switch {
case s == x:
s.IntersectionWith(y)
return
case s == y:
s.IntersectionWith(x)
return
case x == y:
s.Copy(x)
return
}
xb := x.start()
yb := y.start()
sb := s.start()
for xb != &x.root && yb != &y.root {
switch {
case xb.offset < yb.offset:
xb = xb.next
continue
case xb.offset > yb.offset:
yb = yb.next
continue
}
if sb == &s.root {
sb = s.insertBlockBefore(sb)
}
sb.offset = xb.offset
var sum word
for i := range sb.bits {
r := xb.bits[i] & yb.bits[i]
sb.bits[i] = r
sum |= r
}
if sum != 0 {
sb = sb.next
} else {
// sb will be overwritten or removed
}
xb = xb.next
yb = yb.next
}
s.discardTail(sb)
}
// Intersects reports whether s ∩ x ≠ ∅.
func (s *Sparse) Intersects(x *Sparse) bool {
sb := s.start()
xb := x.start()
for sb != &s.root && xb != &x.root {
switch {
case xb.offset < sb.offset:
xb = xb.next
case xb.offset > sb.offset:
sb = sb.next
default:
for i := range sb.bits {
if sb.bits[i]&xb.bits[i] != 0 {
return true
}
}
sb = sb.next
xb = xb.next
}
}
return false
}
// UnionWith sets s to the union s x, and reports whether s grew.
func (s *Sparse) UnionWith(x *Sparse) bool {
if s == x {
return false
}
var changed bool
xb := x.start()
sb := s.start()
for xb != &x.root {
if sb != &s.root && sb.offset == xb.offset {
for i := range xb.bits {
if sb.bits[i] != xb.bits[i] {
sb.bits[i] |= xb.bits[i]
changed = true
}
}
xb = xb.next
} else if sb == &s.root || sb.offset > xb.offset {
sb = s.insertBlockBefore(sb)
sb.offset = xb.offset
sb.bits = xb.bits
changed = true
xb = xb.next
}
sb = sb.next
}
return changed
}
// Union sets s to the union x y.
func (s *Sparse) Union(x, y *Sparse) {
switch {
case x == y:
s.Copy(x)
return
case s == x:
s.UnionWith(y)
return
case s == y:
s.UnionWith(x)
return
}
xb := x.start()
yb := y.start()
sb := s.start()
for xb != &x.root || yb != &y.root {
if sb == &s.root {
sb = s.insertBlockBefore(sb)
}
switch {
case yb == &y.root || (xb != &x.root && xb.offset < yb.offset):
sb.offset = xb.offset
sb.bits = xb.bits
xb = xb.next
case xb == &x.root || (yb != &y.root && yb.offset < xb.offset):
sb.offset = yb.offset
sb.bits = yb.bits
yb = yb.next
default:
sb.offset = xb.offset
for i := range xb.bits {
sb.bits[i] = xb.bits[i] | yb.bits[i]
}
xb = xb.next
yb = yb.next
}
sb = sb.next
}
s.discardTail(sb)
}
// DifferenceWith sets s to the difference s x.
func (s *Sparse) DifferenceWith(x *Sparse) {
if s == x {
s.Clear()
return
}
xb := x.start()
sb := s.start()
for xb != &x.root && sb != &s.root {
switch {
case xb.offset > sb.offset:
sb = sb.next
case xb.offset < sb.offset:
xb = xb.next
default:
var sum word
for i := range sb.bits {
r := sb.bits[i] & ^xb.bits[i]
sb.bits[i] = r
sum |= r
}
sb = sb.next
xb = xb.next
if sum == 0 {
s.removeBlock(sb.prev)
}
}
}
}
// Difference sets s to the difference x y.
func (s *Sparse) Difference(x, y *Sparse) {
switch {
case x == y:
s.Clear()
return
case s == x:
s.DifferenceWith(y)
return
case s == y:
var y2 Sparse
y2.Copy(y)
s.Difference(x, &y2)
return
}
xb := x.start()
yb := y.start()
sb := s.start()
for xb != &x.root && yb != &y.root {
if xb.offset > yb.offset {
// y has block, x has none
yb = yb.next
continue
}
if sb == &s.root {
sb = s.insertBlockBefore(sb)
}
sb.offset = xb.offset
switch {
case xb.offset < yb.offset:
// x has block, y has none
sb.bits = xb.bits
sb = sb.next
default:
// x and y have corresponding blocks
var sum word
for i := range sb.bits {
r := xb.bits[i] & ^yb.bits[i]
sb.bits[i] = r
sum |= r
}
if sum != 0 {
sb = sb.next
} else {
// sb will be overwritten or removed
}
yb = yb.next
}
xb = xb.next
}
for xb != &x.root {
if sb == &s.root {
sb = s.insertBlockBefore(sb)
}
sb.offset = xb.offset
sb.bits = xb.bits
sb = sb.next
xb = xb.next
}
s.discardTail(sb)
}
// SymmetricDifferenceWith sets s to the symmetric difference s ∆ x.
func (s *Sparse) SymmetricDifferenceWith(x *Sparse) {
if s == x {
s.Clear()
return
}
sb := s.start()
xb := x.start()
for xb != &x.root && sb != &s.root {
switch {
case sb.offset < xb.offset:
sb = sb.next
case xb.offset < sb.offset:
nb := s.insertBlockBefore(sb)
nb.offset = xb.offset
nb.bits = xb.bits
xb = xb.next
default:
var sum word
for i := range sb.bits {
r := sb.bits[i] ^ xb.bits[i]
sb.bits[i] = r
sum |= r
}
sb = sb.next
xb = xb.next
if sum == 0 {
s.removeBlock(sb.prev)
}
}
}
for xb != &x.root { // append the tail of x to s
sb = s.insertBlockBefore(sb)
sb.offset = xb.offset
sb.bits = xb.bits
sb = sb.next
xb = xb.next
}
}
// SymmetricDifference sets s to the symmetric difference x ∆ y.
func (s *Sparse) SymmetricDifference(x, y *Sparse) {
switch {
case x == y:
s.Clear()
return
case s == x:
s.SymmetricDifferenceWith(y)
return
case s == y:
s.SymmetricDifferenceWith(x)
return
}
sb := s.start()
xb := x.start()
yb := y.start()
for xb != &x.root && yb != &y.root {
if sb == &s.root {
sb = s.insertBlockBefore(sb)
}
switch {
case yb.offset < xb.offset:
sb.offset = yb.offset
sb.bits = yb.bits
sb = sb.next
yb = yb.next
case xb.offset < yb.offset:
sb.offset = xb.offset
sb.bits = xb.bits
sb = sb.next
xb = xb.next
default:
var sum word
for i := range sb.bits {
r := xb.bits[i] ^ yb.bits[i]
sb.bits[i] = r
sum |= r
}
if sum != 0 {
sb.offset = xb.offset
sb = sb.next
}
xb = xb.next
yb = yb.next
}
}
for xb != &x.root { // append the tail of x to s
if sb == &s.root {
sb = s.insertBlockBefore(sb)
}
sb.offset = xb.offset
sb.bits = xb.bits
sb = sb.next
xb = xb.next
}
for yb != &y.root { // append the tail of y to s
if sb == &s.root {
sb = s.insertBlockBefore(sb)
}
sb.offset = yb.offset
sb.bits = yb.bits
sb = sb.next
yb = yb.next
}
s.discardTail(sb)
}
// SubsetOf reports whether s x = ∅.
func (s *Sparse) SubsetOf(x *Sparse) bool {
if s == x {
return true
}
sb := s.start()
xb := x.start()
for sb != &s.root {
switch {
case xb == &x.root || xb.offset > sb.offset:
return false
case xb.offset < sb.offset:
xb = xb.next
default:
for i := range sb.bits {
if sb.bits[i]&^xb.bits[i] != 0 {
return false
}
}
sb = sb.next
xb = xb.next
}
}
return true
}
// Equals reports whether the sets s and t have the same elements.
func (s *Sparse) Equals(t *Sparse) bool {
if s == t {
return true
}
sb := s.start()
tb := t.start()
for {
switch {
case sb == &s.root && tb == &t.root:
return true
case sb == &s.root || tb == &t.root:
return false
case sb.offset != tb.offset:
return false
case sb.bits != tb.bits:
return false
}
sb = sb.next
tb = tb.next
}
}
// String returns a human-readable description of the set s.
func (s *Sparse) String() string {
var buf bytes.Buffer
buf.WriteByte('{')
s.forEach(func(x int) {
if buf.Len() > 1 {
buf.WriteByte(' ')
}
fmt.Fprintf(&buf, "%d", x)
})
buf.WriteByte('}')
return buf.String()
}
// BitString returns the set as a string of 1s and 0s denoting the sum
// of the i'th powers of 2, for each i in s. A radix point, always
// preceded by a digit, appears if the sum is non-integral.
//
// Examples:
// {}.BitString() = "0"
// {4,5}.BitString() = "110000"
// {-3}.BitString() = "0.001"
// {-3,0,4,5}.BitString() = "110001.001"
//
func (s *Sparse) BitString() string {
if s.IsEmpty() {
return "0"
}
min, max := s.Min(), s.Max()
var nbytes int
if max > 0 {
nbytes = max
}
nbytes++ // zero bit
radix := nbytes
if min < 0 {
nbytes += len(".") - min
}
b := make([]byte, nbytes)
for i := range b {
b[i] = '0'
}
if radix < nbytes {
b[radix] = '.'
}
s.forEach(func(x int) {
if x >= 0 {
x += len(".")
}
b[radix-x] = '1'
})
return string(b)
}
// GoString returns a string showing the internal representation of
// the set s.
//
func (s *Sparse) GoString() string {
var buf bytes.Buffer
for b := s.start(); b != &s.root; b = b.next {
fmt.Fprintf(&buf, "block %p {offset=%d next=%p prev=%p",
b, b.offset, b.next, b.prev)
for _, w := range b.bits {
fmt.Fprintf(&buf, " 0%016x", w)
}
fmt.Fprintf(&buf, "}\n")
}
return buf.String()
}
// AppendTo returns the result of appending the elements of s to slice
// in order.
func (s *Sparse) AppendTo(slice []int) []int {
s.forEach(func(x int) {
slice = append(slice, x)
})
return slice
}
// -- Testing/debugging ------------------------------------------------
// check returns an error if the representation invariants of s are violated.
func (s *Sparse) check() error {
if !s.root.empty() {
return fmt.Errorf("non-empty root block")
}
if s.root.offset != 0 {
return fmt.Errorf("root block has non-zero offset %d", s.root.offset)
}
for b := s.start(); b != &s.root; b = b.next {
if b.offset%bitsPerBlock != 0 {
return fmt.Errorf("bad offset modulo: %d", b.offset)
}
if b.empty() {
return fmt.Errorf("empty block")
}
if b.prev.next != b {
return fmt.Errorf("bad prev.next link")
}
if b.next.prev != b {
return fmt.Errorf("bad next.prev link")
}
if b.prev != &s.root {
if b.offset <= b.prev.offset {
return fmt.Errorf("bad offset order: b.offset=%d, prev.offset=%d",
b.offset, b.prev.offset)
}
}
}
return nil
}

75
vendor/golang.org/x/tools/container/intsets/util.go generated vendored Normal file
View File

@ -0,0 +1,75 @@
// Copyright 2013 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 intsets
var a [1 << 8]byte
func init() {
for i := range a {
var n byte
for x := i; x != 0; x >>= 1 {
if x&1 != 0 {
n++
}
}
a[i] = n
}
}
// popcount returns the population count (number of set bits) of x.
func popcount(x word) int {
return int(a[byte(x>>(0*8))] +
a[byte(x>>(1*8))] +
a[byte(x>>(2*8))] +
a[byte(x>>(3*8))] +
a[byte(x>>(4*8))] +
a[byte(x>>(5*8))] +
a[byte(x>>(6*8))] +
a[byte(x>>(7*8))])
}
// nlz returns the number of leading zeros of x.
// From Hacker's Delight, fig 5.11.
func nlz(x word) int {
x |= (x >> 1)
x |= (x >> 2)
x |= (x >> 4)
x |= (x >> 8)
x |= (x >> 16)
x |= (x >> 32)
return popcount(^x)
}
// ntz returns the number of trailing zeros of x.
// From Hacker's Delight, fig 5.13.
func ntz(x word) int {
if x == 0 {
return bitsPerWord
}
n := 1
if bitsPerWord == 64 {
if (x & 0xffffffff) == 0 {
n = n + 32
x = x >> 32
}
}
if (x & 0x0000ffff) == 0 {
n = n + 16
x = x >> 16
}
if (x & 0x000000ff) == 0 {
n = n + 8
x = x >> 8
}
if (x & 0x0000000f) == 0 {
n = n + 4
x = x >> 4
}
if (x & 0x00000003) == 0 {
n = n + 2
x = x >> 2
}
return n - int(x&1)
}

625
vendor/golang.org/x/tools/go/ast/astutil/enclosing.go generated vendored Normal file
View File

@ -0,0 +1,625 @@
// Copyright 2013 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 astutil
// This file defines utilities for working with source positions.
import (
"fmt"
"go/ast"
"go/token"
"sort"
)
// PathEnclosingInterval returns the node that encloses the source
// interval [start, end), and all its ancestors up to the AST root.
//
// The definition of "enclosing" used by this function considers
// additional whitespace abutting a node to be enclosed by it.
// In this example:
//
// z := x + y // add them
// <-A->
// <----B----->
//
// the ast.BinaryExpr(+) node is considered to enclose interval B
// even though its [Pos()..End()) is actually only interval A.
// This behaviour makes user interfaces more tolerant of imperfect
// input.
//
// This function treats tokens as nodes, though they are not included
// in the result. e.g. PathEnclosingInterval("+") returns the
// enclosing ast.BinaryExpr("x + y").
//
// If start==end, the 1-char interval following start is used instead.
//
// The 'exact' result is true if the interval contains only path[0]
// and perhaps some adjacent whitespace. It is false if the interval
// overlaps multiple children of path[0], or if it contains only
// interior whitespace of path[0].
// In this example:
//
// z := x + y // add them
// <--C--> <---E-->
// ^
// D
//
// intervals C, D and E are inexact. C is contained by the
// z-assignment statement, because it spans three of its children (:=,
// x, +). So too is the 1-char interval D, because it contains only
// interior whitespace of the assignment. E is considered interior
// whitespace of the BlockStmt containing the assignment.
//
// Precondition: [start, end) both lie within the same file as root.
// TODO(adonovan): return (nil, false) in this case and remove precond.
// Requires FileSet; see loader.tokenFileContainsPos.
//
// Postcondition: path is never nil; it always contains at least 'root'.
//
func PathEnclosingInterval(root *ast.File, start, end token.Pos) (path []ast.Node, exact bool) {
// fmt.Printf("EnclosingInterval %d %d\n", start, end) // debugging
// Precondition: node.[Pos..End) and adjoining whitespace contain [start, end).
var visit func(node ast.Node) bool
visit = func(node ast.Node) bool {
path = append(path, node)
nodePos := node.Pos()
nodeEnd := node.End()
// fmt.Printf("visit(%T, %d, %d)\n", node, nodePos, nodeEnd) // debugging
// Intersect [start, end) with interval of node.
if start < nodePos {
start = nodePos
}
if end > nodeEnd {
end = nodeEnd
}
// Find sole child that contains [start, end).
children := childrenOf(node)
l := len(children)
for i, child := range children {
// [childPos, childEnd) is unaugmented interval of child.
childPos := child.Pos()
childEnd := child.End()
// [augPos, augEnd) is whitespace-augmented interval of child.
augPos := childPos
augEnd := childEnd
if i > 0 {
augPos = children[i-1].End() // start of preceding whitespace
}
if i < l-1 {
nextChildPos := children[i+1].Pos()
// Does [start, end) lie between child and next child?
if start >= augEnd && end <= nextChildPos {
return false // inexact match
}
augEnd = nextChildPos // end of following whitespace
}
// fmt.Printf("\tchild %d: [%d..%d)\tcontains interval [%d..%d)?\n",
// i, augPos, augEnd, start, end) // debugging
// Does augmented child strictly contain [start, end)?
if augPos <= start && end <= augEnd {
_, isToken := child.(tokenNode)
return isToken || visit(child)
}
// Does [start, end) overlap multiple children?
// i.e. left-augmented child contains start
// but LR-augmented child does not contain end.
if start < childEnd && end > augEnd {
break
}
}
// No single child contained [start, end),
// so node is the result. Is it exact?
// (It's tempting to put this condition before the
// child loop, but it gives the wrong result in the
// case where a node (e.g. ExprStmt) and its sole
// child have equal intervals.)
if start == nodePos && end == nodeEnd {
return true // exact match
}
return false // inexact: overlaps multiple children
}
if start > end {
start, end = end, start
}
if start < root.End() && end > root.Pos() {
if start == end {
end = start + 1 // empty interval => interval of size 1
}
exact = visit(root)
// Reverse the path:
for i, l := 0, len(path); i < l/2; i++ {
path[i], path[l-1-i] = path[l-1-i], path[i]
}
} else {
// Selection lies within whitespace preceding the
// first (or following the last) declaration in the file.
// The result nonetheless always includes the ast.File.
path = append(path, root)
}
return
}
// tokenNode is a dummy implementation of ast.Node for a single token.
// They are used transiently by PathEnclosingInterval but never escape
// this package.
//
type tokenNode struct {
pos token.Pos
end token.Pos
}
func (n tokenNode) Pos() token.Pos {
return n.pos
}
func (n tokenNode) End() token.Pos {
return n.end
}
func tok(pos token.Pos, len int) ast.Node {
return tokenNode{pos, pos + token.Pos(len)}
}
// childrenOf returns the direct non-nil children of ast.Node n.
// It may include fake ast.Node implementations for bare tokens.
// it is not safe to call (e.g.) ast.Walk on such nodes.
//
func childrenOf(n ast.Node) []ast.Node {
var children []ast.Node
// First add nodes for all true subtrees.
ast.Inspect(n, func(node ast.Node) bool {
if node == n { // push n
return true // recur
}
if node != nil { // push child
children = append(children, node)
}
return false // no recursion
})
// Then add fake Nodes for bare tokens.
switch n := n.(type) {
case *ast.ArrayType:
children = append(children,
tok(n.Lbrack, len("[")),
tok(n.Elt.End(), len("]")))
case *ast.AssignStmt:
children = append(children,
tok(n.TokPos, len(n.Tok.String())))
case *ast.BasicLit:
children = append(children,
tok(n.ValuePos, len(n.Value)))
case *ast.BinaryExpr:
children = append(children, tok(n.OpPos, len(n.Op.String())))
case *ast.BlockStmt:
children = append(children,
tok(n.Lbrace, len("{")),
tok(n.Rbrace, len("}")))
case *ast.BranchStmt:
children = append(children,
tok(n.TokPos, len(n.Tok.String())))
case *ast.CallExpr:
children = append(children,
tok(n.Lparen, len("(")),
tok(n.Rparen, len(")")))
if n.Ellipsis != 0 {
children = append(children, tok(n.Ellipsis, len("...")))
}
case *ast.CaseClause:
if n.List == nil {
children = append(children,
tok(n.Case, len("default")))
} else {
children = append(children,
tok(n.Case, len("case")))
}
children = append(children, tok(n.Colon, len(":")))
case *ast.ChanType:
switch n.Dir {
case ast.RECV:
children = append(children, tok(n.Begin, len("<-chan")))
case ast.SEND:
children = append(children, tok(n.Begin, len("chan<-")))
case ast.RECV | ast.SEND:
children = append(children, tok(n.Begin, len("chan")))
}
case *ast.CommClause:
if n.Comm == nil {
children = append(children,
tok(n.Case, len("default")))
} else {
children = append(children,
tok(n.Case, len("case")))
}
children = append(children, tok(n.Colon, len(":")))
case *ast.Comment:
// nop
case *ast.CommentGroup:
// nop
case *ast.CompositeLit:
children = append(children,
tok(n.Lbrace, len("{")),
tok(n.Rbrace, len("{")))
case *ast.DeclStmt:
// nop
case *ast.DeferStmt:
children = append(children,
tok(n.Defer, len("defer")))
case *ast.Ellipsis:
children = append(children,
tok(n.Ellipsis, len("...")))
case *ast.EmptyStmt:
// nop
case *ast.ExprStmt:
// nop
case *ast.Field:
// TODO(adonovan): Field.{Doc,Comment,Tag}?
case *ast.FieldList:
children = append(children,
tok(n.Opening, len("(")),
tok(n.Closing, len(")")))
case *ast.File:
// TODO test: Doc
children = append(children,
tok(n.Package, len("package")))
case *ast.ForStmt:
children = append(children,
tok(n.For, len("for")))
case *ast.FuncDecl:
// TODO(adonovan): FuncDecl.Comment?
// Uniquely, FuncDecl breaks the invariant that
// preorder traversal yields tokens in lexical order:
// in fact, FuncDecl.Recv precedes FuncDecl.Type.Func.
//
// As a workaround, we inline the case for FuncType
// here and order things correctly.
//
children = nil // discard ast.Walk(FuncDecl) info subtrees
children = append(children, tok(n.Type.Func, len("func")))
if n.Recv != nil {
children = append(children, n.Recv)
}
children = append(children, n.Name)
if n.Type.Params != nil {
children = append(children, n.Type.Params)
}
if n.Type.Results != nil {
children = append(children, n.Type.Results)
}
if n.Body != nil {
children = append(children, n.Body)
}
case *ast.FuncLit:
// nop
case *ast.FuncType:
if n.Func != 0 {
children = append(children,
tok(n.Func, len("func")))
}
case *ast.GenDecl:
children = append(children,
tok(n.TokPos, len(n.Tok.String())))
if n.Lparen != 0 {
children = append(children,
tok(n.Lparen, len("(")),
tok(n.Rparen, len(")")))
}
case *ast.GoStmt:
children = append(children,
tok(n.Go, len("go")))
case *ast.Ident:
children = append(children,
tok(n.NamePos, len(n.Name)))
case *ast.IfStmt:
children = append(children,
tok(n.If, len("if")))
case *ast.ImportSpec:
// TODO(adonovan): ImportSpec.{Doc,EndPos}?
case *ast.IncDecStmt:
children = append(children,
tok(n.TokPos, len(n.Tok.String())))
case *ast.IndexExpr:
children = append(children,
tok(n.Lbrack, len("{")),
tok(n.Rbrack, len("}")))
case *ast.InterfaceType:
children = append(children,
tok(n.Interface, len("interface")))
case *ast.KeyValueExpr:
children = append(children,
tok(n.Colon, len(":")))
case *ast.LabeledStmt:
children = append(children,
tok(n.Colon, len(":")))
case *ast.MapType:
children = append(children,
tok(n.Map, len("map")))
case *ast.ParenExpr:
children = append(children,
tok(n.Lparen, len("(")),
tok(n.Rparen, len(")")))
case *ast.RangeStmt:
children = append(children,
tok(n.For, len("for")),
tok(n.TokPos, len(n.Tok.String())))
case *ast.ReturnStmt:
children = append(children,
tok(n.Return, len("return")))
case *ast.SelectStmt:
children = append(children,
tok(n.Select, len("select")))
case *ast.SelectorExpr:
// nop
case *ast.SendStmt:
children = append(children,
tok(n.Arrow, len("<-")))
case *ast.SliceExpr:
children = append(children,
tok(n.Lbrack, len("[")),
tok(n.Rbrack, len("]")))
case *ast.StarExpr:
children = append(children, tok(n.Star, len("*")))
case *ast.StructType:
children = append(children, tok(n.Struct, len("struct")))
case *ast.SwitchStmt:
children = append(children, tok(n.Switch, len("switch")))
case *ast.TypeAssertExpr:
children = append(children,
tok(n.Lparen-1, len(".")),
tok(n.Lparen, len("(")),
tok(n.Rparen, len(")")))
case *ast.TypeSpec:
// TODO(adonovan): TypeSpec.{Doc,Comment}?
case *ast.TypeSwitchStmt:
children = append(children, tok(n.Switch, len("switch")))
case *ast.UnaryExpr:
children = append(children, tok(n.OpPos, len(n.Op.String())))
case *ast.ValueSpec:
// TODO(adonovan): ValueSpec.{Doc,Comment}?
default:
// Includes *ast.BadDecl, *ast.BadExpr, *ast.BadStmt.
panic(fmt.Sprintf("unexpected node type %T", n))
}
// TODO(adonovan): opt: merge the logic of ast.Inspect() into
// the switch above so we can make interleaved callbacks for
// both Nodes and Tokens in the right order and avoid the need
// to sort.
sort.Sort(byPos(children))
return children
}
type byPos []ast.Node
func (sl byPos) Len() int {
return len(sl)
}
func (sl byPos) Less(i, j int) bool {
return sl[i].Pos() < sl[j].Pos()
}
func (sl byPos) Swap(i, j int) {
sl[i], sl[j] = sl[j], sl[i]
}
// NodeDescription returns a description of the concrete type of n suitable
// for a user interface.
//
// TODO(adonovan): in some cases (e.g. Field, FieldList, Ident,
// StarExpr) we could be much more specific given the path to the AST
// root. Perhaps we should do that.
//
func NodeDescription(n ast.Node) string {
switch n := n.(type) {
case *ast.ArrayType:
return "array type"
case *ast.AssignStmt:
return "assignment"
case *ast.BadDecl:
return "bad declaration"
case *ast.BadExpr:
return "bad expression"
case *ast.BadStmt:
return "bad statement"
case *ast.BasicLit:
return "basic literal"
case *ast.BinaryExpr:
return fmt.Sprintf("binary %s operation", n.Op)
case *ast.BlockStmt:
return "block"
case *ast.BranchStmt:
switch n.Tok {
case token.BREAK:
return "break statement"
case token.CONTINUE:
return "continue statement"
case token.GOTO:
return "goto statement"
case token.FALLTHROUGH:
return "fall-through statement"
}
case *ast.CallExpr:
return "function call (or conversion)"
case *ast.CaseClause:
return "case clause"
case *ast.ChanType:
return "channel type"
case *ast.CommClause:
return "communication clause"
case *ast.Comment:
return "comment"
case *ast.CommentGroup:
return "comment group"
case *ast.CompositeLit:
return "composite literal"
case *ast.DeclStmt:
return NodeDescription(n.Decl) + " statement"
case *ast.DeferStmt:
return "defer statement"
case *ast.Ellipsis:
return "ellipsis"
case *ast.EmptyStmt:
return "empty statement"
case *ast.ExprStmt:
return "expression statement"
case *ast.Field:
// Can be any of these:
// struct {x, y int} -- struct field(s)
// struct {T} -- anon struct field
// interface {I} -- interface embedding
// interface {f()} -- interface method
// func (A) func(B) C -- receiver, param(s), result(s)
return "field/method/parameter"
case *ast.FieldList:
return "field/method/parameter list"
case *ast.File:
return "source file"
case *ast.ForStmt:
return "for loop"
case *ast.FuncDecl:
return "function declaration"
case *ast.FuncLit:
return "function literal"
case *ast.FuncType:
return "function type"
case *ast.GenDecl:
switch n.Tok {
case token.IMPORT:
return "import declaration"
case token.CONST:
return "constant declaration"
case token.TYPE:
return "type declaration"
case token.VAR:
return "variable declaration"
}
case *ast.GoStmt:
return "go statement"
case *ast.Ident:
return "identifier"
case *ast.IfStmt:
return "if statement"
case *ast.ImportSpec:
return "import specification"
case *ast.IncDecStmt:
if n.Tok == token.INC {
return "increment statement"
}
return "decrement statement"
case *ast.IndexExpr:
return "index expression"
case *ast.InterfaceType:
return "interface type"
case *ast.KeyValueExpr:
return "key/value association"
case *ast.LabeledStmt:
return "statement label"
case *ast.MapType:
return "map type"
case *ast.Package:
return "package"
case *ast.ParenExpr:
return "parenthesized " + NodeDescription(n.X)
case *ast.RangeStmt:
return "range loop"
case *ast.ReturnStmt:
return "return statement"
case *ast.SelectStmt:
return "select statement"
case *ast.SelectorExpr:
return "selector"
case *ast.SendStmt:
return "channel send"
case *ast.SliceExpr:
return "slice expression"
case *ast.StarExpr:
return "*-operation" // load/store expr or pointer type
case *ast.StructType:
return "struct type"
case *ast.SwitchStmt:
return "switch statement"
case *ast.TypeAssertExpr:
return "type assertion"
case *ast.TypeSpec:
return "type specification"
case *ast.TypeSwitchStmt:
return "type switch"
case *ast.UnaryExpr:
return fmt.Sprintf("unary %s operation", n.Op)
case *ast.ValueSpec:
return "value specification"
}
panic(fmt.Sprintf("unexpected node type: %T", n))
}

359
vendor/golang.org/x/tools/go/ast/astutil/imports.go generated vendored Normal file
View File

@ -0,0 +1,359 @@
// Copyright 2013 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 astutil contains common utilities for working with the Go AST.
package astutil // import "golang.org/x/tools/go/ast/astutil"
import (
"fmt"
"go/ast"
"go/token"
"strconv"
"strings"
)
// AddImport adds the import path to the file f, if absent.
func AddImport(fset *token.FileSet, f *ast.File, ipath string) (added bool) {
return AddNamedImport(fset, f, "", ipath)
}
// AddNamedImport adds the import path to the file f, if absent.
// If name is not empty, it is used to rename the import.
//
// For example, calling
// AddNamedImport(fset, f, "pathpkg", "path")
// adds
// import pathpkg "path"
func AddNamedImport(fset *token.FileSet, f *ast.File, name, ipath string) (added bool) {
if imports(f, ipath) {
return false
}
newImport := &ast.ImportSpec{
Path: &ast.BasicLit{
Kind: token.STRING,
Value: strconv.Quote(ipath),
},
}
if name != "" {
newImport.Name = &ast.Ident{Name: name}
}
// Find an import decl to add to.
// The goal is to find an existing import
// whose import path has the longest shared
// prefix with ipath.
var (
bestMatch = -1 // length of longest shared prefix
lastImport = -1 // index in f.Decls of the file's final import decl
impDecl *ast.GenDecl // import decl containing the best match
impIndex = -1 // spec index in impDecl containing the best match
)
for i, decl := range f.Decls {
gen, ok := decl.(*ast.GenDecl)
if ok && gen.Tok == token.IMPORT {
lastImport = i
// Do not add to import "C", to avoid disrupting the
// association with its doc comment, breaking cgo.
if declImports(gen, "C") {
continue
}
// Match an empty import decl if that's all that is available.
if len(gen.Specs) == 0 && bestMatch == -1 {
impDecl = gen
}
// Compute longest shared prefix with imports in this group.
for j, spec := range gen.Specs {
impspec := spec.(*ast.ImportSpec)
n := matchLen(importPath(impspec), ipath)
if n > bestMatch {
bestMatch = n
impDecl = gen
impIndex = j
}
}
}
}
// If no import decl found, add one after the last import.
if impDecl == nil {
impDecl = &ast.GenDecl{
Tok: token.IMPORT,
}
if lastImport >= 0 {
impDecl.TokPos = f.Decls[lastImport].End()
} else {
// There are no existing imports.
// Our new import goes after the package declaration and after
// the comment, if any, that starts on the same line as the
// package declaration.
impDecl.TokPos = f.Package
file := fset.File(f.Package)
pkgLine := file.Line(f.Package)
for _, c := range f.Comments {
if file.Line(c.Pos()) > pkgLine {
break
}
impDecl.TokPos = c.End()
}
}
f.Decls = append(f.Decls, nil)
copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:])
f.Decls[lastImport+1] = impDecl
}
// Insert new import at insertAt.
insertAt := 0
if impIndex >= 0 {
// insert after the found import
insertAt = impIndex + 1
}
impDecl.Specs = append(impDecl.Specs, nil)
copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:])
impDecl.Specs[insertAt] = newImport
pos := impDecl.Pos()
if insertAt > 0 {
// Assign same position as the previous import,
// so that the sorter sees it as being in the same block.
pos = impDecl.Specs[insertAt-1].Pos()
}
if newImport.Name != nil {
newImport.Name.NamePos = pos
}
newImport.Path.ValuePos = pos
newImport.EndPos = pos
// Clean up parens. impDecl contains at least one spec.
if len(impDecl.Specs) == 1 {
// Remove unneeded parens.
impDecl.Lparen = token.NoPos
} else if !impDecl.Lparen.IsValid() {
// impDecl needs parens added.
impDecl.Lparen = impDecl.Specs[0].Pos()
}
f.Imports = append(f.Imports, newImport)
return true
}
// DeleteImport deletes the import path from the file f, if present.
func DeleteImport(fset *token.FileSet, f *ast.File, path string) (deleted bool) {
var delspecs []*ast.ImportSpec
// Find the import nodes that import path, if any.
for i := 0; i < len(f.Decls); i++ {
decl := f.Decls[i]
gen, ok := decl.(*ast.GenDecl)
if !ok || gen.Tok != token.IMPORT {
continue
}
for j := 0; j < len(gen.Specs); j++ {
spec := gen.Specs[j]
impspec := spec.(*ast.ImportSpec)
if importPath(impspec) != path {
continue
}
// We found an import spec that imports path.
// Delete it.
delspecs = append(delspecs, impspec)
deleted = true
copy(gen.Specs[j:], gen.Specs[j+1:])
gen.Specs = gen.Specs[:len(gen.Specs)-1]
// If this was the last import spec in this decl,
// delete the decl, too.
if len(gen.Specs) == 0 {
copy(f.Decls[i:], f.Decls[i+1:])
f.Decls = f.Decls[:len(f.Decls)-1]
i--
break
} else if len(gen.Specs) == 1 {
gen.Lparen = token.NoPos // drop parens
}
if j > 0 {
lastImpspec := gen.Specs[j-1].(*ast.ImportSpec)
lastLine := fset.Position(lastImpspec.Path.ValuePos).Line
line := fset.Position(impspec.Path.ValuePos).Line
// We deleted an entry but now there may be
// a blank line-sized hole where the import was.
if line-lastLine > 1 {
// There was a blank line immediately preceding the deleted import,
// so there's no need to close the hole.
// Do nothing.
} else {
// There was no blank line. Close the hole.
fset.File(gen.Rparen).MergeLine(line)
}
}
j--
}
}
// Delete them from f.Imports.
for i := 0; i < len(f.Imports); i++ {
imp := f.Imports[i]
for j, del := range delspecs {
if imp == del {
copy(f.Imports[i:], f.Imports[i+1:])
f.Imports = f.Imports[:len(f.Imports)-1]
copy(delspecs[j:], delspecs[j+1:])
delspecs = delspecs[:len(delspecs)-1]
i--
break
}
}
}
if len(delspecs) > 0 {
panic(fmt.Sprintf("deleted specs from Decls but not Imports: %v", delspecs))
}
return
}
// RewriteImport rewrites any import of path oldPath to path newPath.
func RewriteImport(fset *token.FileSet, f *ast.File, oldPath, newPath string) (rewrote bool) {
for _, imp := range f.Imports {
if importPath(imp) == oldPath {
rewrote = true
// record old End, because the default is to compute
// it using the length of imp.Path.Value.
imp.EndPos = imp.End()
imp.Path.Value = strconv.Quote(newPath)
}
}
return
}
// UsesImport reports whether a given import is used.
func UsesImport(f *ast.File, path string) (used bool) {
spec := importSpec(f, path)
if spec == nil {
return
}
name := spec.Name.String()
switch name {
case "<nil>":
// If the package name is not explicitly specified,
// make an educated guess. This is not guaranteed to be correct.
lastSlash := strings.LastIndex(path, "/")
if lastSlash == -1 {
name = path
} else {
name = path[lastSlash+1:]
}
case "_", ".":
// Not sure if this import is used - err on the side of caution.
return true
}
ast.Walk(visitFn(func(n ast.Node) {
sel, ok := n.(*ast.SelectorExpr)
if ok && isTopName(sel.X, name) {
used = true
}
}), f)
return
}
type visitFn func(node ast.Node)
func (fn visitFn) Visit(node ast.Node) ast.Visitor {
fn(node)
return fn
}
// imports returns true if f imports path.
func imports(f *ast.File, path string) bool {
return importSpec(f, path) != nil
}
// importSpec returns the import spec if f imports path,
// or nil otherwise.
func importSpec(f *ast.File, path string) *ast.ImportSpec {
for _, s := range f.Imports {
if importPath(s) == path {
return s
}
}
return nil
}
// importPath returns the unquoted import path of s,
// or "" if the path is not properly quoted.
func importPath(s *ast.ImportSpec) string {
t, err := strconv.Unquote(s.Path.Value)
if err == nil {
return t
}
return ""
}
// declImports reports whether gen contains an import of path.
func declImports(gen *ast.GenDecl, path string) bool {
if gen.Tok != token.IMPORT {
return false
}
for _, spec := range gen.Specs {
impspec := spec.(*ast.ImportSpec)
if importPath(impspec) == path {
return true
}
}
return false
}
// matchLen returns the length of the longest path segment prefix shared by x and y.
func matchLen(x, y string) int {
n := 0
for i := 0; i < len(x) && i < len(y) && x[i] == y[i]; i++ {
if x[i] == '/' {
n++
}
}
return n
}
// isTopName returns true if n is a top-level unresolved identifier with the given name.
func isTopName(n ast.Expr, name string) bool {
id, ok := n.(*ast.Ident)
return ok && id.Name == name && id.Obj == nil
}
// Imports returns the file imports grouped by paragraph.
func Imports(fset *token.FileSet, f *ast.File) [][]*ast.ImportSpec {
var groups [][]*ast.ImportSpec
for _, decl := range f.Decls {
genDecl, ok := decl.(*ast.GenDecl)
if !ok || genDecl.Tok != token.IMPORT {
break
}
group := []*ast.ImportSpec{}
var lastLine int
for _, spec := range genDecl.Specs {
importSpec := spec.(*ast.ImportSpec)
pos := importSpec.Path.ValuePos
line := fset.Position(pos).Line
if lastLine > 0 && pos > 0 && line-lastLine > 1 {
groups = append(groups, group)
group = []*ast.ImportSpec{}
}
group = append(group, importSpec)
lastLine = line
}
groups = append(groups, group)
}
return groups
}

14
vendor/golang.org/x/tools/go/ast/astutil/util.go generated vendored Normal file
View File

@ -0,0 +1,14 @@
package astutil
import "go/ast"
// Unparen returns e with any enclosing parentheses stripped.
func Unparen(e ast.Expr) ast.Expr {
for {
p, ok := e.(*ast.ParenExpr)
if !ok {
return e
}
e = p.X
}
}

123
vendor/golang.org/x/tools/go/buildutil/allpackages.go generated vendored Normal file
View File

@ -0,0 +1,123 @@
// Copyright 2014 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 buildutil provides utilities related to the go/build
// package in the standard library.
//
// All I/O is done via the build.Context file system interface, which must
// be concurrency-safe.
package buildutil // import "golang.org/x/tools/go/buildutil"
import (
"go/build"
"os"
"path/filepath"
"sort"
"strings"
"sync"
)
// AllPackages returns the import path of each Go package in any source
// directory of the specified build context (e.g. $GOROOT or an element
// of $GOPATH). Errors are ignored. The results are sorted.
//
// The result may include import paths for directories that contain no
// *.go files, such as "archive" (in $GOROOT/src).
//
// All I/O is done via the build.Context file system interface,
// which must be concurrency-safe.
//
func AllPackages(ctxt *build.Context) []string {
var list []string
ForEachPackage(ctxt, func(pkg string, _ error) {
list = append(list, pkg)
})
sort.Strings(list)
return list
}
// ForEachPackage calls the found function with the import path of
// each Go package it finds in any source directory of the specified
// build context (e.g. $GOROOT or an element of $GOPATH).
//
// If the package directory exists but could not be read, the second
// argument to the found function provides the error.
//
// All I/O is done via the build.Context file system interface,
// which must be concurrency-safe.
//
func ForEachPackage(ctxt *build.Context, found func(importPath string, err error)) {
// We use a counting semaphore to limit
// the number of parallel calls to ReadDir.
sema := make(chan bool, 20)
ch := make(chan item)
var wg sync.WaitGroup
for _, root := range ctxt.SrcDirs() {
root := root
wg.Add(1)
go func() {
allPackages(ctxt, sema, root, ch)
wg.Done()
}()
}
go func() {
wg.Wait()
close(ch)
}()
// All calls to found occur in the caller's goroutine.
for i := range ch {
found(i.importPath, i.err)
}
}
type item struct {
importPath string
err error // (optional)
}
func allPackages(ctxt *build.Context, sema chan bool, root string, ch chan<- item) {
root = filepath.Clean(root) + string(os.PathSeparator)
var wg sync.WaitGroup
var walkDir func(dir string)
walkDir = func(dir string) {
// Avoid .foo, _foo, and testdata directory trees.
base := filepath.Base(dir)
if base == "" || base[0] == '.' || base[0] == '_' || base == "testdata" {
return
}
pkg := filepath.ToSlash(strings.TrimPrefix(dir, root))
// Prune search if we encounter any of these import paths.
switch pkg {
case "builtin":
return
}
sema <- true
files, err := ReadDir(ctxt, dir)
<-sema
if pkg != "" || err != nil {
ch <- item{pkg, err}
}
for _, fi := range files {
fi := fi
if fi.IsDir() {
wg.Add(1)
go func() {
walkDir(filepath.Join(dir, fi.Name()))
wg.Done()
}()
}
}
}
walkDir(root)
wg.Wait()
}

108
vendor/golang.org/x/tools/go/buildutil/fakecontext.go generated vendored Normal file
View File

@ -0,0 +1,108 @@
package buildutil
import (
"fmt"
"go/build"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"sort"
"strings"
"time"
)
// FakeContext returns a build.Context for the fake file tree specified
// by pkgs, which maps package import paths to a mapping from file base
// names to contents.
//
// The fake Context has a GOROOT of "/go" and no GOPATH, and overrides
// the necessary file access methods to read from memory instead of the
// real file system.
//
// Unlike a real file tree, the fake one has only two levels---packages
// and files---so ReadDir("/go/src/") returns all packages under
// /go/src/ including, for instance, "math" and "math/big".
// ReadDir("/go/src/math/big") would return all the files in the
// "math/big" package.
//
func FakeContext(pkgs map[string]map[string]string) *build.Context {
clean := func(filename string) string {
f := path.Clean(filepath.ToSlash(filename))
// Removing "/go/src" while respecting segment
// boundaries has this unfortunate corner case:
if f == "/go/src" {
return ""
}
return strings.TrimPrefix(f, "/go/src/")
}
ctxt := build.Default // copy
ctxt.GOROOT = "/go"
ctxt.GOPATH = ""
ctxt.IsDir = func(dir string) bool {
dir = clean(dir)
if dir == "" {
return true // needed by (*build.Context).SrcDirs
}
return pkgs[dir] != nil
}
ctxt.ReadDir = func(dir string) ([]os.FileInfo, error) {
dir = clean(dir)
var fis []os.FileInfo
if dir == "" {
// enumerate packages
for importPath := range pkgs {
fis = append(fis, fakeDirInfo(importPath))
}
} else {
// enumerate files of package
for basename := range pkgs[dir] {
fis = append(fis, fakeFileInfo(basename))
}
}
sort.Sort(byName(fis))
return fis, nil
}
ctxt.OpenFile = func(filename string) (io.ReadCloser, error) {
filename = clean(filename)
dir, base := path.Split(filename)
content, ok := pkgs[path.Clean(dir)][base]
if !ok {
return nil, fmt.Errorf("file not found: %s", filename)
}
return ioutil.NopCloser(strings.NewReader(content)), nil
}
ctxt.IsAbsPath = func(path string) bool {
path = filepath.ToSlash(path)
// Don't rely on the default (filepath.Path) since on
// Windows, it reports virtual paths as non-absolute.
return strings.HasPrefix(path, "/")
}
return &ctxt
}
type byName []os.FileInfo
func (s byName) Len() int { return len(s) }
func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
type fakeFileInfo string
func (fi fakeFileInfo) Name() string { return string(fi) }
func (fakeFileInfo) Sys() interface{} { return nil }
func (fakeFileInfo) ModTime() time.Time { return time.Time{} }
func (fakeFileInfo) IsDir() bool { return false }
func (fakeFileInfo) Size() int64 { return 0 }
func (fakeFileInfo) Mode() os.FileMode { return 0644 }
type fakeDirInfo string
func (fd fakeDirInfo) Name() string { return string(fd) }
func (fakeDirInfo) Sys() interface{} { return nil }
func (fakeDirInfo) ModTime() time.Time { return time.Time{} }
func (fakeDirInfo) IsDir() bool { return true }
func (fakeDirInfo) Size() int64 { return 0 }
func (fakeDirInfo) Mode() os.FileMode { return 0755 }

73
vendor/golang.org/x/tools/go/buildutil/tags.go generated vendored Normal file
View File

@ -0,0 +1,73 @@
package buildutil
// This logic was copied from stringsFlag from $GOROOT/src/cmd/go/build.go.
import "fmt"
const TagsFlagDoc = "a list of `build tags` to consider satisfied during the build. " +
"For more information about build tags, see the description of " +
"build constraints in the documentation for the go/build package"
// TagsFlag is an implementation of the flag.Value interface that parses
// a flag value in the same manner as go build's -tags flag and
// populates a []string slice.
//
// See $GOROOT/src/go/build/doc.go for description of build tags.
// See $GOROOT/src/cmd/go/doc.go for description of 'go build -tags' flag.
//
// Example:
// flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsDoc)
type TagsFlag []string
func (v *TagsFlag) Set(s string) error {
var err error
*v, err = splitQuotedFields(s)
if *v == nil {
*v = []string{}
}
return err
}
func splitQuotedFields(s string) ([]string, error) {
// Split fields allowing '' or "" around elements.
// Quotes further inside the string do not count.
var f []string
for len(s) > 0 {
for len(s) > 0 && isSpaceByte(s[0]) {
s = s[1:]
}
if len(s) == 0 {
break
}
// Accepted quoted string. No unescaping inside.
if s[0] == '"' || s[0] == '\'' {
quote := s[0]
s = s[1:]
i := 0
for i < len(s) && s[i] != quote {
i++
}
if i >= len(s) {
return nil, fmt.Errorf("unterminated %c string", quote)
}
f = append(f, s[:i])
s = s[i+1:]
continue
}
i := 0
for i < len(s) && !isSpaceByte(s[i]) {
i++
}
f = append(f, s[:i])
s = s[i:]
}
return f, nil
}
func (v *TagsFlag) String() string {
return "<tagsFlag>"
}
func isSpaceByte(c byte) bool {
return c == ' ' || c == '\t' || c == '\n' || c == '\r'
}

158
vendor/golang.org/x/tools/go/buildutil/util.go generated vendored Normal file
View File

@ -0,0 +1,158 @@
// Copyright 2014 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 buildutil
import (
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
)
// ParseFile behaves like parser.ParseFile,
// but uses the build context's file system interface, if any.
//
// If file is not absolute (as defined by IsAbsPath), the (dir, file)
// components are joined using JoinPath; dir must be absolute.
//
// The displayPath function, if provided, is used to transform the
// filename that will be attached to the ASTs.
//
// TODO(adonovan): call this from go/loader.parseFiles when the tree thaws.
//
func ParseFile(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, file string, mode parser.Mode) (*ast.File, error) {
if !IsAbsPath(ctxt, file) {
file = JoinPath(ctxt, dir, file)
}
rd, err := OpenFile(ctxt, file)
if err != nil {
return nil, err
}
defer rd.Close() // ignore error
if displayPath != nil {
file = displayPath(file)
}
return parser.ParseFile(fset, file, rd, mode)
}
// ContainingPackage returns the package containing filename.
//
// If filename is not absolute, it is interpreted relative to working directory dir.
// All I/O is via the build context's file system interface, if any.
//
// The '...Files []string' fields of the resulting build.Package are not
// populated (build.FindOnly mode).
//
// TODO(adonovan): call this from oracle when the tree thaws.
//
func ContainingPackage(ctxt *build.Context, dir, filename string) (*build.Package, error) {
if !IsAbsPath(ctxt, filename) {
filename = JoinPath(ctxt, dir, filename)
}
// We must not assume the file tree uses
// "/" always,
// `\` always,
// or os.PathSeparator (which varies by platform),
// but to make any progress, we are forced to assume that
// paths will not use `\` unless the PathSeparator
// is also `\`, thus we can rely on filepath.ToSlash for some sanity.
dirSlash := path.Dir(filepath.ToSlash(filename)) + "/"
// We assume that no source root (GOPATH[i] or GOROOT) contains any other.
for _, srcdir := range ctxt.SrcDirs() {
srcdirSlash := filepath.ToSlash(srcdir) + "/"
if strings.HasPrefix(dirSlash, srcdirSlash) {
importPath := dirSlash[len(srcdirSlash) : len(dirSlash)-len("/")]
return ctxt.Import(importPath, dir, build.FindOnly)
}
}
return nil, fmt.Errorf("can't find package containing %s", filename)
}
// -- Effective methods of file system interface -------------------------
// (go/build.Context defines these as methods, but does not export them.)
// TODO(adonovan): HasSubdir?
// FileExists returns true if the specified file exists,
// using the build context's file system interface.
func FileExists(ctxt *build.Context, path string) bool {
if ctxt.OpenFile != nil {
r, err := ctxt.OpenFile(path)
if err != nil {
return false
}
r.Close() // ignore error
return true
}
_, err := os.Stat(path)
return err == nil
}
// OpenFile behaves like os.Open,
// but uses the build context's file system interface, if any.
func OpenFile(ctxt *build.Context, path string) (io.ReadCloser, error) {
if ctxt.OpenFile != nil {
return ctxt.OpenFile(path)
}
return os.Open(path)
}
// IsAbsPath behaves like filepath.IsAbs,
// but uses the build context's file system interface, if any.
func IsAbsPath(ctxt *build.Context, path string) bool {
if ctxt.IsAbsPath != nil {
return ctxt.IsAbsPath(path)
}
return filepath.IsAbs(path)
}
// JoinPath behaves like filepath.Join,
// but uses the build context's file system interface, if any.
func JoinPath(ctxt *build.Context, path ...string) string {
if ctxt.JoinPath != nil {
return ctxt.JoinPath(path...)
}
return filepath.Join(path...)
}
// IsDir behaves like os.Stat plus IsDir,
// but uses the build context's file system interface, if any.
func IsDir(ctxt *build.Context, path string) bool {
if ctxt.IsDir != nil {
return ctxt.IsDir(path)
}
fi, err := os.Stat(path)
return err == nil && fi.IsDir()
}
// ReadDir behaves like ioutil.ReadDir,
// but uses the build context's file system interface, if any.
func ReadDir(ctxt *build.Context, path string) ([]os.FileInfo, error) {
if ctxt.ReadDir != nil {
return ctxt.ReadDir(path)
}
return ioutil.ReadDir(path)
}
// SplitPathList behaves like filepath.SplitList,
// but uses the build context's file system interface, if any.
func SplitPathList(ctxt *build.Context, s string) []string {
if ctxt.SplitPathList != nil {
return ctxt.SplitPathList(s)
}
return filepath.SplitList(s)
}

129
vendor/golang.org/x/tools/go/callgraph/callgraph.go generated vendored Normal file
View File

@ -0,0 +1,129 @@
// Copyright 2013 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 callgraph defines the call graph and various algorithms
and utilities to operate on it.
A call graph is a labelled directed graph whose nodes represent
functions and whose edge labels represent syntactic function call
sites. The presence of a labelled edge (caller, site, callee)
indicates that caller may call callee at the specified call site.
A call graph is a multigraph: it may contain multiple edges (caller,
*, callee) connecting the same pair of nodes, so long as the edges
differ by label; this occurs when one function calls another function
from multiple call sites. Also, it may contain multiple edges
(caller, site, *) that differ only by callee; this indicates a
polymorphic call.
A SOUND call graph is one that overapproximates the dynamic calling
behaviors of the program in all possible executions. One call graph
is more PRECISE than another if it is a smaller overapproximation of
the dynamic behavior.
All call graphs have a synthetic root node which is responsible for
calling main() and init().
Calls to built-in functions (e.g. panic, println) are not represented
in the call graph; they are treated like built-in operators of the
language.
*/
package callgraph // import "golang.org/x/tools/go/callgraph"
// TODO(adonovan): add a function to eliminate wrappers from the
// callgraph, preserving topology.
// More generally, we could eliminate "uninteresting" nodes such as
// nodes from packages we don't care about.
import (
"fmt"
"go/token"
"golang.org/x/tools/go/ssa"
)
// A Graph represents a call graph.
//
// A graph may contain nodes that are not reachable from the root.
// If the call graph is sound, such nodes indicate unreachable
// functions.
//
type Graph struct {
Root *Node // the distinguished root node
Nodes map[*ssa.Function]*Node // all nodes by function
}
// New returns a new Graph with the specified root node.
func New(root *ssa.Function) *Graph {
g := &Graph{Nodes: make(map[*ssa.Function]*Node)}
g.Root = g.CreateNode(root)
return g
}
// CreateNode returns the Node for fn, creating it if not present.
func (g *Graph) CreateNode(fn *ssa.Function) *Node {
n, ok := g.Nodes[fn]
if !ok {
n = &Node{Func: fn, ID: len(g.Nodes)}
g.Nodes[fn] = n
}
return n
}
// A Node represents a node in a call graph.
type Node struct {
Func *ssa.Function // the function this node represents
ID int // 0-based sequence number
In []*Edge // unordered set of incoming call edges (n.In[*].Callee == n)
Out []*Edge // unordered set of outgoing call edges (n.Out[*].Caller == n)
}
func (n *Node) String() string {
return fmt.Sprintf("n%d:%s", n.ID, n.Func)
}
// A Edge represents an edge in the call graph.
//
// Site is nil for edges originating in synthetic or intrinsic
// functions, e.g. reflect.Call or the root of the call graph.
type Edge struct {
Caller *Node
Site ssa.CallInstruction
Callee *Node
}
func (e Edge) String() string {
return fmt.Sprintf("%s --> %s", e.Caller, e.Callee)
}
func (e Edge) Description() string {
var prefix string
switch e.Site.(type) {
case nil:
return "synthetic call"
case *ssa.Go:
prefix = "concurrent "
case *ssa.Defer:
prefix = "deferred "
}
return prefix + e.Site.Common().Description()
}
func (e Edge) Pos() token.Pos {
if e.Site == nil {
return token.NoPos
}
return e.Site.Pos()
}
// AddEdge adds the edge (caller, site, callee) to the call graph.
// Elimination of duplicate edges is the caller's responsibility.
func AddEdge(caller *Node, site ssa.CallInstruction, callee *Node) {
e := &Edge{caller, site, callee}
callee.In = append(callee.In, e)
caller.Out = append(caller.Out, e)
}

181
vendor/golang.org/x/tools/go/callgraph/util.go generated vendored Normal file
View File

@ -0,0 +1,181 @@
// Copyright 2013 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 callgraph
import "golang.org/x/tools/go/ssa"
// This file provides various utilities over call graphs, such as
// visitation and path search.
// CalleesOf returns a new set containing all direct callees of the
// caller node.
//
func CalleesOf(caller *Node) map[*Node]bool {
callees := make(map[*Node]bool)
for _, e := range caller.Out {
callees[e.Callee] = true
}
return callees
}
// GraphVisitEdges visits all the edges in graph g in depth-first order.
// The edge function is called for each edge in postorder. If it
// returns non-nil, visitation stops and GraphVisitEdges returns that
// value.
//
func GraphVisitEdges(g *Graph, edge func(*Edge) error) error {
seen := make(map[*Node]bool)
var visit func(n *Node) error
visit = func(n *Node) error {
if !seen[n] {
seen[n] = true
for _, e := range n.Out {
if err := visit(e.Callee); err != nil {
return err
}
if err := edge(e); err != nil {
return err
}
}
}
return nil
}
for _, n := range g.Nodes {
if err := visit(n); err != nil {
return err
}
}
return nil
}
// PathSearch finds an arbitrary path starting at node start and
// ending at some node for which isEnd() returns true. On success,
// PathSearch returns the path as an ordered list of edges; on
// failure, it returns nil.
//
func PathSearch(start *Node, isEnd func(*Node) bool) []*Edge {
stack := make([]*Edge, 0, 32)
seen := make(map[*Node]bool)
var search func(n *Node) []*Edge
search = func(n *Node) []*Edge {
if !seen[n] {
seen[n] = true
if isEnd(n) {
return stack
}
for _, e := range n.Out {
stack = append(stack, e) // push
if found := search(e.Callee); found != nil {
return found
}
stack = stack[:len(stack)-1] // pop
}
}
return nil
}
return search(start)
}
// DeleteSyntheticNodes removes from call graph g all nodes for
// synthetic functions (except g.Root and package initializers),
// preserving the topology. In effect, calls to synthetic wrappers
// are "inlined".
//
func (g *Graph) DeleteSyntheticNodes() {
// Measurements on the standard library and go.tools show that
// resulting graph has ~15% fewer nodes and 4-8% fewer edges
// than the input.
//
// Inlining a wrapper of in-degree m, out-degree n adds m*n
// and removes m+n edges. Since most wrappers are monomorphic
// (n=1) this results in a slight reduction. Polymorphic
// wrappers (n>1), e.g. from embedding an interface value
// inside a struct to satisfy some interface, cause an
// increase in the graph, but they seem to be uncommon.
// Hash all existing edges to avoid creating duplicates.
edges := make(map[Edge]bool)
for _, cgn := range g.Nodes {
for _, e := range cgn.Out {
edges[*e] = true
}
}
for fn, cgn := range g.Nodes {
if cgn == g.Root || fn.Synthetic == "" || isInit(cgn.Func) {
continue // keep
}
for _, eIn := range cgn.In {
for _, eOut := range cgn.Out {
newEdge := Edge{eIn.Caller, eIn.Site, eOut.Callee}
if edges[newEdge] {
continue // don't add duplicate
}
AddEdge(eIn.Caller, eIn.Site, eOut.Callee)
edges[newEdge] = true
}
}
g.DeleteNode(cgn)
}
}
func isInit(fn *ssa.Function) bool {
return fn.Pkg != nil && fn.Pkg.Func("init") == fn
}
// DeleteNode removes node n and its edges from the graph g.
// (NB: not efficient for batch deletion.)
func (g *Graph) DeleteNode(n *Node) {
n.deleteIns()
n.deleteOuts()
delete(g.Nodes, n.Func)
}
// deleteIns deletes all incoming edges to n.
func (n *Node) deleteIns() {
for _, e := range n.In {
removeOutEdge(e)
}
n.In = nil
}
// deleteOuts deletes all outgoing edges from n.
func (n *Node) deleteOuts() {
for _, e := range n.Out {
removeInEdge(e)
}
n.Out = nil
}
// removeOutEdge removes edge.Caller's outgoing edge 'edge'.
func removeOutEdge(edge *Edge) {
caller := edge.Caller
n := len(caller.Out)
for i, e := range caller.Out {
if e == edge {
// Replace it with the final element and shrink the slice.
caller.Out[i] = caller.Out[n-1]
caller.Out[n-1] = nil // aid GC
caller.Out = caller.Out[:n-1]
return
}
}
panic("edge not found: " + edge.String())
}
// removeInEdge removes edge.Callee's incoming edge 'edge'.
func removeInEdge(edge *Edge) {
caller := edge.Callee
n := len(caller.In)
for i, e := range caller.In {
if e == edge {
// Replace it with the final element and shrink the slice.
caller.In[i] = caller.In[n-1]
caller.In[n-1] = nil // aid GC
caller.In = caller.In[:n-1]
return
}
}
panic("edge not found: " + edge.String())
}

920
vendor/golang.org/x/tools/go/exact/exact.go generated vendored Normal file
View File

@ -0,0 +1,920 @@
// Copyright 2013 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 exact implements Values representing untyped
// Go constants and the corresponding operations. Values
// and operations have unlimited precision.
//
// A special Unknown value may be used when a value
// is unknown due to an error. Operations on unknown
// values produce unknown values unless specified
// otherwise.
//
package exact // import "golang.org/x/tools/go/exact"
import (
"fmt"
"go/token"
"math/big"
"strconv"
)
// Kind specifies the kind of value represented by a Value.
type Kind int
// Implementation note: Kinds must be enumerated in
// order of increasing "complexity" (used by match).
const (
// unknown values
Unknown Kind = iota
// non-numeric values
Bool
String
// numeric values
Int
Float
Complex
)
// A Value represents a mathematically exact value of a given Kind.
type Value interface {
// Kind returns the value kind; it is always the smallest
// kind in which the value can be represented exactly.
Kind() Kind
// String returns a human-readable form of the value.
String() string
// Prevent external implementations.
implementsValue()
}
// ----------------------------------------------------------------------------
// Implementations
type (
unknownVal struct{}
boolVal bool
stringVal string
int64Val int64
intVal struct{ val *big.Int }
floatVal struct{ val *big.Rat }
complexVal struct{ re, im *big.Rat }
)
func (unknownVal) Kind() Kind { return Unknown }
func (boolVal) Kind() Kind { return Bool }
func (stringVal) Kind() Kind { return String }
func (int64Val) Kind() Kind { return Int }
func (intVal) Kind() Kind { return Int }
func (floatVal) Kind() Kind { return Float }
func (complexVal) Kind() Kind { return Complex }
func (unknownVal) String() string { return "unknown" }
func (x boolVal) String() string { return fmt.Sprintf("%v", bool(x)) }
func (x stringVal) String() string { return strconv.Quote(string(x)) }
func (x int64Val) String() string { return strconv.FormatInt(int64(x), 10) }
func (x intVal) String() string { return x.val.String() }
func (x floatVal) String() string { return x.val.String() }
func (x complexVal) String() string { return fmt.Sprintf("(%s + %si)", x.re, x.im) }
func (unknownVal) implementsValue() {}
func (boolVal) implementsValue() {}
func (stringVal) implementsValue() {}
func (int64Val) implementsValue() {}
func (intVal) implementsValue() {}
func (floatVal) implementsValue() {}
func (complexVal) implementsValue() {}
// int64 bounds
var (
minInt64 = big.NewInt(-1 << 63)
maxInt64 = big.NewInt(1<<63 - 1)
)
func normInt(x *big.Int) Value {
if minInt64.Cmp(x) <= 0 && x.Cmp(maxInt64) <= 0 {
return int64Val(x.Int64())
}
return intVal{x}
}
func normFloat(x *big.Rat) Value {
if x.IsInt() {
return normInt(x.Num())
}
return floatVal{x}
}
func normComplex(re, im *big.Rat) Value {
if im.Sign() == 0 {
return normFloat(re)
}
return complexVal{re, im}
}
// ----------------------------------------------------------------------------
// Factories
// MakeUnknown returns the Unknown value.
func MakeUnknown() Value { return unknownVal{} }
// MakeBool returns the Bool value for x.
func MakeBool(b bool) Value { return boolVal(b) }
// MakeString returns the String value for x.
func MakeString(s string) Value { return stringVal(s) }
// MakeInt64 returns the Int value for x.
func MakeInt64(x int64) Value { return int64Val(x) }
// MakeUint64 returns the Int value for x.
func MakeUint64(x uint64) Value { return normInt(new(big.Int).SetUint64(x)) }
// MakeFloat64 returns the numeric value for x.
// If x is not finite, the result is unknown.
func MakeFloat64(x float64) Value {
if f := new(big.Rat).SetFloat64(x); f != nil {
return normFloat(f)
}
return unknownVal{}
}
// MakeFromLiteral returns the corresponding integer, floating-point,
// imaginary, character, or string value for a Go literal string. The
// result is nil if the literal string is invalid.
func MakeFromLiteral(lit string, tok token.Token) Value {
switch tok {
case token.INT:
if x, err := strconv.ParseInt(lit, 0, 64); err == nil {
return int64Val(x)
}
if x, ok := new(big.Int).SetString(lit, 0); ok {
return intVal{x}
}
case token.FLOAT:
if x, ok := new(big.Rat).SetString(lit); ok {
return normFloat(x)
}
case token.IMAG:
if n := len(lit); n > 0 && lit[n-1] == 'i' {
if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok {
return normComplex(big.NewRat(0, 1), im)
}
}
case token.CHAR:
if n := len(lit); n >= 2 {
if code, _, _, err := strconv.UnquoteChar(lit[1:n-1], '\''); err == nil {
return int64Val(code)
}
}
case token.STRING:
if s, err := strconv.Unquote(lit); err == nil {
return stringVal(s)
}
}
return nil
}
// ----------------------------------------------------------------------------
// Accessors
//
// For unknown arguments the result is the zero value for the respective
// accessor type, except for Sign, where the result is 1.
// BoolVal returns the Go boolean value of x, which must be a Bool or an Unknown.
// If x is Unknown, the result is false.
func BoolVal(x Value) bool {
switch x := x.(type) {
case boolVal:
return bool(x)
case unknownVal:
return false
}
panic(fmt.Sprintf("%v not a Bool", x))
}
// StringVal returns the Go string value of x, which must be a String or an Unknown.
// If x is Unknown, the result is "".
func StringVal(x Value) string {
switch x := x.(type) {
case stringVal:
return string(x)
case unknownVal:
return ""
}
panic(fmt.Sprintf("%v not a String", x))
}
// Int64Val returns the Go int64 value of x and whether the result is exact;
// x must be an Int or an Unknown. If the result is not exact, its value is undefined.
// If x is Unknown, the result is (0, false).
func Int64Val(x Value) (int64, bool) {
switch x := x.(type) {
case int64Val:
return int64(x), true
case intVal:
return x.val.Int64(), x.val.BitLen() <= 63
case unknownVal:
return 0, false
}
panic(fmt.Sprintf("%v not an Int", x))
}
// Uint64Val returns the Go uint64 value of x and whether the result is exact;
// x must be an Int or an Unknown. If the result is not exact, its value is undefined.
// If x is Unknown, the result is (0, false).
func Uint64Val(x Value) (uint64, bool) {
switch x := x.(type) {
case int64Val:
return uint64(x), x >= 0
case intVal:
return x.val.Uint64(), x.val.Sign() >= 0 && x.val.BitLen() <= 64
case unknownVal:
return 0, false
}
panic(fmt.Sprintf("%v not an Int", x))
}
// Float32Val is like Float64Val but for float32 instead of float64.
func Float32Val(x Value) (float32, bool) {
switch x := x.(type) {
case int64Val:
f := float32(x)
return f, int64Val(f) == x
case intVal:
return ratToFloat32(new(big.Rat).SetFrac(x.val, int1))
case floatVal:
return ratToFloat32(x.val)
case unknownVal:
return 0, false
}
panic(fmt.Sprintf("%v not a Float", x))
}
// Float64Val returns the nearest Go float64 value of x and whether the result is exact;
// x must be numeric but not Complex, or Unknown. For values too small (too close to 0)
// to represent as float64, Float64Val silently underflows to 0. The result sign always
// matches the sign of x, even for 0.
// If x is Unknown, the result is (0, false).
func Float64Val(x Value) (float64, bool) {
switch x := x.(type) {
case int64Val:
f := float64(int64(x))
return f, int64Val(f) == x
case intVal:
return new(big.Rat).SetFrac(x.val, int1).Float64()
case floatVal:
return x.val.Float64()
case unknownVal:
return 0, false
}
panic(fmt.Sprintf("%v not a Float", x))
}
// BitLen returns the number of bits required to represent
// the absolute value x in binary representation; x must be an Int or an Unknown.
// If x is Unknown, the result is 0.
func BitLen(x Value) int {
switch x := x.(type) {
case int64Val:
return new(big.Int).SetInt64(int64(x)).BitLen()
case intVal:
return x.val.BitLen()
case unknownVal:
return 0
}
panic(fmt.Sprintf("%v not an Int", x))
}
// Sign returns -1, 0, or 1 depending on whether x < 0, x == 0, or x > 0;
// x must be numeric or Unknown. For complex values x, the sign is 0 if x == 0,
// otherwise it is != 0. If x is Unknown, the result is 1.
func Sign(x Value) int {
switch x := x.(type) {
case int64Val:
switch {
case x < 0:
return -1
case x > 0:
return 1
}
return 0
case intVal:
return x.val.Sign()
case floatVal:
return x.val.Sign()
case complexVal:
return x.re.Sign() | x.im.Sign()
case unknownVal:
return 1 // avoid spurious division by zero errors
}
panic(fmt.Sprintf("%v not numeric", x))
}
// ----------------------------------------------------------------------------
// Support for serializing/deserializing integers
const (
// Compute the size of a Word in bytes.
_m = ^big.Word(0)
_log = _m>>8&1 + _m>>16&1 + _m>>32&1
wordSize = 1 << _log
)
// Bytes returns the bytes for the absolute value of x in little-
// endian binary representation; x must be an Int.
func Bytes(x Value) []byte {
var val *big.Int
switch x := x.(type) {
case int64Val:
val = new(big.Int).SetInt64(int64(x))
case intVal:
val = x.val
default:
panic(fmt.Sprintf("%v not an Int", x))
}
words := val.Bits()
bytes := make([]byte, len(words)*wordSize)
i := 0
for _, w := range words {
for j := 0; j < wordSize; j++ {
bytes[i] = byte(w)
w >>= 8
i++
}
}
// remove leading 0's
for i > 0 && bytes[i-1] == 0 {
i--
}
return bytes[:i]
}
// MakeFromBytes returns the Int value given the bytes of its little-endian
// binary representation. An empty byte slice argument represents 0.
func MakeFromBytes(bytes []byte) Value {
words := make([]big.Word, (len(bytes)+(wordSize-1))/wordSize)
i := 0
var w big.Word
var s uint
for _, b := range bytes {
w |= big.Word(b) << s
if s += 8; s == wordSize*8 {
words[i] = w
i++
w = 0
s = 0
}
}
// store last word
if i < len(words) {
words[i] = w
i++
}
// remove leading 0's
for i > 0 && words[i-1] == 0 {
i--
}
return normInt(new(big.Int).SetBits(words[:i]))
}
// ----------------------------------------------------------------------------
// Support for disassembling fractions
// Num returns the numerator of x; x must be Int, Float, or Unknown.
// If x is Unknown, the result is Unknown, otherwise it is an Int
// with the same sign as x.
func Num(x Value) Value {
switch x := x.(type) {
case unknownVal, int64Val, intVal:
return x
case floatVal:
return normInt(x.val.Num())
}
panic(fmt.Sprintf("%v not Int or Float", x))
}
// Denom returns the denominator of x; x must be Int, Float, or Unknown.
// If x is Unknown, the result is Unknown, otherwise it is an Int >= 1.
func Denom(x Value) Value {
switch x := x.(type) {
case unknownVal:
return x
case int64Val, intVal:
return int64Val(1)
case floatVal:
return normInt(x.val.Denom())
}
panic(fmt.Sprintf("%v not Int or Float", x))
}
// ----------------------------------------------------------------------------
// Support for assembling/disassembling complex numbers
// MakeImag returns the numeric value x*i (possibly 0);
// x must be Int, Float, or Unknown.
// If x is Unknown, the result is Unknown.
func MakeImag(x Value) Value {
var im *big.Rat
switch x := x.(type) {
case unknownVal:
return x
case int64Val:
im = big.NewRat(int64(x), 1)
case intVal:
im = new(big.Rat).SetFrac(x.val, int1)
case floatVal:
im = x.val
default:
panic(fmt.Sprintf("%v not Int or Float", x))
}
return normComplex(rat0, im)
}
// Real returns the real part of x, which must be a numeric or unknown value.
// If x is Unknown, the result is Unknown.
func Real(x Value) Value {
switch x := x.(type) {
case unknownVal, int64Val, intVal, floatVal:
return x
case complexVal:
return normFloat(x.re)
}
panic(fmt.Sprintf("%v not numeric", x))
}
// Imag returns the imaginary part of x, which must be a numeric or unknown value.
// If x is Unknown, the result is Unknown.
func Imag(x Value) Value {
switch x := x.(type) {
case unknownVal:
return x
case int64Val, intVal, floatVal:
return int64Val(0)
case complexVal:
return normFloat(x.im)
}
panic(fmt.Sprintf("%v not numeric", x))
}
// ----------------------------------------------------------------------------
// Operations
// is32bit reports whether x can be represented using 32 bits.
func is32bit(x int64) bool {
const s = 32
return -1<<(s-1) <= x && x <= 1<<(s-1)-1
}
// is63bit reports whether x can be represented using 63 bits.
func is63bit(x int64) bool {
const s = 63
return -1<<(s-1) <= x && x <= 1<<(s-1)-1
}
// UnaryOp returns the result of the unary expression op y.
// The operation must be defined for the operand.
// If size >= 0 it specifies the ^ (xor) result size in bytes.
// If y is Unknown, the result is Unknown.
//
func UnaryOp(op token.Token, y Value, size int) Value {
switch op {
case token.ADD:
switch y.(type) {
case unknownVal, int64Val, intVal, floatVal, complexVal:
return y
}
case token.SUB:
switch y := y.(type) {
case unknownVal:
return y
case int64Val:
if z := -y; z != y {
return z // no overflow
}
return normInt(new(big.Int).Neg(big.NewInt(int64(y))))
case intVal:
return normInt(new(big.Int).Neg(y.val))
case floatVal:
return normFloat(new(big.Rat).Neg(y.val))
case complexVal:
return normComplex(new(big.Rat).Neg(y.re), new(big.Rat).Neg(y.im))
}
case token.XOR:
var z big.Int
switch y := y.(type) {
case unknownVal:
return y
case int64Val:
z.Not(big.NewInt(int64(y)))
case intVal:
z.Not(y.val)
default:
goto Error
}
// For unsigned types, the result will be negative and
// thus "too large": We must limit the result size to
// the type's size.
if size >= 0 {
s := uint(size) * 8
z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), s)) // z &^= (-1)<<s
}
return normInt(&z)
case token.NOT:
switch y := y.(type) {
case unknownVal:
return y
case boolVal:
return !y
}
}
Error:
panic(fmt.Sprintf("invalid unary operation %s%v", op, y))
}
var (
int1 = big.NewInt(1)
rat0 = big.NewRat(0, 1)
)
func ord(x Value) int {
switch x.(type) {
default:
return 0
case boolVal, stringVal:
return 1
case int64Val:
return 2
case intVal:
return 3
case floatVal:
return 4
case complexVal:
return 5
}
}
// match returns the matching representation (same type) with the
// smallest complexity for two values x and y. If one of them is
// numeric, both of them must be numeric. If one of them is Unknown,
// both results are Unknown.
//
func match(x, y Value) (_, _ Value) {
if ord(x) > ord(y) {
y, x = match(y, x)
return x, y
}
// ord(x) <= ord(y)
switch x := x.(type) {
case unknownVal:
return x, x
case boolVal, stringVal, complexVal:
return x, y
case int64Val:
switch y := y.(type) {
case int64Val:
return x, y
case intVal:
return intVal{big.NewInt(int64(x))}, y
case floatVal:
return floatVal{big.NewRat(int64(x), 1)}, y
case complexVal:
return complexVal{big.NewRat(int64(x), 1), rat0}, y
}
case intVal:
switch y := y.(type) {
case intVal:
return x, y
case floatVal:
return floatVal{new(big.Rat).SetFrac(x.val, int1)}, y
case complexVal:
return complexVal{new(big.Rat).SetFrac(x.val, int1), rat0}, y
}
case floatVal:
switch y := y.(type) {
case floatVal:
return x, y
case complexVal:
return complexVal{x.val, rat0}, y
}
}
panic("unreachable")
}
// BinaryOp returns the result of the binary expression x op y.
// The operation must be defined for the operands. If one of the
// operands is Unknown, the result is Unknown.
// To force integer division of Int operands, use op == token.QUO_ASSIGN
// instead of token.QUO; the result is guaranteed to be Int in this case.
// Division by zero leads to a run-time panic.
//
func BinaryOp(x Value, op token.Token, y Value) Value {
x, y = match(x, y)
switch x := x.(type) {
case unknownVal:
return x
case boolVal:
y := y.(boolVal)
switch op {
case token.LAND:
return x && y
case token.LOR:
return x || y
}
case int64Val:
a := int64(x)
b := int64(y.(int64Val))
var c int64
switch op {
case token.ADD:
if !is63bit(a) || !is63bit(b) {
return normInt(new(big.Int).Add(big.NewInt(a), big.NewInt(b)))
}
c = a + b
case token.SUB:
if !is63bit(a) || !is63bit(b) {
return normInt(new(big.Int).Sub(big.NewInt(a), big.NewInt(b)))
}
c = a - b
case token.MUL:
if !is32bit(a) || !is32bit(b) {
return normInt(new(big.Int).Mul(big.NewInt(a), big.NewInt(b)))
}
c = a * b
case token.QUO:
return normFloat(new(big.Rat).SetFrac(big.NewInt(a), big.NewInt(b)))
case token.QUO_ASSIGN: // force integer division
c = a / b
case token.REM:
c = a % b
case token.AND:
c = a & b
case token.OR:
c = a | b
case token.XOR:
c = a ^ b
case token.AND_NOT:
c = a &^ b
default:
goto Error
}
return int64Val(c)
case intVal:
a := x.val
b := y.(intVal).val
var c big.Int
switch op {
case token.ADD:
c.Add(a, b)
case token.SUB:
c.Sub(a, b)
case token.MUL:
c.Mul(a, b)
case token.QUO:
return normFloat(new(big.Rat).SetFrac(a, b))
case token.QUO_ASSIGN: // force integer division
c.Quo(a, b)
case token.REM:
c.Rem(a, b)
case token.AND:
c.And(a, b)
case token.OR:
c.Or(a, b)
case token.XOR:
c.Xor(a, b)
case token.AND_NOT:
c.AndNot(a, b)
default:
goto Error
}
return normInt(&c)
case floatVal:
a := x.val
b := y.(floatVal).val
var c big.Rat
switch op {
case token.ADD:
c.Add(a, b)
case token.SUB:
c.Sub(a, b)
case token.MUL:
c.Mul(a, b)
case token.QUO:
c.Quo(a, b)
default:
goto Error
}
return normFloat(&c)
case complexVal:
y := y.(complexVal)
a, b := x.re, x.im
c, d := y.re, y.im
var re, im big.Rat
switch op {
case token.ADD:
// (a+c) + i(b+d)
re.Add(a, c)
im.Add(b, d)
case token.SUB:
// (a-c) + i(b-d)
re.Sub(a, c)
im.Sub(b, d)
case token.MUL:
// (ac-bd) + i(bc+ad)
var ac, bd, bc, ad big.Rat
ac.Mul(a, c)
bd.Mul(b, d)
bc.Mul(b, c)
ad.Mul(a, d)
re.Sub(&ac, &bd)
im.Add(&bc, &ad)
case token.QUO:
// (ac+bd)/s + i(bc-ad)/s, with s = cc + dd
var ac, bd, bc, ad, s, cc, dd big.Rat
ac.Mul(a, c)
bd.Mul(b, d)
bc.Mul(b, c)
ad.Mul(a, d)
cc.Mul(c, c)
dd.Mul(d, d)
s.Add(&cc, &dd)
re.Add(&ac, &bd)
re.Quo(&re, &s)
im.Sub(&bc, &ad)
im.Quo(&im, &s)
default:
goto Error
}
return normComplex(&re, &im)
case stringVal:
if op == token.ADD {
return x + y.(stringVal)
}
}
Error:
panic(fmt.Sprintf("invalid binary operation %v %s %v", x, op, y))
}
// Shift returns the result of the shift expression x op s
// with op == token.SHL or token.SHR (<< or >>). x must be
// an Int or an Unknown. If x is Unknown, the result is x.
//
func Shift(x Value, op token.Token, s uint) Value {
switch x := x.(type) {
case unknownVal:
return x
case int64Val:
if s == 0 {
return x
}
switch op {
case token.SHL:
z := big.NewInt(int64(x))
return normInt(z.Lsh(z, s))
case token.SHR:
return x >> s
}
case intVal:
if s == 0 {
return x
}
var z big.Int
switch op {
case token.SHL:
return normInt(z.Lsh(x.val, s))
case token.SHR:
return normInt(z.Rsh(x.val, s))
}
}
panic(fmt.Sprintf("invalid shift %v %s %d", x, op, s))
}
func cmpZero(x int, op token.Token) bool {
switch op {
case token.EQL:
return x == 0
case token.NEQ:
return x != 0
case token.LSS:
return x < 0
case token.LEQ:
return x <= 0
case token.GTR:
return x > 0
case token.GEQ:
return x >= 0
}
panic("unreachable")
}
// Compare returns the result of the comparison x op y.
// The comparison must be defined for the operands.
// If one of the operands is Unknown, the result is
// false.
//
func Compare(x Value, op token.Token, y Value) bool {
x, y = match(x, y)
switch x := x.(type) {
case unknownVal:
return false
case boolVal:
y := y.(boolVal)
switch op {
case token.EQL:
return x == y
case token.NEQ:
return x != y
}
case int64Val:
y := y.(int64Val)
switch op {
case token.EQL:
return x == y
case token.NEQ:
return x != y
case token.LSS:
return x < y
case token.LEQ:
return x <= y
case token.GTR:
return x > y
case token.GEQ:
return x >= y
}
case intVal:
return cmpZero(x.val.Cmp(y.(intVal).val), op)
case floatVal:
return cmpZero(x.val.Cmp(y.(floatVal).val), op)
case complexVal:
y := y.(complexVal)
re := x.re.Cmp(y.re)
im := x.im.Cmp(y.im)
switch op {
case token.EQL:
return re == 0 && im == 0
case token.NEQ:
return re != 0 || im != 0
}
case stringVal:
y := y.(stringVal)
switch op {
case token.EQL:
return x == y
case token.NEQ:
return x != y
case token.LSS:
return x < y
case token.LEQ:
return x <= y
case token.GTR:
return x > y
case token.GEQ:
return x >= y
}
}
panic(fmt.Sprintf("invalid comparison %v %s %v", x, op, y))
}

24
vendor/golang.org/x/tools/go/exact/go13.go generated vendored Normal file
View File

@ -0,0 +1,24 @@
// Copyright 2014 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.
// +build !go1.4
package exact
import (
"math"
"math/big"
)
func ratToFloat32(x *big.Rat) (float32, bool) {
// Before 1.4, there's no Rat.Float32.
// Emulate it, albeit at the cost of
// imprecision in corner cases.
x64, exact := x.Float64()
x32 := float32(x64)
if math.IsInf(float64(x32), 0) {
exact = false
}
return x32, exact
}

13
vendor/golang.org/x/tools/go/exact/go14.go generated vendored Normal file
View File

@ -0,0 +1,13 @@
// Copyright 2014 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.
// +build go1.4
package exact
import "math/big"
func ratToFloat32(x *big.Rat) (float32, bool) {
return x.Float32()
}

108
vendor/golang.org/x/tools/go/gcimporter/exportdata.go generated vendored Normal file
View File

@ -0,0 +1,108 @@
// Copyright 2011 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.
// This file implements FindExportData.
package gcimporter
import (
"bufio"
"errors"
"fmt"
"io"
"strconv"
"strings"
)
func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
// See $GOROOT/include/ar.h.
hdr := make([]byte, 16+12+6+6+8+10+2)
_, err = io.ReadFull(r, hdr)
if err != nil {
return
}
// leave for debugging
if false {
fmt.Printf("header: %s", hdr)
}
s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
size, err = strconv.Atoi(s)
if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
err = errors.New("invalid archive header")
return
}
name = strings.TrimSpace(string(hdr[:16]))
return
}
// FindExportData positions the reader r at the beginning of the
// export data section of an underlying GC-created object/archive
// file by reading from it. The reader must be positioned at the
// start of the file before calling this function.
//
func FindExportData(r *bufio.Reader) (err error) {
// Read first line to make sure this is an object file.
line, err := r.ReadSlice('\n')
if err != nil {
return
}
if string(line) == "!<arch>\n" {
// Archive file. Scan to __.PKGDEF.
var name string
var size int
if name, size, err = readGopackHeader(r); err != nil {
return
}
// Optional leading __.GOSYMDEF or __.SYMDEF.
// Read and discard.
if name == "__.SYMDEF" || name == "__.GOSYMDEF" {
const block = 4096
tmp := make([]byte, block)
for size > 0 {
n := size
if n > block {
n = block
}
if _, err = io.ReadFull(r, tmp[:n]); err != nil {
return
}
size -= n
}
if name, size, err = readGopackHeader(r); err != nil {
return
}
}
// First real entry should be __.PKGDEF.
if name != "__.PKGDEF" {
err = errors.New("go archive is missing __.PKGDEF")
return
}
// Read first line of __.PKGDEF data, so that line
// is once again the first line of the input.
if line, err = r.ReadSlice('\n'); err != nil {
return
}
}
// Now at __.PKGDEF in archive or still at beginning of file.
// Either way, line should begin with "go object ".
if !strings.HasPrefix(string(line), "go object ") {
err = errors.New("not a go object file")
return
}
// Skip over object header to export data.
// Begins after first line with $$.
for line[0] != '$' {
if line, err = r.ReadSlice('\n'); err != nil {
return
}
}
return
}

995
vendor/golang.org/x/tools/go/gcimporter/gcimporter.go generated vendored Normal file
View File

@ -0,0 +1,995 @@
// Copyright 2011 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 gcimporter implements Import for gc-generated object files.
// Importing this package installs Import as go/types.DefaultImport.
package gcimporter // import "golang.org/x/tools/go/gcimporter"
import (
"bufio"
"errors"
"fmt"
"go/build"
"go/token"
"io"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"text/scanner"
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/types"
)
// debugging/development support
const debug = false
func init() {
types.DefaultImport = Import
}
var pkgExts = [...]string{".a", ".5", ".6", ".7", ".8", ".9"}
// FindPkg returns the filename and unique package id for an import
// path based on package information provided by build.Import (using
// the build.Default build.Context).
// If no file was found, an empty filename is returned.
//
func FindPkg(path, srcDir string) (filename, id string) {
if len(path) == 0 {
return
}
id = path
var noext string
switch {
default:
// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
// Don't require the source files to be present.
bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
if bp.PkgObj == "" {
return
}
noext = strings.TrimSuffix(bp.PkgObj, ".a")
case build.IsLocalImport(path):
// "./x" -> "/this/directory/x.ext", "/this/directory/x"
noext = filepath.Join(srcDir, path)
id = noext
case filepath.IsAbs(path):
// for completeness only - go/build.Import
// does not support absolute imports
// "/x" -> "/x.ext", "/x"
noext = path
}
// try extensions
for _, ext := range pkgExts {
filename = noext + ext
if f, err := os.Stat(filename); err == nil && !f.IsDir() {
return
}
}
filename = "" // not found
return
}
// ImportData imports a package by reading the gc-generated export data,
// adds the corresponding package object to the packages map indexed by id,
// and returns the object.
//
// The packages map must contains all packages already imported. The data
// reader position must be the beginning of the export data section. The
// filename is only used in error messages.
//
// If packages[id] contains the completely imported package, that package
// can be used directly, and there is no need to call this function (but
// there is also no harm but for extra time used).
//
func ImportData(packages map[string]*types.Package, filename, id string, data io.Reader) (pkg *types.Package, err error) {
// support for parser error handling
defer func() {
switch r := recover().(type) {
case nil:
// nothing to do
case importError:
err = r
default:
panic(r) // internal error
}
}()
var p parser
p.init(filename, id, data, packages)
pkg = p.parseExport()
return
}
// Import imports a gc-generated package given its import path, adds the
// corresponding package object to the packages map, and returns the object.
// Local import paths are interpreted relative to the current working directory.
// The packages map must contains all packages already imported.
//
func Import(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
if path == "unsafe" {
return types.Unsafe, nil
}
srcDir := "."
if build.IsLocalImport(path) {
srcDir, err = os.Getwd()
if err != nil {
return
}
}
filename, id := FindPkg(path, srcDir)
if filename == "" {
err = fmt.Errorf("can't find import: %s", id)
return
}
// no need to re-import if the package was imported completely before
if pkg = packages[id]; pkg != nil && pkg.Complete() {
return
}
// open file
f, err := os.Open(filename)
if err != nil {
return
}
defer func() {
f.Close()
if err != nil {
// add file name to error
err = fmt.Errorf("reading export data: %s: %v", filename, err)
}
}()
buf := bufio.NewReader(f)
if err = FindExportData(buf); err != nil {
return
}
pkg, err = ImportData(packages, filename, id, buf)
return
}
// ----------------------------------------------------------------------------
// Parser
// TODO(gri) Imported objects don't have position information.
// Ideally use the debug table line info; alternatively
// create some fake position (or the position of the
// import). That way error messages referring to imported
// objects can print meaningful information.
// parser parses the exports inside a gc compiler-produced
// object/archive file and populates its scope with the results.
type parser struct {
scanner scanner.Scanner
tok rune // current token
lit string // literal string; only valid for Ident, Int, String tokens
id string // package id of imported package
sharedPkgs map[string]*types.Package // package id -> package object (across importer)
localPkgs map[string]*types.Package // package id -> package object (just this package)
}
func (p *parser) init(filename, id string, src io.Reader, packages map[string]*types.Package) {
p.scanner.Init(src)
p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
p.scanner.Whitespace = 1<<'\t' | 1<<' '
p.scanner.Filename = filename // for good error messages
p.next()
p.id = id
p.sharedPkgs = packages
if debug {
// check consistency of packages map
for _, pkg := range packages {
if pkg.Name() == "" {
fmt.Printf("no package name for %s\n", pkg.Path())
}
}
}
}
func (p *parser) next() {
p.tok = p.scanner.Scan()
switch p.tok {
case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·':
p.lit = p.scanner.TokenText()
default:
p.lit = ""
}
if debug {
fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
}
}
func declTypeName(pkg *types.Package, name string) *types.TypeName {
scope := pkg.Scope()
if obj := scope.Lookup(name); obj != nil {
return obj.(*types.TypeName)
}
obj := types.NewTypeName(token.NoPos, pkg, name, nil)
// a named type may be referred to before the underlying type
// is known - set it up
types.NewNamed(obj, nil, nil)
scope.Insert(obj)
return obj
}
// ----------------------------------------------------------------------------
// Error handling
// Internal errors are boxed as importErrors.
type importError struct {
pos scanner.Position
err error
}
func (e importError) Error() string {
return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
}
func (p *parser) error(err interface{}) {
if s, ok := err.(string); ok {
err = errors.New(s)
}
// panic with a runtime.Error if err is not an error
panic(importError{p.scanner.Pos(), err.(error)})
}
func (p *parser) errorf(format string, args ...interface{}) {
p.error(fmt.Sprintf(format, args...))
}
func (p *parser) expect(tok rune) string {
lit := p.lit
if p.tok != tok {
p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
}
p.next()
return lit
}
func (p *parser) expectSpecial(tok string) {
sep := 'x' // not white space
i := 0
for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' {
sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
p.next()
i++
}
if i < len(tok) {
p.errorf("expected %q, got %q", tok, tok[0:i])
}
}
func (p *parser) expectKeyword(keyword string) {
lit := p.expect(scanner.Ident)
if lit != keyword {
p.errorf("expected keyword %s, got %q", keyword, lit)
}
}
// ----------------------------------------------------------------------------
// Qualified and unqualified names
// PackageId = string_lit .
//
func (p *parser) parsePackageId() string {
id, err := strconv.Unquote(p.expect(scanner.String))
if err != nil {
p.error(err)
}
// id == "" stands for the imported package id
// (only known at time of package installation)
if id == "" {
id = p.id
}
return id
}
// PackageName = ident .
//
func (p *parser) parsePackageName() string {
return p.expect(scanner.Ident)
}
// dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
func (p *parser) parseDotIdent() string {
ident := ""
if p.tok != scanner.Int {
sep := 'x' // not white space
for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' {
ident += p.lit
sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
p.next()
}
}
if ident == "" {
p.expect(scanner.Ident) // use expect() for error handling
}
return ident
}
// QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) .
//
func (p *parser) parseQualifiedName() (id, name string) {
p.expect('@')
id = p.parsePackageId()
p.expect('.')
// Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields.
if p.tok == '?' {
p.next()
} else {
name = p.parseDotIdent()
}
return
}
// getPkg returns the package for a given id. If the package is
// not found but we have a package name, create the package and
// add it to the p.localPkgs and p.sharedPkgs maps.
//
// id identifies a package, usually by a canonical package path like
// "encoding/json" but possibly by a non-canonical import path like
// "./json".
//
func (p *parser) getPkg(id, name string) *types.Package {
// package unsafe is not in the packages maps - handle explicitly
if id == "unsafe" {
return types.Unsafe
}
pkg := p.localPkgs[id]
if pkg == nil && name != "" {
// first import of id from this package
pkg = p.sharedPkgs[id]
if pkg == nil {
// first import of id by this importer
pkg = types.NewPackage(id, name)
p.sharedPkgs[id] = pkg
}
if p.localPkgs == nil {
p.localPkgs = make(map[string]*types.Package)
}
p.localPkgs[id] = pkg
}
return pkg
}
// parseExportedName is like parseQualifiedName, but
// the package id is resolved to an imported *types.Package.
//
func (p *parser) parseExportedName() (pkg *types.Package, name string) {
id, name := p.parseQualifiedName()
pkg = p.getPkg(id, "")
if pkg == nil {
p.errorf("%s package not found", id)
}
return
}
// ----------------------------------------------------------------------------
// Types
// BasicType = identifier .
//
func (p *parser) parseBasicType() types.Type {
id := p.expect(scanner.Ident)
obj := types.Universe.Lookup(id)
if obj, ok := obj.(*types.TypeName); ok {
return obj.Type()
}
p.errorf("not a basic type: %s", id)
return nil
}
// ArrayType = "[" int_lit "]" Type .
//
func (p *parser) parseArrayType() types.Type {
// "[" already consumed and lookahead known not to be "]"
lit := p.expect(scanner.Int)
p.expect(']')
elem := p.parseType()
n, err := strconv.ParseInt(lit, 10, 64)
if err != nil {
p.error(err)
}
return types.NewArray(elem, n)
}
// MapType = "map" "[" Type "]" Type .
//
func (p *parser) parseMapType() types.Type {
p.expectKeyword("map")
p.expect('[')
key := p.parseType()
p.expect(']')
elem := p.parseType()
return types.NewMap(key, elem)
}
// Name = identifier | "?" | QualifiedName .
//
// If materializePkg is set, the returned package is guaranteed to be set.
// For fully qualified names, the returned package may be a fake package
// (without name, scope, and not in the p.imports map), created for the
// sole purpose of providing a package path. Fake packages are created
// when the package id is not found in the p.imports map; in that case
// we cannot create a real package because we don't have a package name.
// For non-qualified names, the returned package is the imported package.
//
func (p *parser) parseName(materializePkg bool) (pkg *types.Package, name string) {
switch p.tok {
case scanner.Ident:
pkg = p.sharedPkgs[p.id]
name = p.lit
p.next()
case '?':
// anonymous
pkg = p.sharedPkgs[p.id]
p.next()
case '@':
// exported name prefixed with package path
var id string
id, name = p.parseQualifiedName()
if materializePkg {
// we don't have a package name - if the package
// doesn't exist yet, create a fake package instead
pkg = p.getPkg(id, "")
if pkg == nil {
pkg = types.NewPackage(id, "")
}
}
default:
p.error("name expected")
}
return
}
func deref(typ types.Type) types.Type {
if p, _ := typ.(*types.Pointer); p != nil {
return p.Elem()
}
return typ
}
// Field = Name Type [ string_lit ] .
//
func (p *parser) parseField() (*types.Var, string) {
pkg, name := p.parseName(true)
typ := p.parseType()
anonymous := false
if name == "" {
// anonymous field - typ must be T or *T and T must be a type name
switch typ := deref(typ).(type) {
case *types.Basic: // basic types are named types
pkg = nil
name = typ.Name()
case *types.Named:
name = typ.Obj().Name()
default:
p.errorf("anonymous field expected")
}
anonymous = true
}
tag := ""
if p.tok == scanner.String {
s := p.expect(scanner.String)
var err error
tag, err = strconv.Unquote(s)
if err != nil {
p.errorf("invalid struct tag %s: %s", s, err)
}
}
return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag
}
// StructType = "struct" "{" [ FieldList ] "}" .
// FieldList = Field { ";" Field } .
//
func (p *parser) parseStructType() types.Type {
var fields []*types.Var
var tags []string
p.expectKeyword("struct")
p.expect('{')
for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ {
if i > 0 {
p.expect(';')
}
fld, tag := p.parseField()
if tag != "" && tags == nil {
tags = make([]string, i)
}
if tags != nil {
tags = append(tags, tag)
}
fields = append(fields, fld)
}
p.expect('}')
return types.NewStruct(fields, tags)
}
// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
//
func (p *parser) parseParameter() (par *types.Var, isVariadic bool) {
_, name := p.parseName(false)
// remove gc-specific parameter numbering
if i := strings.Index(name, "·"); i >= 0 {
name = name[:i]
}
if p.tok == '.' {
p.expectSpecial("...")
isVariadic = true
}
typ := p.parseType()
if isVariadic {
typ = types.NewSlice(typ)
}
// ignore argument tag (e.g. "noescape")
if p.tok == scanner.String {
p.next()
}
// TODO(gri) should we provide a package?
par = types.NewVar(token.NoPos, nil, name, typ)
return
}
// Parameters = "(" [ ParameterList ] ")" .
// ParameterList = { Parameter "," } Parameter .
//
func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) {
p.expect('(')
for p.tok != ')' && p.tok != scanner.EOF {
if len(list) > 0 {
p.expect(',')
}
par, variadic := p.parseParameter()
list = append(list, par)
if variadic {
if isVariadic {
p.error("... not on final argument")
}
isVariadic = true
}
}
p.expect(')')
return
}
// Signature = Parameters [ Result ] .
// Result = Type | Parameters .
//
func (p *parser) parseSignature(recv *types.Var) *types.Signature {
params, isVariadic := p.parseParameters()
// optional result type
var results []*types.Var
if p.tok == '(' {
var variadic bool
results, variadic = p.parseParameters()
if variadic {
p.error("... not permitted on result type")
}
}
return types.NewSignature(recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic)
}
// InterfaceType = "interface" "{" [ MethodList ] "}" .
// MethodList = Method { ";" Method } .
// Method = Name Signature .
//
// The methods of embedded interfaces are always "inlined"
// by the compiler and thus embedded interfaces are never
// visible in the export data.
//
func (p *parser) parseInterfaceType() types.Type {
var methods []*types.Func
p.expectKeyword("interface")
p.expect('{')
for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ {
if i > 0 {
p.expect(';')
}
pkg, name := p.parseName(true)
sig := p.parseSignature(nil)
methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig))
}
p.expect('}')
// Complete requires the type's embedded interfaces to be fully defined,
// but we do not define any
return types.NewInterface(methods, nil).Complete()
}
// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
//
func (p *parser) parseChanType() types.Type {
dir := types.SendRecv
if p.tok == scanner.Ident {
p.expectKeyword("chan")
if p.tok == '<' {
p.expectSpecial("<-")
dir = types.SendOnly
}
} else {
p.expectSpecial("<-")
p.expectKeyword("chan")
dir = types.RecvOnly
}
elem := p.parseType()
return types.NewChan(dir, elem)
}
// Type =
// BasicType | TypeName | ArrayType | SliceType | StructType |
// PointerType | FuncType | InterfaceType | MapType | ChanType |
// "(" Type ")" .
//
// BasicType = ident .
// TypeName = ExportedName .
// SliceType = "[" "]" Type .
// PointerType = "*" Type .
// FuncType = "func" Signature .
//
func (p *parser) parseType() types.Type {
switch p.tok {
case scanner.Ident:
switch p.lit {
default:
return p.parseBasicType()
case "struct":
return p.parseStructType()
case "func":
// FuncType
p.next()
return p.parseSignature(nil)
case "interface":
return p.parseInterfaceType()
case "map":
return p.parseMapType()
case "chan":
return p.parseChanType()
}
case '@':
// TypeName
pkg, name := p.parseExportedName()
return declTypeName(pkg, name).Type()
case '[':
p.next() // look ahead
if p.tok == ']' {
// SliceType
p.next()
return types.NewSlice(p.parseType())
}
return p.parseArrayType()
case '*':
// PointerType
p.next()
return types.NewPointer(p.parseType())
case '<':
return p.parseChanType()
case '(':
// "(" Type ")"
p.next()
typ := p.parseType()
p.expect(')')
return typ
}
p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit)
return nil
}
// ----------------------------------------------------------------------------
// Declarations
// ImportDecl = "import" PackageName PackageId .
//
func (p *parser) parseImportDecl() {
p.expectKeyword("import")
name := p.parsePackageName()
p.getPkg(p.parsePackageId(), name)
}
// int_lit = [ "+" | "-" ] { "0" ... "9" } .
//
func (p *parser) parseInt() string {
s := ""
switch p.tok {
case '-':
s = "-"
p.next()
case '+':
p.next()
}
return s + p.expect(scanner.Int)
}
// number = int_lit [ "p" int_lit ] .
//
func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) {
// mantissa
mant := exact.MakeFromLiteral(p.parseInt(), token.INT)
if mant == nil {
panic("invalid mantissa")
}
if p.lit == "p" {
// exponent (base 2)
p.next()
exp, err := strconv.ParseInt(p.parseInt(), 10, 0)
if err != nil {
p.error(err)
}
if exp < 0 {
denom := exact.MakeInt64(1)
denom = exact.Shift(denom, token.SHL, uint(-exp))
typ = types.Typ[types.UntypedFloat]
val = exact.BinaryOp(mant, token.QUO, denom)
return
}
if exp > 0 {
mant = exact.Shift(mant, token.SHL, uint(exp))
}
typ = types.Typ[types.UntypedFloat]
val = mant
return
}
typ = types.Typ[types.UntypedInt]
val = mant
return
}
// ConstDecl = "const" ExportedName [ Type ] "=" Literal .
// Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit .
// bool_lit = "true" | "false" .
// complex_lit = "(" float_lit "+" float_lit "i" ")" .
// rune_lit = "(" int_lit "+" int_lit ")" .
// string_lit = `"` { unicode_char } `"` .
//
func (p *parser) parseConstDecl() {
p.expectKeyword("const")
pkg, name := p.parseExportedName()
var typ0 types.Type
if p.tok != '=' {
typ0 = p.parseType()
}
p.expect('=')
var typ types.Type
var val exact.Value
switch p.tok {
case scanner.Ident:
// bool_lit
if p.lit != "true" && p.lit != "false" {
p.error("expected true or false")
}
typ = types.Typ[types.UntypedBool]
val = exact.MakeBool(p.lit == "true")
p.next()
case '-', scanner.Int:
// int_lit
typ, val = p.parseNumber()
case '(':
// complex_lit or rune_lit
p.next()
if p.tok == scanner.Char {
p.next()
p.expect('+')
typ = types.Typ[types.UntypedRune]
_, val = p.parseNumber()
p.expect(')')
break
}
_, re := p.parseNumber()
p.expect('+')
_, im := p.parseNumber()
p.expectKeyword("i")
p.expect(')')
typ = types.Typ[types.UntypedComplex]
val = exact.BinaryOp(re, token.ADD, exact.MakeImag(im))
case scanner.Char:
// rune_lit
typ = types.Typ[types.UntypedRune]
val = exact.MakeFromLiteral(p.lit, token.CHAR)
p.next()
case scanner.String:
// string_lit
typ = types.Typ[types.UntypedString]
val = exact.MakeFromLiteral(p.lit, token.STRING)
p.next()
default:
p.errorf("expected literal got %s", scanner.TokenString(p.tok))
}
if typ0 == nil {
typ0 = typ
}
pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val))
}
// TypeDecl = "type" ExportedName Type .
//
func (p *parser) parseTypeDecl() {
p.expectKeyword("type")
pkg, name := p.parseExportedName()
obj := declTypeName(pkg, name)
// The type object may have been imported before and thus already
// have a type associated with it. We still need to parse the type
// structure, but throw it away if the object already has a type.
// This ensures that all imports refer to the same type object for
// a given type declaration.
typ := p.parseType()
if name := obj.Type().(*types.Named); name.Underlying() == nil {
name.SetUnderlying(typ)
}
}
// VarDecl = "var" ExportedName Type .
//
func (p *parser) parseVarDecl() {
p.expectKeyword("var")
pkg, name := p.parseExportedName()
typ := p.parseType()
pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ))
}
// Func = Signature [ Body ] .
// Body = "{" ... "}" .
//
func (p *parser) parseFunc(recv *types.Var) *types.Signature {
sig := p.parseSignature(recv)
if p.tok == '{' {
p.next()
for i := 1; i > 0; p.next() {
switch p.tok {
case '{':
i++
case '}':
i--
}
}
}
return sig
}
// MethodDecl = "func" Receiver Name Func .
// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
//
func (p *parser) parseMethodDecl() {
// "func" already consumed
p.expect('(')
recv, _ := p.parseParameter() // receiver
p.expect(')')
// determine receiver base type object
base := deref(recv.Type()).(*types.Named)
// parse method name, signature, and possibly inlined body
_, name := p.parseName(true)
sig := p.parseFunc(recv)
// methods always belong to the same package as the base type object
pkg := base.Obj().Pkg()
// add method to type unless type was imported before
// and method exists already
// TODO(gri) This leads to a quadratic algorithm - ok for now because method counts are small.
base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
}
// FuncDecl = "func" ExportedName Func .
//
func (p *parser) parseFuncDecl() {
// "func" already consumed
pkg, name := p.parseExportedName()
typ := p.parseFunc(nil)
pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ))
}
// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
//
func (p *parser) parseDecl() {
if p.tok == scanner.Ident {
switch p.lit {
case "import":
p.parseImportDecl()
case "const":
p.parseConstDecl()
case "type":
p.parseTypeDecl()
case "var":
p.parseVarDecl()
case "func":
p.next() // look ahead
if p.tok == '(' {
p.parseMethodDecl()
} else {
p.parseFuncDecl()
}
}
}
p.expect('\n')
}
// ----------------------------------------------------------------------------
// Export
// Export = "PackageClause { Decl } "$$" .
// PackageClause = "package" PackageName [ "safe" ] "\n" .
//
func (p *parser) parseExport() *types.Package {
p.expectKeyword("package")
name := p.parsePackageName()
if p.tok == scanner.Ident && p.lit == "safe" {
// package was compiled with -u option - ignore
p.next()
}
p.expect('\n')
pkg := p.getPkg(p.id, name)
for p.tok != '$' && p.tok != scanner.EOF {
p.parseDecl()
}
if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' {
// don't call next()/expect() since reading past the
// export data may cause scanner errors (e.g. NUL chars)
p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch)
}
if n := p.scanner.ErrorCount; n != 0 {
p.errorf("expected no scanner errors, got %d", n)
}
// Record all referenced packages as imports.
var imports []*types.Package
for id, pkg2 := range p.localPkgs {
if id == p.id {
continue // avoid self-edge
}
imports = append(imports, pkg2)
}
sort.Sort(byPath(imports))
pkg.SetImports(imports)
// package was imported completely and without errors
pkg.MarkComplete()
return pkg
}
type byPath []*types.Package
func (a byPath) Len() int { return len(a) }
func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }

199
vendor/golang.org/x/tools/go/loader/cgo.go generated vendored Normal file
View File

@ -0,0 +1,199 @@
package loader
// This file handles cgo preprocessing of files containing `import "C"`.
//
// DESIGN
//
// The approach taken is to run the cgo processor on the package's
// CgoFiles and parse the output, faking the filenames of the
// resulting ASTs so that the synthetic file containing the C types is
// called "C" (e.g. "~/go/src/net/C") and the preprocessed files
// have their original names (e.g. "~/go/src/net/cgo_unix.go"),
// not the names of the actual temporary files.
//
// The advantage of this approach is its fidelity to 'go build'. The
// downside is that the token.Position.Offset for each AST node is
// incorrect, being an offset within the temporary file. Line numbers
// should still be correct because of the //line comments.
//
// The logic of this file is mostly plundered from the 'go build'
// tool, which also invokes the cgo preprocessor.
//
//
// REJECTED ALTERNATIVE
//
// An alternative approach that we explored is to extend go/types'
// Importer mechanism to provide the identity of the importing package
// so that each time `import "C"` appears it resolves to a different
// synthetic package containing just the objects needed in that case.
// The loader would invoke cgo but parse only the cgo_types.go file
// defining the package-level objects, discarding the other files
// resulting from preprocessing.
//
// The benefit of this approach would have been that source-level
// syntax information would correspond exactly to the original cgo
// file, with no preprocessing involved, making source tools like
// godoc, oracle, and eg happy. However, the approach was rejected
// due to the additional complexity it would impose on go/types. (It
// made for a beautiful demo, though.)
//
// cgo files, despite their *.go extension, are not legal Go source
// files per the specification since they may refer to unexported
// members of package "C" such as C.int. Also, a function such as
// C.getpwent has in effect two types, one matching its C type and one
// which additionally returns (errno C.int). The cgo preprocessor
// uses name mangling to distinguish these two functions in the
// processed code, but go/types would need to duplicate this logic in
// its handling of function calls, analogous to the treatment of map
// lookups in which y=m[k] and y,ok=m[k] are both legal.
import (
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
)
// processCgoFiles invokes the cgo preprocessor on bp.CgoFiles, parses
// the output and returns the resulting ASTs.
//
func processCgoFiles(bp *build.Package, fset *token.FileSet, DisplayPath func(path string) string, mode parser.Mode) ([]*ast.File, error) {
tmpdir, err := ioutil.TempDir("", strings.Replace(bp.ImportPath, "/", "_", -1)+"_C")
if err != nil {
return nil, err
}
defer os.RemoveAll(tmpdir)
pkgdir := bp.Dir
if DisplayPath != nil {
pkgdir = DisplayPath(pkgdir)
}
cgoFiles, cgoDisplayFiles, err := runCgo(bp, pkgdir, tmpdir)
if err != nil {
return nil, err
}
var files []*ast.File
for i := range cgoFiles {
rd, err := os.Open(cgoFiles[i])
if err != nil {
return nil, err
}
defer rd.Close()
display := filepath.Join(bp.Dir, cgoDisplayFiles[i])
f, err := parser.ParseFile(fset, display, rd, mode)
if err != nil {
return nil, err
}
files = append(files, f)
}
return files, nil
}
var cgoRe = regexp.MustCompile(`[/\\:]`)
// runCgo invokes the cgo preprocessor on bp.CgoFiles and returns two
// lists of files: the resulting processed files (in temporary
// directory tmpdir) and the corresponding names of the unprocessed files.
//
// runCgo is adapted from (*builder).cgo in
// $GOROOT/src/cmd/go/build.go, but these features are unsupported:
// pkg-config, Objective C, CGOPKGPATH, CGO_FLAGS.
//
func runCgo(bp *build.Package, pkgdir, tmpdir string) (files, displayFiles []string, err error) {
cgoCPPFLAGS, _, _, _ := cflags(bp, true)
_, cgoexeCFLAGS, _, _ := cflags(bp, false)
if len(bp.CgoPkgConfig) > 0 {
return nil, nil, fmt.Errorf("cgo pkg-config not supported")
}
// Allows including _cgo_export.h from .[ch] files in the package.
cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", tmpdir)
// _cgo_gotypes.go (displayed "C") contains the type definitions.
files = append(files, filepath.Join(tmpdir, "_cgo_gotypes.go"))
displayFiles = append(displayFiles, "C")
for _, fn := range bp.CgoFiles {
// "foo.cgo1.go" (displayed "foo.go") is the processed Go source.
f := cgoRe.ReplaceAllString(fn[:len(fn)-len("go")], "_")
files = append(files, filepath.Join(tmpdir, f+"cgo1.go"))
displayFiles = append(displayFiles, fn)
}
var cgoflags []string
if bp.Goroot && bp.ImportPath == "runtime/cgo" {
cgoflags = append(cgoflags, "-import_runtime_cgo=false")
}
if bp.Goroot && bp.ImportPath == "runtime/race" || bp.ImportPath == "runtime/cgo" {
cgoflags = append(cgoflags, "-import_syscall=false")
}
args := stringList(
"go", "tool", "cgo", "-objdir", tmpdir, cgoflags, "--",
cgoCPPFLAGS, cgoexeCFLAGS, bp.CgoFiles,
)
if false {
log.Printf("Running cgo for package %q: %s (dir=%s)", bp.ImportPath, args, pkgdir)
}
cmd := exec.Command(args[0], args[1:]...)
cmd.Dir = pkgdir
cmd.Stdout = os.Stderr
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
return nil, nil, fmt.Errorf("cgo failed: %s: %s", args, err)
}
return files, displayFiles, nil
}
// -- unmodified from 'go build' ---------------------------------------
// Return the flags to use when invoking the C or C++ compilers, or cgo.
func cflags(p *build.Package, def bool) (cppflags, cflags, cxxflags, ldflags []string) {
var defaults string
if def {
defaults = "-g -O2"
}
cppflags = stringList(envList("CGO_CPPFLAGS", ""), p.CgoCPPFLAGS)
cflags = stringList(envList("CGO_CFLAGS", defaults), p.CgoCFLAGS)
cxxflags = stringList(envList("CGO_CXXFLAGS", defaults), p.CgoCXXFLAGS)
ldflags = stringList(envList("CGO_LDFLAGS", defaults), p.CgoLDFLAGS)
return
}
// envList returns the value of the given environment variable broken
// into fields, using the default value when the variable is empty.
func envList(key, def string) []string {
v := os.Getenv(key)
if v == "" {
v = def
}
return strings.Fields(v)
}
// stringList's arguments should be a sequence of string or []string values.
// stringList flattens them into a single []string.
func stringList(args ...interface{}) []string {
var x []string
for _, arg := range args {
switch arg := arg.(type) {
case []string:
x = append(x, arg...)
case string:
x = append(x, arg)
default:
panic("stringList: invalid argument")
}
}
return x
}

189
vendor/golang.org/x/tools/go/loader/doc.go generated vendored Normal file
View File

@ -0,0 +1,189 @@
// 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 loader loads a complete Go program from source code, parsing
// and type-checking the initial packages plus their transitive closure
// of dependencies. The ASTs and the derived facts are retained for
// later use.
//
// THIS INTERFACE IS EXPERIMENTAL AND IS LIKELY TO CHANGE.
//
// The package defines two primary types: Config, which specifies a
// set of initial packages to load and various other options; and
// Program, which is the result of successfully loading the packages
// specified by a configuration.
//
// The configuration can be set directly, but *Config provides various
// convenience methods to simplify the common cases, each of which can
// be called any number of times. Finally, these are followed by a
// call to Load() to actually load and type-check the program.
//
// var conf loader.Config
//
// // Use the command-line arguments to specify
// // a set of initial packages to load from source.
// // See FromArgsUsage for help.
// rest, err := conf.FromArgs(os.Args[1:], wantTests)
//
// // Parse the specified files and create an ad hoc package with path "foo".
// // All files must have the same 'package' declaration.
// conf.CreateFromFilenames("foo", "foo.go", "bar.go")
//
// // Create an ad hoc package with path "foo" from
// // the specified already-parsed files.
// // All ASTs must have the same 'package' declaration.
// conf.CreateFromFiles("foo", parsedFiles)
//
// // Add "runtime" to the set of packages to be loaded.
// conf.Import("runtime")
//
// // Adds "fmt" and "fmt_test" to the set of packages
// // to be loaded. "fmt" will include *_test.go files.
// conf.ImportWithTests("fmt")
//
// // Finally, load all the packages specified by the configuration.
// prog, err := conf.Load()
//
// See examples_test.go for examples of API usage.
//
//
// CONCEPTS AND TERMINOLOGY
//
// An AD HOC package is one specified as a set of source files on the
// command line. In the simplest case, it may consist of a single file
// such as $GOROOT/src/net/http/triv.go.
//
// EXTERNAL TEST packages are those comprised of a set of *_test.go
// files all with the same 'package foo_test' declaration, all in the
// same directory. (go/build.Package calls these files XTestFiles.)
//
// An IMPORTABLE package is one that can be referred to by some import
// spec. The Path() of each importable package is unique within a
// Program.
//
// ad hoc packages and external test packages are NON-IMPORTABLE. The
// Path() of an ad hoc package is inferred from the package
// declarations of its files and is therefore not a unique package key.
// For example, Config.CreatePkgs may specify two initial ad hoc
// packages both called "main".
//
// An AUGMENTED package is an importable package P plus all the
// *_test.go files with same 'package foo' declaration as P.
// (go/build.Package calls these files TestFiles.)
//
// The INITIAL packages are those specified in the configuration. A
// DEPENDENCY is a package loaded to satisfy an import in an initial
// package or another dependency.
//
package loader
// IMPLEMENTATION NOTES
//
// 'go test', in-package test files, and import cycles
// ---------------------------------------------------
//
// An external test package may depend upon members of the augmented
// package that are not in the unaugmented package, such as functions
// that expose internals. (See bufio/export_test.go for an example.)
// So, the loader must ensure that for each external test package
// it loads, it also augments the corresponding non-test package.
//
// The import graph over n unaugmented packages must be acyclic; the
// import graph over n-1 unaugmented packages plus one augmented
// package must also be acyclic. ('go test' relies on this.) But the
// import graph over n augmented packages may contain cycles.
//
// First, all the (unaugmented) non-test packages and their
// dependencies are imported in the usual way; the loader reports an
// error if it detects an import cycle.
//
// Then, each package P for which testing is desired is augmented by
// the list P' of its in-package test files, by calling
// (*types.Checker).Files. This arrangement ensures that P' may
// reference definitions within P, but P may not reference definitions
// within P'. Furthermore, P' may import any other package, including
// ones that depend upon P, without an import cycle error.
//
// Consider two packages A and B, both of which have lists of
// in-package test files we'll call A' and B', and which have the
// following import graph edges:
// B imports A
// B' imports A
// A' imports B
// This last edge would be expected to create an error were it not
// for the special type-checking discipline above.
// Cycles of size greater than two are possible. For example:
// compress/bzip2/bzip2_test.go (package bzip2) imports "io/ioutil"
// io/ioutil/tempfile_test.go (package ioutil) imports "regexp"
// regexp/exec_test.go (package regexp) imports "compress/bzip2"
//
//
// Concurrency
// -----------
//
// Let us define the import dependency graph as follows. Each node is a
// list of files passed to (Checker).Files at once. Many of these lists
// are the production code of an importable Go package, so those nodes
// are labelled by the package's import path. The remaining nodes are
// ad hoc packages and lists of in-package *_test.go files that augment
// an importable package; those nodes have no label.
//
// The edges of the graph represent import statements appearing within a
// file. An edge connects a node (a list of files) to the node it
// imports, which is importable and thus always labelled.
//
// Loading is controlled by this dependency graph.
//
// To reduce I/O latency, we start loading a package's dependencies
// asynchronously as soon as we've parsed its files and enumerated its
// imports (scanImports). This performs a preorder traversal of the
// import dependency graph.
//
// To exploit hardware parallelism, we type-check unrelated packages in
// parallel, where "unrelated" means not ordered by the partial order of
// the import dependency graph.
//
// We use a concurrency-safe blocking cache (importer.imported) to
// record the results of type-checking, whether success or failure. An
// entry is created in this cache by startLoad the first time the
// package is imported. The first goroutine to request an entry becomes
// responsible for completing the task and broadcasting completion to
// subsequent requestors, which block until then.
//
// Type checking occurs in (parallel) postorder: we cannot type-check a
// set of files until we have loaded and type-checked all of their
// immediate dependencies (and thus all of their transitive
// dependencies). If the input were guaranteed free of import cycles,
// this would be trivial: we could simply wait for completion of the
// dependencies and then invoke the typechecker.
//
// But as we saw in the 'go test' section above, some cycles in the
// import graph over packages are actually legal, so long as the
// cycle-forming edge originates in the in-package test files that
// augment the package. This explains why the nodes of the import
// dependency graph are not packages, but lists of files: the unlabelled
// nodes avoid the cycles. Consider packages A and B where B imports A
// and A's in-package tests AT import B. The naively constructed import
// graph over packages would contain a cycle (A+AT) --> B --> (A+AT) but
// the graph over lists of files is AT --> B --> A, where AT is an
// unlabelled node.
//
// Awaiting completion of the dependencies in a cyclic graph would
// deadlock, so we must materialize the import dependency graph (as
// importer.graph) and check whether each import edge forms a cycle. If
// x imports y, and the graph already contains a path from y to x, then
// there is an import cycle, in which case the processing of x must not
// wait for the completion of processing of y.
//
// When the type-checker makes a callback (doImport) to the loader for a
// given import edge, there are two possible cases. In the normal case,
// the dependency has already been completely type-checked; doImport
// does a cache lookup and returns it. In the cyclic case, the entry in
// the cache is still necessarily incomplete, indicating a cycle. We
// perform the cycle check again to obtain the error message, and return
// the error.
//
// The result of using concurrency is about a 2.5x speedup for stdlib_test.
// TODO(adonovan): overhaul the package documentation.

966
vendor/golang.org/x/tools/go/loader/loader.go generated vendored Normal file
View File

@ -0,0 +1,966 @@
// Copyright 2013 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 loader
// See doc.go for package documentation and implementation notes.
import (
"errors"
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"os"
"sort"
"strings"
"sync"
"time"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/types"
)
const trace = false // show timing info for type-checking
// Config specifies the configuration for loading a whole program from
// Go source code.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
// Fset is the file set for the parser to use when loading the
// program. If nil, it may be lazily initialized by any
// method of Config.
Fset *token.FileSet
// ParserMode specifies the mode to be used by the parser when
// loading source packages.
ParserMode parser.Mode
// TypeChecker contains options relating to the type checker.
//
// The supplied IgnoreFuncBodies is not used; the effective
// value comes from the TypeCheckFuncBodies func below.
// The supplied Import function is not used either.
TypeChecker types.Config
// TypeCheckFuncBodies is a predicate over package import
// paths. A package for which the predicate is false will
// have its package-level declarations type checked, but not
// its function bodies; this can be used to quickly load
// dependencies from source. If nil, all func bodies are type
// checked.
TypeCheckFuncBodies func(string) bool
// If Build is non-nil, it is used to locate source packages.
// Otherwise &build.Default is used.
//
// By default, cgo is invoked to preprocess Go files that
// import the fake package "C". This behaviour can be
// disabled by setting CGO_ENABLED=0 in the environment prior
// to startup, or by setting Build.CgoEnabled=false.
Build *build.Context
// The current directory, used for resolving relative package
// references such as "./go/loader". If empty, os.Getwd will be
// used instead.
Cwd string
// If DisplayPath is non-nil, it is used to transform each
// file name obtained from Build.Import(). This can be used
// to prevent a virtualized build.Config's file names from
// leaking into the user interface.
DisplayPath func(path string) string
// If AllowErrors is true, Load will return a Program even
// if some of the its packages contained I/O, parser or type
// errors; such errors are accessible via PackageInfo.Errors. If
// false, Load will fail if any package had an error.
AllowErrors bool
// CreatePkgs specifies a list of non-importable initial
// packages to create. The resulting packages will appear in
// the corresponding elements of the Program.Created slice.
CreatePkgs []PkgSpec
// ImportPkgs specifies a set of initial packages to load from
// source. The map keys are package import paths, used to
// locate the package relative to $GOROOT.
//
// The map value indicates whether to load tests. If true, Load
// will add and type-check two lists of files to the package:
// non-test files followed by in-package *_test.go files. In
// addition, it will append the external test package (if any)
// to Program.Created.
ImportPkgs map[string]bool
// FindPackage is called during Load to create the build.Package
// for a given import path. If nil, a default implementation
// based on ctxt.Import is used. A client may use this hook to
// adapt to a proprietary build system that does not follow the
// "go build" layout conventions, for example.
//
// It must be safe to call concurrently from multiple goroutines.
FindPackage func(ctxt *build.Context, importPath string) (*build.Package, error)
}
// A PkgSpec specifies a non-importable package to be created by Load.
// Files are processed first, but typically only one of Files and
// Filenames is provided. The path needn't be globally unique.
//
type PkgSpec struct {
Path string // import path ("" => use package declaration)
Files []*ast.File // ASTs of already-parsed files
Filenames []string // names of files to be parsed
}
// A Program is a Go program loaded from source as specified by a Config.
type Program struct {
Fset *token.FileSet // the file set for this program
// Created[i] contains the initial package whose ASTs or
// filenames were supplied by Config.CreatePkgs[i], followed by
// the external test package, if any, of each package in
// Config.ImportPkgs ordered by ImportPath.
Created []*PackageInfo
// Imported contains the initially imported packages,
// as specified by Config.ImportPkgs.
Imported map[string]*PackageInfo
// AllPackages contains the PackageInfo of every package
// encountered by Load: all initial packages and all
// dependencies, including incomplete ones.
AllPackages map[*types.Package]*PackageInfo
// importMap is the canonical mapping of import paths to
// packages. It contains all Imported initial packages, but not
// Created ones, and all imported dependencies.
importMap map[string]*types.Package
}
// PackageInfo holds the ASTs and facts derived by the type-checker
// for a single package.
//
// Not mutated once exposed via the API.
//
type PackageInfo struct {
Pkg *types.Package
Importable bool // true if 'import "Pkg.Path()"' would resolve to this
TransitivelyErrorFree bool // true if Pkg and all its dependencies are free of errors
Files []*ast.File // syntax trees for the package's files
Errors []error // non-nil if the package had errors
types.Info // type-checker deductions.
checker *types.Checker // transient type-checker state
errorFunc func(error)
}
func (info *PackageInfo) String() string { return info.Pkg.Path() }
func (info *PackageInfo) appendError(err error) {
if info.errorFunc != nil {
info.errorFunc(err)
} else {
fmt.Fprintln(os.Stderr, err)
}
info.Errors = append(info.Errors, err)
}
func (conf *Config) fset() *token.FileSet {
if conf.Fset == nil {
conf.Fset = token.NewFileSet()
}
return conf.Fset
}
// ParseFile is a convenience function (intended for testing) that invokes
// the parser using the Config's FileSet, which is initialized if nil.
//
// src specifies the parser input as a string, []byte, or io.Reader, and
// filename is its apparent name. If src is nil, the contents of
// filename are read from the file system.
//
func (conf *Config) ParseFile(filename string, src interface{}) (*ast.File, error) {
// TODO(adonovan): use conf.build() etc like parseFiles does.
return parser.ParseFile(conf.fset(), filename, src, conf.ParserMode)
}
// FromArgsUsage is a partial usage message that applications calling
// FromArgs may wish to include in their -help output.
const FromArgsUsage = `
<args> is a list of arguments denoting a set of initial packages.
It may take one of two forms:
1. A list of *.go source files.
All of the specified files are loaded, parsed and type-checked
as a single package. All the files must belong to the same directory.
2. A list of import paths, each denoting a package.
The package's directory is found relative to the $GOROOT and
$GOPATH using similar logic to 'go build', and the *.go files in
that directory are loaded, parsed and type-checked as a single
package.
In addition, all *_test.go files in the directory are then loaded
and parsed. Those files whose package declaration equals that of
the non-*_test.go files are included in the primary package. Test
files whose package declaration ends with "_test" are type-checked
as another package, the 'external' test package, so that a single
import path may denote two packages. (Whether this behaviour is
enabled is tool-specific, and may depend on additional flags.)
A '--' argument terminates the list of packages.
`
// FromArgs interprets args as a set of initial packages to load from
// source and updates the configuration. It returns the list of
// unconsumed arguments.
//
// It is intended for use in command-line interfaces that require a
// set of initial packages to be specified; see FromArgsUsage message
// for details.
//
// Only superficial errors are reported at this stage; errors dependent
// on I/O are detected during Load.
//
func (conf *Config) FromArgs(args []string, xtest bool) ([]string, error) {
var rest []string
for i, arg := range args {
if arg == "--" {
rest = args[i+1:]
args = args[:i]
break // consume "--" and return the remaining args
}
}
if len(args) > 0 && strings.HasSuffix(args[0], ".go") {
// Assume args is a list of a *.go files
// denoting a single ad hoc package.
for _, arg := range args {
if !strings.HasSuffix(arg, ".go") {
return nil, fmt.Errorf("named files must be .go files: %s", arg)
}
}
conf.CreateFromFilenames("", args...)
} else {
// Assume args are directories each denoting a
// package and (perhaps) an external test, iff xtest.
for _, arg := range args {
if xtest {
conf.ImportWithTests(arg)
} else {
conf.Import(arg)
}
}
}
return rest, nil
}
// CreateFromFilenames is a convenience function that adds
// a conf.CreatePkgs entry to create a package of the specified *.go
// files.
//
func (conf *Config) CreateFromFilenames(path string, filenames ...string) {
conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Filenames: filenames})
}
// CreateFromFiles is a convenience function that adds a conf.CreatePkgs
// entry to create package of the specified path and parsed files.
//
func (conf *Config) CreateFromFiles(path string, files ...*ast.File) {
conf.CreatePkgs = append(conf.CreatePkgs, PkgSpec{Path: path, Files: files})
}
// ImportWithTests is a convenience function that adds path to
// ImportPkgs, the set of initial source packages located relative to
// $GOPATH. The package will be augmented by any *_test.go files in
// its directory that contain a "package x" (not "package x_test")
// declaration.
//
// In addition, if any *_test.go files contain a "package x_test"
// declaration, an additional package comprising just those files will
// be added to CreatePkgs.
//
func (conf *Config) ImportWithTests(path string) { conf.addImport(path, true) }
// Import is a convenience function that adds path to ImportPkgs, the
// set of initial packages that will be imported from source.
//
func (conf *Config) Import(path string) { conf.addImport(path, false) }
func (conf *Config) addImport(path string, tests bool) {
if path == "C" || path == "unsafe" {
return // ignore; not a real package
}
if conf.ImportPkgs == nil {
conf.ImportPkgs = make(map[string]bool)
}
conf.ImportPkgs[path] = conf.ImportPkgs[path] || tests
}
// PathEnclosingInterval returns the PackageInfo and ast.Node that
// contain source interval [start, end), and all the node's ancestors
// up to the AST root. It searches all ast.Files of all packages in prog.
// exact is defined as for astutil.PathEnclosingInterval.
//
// The zero value is returned if not found.
//
func (prog *Program) PathEnclosingInterval(start, end token.Pos) (pkg *PackageInfo, path []ast.Node, exact bool) {
for _, info := range prog.AllPackages {
for _, f := range info.Files {
if f.Pos() == token.NoPos {
// This can happen if the parser saw
// too many errors and bailed out.
// (Use parser.AllErrors to prevent that.)
continue
}
if !tokenFileContainsPos(prog.Fset.File(f.Pos()), start) {
continue
}
if path, exact := astutil.PathEnclosingInterval(f, start, end); path != nil {
return info, path, exact
}
}
}
return nil, nil, false
}
// InitialPackages returns a new slice containing the set of initial
// packages (Created + Imported) in unspecified order.
//
func (prog *Program) InitialPackages() []*PackageInfo {
infos := make([]*PackageInfo, 0, len(prog.Created)+len(prog.Imported))
infos = append(infos, prog.Created...)
for _, info := range prog.Imported {
infos = append(infos, info)
}
return infos
}
// Package returns the ASTs and results of type checking for the
// specified package.
func (prog *Program) Package(path string) *PackageInfo {
if info, ok := prog.AllPackages[prog.importMap[path]]; ok {
return info
}
for _, info := range prog.Created {
if path == info.Pkg.Path() {
return info
}
}
return nil
}
// ---------- Implementation ----------
// importer holds the working state of the algorithm.
type importer struct {
conf *Config // the client configuration
start time.Time // for logging
progMu sync.Mutex // guards prog
prog *Program // the resulting program
importedMu sync.Mutex // guards imported
imported map[string]*importInfo // all imported packages (incl. failures) by import path
// import dependency graph: graph[x][y] => x imports y
//
// Since non-importable packages cannot be cyclic, we ignore
// their imports, thus we only need the subgraph over importable
// packages. Nodes are identified by their import paths.
graphMu sync.Mutex
graph map[string]map[string]bool
}
// importInfo tracks the success or failure of a single import.
//
// Upon completion, exactly one of info and err is non-nil:
// info on successful creation of a package, err otherwise.
// A successful package may still contain type errors.
//
type importInfo struct {
path string // import path
mu sync.Mutex // guards the following fields prior to completion
info *PackageInfo // results of typechecking (including errors)
err error // reason for failure to create a package
complete sync.Cond // complete condition is that one of info, err is non-nil.
}
// awaitCompletion blocks until ii is complete,
// i.e. the info and err fields are safe to inspect without a lock.
// It is concurrency-safe and idempotent.
func (ii *importInfo) awaitCompletion() {
ii.mu.Lock()
for ii.info == nil && ii.err == nil {
ii.complete.Wait()
}
ii.mu.Unlock()
}
// Complete marks ii as complete.
// Its info and err fields will not be subsequently updated.
func (ii *importInfo) Complete(info *PackageInfo, err error) {
if info == nil && err == nil {
panic("Complete(nil, nil)")
}
ii.mu.Lock()
ii.info = info
ii.err = err
ii.complete.Broadcast()
ii.mu.Unlock()
}
// Load creates the initial packages specified by conf.{Create,Import}Pkgs,
// loading their dependencies packages as needed.
//
// On success, Load returns a Program containing a PackageInfo for
// each package. On failure, it returns an error.
//
// If AllowErrors is true, Load will return a Program even if some
// packages contained I/O, parser or type errors, or if dependencies
// were missing. (Such errors are accessible via PackageInfo.Errors. If
// false, Load will fail if any package had an error.
//
// It is an error if no packages were loaded.
//
func (conf *Config) Load() (*Program, error) {
// Create a simple default error handler for parse/type errors.
if conf.TypeChecker.Error == nil {
conf.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
}
// Set default working directory for relative package references.
if conf.Cwd == "" {
var err error
conf.Cwd, err = os.Getwd()
if err != nil {
return nil, err
}
}
// Install default FindPackage hook using go/build logic.
if conf.FindPackage == nil {
conf.FindPackage = func(ctxt *build.Context, path string) (*build.Package, error) {
// TODO(adonovan): cache calls to build.Import
// so we don't do it three times per test package.
bp, err := ctxt.Import(path, conf.Cwd, 0)
if _, ok := err.(*build.NoGoError); ok {
return bp, nil // empty directory is not an error
}
return bp, err
}
}
prog := &Program{
Fset: conf.fset(),
Imported: make(map[string]*PackageInfo),
importMap: make(map[string]*types.Package),
AllPackages: make(map[*types.Package]*PackageInfo),
}
imp := importer{
conf: conf,
prog: prog,
imported: make(map[string]*importInfo),
start: time.Now(),
graph: make(map[string]map[string]bool),
}
// -- loading proper (concurrent phase) --------------------------------
var errpkgs []string // packages that contained errors
// Load the initially imported packages and their dependencies,
// in parallel.
for _, ii := range imp.loadAll("", conf.ImportPkgs) {
if ii.err != nil {
conf.TypeChecker.Error(ii.err) // failed to create package
errpkgs = append(errpkgs, ii.path)
continue
}
prog.Imported[ii.info.Pkg.Path()] = ii.info
}
// Augment the designated initial packages by their tests.
// Dependencies are loaded in parallel.
var xtestPkgs []*build.Package
for path, augment := range conf.ImportPkgs {
if !augment {
continue
}
bp, err := conf.FindPackage(conf.build(), path)
if err != nil {
// Package not found, or can't even parse package declaration.
// Already reported by previous loop; ignore it.
continue
}
// Needs external test package?
if len(bp.XTestGoFiles) > 0 {
xtestPkgs = append(xtestPkgs, bp)
}
imp.importedMu.Lock() // (unnecessary, we're sequential here)
ii, ok := imp.imported[path]
// Paranoid checks added due to issue #11012.
if !ok {
// Unreachable.
// The previous loop called loadAll and thus
// startLoad for each path in ImportPkgs, which
// populates imp.imported[path] with a non-zero value.
panic(fmt.Sprintf("imported[%q] not found", path))
}
if ii == nil {
// Unreachable.
// The ii values in this loop are the same as in
// the previous loop, which enforced the invariant
// that at least one of ii.err and ii.info is non-nil.
panic(fmt.Sprintf("imported[%q] == nil", path))
}
if ii.err != nil {
// The sole possible cause is failure of the
// FindPackage call in (*importer).load,
// but we rechecked that condition above.
// Perhaps the state of the file system changed
// in between? Seems unlikely.
panic(fmt.Sprintf("imported[%q].err = %v", path, ii.err))
}
if ii.info == nil {
// Unreachable.
// Complete has this postcondition:
// ii.err != nil || ii.info != nil
// and we know that ii.err == nil here.
panic(fmt.Sprintf("imported[%q].info = nil", path))
}
info := ii.info
imp.importedMu.Unlock()
// Parse the in-package test files.
files, errs := imp.conf.parsePackageFiles(bp, 't')
for _, err := range errs {
info.appendError(err)
}
// The test files augmenting package P cannot be imported,
// but may import packages that import P,
// so we must disable the cycle check.
imp.addFiles(info, files, false)
}
createPkg := func(path string, files []*ast.File, errs []error) {
info := imp.newPackageInfo(path)
for _, err := range errs {
info.appendError(err)
}
// Ad hoc packages are non-importable,
// so no cycle check is needed.
// addFiles loads dependencies in parallel.
imp.addFiles(info, files, false)
prog.Created = append(prog.Created, info)
}
// Create packages specified by conf.CreatePkgs.
for _, cp := range conf.CreatePkgs {
files, errs := parseFiles(conf.fset(), conf.build(), nil, ".", cp.Filenames, conf.ParserMode)
files = append(files, cp.Files...)
path := cp.Path
if path == "" {
if len(files) > 0 {
path = files[0].Name.Name
} else {
path = "(unnamed)"
}
}
createPkg(path, files, errs)
}
// Create external test packages.
sort.Sort(byImportPath(xtestPkgs))
for _, bp := range xtestPkgs {
files, errs := imp.conf.parsePackageFiles(bp, 'x')
createPkg(bp.ImportPath+"_test", files, errs)
}
// -- finishing up (sequential) ----------------------------------------
if len(prog.Imported)+len(prog.Created) == 0 {
return nil, errors.New("no initial packages were loaded")
}
// Create infos for indirectly imported packages.
// e.g. incomplete packages without syntax, loaded from export data.
for _, obj := range prog.importMap {
info := prog.AllPackages[obj]
if info == nil {
prog.AllPackages[obj] = &PackageInfo{Pkg: obj, Importable: true}
} else {
// finished
info.checker = nil
info.errorFunc = nil
}
}
if !conf.AllowErrors {
// Report errors in indirectly imported packages.
for _, info := range prog.AllPackages {
if len(info.Errors) > 0 {
errpkgs = append(errpkgs, info.Pkg.Path())
}
}
if errpkgs != nil {
var more string
if len(errpkgs) > 3 {
more = fmt.Sprintf(" and %d more", len(errpkgs)-3)
errpkgs = errpkgs[:3]
}
return nil, fmt.Errorf("couldn't load packages due to errors: %s%s",
strings.Join(errpkgs, ", "), more)
}
}
markErrorFreePackages(prog.AllPackages)
return prog, nil
}
type byImportPath []*build.Package
func (b byImportPath) Len() int { return len(b) }
func (b byImportPath) Less(i, j int) bool { return b[i].ImportPath < b[j].ImportPath }
func (b byImportPath) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
// markErrorFreePackages sets the TransitivelyErrorFree flag on all
// applicable packages.
func markErrorFreePackages(allPackages map[*types.Package]*PackageInfo) {
// Build the transpose of the import graph.
importedBy := make(map[*types.Package]map[*types.Package]bool)
for P := range allPackages {
for _, Q := range P.Imports() {
clients, ok := importedBy[Q]
if !ok {
clients = make(map[*types.Package]bool)
importedBy[Q] = clients
}
clients[P] = true
}
}
// Find all packages reachable from some error package.
reachable := make(map[*types.Package]bool)
var visit func(*types.Package)
visit = func(p *types.Package) {
if !reachable[p] {
reachable[p] = true
for q := range importedBy[p] {
visit(q)
}
}
}
for _, info := range allPackages {
if len(info.Errors) > 0 {
visit(info.Pkg)
}
}
// Mark the others as "transitively error-free".
for _, info := range allPackages {
if !reachable[info.Pkg] {
info.TransitivelyErrorFree = true
}
}
}
// build returns the effective build context.
func (conf *Config) build() *build.Context {
if conf.Build != nil {
return conf.Build
}
return &build.Default
}
// parsePackageFiles enumerates the files belonging to package path,
// then loads, parses and returns them, plus a list of I/O or parse
// errors that were encountered.
//
// 'which' indicates which files to include:
// 'g': include non-test *.go source files (GoFiles + processed CgoFiles)
// 't': include in-package *_test.go source files (TestGoFiles)
// 'x': include external *_test.go source files. (XTestGoFiles)
//
func (conf *Config) parsePackageFiles(bp *build.Package, which rune) ([]*ast.File, []error) {
var filenames []string
switch which {
case 'g':
filenames = bp.GoFiles
case 't':
filenames = bp.TestGoFiles
case 'x':
filenames = bp.XTestGoFiles
default:
panic(which)
}
files, errs := parseFiles(conf.fset(), conf.build(), conf.DisplayPath, bp.Dir, filenames, conf.ParserMode)
// Preprocess CgoFiles and parse the outputs (sequentially).
if which == 'g' && bp.CgoFiles != nil {
cgofiles, err := processCgoFiles(bp, conf.fset(), conf.DisplayPath, conf.ParserMode)
if err != nil {
errs = append(errs, err)
} else {
files = append(files, cgofiles...)
}
}
return files, errs
}
// doImport imports the package denoted by path.
// It implements the types.Importer signature.
//
// imports is the type-checker's package canonicalization map.
//
// It returns an error if a package could not be created
// (e.g. go/build or parse error), but type errors are reported via
// the types.Config.Error callback (the first of which is also saved
// in the package's PackageInfo).
//
// Idempotent.
//
func (imp *importer) doImport(from *PackageInfo, to string) (*types.Package, error) {
// Package unsafe is handled specially, and has no PackageInfo.
// TODO(adonovan): move this check into go/types?
if to == "unsafe" {
return types.Unsafe, nil
}
if to == "C" {
// This should be unreachable, but ad hoc packages are
// not currently subject to cgo preprocessing.
// See https://github.com/golang/go/issues/11627.
return nil, fmt.Errorf(`the loader doesn't cgo-process ad hoc packages like %q; see Go issue 11627`,
from.Pkg.Path())
}
imp.importedMu.Lock()
ii := imp.imported[to]
imp.importedMu.Unlock()
if ii == nil {
panic("internal error: unexpected import: " + to)
}
if ii.err != nil {
return nil, ii.err
}
if ii.info != nil {
return ii.info.Pkg, nil
}
// Import of incomplete package: this indicates a cycle.
fromPath := from.Pkg.Path()
if cycle := imp.findPath(to, fromPath); cycle != nil {
cycle = append([]string{fromPath}, cycle...)
return nil, fmt.Errorf("import cycle: %s", strings.Join(cycle, " -> "))
}
panic("internal error: import of incomplete (yet acyclic) package: " + fromPath)
}
// loadAll loads, parses, and type-checks the specified packages in
// parallel and returns their completed importInfos in unspecified order.
//
// fromPath is the import path of the importing package, if it is
// importable, "" otherwise. It is used for cycle detection.
//
func (imp *importer) loadAll(fromPath string, paths map[string]bool) []*importInfo {
result := make([]*importInfo, 0, len(paths))
for path := range paths {
result = append(result, imp.startLoad(path))
}
if fromPath != "" {
// We're loading a set of imports.
//
// We must record graph edges from the importing package
// to its dependencies, and check for cycles.
imp.graphMu.Lock()
deps, ok := imp.graph[fromPath]
if !ok {
deps = make(map[string]bool)
imp.graph[fromPath] = deps
}
for path := range paths {
deps[path] = true
}
imp.graphMu.Unlock()
}
for _, ii := range result {
if fromPath != "" {
if cycle := imp.findPath(ii.path, fromPath); cycle != nil {
// Cycle-forming import: we must not await its
// completion since it would deadlock.
//
// We don't record the error in ii since
// the error is really associated with the
// cycle-forming edge, not the package itself.
// (Also it would complicate the
// invariants of importPath completion.)
if trace {
fmt.Fprintln(os.Stderr, "import cycle: %q", cycle)
}
continue
}
}
ii.awaitCompletion()
}
return result
}
// findPath returns an arbitrary path from 'from' to 'to' in the import
// graph, or nil if there was none.
func (imp *importer) findPath(from, to string) []string {
imp.graphMu.Lock()
defer imp.graphMu.Unlock()
seen := make(map[string]bool)
var search func(stack []string, importPath string) []string
search = func(stack []string, importPath string) []string {
if !seen[importPath] {
seen[importPath] = true
stack = append(stack, importPath)
if importPath == to {
return stack
}
for x := range imp.graph[importPath] {
if p := search(stack, x); p != nil {
return p
}
}
}
return nil
}
return search(make([]string, 0, 20), from)
}
// startLoad initiates the loading, parsing and type-checking of the
// specified package and its dependencies, if it has not already begun.
//
// It returns an importInfo, not necessarily in a completed state. The
// caller must call awaitCompletion() before accessing its info and err
// fields.
//
// startLoad is concurrency-safe and idempotent.
//
func (imp *importer) startLoad(path string) *importInfo {
imp.importedMu.Lock()
ii, ok := imp.imported[path]
if !ok {
ii = &importInfo{path: path}
ii.complete.L = &ii.mu
imp.imported[path] = ii
go func() {
ii.Complete(imp.load(path))
}()
}
imp.importedMu.Unlock()
return ii
}
// load implements package loading by parsing Go source files
// located by go/build.
//
func (imp *importer) load(path string) (*PackageInfo, error) {
bp, err := imp.conf.FindPackage(imp.conf.build(), path)
if err != nil {
return nil, err // package not found
}
info := imp.newPackageInfo(bp.ImportPath)
info.Importable = true
files, errs := imp.conf.parsePackageFiles(bp, 'g')
for _, err := range errs {
info.appendError(err)
}
imp.addFiles(info, files, true)
imp.progMu.Lock()
imp.prog.importMap[path] = info.Pkg
imp.progMu.Unlock()
return info, nil
}
// addFiles adds and type-checks the specified files to info, loading
// their dependencies if needed. The order of files determines the
// package initialization order. It may be called multiple times on the
// same package. Errors are appended to the info.Errors field.
//
// cycleCheck determines whether the imports within files create
// dependency edges that should be checked for potential cycles.
//
func (imp *importer) addFiles(info *PackageInfo, files []*ast.File, cycleCheck bool) {
info.Files = append(info.Files, files...)
// Ensure the dependencies are loaded, in parallel.
var fromPath string
if cycleCheck {
fromPath = info.Pkg.Path()
}
imp.loadAll(fromPath, scanImports(files))
if trace {
fmt.Fprintf(os.Stderr, "%s: start %q (%d)\n",
time.Since(imp.start), info.Pkg.Path(), len(files))
}
// Ignore the returned (first) error since we
// already collect them all in the PackageInfo.
info.checker.Files(files)
if trace {
fmt.Fprintf(os.Stderr, "%s: stop %q\n",
time.Since(imp.start), info.Pkg.Path())
}
}
func (imp *importer) newPackageInfo(path string) *PackageInfo {
pkg := types.NewPackage(path, "")
info := &PackageInfo{
Pkg: pkg,
Info: types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
},
errorFunc: imp.conf.TypeChecker.Error,
}
// Copy the types.Config so we can vary it across PackageInfos.
tc := imp.conf.TypeChecker
tc.IgnoreFuncBodies = false
if f := imp.conf.TypeCheckFuncBodies; f != nil {
tc.IgnoreFuncBodies = !f(path)
}
tc.Import = func(_ map[string]*types.Package, to string) (*types.Package, error) {
return imp.doImport(info, to)
}
tc.Error = info.appendError // appendError wraps the user's Error function
info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info)
imp.progMu.Lock()
imp.prog.AllPackages[pkg] = info
imp.progMu.Unlock()
return info
}

124
vendor/golang.org/x/tools/go/loader/util.go generated vendored Normal file
View File

@ -0,0 +1,124 @@
// Copyright 2013 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 loader
import (
"go/ast"
"go/build"
"go/parser"
"go/token"
"io"
"os"
"strconv"
"sync"
"golang.org/x/tools/go/buildutil"
)
// We use a counting semaphore to limit
// the number of parallel I/O calls per process.
var sema = make(chan bool, 10)
// parseFiles parses the Go source files within directory dir and
// returns the ASTs of the ones that could be at least partially parsed,
// along with a list of I/O and parse errors encountered.
//
// I/O is done via ctxt, which may specify a virtual file system.
// displayPath is used to transform the filenames attached to the ASTs.
//
func parseFiles(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, files []string, mode parser.Mode) ([]*ast.File, []error) {
if displayPath == nil {
displayPath = func(path string) string { return path }
}
var wg sync.WaitGroup
n := len(files)
parsed := make([]*ast.File, n)
errors := make([]error, n)
for i, file := range files {
if !buildutil.IsAbsPath(ctxt, file) {
file = buildutil.JoinPath(ctxt, dir, file)
}
wg.Add(1)
go func(i int, file string) {
sema <- true // wait
defer func() {
wg.Done()
<-sema // signal
}()
var rd io.ReadCloser
var err error
if ctxt.OpenFile != nil {
rd, err = ctxt.OpenFile(file)
} else {
rd, err = os.Open(file)
}
if err != nil {
errors[i] = err // open failed
return
}
// ParseFile may return both an AST and an error.
parsed[i], errors[i] = parser.ParseFile(fset, displayPath(file), rd, mode)
rd.Close()
}(i, file)
}
wg.Wait()
// Eliminate nils, preserving order.
var o int
for _, f := range parsed {
if f != nil {
parsed[o] = f
o++
}
}
parsed = parsed[:o]
o = 0
for _, err := range errors {
if err != nil {
errors[o] = err
o++
}
}
errors = errors[:o]
return parsed, errors
}
// scanImports returns the set of all package import paths from all
// import specs in the specified files.
func scanImports(files []*ast.File) map[string]bool {
imports := make(map[string]bool)
for _, f := range files {
for _, decl := range f.Decls {
if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT {
for _, spec := range decl.Specs {
spec := spec.(*ast.ImportSpec)
// NB: do not assume the program is well-formed!
path, err := strconv.Unquote(spec.Path.Value)
if err != nil {
continue // quietly ignore the error
}
if path == "C" || path == "unsafe" {
continue // skip pseudo packages
}
imports[path] = true
}
}
}
}
return imports
}
// ---------- Internal helpers ----------
// TODO(adonovan): make this a method: func (*token.File) Contains(token.Pos)
func tokenFileContainsPos(f *token.File, pos token.Pos) bool {
p := int(pos)
base := f.Base()
return base <= p && p < base+f.Size()
}

33
vendor/golang.org/x/tools/go/pointer/TODO generated vendored Normal file
View File

@ -0,0 +1,33 @@
-*- text -*-
Pointer analysis to-do list
===========================
CONSTRAINT GENERATION:
- support reflection:
- a couple of operators are missing
- reflect.Values may contain lvalues (CanAddr)
- implement native intrinsics. These vary by platform.
- add to pts(a.panic) a label representing all runtime panics, e.g.
runtime.{TypeAssertionError,errorString,errorCString}.
OPTIMISATIONS
- pre-solver:
pointer equivalence: extend HVN to HRU
location equivalence
- solver: HCD, LCD.
- experiment with map+slice worklist in lieu of bitset.
It may have faster insert.
MISC:
- Test on all platforms.
Currently we assume these go/build tags: linux, amd64, !cgo.
MAINTAINABILITY
- Think about ways to make debugging this code easier. PTA logs
routinely exceed a million lines and require training to read.
BUGS:
- There's a crash bug in stdlib_test + reflection, rVCallConstraint.

447
vendor/golang.org/x/tools/go/pointer/analysis.go generated vendored Normal file
View File

@ -0,0 +1,447 @@
// Copyright 2013 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 pointer
// This file defines the main datatypes and Analyze function of the pointer analysis.
import (
"fmt"
"go/token"
"io"
"os"
"reflect"
"runtime"
"runtime/debug"
"sort"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
const (
// optimization options; enable all when committing
optRenumber = true // enable renumbering optimization (makes logs hard to read)
optHVN = true // enable pointer equivalence via Hash-Value Numbering
// debugging options; disable all when committing
debugHVN = false // enable assertions in HVN
debugHVNVerbose = false // enable extra HVN logging
debugHVNCrossCheck = false // run solver with/without HVN and compare (caveats below)
debugTimers = false // show running time of each phase
)
// object.flags bitmask values.
const (
otTagged = 1 << iota // type-tagged object
otIndirect // type-tagged object with indirect payload
otFunction // function object
)
// An object represents a contiguous block of memory to which some
// (generalized) pointer may point.
//
// (Note: most variables called 'obj' are not *objects but nodeids
// such that a.nodes[obj].obj != nil.)
//
type object struct {
// flags is a bitset of the node type (ot*) flags defined above.
flags uint32
// Number of following nodes belonging to the same "object"
// allocation. Zero for all other nodes.
size uint32
// data describes this object; it has one of these types:
//
// ssa.Value for an object allocated by an SSA operation.
// types.Type for an rtype instance object or *rtype-tagged object.
// string for an instrinsic object, e.g. the array behind os.Args.
// nil for an object allocated by an instrinsic.
// (cgn provides the identity of the intrinsic.)
data interface{}
// The call-graph node (=context) in which this object was allocated.
// May be nil for global objects: Global, Const, some Functions.
cgn *cgnode
}
// nodeid denotes a node.
// It is an index within analysis.nodes.
// We use small integers, not *node pointers, for many reasons:
// - they are smaller on 64-bit systems.
// - sets of them can be represented compactly in bitvectors or BDDs.
// - order matters; a field offset can be computed by simple addition.
type nodeid uint32
// A node is an equivalence class of memory locations.
// Nodes may be pointers, pointed-to locations, neither, or both.
//
// Nodes that are pointed-to locations ("labels") have an enclosing
// object (see analysis.enclosingObject).
//
type node struct {
// If non-nil, this node is the start of an object
// (addressable memory location).
// The following obj.size nodes implicitly belong to the object;
// they locate their object by scanning back.
obj *object
// The type of the field denoted by this node. Non-aggregate,
// unless this is an tagged.T node (i.e. the thing
// pointed to by an interface) in which case typ is that type.
typ types.Type
// subelement indicates which directly embedded subelement of
// an object of aggregate type (struct, tuple, array) this is.
subelement *fieldInfo // e.g. ".a.b[*].c"
// Solver state for the canonical node of this pointer-
// equivalence class. Each node is created with its own state
// but they become shared after HVN.
solve *solverState
}
// An analysis instance holds the state of a single pointer analysis problem.
type analysis struct {
config *Config // the client's control/observer interface
prog *ssa.Program // the program being analyzed
log io.Writer // log stream; nil to disable
panicNode nodeid // sink for panic, source for recover
nodes []*node // indexed by nodeid
flattenMemo map[types.Type][]*fieldInfo // memoization of flatten()
trackTypes map[types.Type]bool // memoization of shouldTrack()
constraints []constraint // set of constraints
cgnodes []*cgnode // all cgnodes
genq []*cgnode // queue of functions to generate constraints for
intrinsics map[*ssa.Function]intrinsic // non-nil values are summaries for intrinsic fns
globalval map[ssa.Value]nodeid // node for each global ssa.Value
globalobj map[ssa.Value]nodeid // maps v to sole member of pts(v), if singleton
localval map[ssa.Value]nodeid // node for each local ssa.Value
localobj map[ssa.Value]nodeid // maps v to sole member of pts(v), if singleton
atFuncs map[*ssa.Function]bool // address-taken functions (for presolver)
mapValues []nodeid // values of makemap objects (indirect in HVN)
work nodeset // solver's worklist
result *Result // results of the analysis
track track // pointerlike types whose aliasing we track
deltaSpace []int // working space for iterating over PTS deltas
// Reflection & intrinsics:
hasher typeutil.Hasher // cache of type hashes
reflectValueObj types.Object // type symbol for reflect.Value (if present)
reflectValueCall *ssa.Function // (reflect.Value).Call
reflectRtypeObj types.Object // *types.TypeName for reflect.rtype (if present)
reflectRtypePtr *types.Pointer // *reflect.rtype
reflectType *types.Named // reflect.Type
rtypes typeutil.Map // nodeid of canonical *rtype-tagged object for type T
reflectZeros typeutil.Map // nodeid of canonical T-tagged object for zero value
runtimeSetFinalizer *ssa.Function // runtime.SetFinalizer
}
// enclosingObj returns the first node of the addressable memory
// object that encloses node id. Panic ensues if that node does not
// belong to any object.
func (a *analysis) enclosingObj(id nodeid) nodeid {
// Find previous node with obj != nil.
for i := id; i >= 0; i-- {
n := a.nodes[i]
if obj := n.obj; obj != nil {
if i+nodeid(obj.size) <= id {
break // out of bounds
}
return i
}
}
panic("node has no enclosing object")
}
// labelFor returns the Label for node id.
// Panic ensues if that node is not addressable.
func (a *analysis) labelFor(id nodeid) *Label {
return &Label{
obj: a.nodes[a.enclosingObj(id)].obj,
subelement: a.nodes[id].subelement,
}
}
func (a *analysis) warnf(pos token.Pos, format string, args ...interface{}) {
msg := fmt.Sprintf(format, args...)
if a.log != nil {
fmt.Fprintf(a.log, "%s: warning: %s\n", a.prog.Fset.Position(pos), msg)
}
a.result.Warnings = append(a.result.Warnings, Warning{pos, msg})
}
// computeTrackBits sets a.track to the necessary 'track' bits for the pointer queries.
func (a *analysis) computeTrackBits() {
var queryTypes []types.Type
for v := range a.config.Queries {
queryTypes = append(queryTypes, v.Type())
}
for v := range a.config.IndirectQueries {
queryTypes = append(queryTypes, mustDeref(v.Type()))
}
for _, t := range queryTypes {
switch t.Underlying().(type) {
case *types.Chan:
a.track |= trackChan
case *types.Map:
a.track |= trackMap
case *types.Pointer:
a.track |= trackPtr
case *types.Slice:
a.track |= trackSlice
case *types.Interface:
a.track = trackAll
return
}
if rVObj := a.reflectValueObj; rVObj != nil && types.Identical(t, rVObj.Type()) {
a.track = trackAll
return
}
}
}
// Analyze runs the pointer analysis with the scope and options
// specified by config, and returns the (synthetic) root of the callgraph.
//
// Pointer analysis of a transitively closed well-typed program should
// always succeed. An error can occur only due to an internal bug.
//
func Analyze(config *Config) (result *Result, err error) {
if config.Mains == nil {
return nil, fmt.Errorf("no main/test packages to analyze (check $GOROOT/$GOPATH)")
}
defer func() {
if p := recover(); p != nil {
err = fmt.Errorf("internal error in pointer analysis: %v (please report this bug)", p)
fmt.Fprintln(os.Stderr, "Internal panic in pointer analysis:")
debug.PrintStack()
}
}()
a := &analysis{
config: config,
log: config.Log,
prog: config.prog(),
globalval: make(map[ssa.Value]nodeid),
globalobj: make(map[ssa.Value]nodeid),
flattenMemo: make(map[types.Type][]*fieldInfo),
trackTypes: make(map[types.Type]bool),
atFuncs: make(map[*ssa.Function]bool),
hasher: typeutil.MakeHasher(),
intrinsics: make(map[*ssa.Function]intrinsic),
result: &Result{
Queries: make(map[ssa.Value]Pointer),
IndirectQueries: make(map[ssa.Value]Pointer),
},
deltaSpace: make([]int, 0, 100),
}
if false {
a.log = os.Stderr // for debugging crashes; extremely verbose
}
if a.log != nil {
fmt.Fprintln(a.log, "==== Starting analysis")
}
// Pointer analysis requires a complete program for soundness.
// Check to prevent accidental misconfiguration.
for _, pkg := range a.prog.AllPackages() {
// (This only checks that the package scope is complete,
// not that func bodies exist, but it's a good signal.)
if !pkg.Object.Complete() {
return nil, fmt.Errorf(`pointer analysis requires a complete program yet package %q was incomplete`, pkg.Object.Path())
}
}
if reflect := a.prog.ImportedPackage("reflect"); reflect != nil {
rV := reflect.Object.Scope().Lookup("Value")
a.reflectValueObj = rV
a.reflectValueCall = a.prog.LookupMethod(rV.Type(), nil, "Call")
a.reflectType = reflect.Object.Scope().Lookup("Type").Type().(*types.Named)
a.reflectRtypeObj = reflect.Object.Scope().Lookup("rtype")
a.reflectRtypePtr = types.NewPointer(a.reflectRtypeObj.Type())
// Override flattening of reflect.Value, treating it like a basic type.
tReflectValue := a.reflectValueObj.Type()
a.flattenMemo[tReflectValue] = []*fieldInfo{{typ: tReflectValue}}
// Override shouldTrack of reflect.Value and *reflect.rtype.
// Always track pointers of these types.
a.trackTypes[tReflectValue] = true
a.trackTypes[a.reflectRtypePtr] = true
a.rtypes.SetHasher(a.hasher)
a.reflectZeros.SetHasher(a.hasher)
}
if runtime := a.prog.ImportedPackage("runtime"); runtime != nil {
a.runtimeSetFinalizer = runtime.Func("SetFinalizer")
}
a.computeTrackBits()
a.generate()
a.showCounts()
if optRenumber {
a.renumber()
}
N := len(a.nodes) // excludes solver-created nodes
if optHVN {
if debugHVNCrossCheck {
// Cross-check: run the solver once without
// optimization, once with, and compare the
// solutions.
savedConstraints := a.constraints
a.solve()
a.dumpSolution("A.pts", N)
// Restore.
a.constraints = savedConstraints
for _, n := range a.nodes {
n.solve = new(solverState)
}
a.nodes = a.nodes[:N]
// rtypes is effectively part of the solver state.
a.rtypes = typeutil.Map{}
a.rtypes.SetHasher(a.hasher)
}
a.hvn()
}
if debugHVNCrossCheck {
runtime.GC()
runtime.GC()
}
a.solve()
// Compare solutions.
if optHVN && debugHVNCrossCheck {
a.dumpSolution("B.pts", N)
if !diff("A.pts", "B.pts") {
return nil, fmt.Errorf("internal error: optimization changed solution")
}
}
// Create callgraph.Nodes in deterministic order.
if cg := a.result.CallGraph; cg != nil {
for _, caller := range a.cgnodes {
cg.CreateNode(caller.fn)
}
}
// Add dynamic edges to call graph.
var space [100]int
for _, caller := range a.cgnodes {
for _, site := range caller.sites {
for _, callee := range a.nodes[site.targets].solve.pts.AppendTo(space[:0]) {
a.callEdge(caller, site, nodeid(callee))
}
}
}
return a.result, nil
}
// callEdge is called for each edge in the callgraph.
// calleeid is the callee's object node (has otFunction flag).
//
func (a *analysis) callEdge(caller *cgnode, site *callsite, calleeid nodeid) {
obj := a.nodes[calleeid].obj
if obj.flags&otFunction == 0 {
panic(fmt.Sprintf("callEdge %s -> n%d: not a function object", site, calleeid))
}
callee := obj.cgn
if cg := a.result.CallGraph; cg != nil {
// TODO(adonovan): opt: I would expect duplicate edges
// (to wrappers) to arise due to the elimination of
// context information, but I haven't observed any.
// Understand this better.
callgraph.AddEdge(cg.CreateNode(caller.fn), site.instr, cg.CreateNode(callee.fn))
}
if a.log != nil {
fmt.Fprintf(a.log, "\tcall edge %s -> %s\n", site, callee)
}
// Warn about calls to non-intrinsic external functions.
// TODO(adonovan): de-dup these messages.
if fn := callee.fn; fn.Blocks == nil && a.findIntrinsic(fn) == nil {
a.warnf(site.pos(), "unsound call to unknown intrinsic: %s", fn)
a.warnf(fn.Pos(), " (declared here)")
}
}
// dumpSolution writes the PTS solution to the specified file.
//
// It only dumps the nodes that existed before solving. The order in
// which solver-created nodes are created depends on pre-solver
// optimization, so we can't include them in the cross-check.
//
func (a *analysis) dumpSolution(filename string, N int) {
f, err := os.Create(filename)
if err != nil {
panic(err)
}
for id, n := range a.nodes[:N] {
if _, err := fmt.Fprintf(f, "pts(n%d) = {", id); err != nil {
panic(err)
}
var sep string
for _, l := range n.solve.pts.AppendTo(a.deltaSpace) {
if l >= N {
break
}
fmt.Fprintf(f, "%s%d", sep, l)
sep = " "
}
fmt.Fprintf(f, "} : %s\n", n.typ)
}
if err := f.Close(); err != nil {
panic(err)
}
}
// showCounts logs the size of the constraint system. A typical
// optimized distribution is 65% copy, 13% load, 11% addr, 5%
// offsetAddr, 4% store, 2% others.
//
func (a *analysis) showCounts() {
if a.log != nil {
counts := make(map[reflect.Type]int)
for _, c := range a.constraints {
counts[reflect.TypeOf(c)]++
}
fmt.Fprintf(a.log, "# constraints:\t%d\n", len(a.constraints))
var lines []string
for t, n := range counts {
line := fmt.Sprintf("%7d (%2d%%)\t%s", n, 100*n/len(a.constraints), t)
lines = append(lines, line)
}
sort.Sort(sort.Reverse(sort.StringSlice(lines)))
for _, line := range lines {
fmt.Fprintf(a.log, "\t%s\n", line)
}
fmt.Fprintf(a.log, "# nodes:\t%d\n", len(a.nodes))
// Show number of pointer equivalence classes.
m := make(map[*solverState]bool)
for _, n := range a.nodes {
m[n.solve] = true
}
fmt.Fprintf(a.log, "# ptsets:\t%d\n", len(m))
}
}

245
vendor/golang.org/x/tools/go/pointer/api.go generated vendored Normal file
View File

@ -0,0 +1,245 @@
// Copyright 2013 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 pointer
import (
"bytes"
"fmt"
"go/token"
"io"
"golang.org/x/tools/container/intsets"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types/typeutil"
)
// A Config formulates a pointer analysis problem for Analyze().
type Config struct {
// Mains contains the set of 'main' packages to analyze
// Clients must provide the analysis with at least one
// package defining a main() function.
//
// Non-main packages in the ssa.Program that are not
// dependencies of any main package may still affect the
// analysis result, because they contribute runtime types and
// thus methods.
// TODO(adonovan): investigate whether this is desirable.
Mains []*ssa.Package
// Reflection determines whether to handle reflection
// operators soundly, which is currently rather slow since it
// causes constraint to be generated during solving
// proportional to the number of constraint variables, which
// has not yet been reduced by presolver optimisation.
Reflection bool
// BuildCallGraph determines whether to construct a callgraph.
// If enabled, the graph will be available in Result.CallGraph.
BuildCallGraph bool
// The client populates Queries[v] or IndirectQueries[v]
// for each ssa.Value v of interest, to request that the
// points-to sets pts(v) or pts(*v) be computed. If the
// client needs both points-to sets, v may appear in both
// maps.
//
// (IndirectQueries is typically used for Values corresponding
// to source-level lvalues, e.g. an *ssa.Global.)
//
// The analysis populates the corresponding
// Result.{Indirect,}Queries map when it creates the pointer
// variable for v or *v. Upon completion the client can
// inspect that map for the results.
//
// TODO(adonovan): this API doesn't scale well for batch tools
// that want to dump the entire solution. Perhaps optionally
// populate a map[*ssa.DebugRef]Pointer in the Result, one
// entry per source expression.
//
Queries map[ssa.Value]struct{}
IndirectQueries map[ssa.Value]struct{}
// If Log is non-nil, log messages are written to it.
// Logging is extremely verbose.
Log io.Writer
}
type track uint32
const (
trackChan track = 1 << iota // track 'chan' references
trackMap // track 'map' references
trackPtr // track regular pointers
trackSlice // track slice references
trackAll = ^track(0)
)
// AddQuery adds v to Config.Queries.
// Precondition: CanPoint(v.Type()).
// TODO(adonovan): consider returning a new Pointer for this query,
// which will be initialized during analysis. That avoids the needs
// for the corresponding ssa.Value-keyed maps in Config and Result.
func (c *Config) AddQuery(v ssa.Value) {
if !CanPoint(v.Type()) {
panic(fmt.Sprintf("%s is not a pointer-like value: %s", v, v.Type()))
}
if c.Queries == nil {
c.Queries = make(map[ssa.Value]struct{})
}
c.Queries[v] = struct{}{}
}
// AddQuery adds v to Config.IndirectQueries.
// Precondition: CanPoint(v.Type().Underlying().(*types.Pointer).Elem()).
func (c *Config) AddIndirectQuery(v ssa.Value) {
if c.IndirectQueries == nil {
c.IndirectQueries = make(map[ssa.Value]struct{})
}
if !CanPoint(mustDeref(v.Type())) {
panic(fmt.Sprintf("%s is not the address of a pointer-like value: %s", v, v.Type()))
}
c.IndirectQueries[v] = struct{}{}
}
func (c *Config) prog() *ssa.Program {
for _, main := range c.Mains {
return main.Prog
}
panic("empty scope")
}
type Warning struct {
Pos token.Pos
Message string
}
// A Result contains the results of a pointer analysis.
//
// See Config for how to request the various Result components.
//
type Result struct {
CallGraph *callgraph.Graph // discovered call graph
Queries map[ssa.Value]Pointer // pts(v) for each v in Config.Queries.
IndirectQueries map[ssa.Value]Pointer // pts(*v) for each v in Config.IndirectQueries.
Warnings []Warning // warnings of unsoundness
}
// A Pointer is an equivalence class of pointer-like values.
//
// A Pointer doesn't have a unique type because pointers of distinct
// types may alias the same object.
//
type Pointer struct {
a *analysis
n nodeid
}
// A PointsToSet is a set of labels (locations or allocations).
type PointsToSet struct {
a *analysis // may be nil if pts is nil
pts *nodeset
}
func (s PointsToSet) String() string {
var buf bytes.Buffer
buf.WriteByte('[')
if s.pts != nil {
var space [50]int
for i, l := range s.pts.AppendTo(space[:0]) {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(s.a.labelFor(nodeid(l)).String())
}
}
buf.WriteByte(']')
return buf.String()
}
// PointsTo returns the set of labels that this points-to set
// contains.
func (s PointsToSet) Labels() []*Label {
var labels []*Label
if s.pts != nil {
var space [50]int
for _, l := range s.pts.AppendTo(space[:0]) {
labels = append(labels, s.a.labelFor(nodeid(l)))
}
}
return labels
}
// If this PointsToSet came from a Pointer of interface kind
// or a reflect.Value, DynamicTypes returns the set of dynamic
// types that it may contain. (For an interface, they will
// always be concrete types.)
//
// The result is a mapping whose keys are the dynamic types to which
// it may point. For each pointer-like key type, the corresponding
// map value is the PointsToSet for pointers of that type.
//
// The result is empty unless CanHaveDynamicTypes(T).
//
func (s PointsToSet) DynamicTypes() *typeutil.Map {
var tmap typeutil.Map
tmap.SetHasher(s.a.hasher)
if s.pts != nil {
var space [50]int
for _, x := range s.pts.AppendTo(space[:0]) {
ifaceObjId := nodeid(x)
if !s.a.isTaggedObject(ifaceObjId) {
continue // !CanHaveDynamicTypes(tDyn)
}
tDyn, v, indirect := s.a.taggedValue(ifaceObjId)
if indirect {
panic("indirect tagged object") // implement later
}
pts, ok := tmap.At(tDyn).(PointsToSet)
if !ok {
pts = PointsToSet{s.a, new(nodeset)}
tmap.Set(tDyn, pts)
}
pts.pts.addAll(&s.a.nodes[v].solve.pts)
}
}
return &tmap
}
// Intersects reports whether this points-to set and the
// argument points-to set contain common members.
func (x PointsToSet) Intersects(y PointsToSet) bool {
if x.pts == nil || y.pts == nil {
return false
}
// This takes Θ(|x|+|y|) time.
var z intsets.Sparse
z.Intersection(&x.pts.Sparse, &y.pts.Sparse)
return !z.IsEmpty()
}
func (p Pointer) String() string {
return fmt.Sprintf("n%d", p.n)
}
// PointsTo returns the points-to set of this pointer.
func (p Pointer) PointsTo() PointsToSet {
if p.n == 0 {
return PointsToSet{}
}
return PointsToSet{p.a, &p.a.nodes[p.n].solve.pts}
}
// MayAlias reports whether the receiver pointer may alias
// the argument pointer.
func (p Pointer) MayAlias(q Pointer) bool {
return p.PointsTo().Intersects(q.PointsTo())
}
// DynamicTypes returns p.PointsTo().DynamicTypes().
func (p Pointer) DynamicTypes() *typeutil.Map {
return p.PointsTo().DynamicTypes()
}

61
vendor/golang.org/x/tools/go/pointer/callgraph.go generated vendored Normal file
View File

@ -0,0 +1,61 @@
// Copyright 2013 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 pointer
// This file defines the internal (context-sensitive) call graph.
import (
"fmt"
"go/token"
"golang.org/x/tools/go/ssa"
)
type cgnode struct {
fn *ssa.Function
obj nodeid // start of this contour's object block
sites []*callsite // ordered list of callsites within this function
callersite *callsite // where called from, if known; nil for shared contours
}
// contour returns a description of this node's contour.
func (n *cgnode) contour() string {
if n.callersite == nil {
return "shared contour"
}
if n.callersite.instr != nil {
return fmt.Sprintf("as called from %s", n.callersite.instr.Parent())
}
return fmt.Sprintf("as called from intrinsic (targets=n%d)", n.callersite.targets)
}
func (n *cgnode) String() string {
return fmt.Sprintf("cg%d:%s", n.obj, n.fn)
}
// A callsite represents a single call site within a cgnode;
// it is implicitly context-sensitive.
// callsites never represent calls to built-ins;
// they are handled as intrinsics.
//
type callsite struct {
targets nodeid // pts(·) contains objects for dynamically called functions
instr ssa.CallInstruction // the call instruction; nil for synthetic/intrinsic
}
func (c *callsite) String() string {
if c.instr != nil {
return c.instr.Common().Description()
}
return "synthetic function call"
}
// pos returns the source position of this callsite, or token.NoPos if implicit.
func (c *callsite) pos() token.Pos {
if c.instr != nil {
return c.instr.Pos()
}
return token.NoPos
}

151
vendor/golang.org/x/tools/go/pointer/constraint.go generated vendored Normal file
View File

@ -0,0 +1,151 @@
// Copyright 2013 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 pointer
import (
"golang.org/x/tools/go/types"
)
type constraint interface {
// For a complex constraint, returns the nodeid of the pointer
// to which it is attached. For addr and copy, returns dst.
ptr() nodeid
// renumber replaces each nodeid n in the constraint by mapping[n].
renumber(mapping []nodeid)
// presolve is a hook for constraint-specific behaviour during
// pre-solver optimization. Typical implementations mark as
// indirect the set of nodes to which the solver will add copy
// edges or PTS labels.
presolve(h *hvn)
// solve is called for complex constraints when the pts for
// the node to which they are attached has changed.
solve(a *analysis, delta *nodeset)
String() string
}
// dst = &src
// pts(dst) ⊇ {src}
// A base constraint used to initialize the solver's pt sets
type addrConstraint struct {
dst nodeid // (ptr)
src nodeid
}
func (c *addrConstraint) ptr() nodeid { return c.dst }
func (c *addrConstraint) renumber(mapping []nodeid) {
c.dst = mapping[c.dst]
c.src = mapping[c.src]
}
// dst = src
// A simple constraint represented directly as a copyTo graph edge.
type copyConstraint struct {
dst nodeid // (ptr)
src nodeid
}
func (c *copyConstraint) ptr() nodeid { return c.dst }
func (c *copyConstraint) renumber(mapping []nodeid) {
c.dst = mapping[c.dst]
c.src = mapping[c.src]
}
// dst = src[offset]
// A complex constraint attached to src (the pointer)
type loadConstraint struct {
offset uint32
dst nodeid
src nodeid // (ptr)
}
func (c *loadConstraint) ptr() nodeid { return c.src }
func (c *loadConstraint) renumber(mapping []nodeid) {
c.dst = mapping[c.dst]
c.src = mapping[c.src]
}
// dst[offset] = src
// A complex constraint attached to dst (the pointer)
type storeConstraint struct {
offset uint32
dst nodeid // (ptr)
src nodeid
}
func (c *storeConstraint) ptr() nodeid { return c.dst }
func (c *storeConstraint) renumber(mapping []nodeid) {
c.dst = mapping[c.dst]
c.src = mapping[c.src]
}
// dst = &src.f or dst = &src[0]
// A complex constraint attached to dst (the pointer)
type offsetAddrConstraint struct {
offset uint32
dst nodeid
src nodeid // (ptr)
}
func (c *offsetAddrConstraint) ptr() nodeid { return c.src }
func (c *offsetAddrConstraint) renumber(mapping []nodeid) {
c.dst = mapping[c.dst]
c.src = mapping[c.src]
}
// dst = src.(typ) where typ is an interface
// A complex constraint attached to src (the interface).
// No representation change: pts(dst) and pts(src) contains tagged objects.
type typeFilterConstraint struct {
typ types.Type // an interface type
dst nodeid
src nodeid // (ptr)
}
func (c *typeFilterConstraint) ptr() nodeid { return c.src }
func (c *typeFilterConstraint) renumber(mapping []nodeid) {
c.dst = mapping[c.dst]
c.src = mapping[c.src]
}
// dst = src.(typ) where typ is a concrete type
// A complex constraint attached to src (the interface).
//
// If exact, only tagged objects identical to typ are untagged.
// If !exact, tagged objects assignable to typ are untagged too.
// The latter is needed for various reflect operators, e.g. Send.
//
// This entails a representation change:
// pts(src) contains tagged objects,
// pts(dst) contains their payloads.
type untagConstraint struct {
typ types.Type // a concrete type
dst nodeid
src nodeid // (ptr)
exact bool
}
func (c *untagConstraint) ptr() nodeid { return c.src }
func (c *untagConstraint) renumber(mapping []nodeid) {
c.dst = mapping[c.dst]
c.src = mapping[c.src]
}
// src.method(params...)
// A complex constraint attached to iface.
type invokeConstraint struct {
method *types.Func // the abstract method
iface nodeid // (ptr) the interface
params nodeid // the start of the identity/params/results block
}
func (c *invokeConstraint) ptr() nodeid { return c.iface }
func (c *invokeConstraint) renumber(mapping []nodeid) {
c.iface = mapping[c.iface]
c.params = mapping[c.params]
}

610
vendor/golang.org/x/tools/go/pointer/doc.go generated vendored Normal file
View File

@ -0,0 +1,610 @@
// Copyright 2013 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 pointer implements Andersen's analysis, an inclusion-based
pointer analysis algorithm first described in (Andersen, 1994).
A pointer analysis relates every pointer expression in a whole program
to the set of memory locations to which it might point. This
information can be used to construct a call graph of the program that
precisely represents the destinations of dynamic function and method
calls. It can also be used to determine, for example, which pairs of
channel operations operate on the same channel.
The package allows the client to request a set of expressions of
interest for which the points-to information will be returned once the
analysis is complete. In addition, the client may request that a
callgraph is constructed. The example program in example_test.go
demonstrates both of these features. Clients should not request more
information than they need since it may increase the cost of the
analysis significantly.
CLASSIFICATION
Our algorithm is INCLUSION-BASED: the points-to sets for x and y will
be related by pts(y) pts(x) if the program contains the statement
y = x.
It is FLOW-INSENSITIVE: it ignores all control flow constructs and the
order of statements in a program. It is therefore a "MAY ALIAS"
analysis: its facts are of the form "P may/may not point to L",
not "P must point to L".
It is FIELD-SENSITIVE: it builds separate points-to sets for distinct
fields, such as x and y in struct { x, y *int }.
It is mostly CONTEXT-INSENSITIVE: most functions are analyzed once,
so values can flow in at one call to the function and return out at
another. Only some smaller functions are analyzed with consideration
of their calling context.
It has a CONTEXT-SENSITIVE HEAP: objects are named by both allocation
site and context, so the objects returned by two distinct calls to f:
func f() *T { return new(T) }
are distinguished up to the limits of the calling context.
It is a WHOLE PROGRAM analysis: it requires SSA-form IR for the
complete Go program and summaries for native code.
See the (Hind, PASTE'01) survey paper for an explanation of these terms.
SOUNDNESS
The analysis is fully sound when invoked on pure Go programs that do not
use reflection or unsafe.Pointer conversions. In other words, if there
is any possible execution of the program in which pointer P may point to
object O, the analysis will report that fact.
REFLECTION
By default, the "reflect" library is ignored by the analysis, as if all
its functions were no-ops, but if the client enables the Reflection flag,
the analysis will make a reasonable attempt to model the effects of
calls into this library. However, this comes at a significant
performance cost, and not all features of that library are yet
implemented. In addition, some simplifying approximations must be made
to ensure that the analysis terminates; for example, reflection can be
used to construct an infinite set of types and values of those types,
but the analysis arbitrarily bounds the depth of such types.
Most but not all reflection operations are supported.
In particular, addressable reflect.Values are not yet implemented, so
operations such as (reflect.Value).Set have no analytic effect.
UNSAFE POINTER CONVERSIONS
The pointer analysis makes no attempt to understand aliasing between the
operand x and result y of an unsafe.Pointer conversion:
y = (*T)(unsafe.Pointer(x))
It is as if the conversion allocated an entirely new object:
y = new(T)
NATIVE CODE
The analysis cannot model the aliasing effects of functions written in
languages other than Go, such as runtime intrinsics in C or assembly, or
code accessed via cgo. The result is as if such functions are no-ops.
However, various important intrinsics are understood by the analysis,
along with built-ins such as append.
The analysis currently provides no way for users to specify the aliasing
effects of native code.
------------------------------------------------------------------------
IMPLEMENTATION
The remaining documentation is intended for package maintainers and
pointer analysis specialists. Maintainers should have a solid
understanding of the referenced papers (especially those by H&L and PKH)
before making making significant changes.
The implementation is similar to that described in (Pearce et al,
PASTE'04). Unlike many algorithms which interleave constraint
generation and solving, constructing the callgraph as they go, this
implementation for the most part observes a phase ordering (generation
before solving), with only simple (copy) constraints being generated
during solving. (The exception is reflection, which creates various
constraints during solving as new types flow to reflect.Value
operations.) This improves the traction of presolver optimisations,
but imposes certain restrictions, e.g. potential context sensitivity
is limited since all variants must be created a priori.
TERMINOLOGY
A type is said to be "pointer-like" if it is a reference to an object.
Pointer-like types include pointers and also interfaces, maps, channels,
functions and slices.
We occasionally use C's x->f notation to distinguish the case where x
is a struct pointer from x.f where is a struct value.
Pointer analysis literature (and our comments) often uses the notation
dst=*src+offset to mean something different than what it means in Go.
It means: for each node index p in pts(src), the node index p+offset is
in pts(dst). Similarly *dst+offset=src is used for store constraints
and dst=src+offset for offset-address constraints.
NODES
Nodes are the key datastructure of the analysis, and have a dual role:
they represent both constraint variables (equivalence classes of
pointers) and members of points-to sets (things that can be pointed
at, i.e. "labels").
Nodes are naturally numbered. The numbering enables compact
representations of sets of nodes such as bitvectors (or BDDs); and the
ordering enables a very cheap way to group related nodes together. For
example, passing n parameters consists of generating n parallel
constraints from caller+i to callee+i for 0<=i<n.
The zero nodeid means "not a pointer". For simplicity, we generate flow
constraints even for non-pointer types such as int. The pointer
equivalence (PE) presolver optimization detects which variables cannot
point to anything; this includes not only all variables of non-pointer
types (such as int) but also variables of pointer-like types if they are
always nil, or are parameters to a function that is never called.
Each node represents a scalar part of a value or object.
Aggregate types (structs, tuples, arrays) are recursively flattened
out into a sequential list of scalar component types, and all the
elements of an array are represented by a single node. (The
flattening of a basic type is a list containing a single node.)
Nodes are connected into a graph with various kinds of labelled edges:
simple edges (or copy constraints) represent value flow. Complex
edges (load, store, etc) trigger the creation of new simple edges
during the solving phase.
OBJECTS
Conceptually, an "object" is a contiguous sequence of nodes denoting
an addressable location: something that a pointer can point to. The
first node of an object has a non-nil obj field containing information
about the allocation: its size, context, and ssa.Value.
Objects include:
- functions and globals;
- variable allocations in the stack frame or heap;
- maps, channels and slices created by calls to make();
- allocations to construct an interface;
- allocations caused by conversions, e.g. []byte(str).
- arrays allocated by calls to append();
Many objects have no Go types. For example, the func, map and chan type
kinds in Go are all varieties of pointers, but their respective objects
are actual functions (executable code), maps (hash tables), and channels
(synchronized queues). Given the way we model interfaces, they too are
pointers to "tagged" objects with no Go type. And an *ssa.Global denotes
the address of a global variable, but the object for a Global is the
actual data. So, the types of an ssa.Value that creates an object is
"off by one indirection": a pointer to the object.
The individual nodes of an object are sometimes referred to as "labels".
For uniformity, all objects have a non-zero number of fields, even those
of the empty type struct{}. (All arrays are treated as if of length 1,
so there are no empty arrays. The empty tuple is never address-taken,
so is never an object.)
TAGGED OBJECTS
An tagged object has the following layout:
T -- obj.flags {otTagged}
v
...
The T node's typ field is the dynamic type of the "payload": the value
v which follows, flattened out. The T node's obj has the otTagged
flag.
Tagged objects are needed when generalizing across types: interfaces,
reflect.Values, reflect.Types. Each of these three types is modelled
as a pointer that exclusively points to tagged objects.
Tagged objects may be indirect (obj.flags {otIndirect}) meaning that
the value v is not of type T but *T; this is used only for
reflect.Values that represent lvalues. (These are not implemented yet.)
ANALYSIS ABSTRACTION OF EACH TYPE
Variables of the following "scalar" types may be represented by a
single node: basic types, pointers, channels, maps, slices, 'func'
pointers, interfaces.
Pointers
Nothing to say here, oddly.
Basic types (bool, string, numbers, unsafe.Pointer)
Currently all fields in the flattening of a type, including
non-pointer basic types such as int, are represented in objects and
values. Though non-pointer nodes within values are uninteresting,
non-pointer nodes in objects may be useful (if address-taken)
because they permit the analysis to deduce, in this example,
var s struct{ ...; x int; ... }
p := &s.x
that p points to s.x. If we ignored such object fields, we could only
say that p points somewhere within s.
All other basic types are ignored. Expressions of these types have
zero nodeid, and fields of these types within aggregate other types
are omitted.
unsafe.Pointers are not modelled as pointers, so a conversion of an
unsafe.Pointer to *T is (unsoundly) treated equivalent to new(T).
Channels
An expression of type 'chan T' is a kind of pointer that points
exclusively to channel objects, i.e. objects created by MakeChan (or
reflection).
'chan T' is treated like *T.
*ssa.MakeChan is treated as equivalent to new(T).
*ssa.Send and receive (*ssa.UnOp(ARROW)) and are equivalent to store
and load.
Maps
An expression of type 'map[K]V' is a kind of pointer that points
exclusively to map objects, i.e. objects created by MakeMap (or
reflection).
map K[V] is treated like *M where M = struct{k K; v V}.
*ssa.MakeMap is equivalent to new(M).
*ssa.MapUpdate is equivalent to *y=x where *y and x have type M.
*ssa.Lookup is equivalent to y=x.v where x has type *M.
Slices
A slice []T, which dynamically resembles a struct{array *T, len, cap int},
is treated as if it were just a *T pointer; the len and cap fields are
ignored.
*ssa.MakeSlice is treated like new([1]T): an allocation of a
singleton array.
*ssa.Index on a slice is equivalent to a load.
*ssa.IndexAddr on a slice returns the address of the sole element of the
slice, i.e. the same address.
*ssa.Slice is treated as a simple copy.
Functions
An expression of type 'func...' is a kind of pointer that points
exclusively to function objects.
A function object has the following layout:
identity -- typ:*types.Signature; obj.flags {otFunction}
params_0 -- (the receiver, if a method)
...
params_n-1
results_0
...
results_m-1
There may be multiple function objects for the same *ssa.Function
due to context-sensitive treatment of some functions.
The first node is the function's identity node.
Associated with every callsite is a special "targets" variable,
whose pts() contains the identity node of each function to which
the call may dispatch. Identity words are not otherwise used during
the analysis, but we construct the call graph from the pts()
solution for such nodes.
The following block of contiguous nodes represents the flattened-out
types of the parameters ("P-block") and results ("R-block") of the
function object.
The treatment of free variables of closures (*ssa.FreeVar) is like
that of global variables; it is not context-sensitive.
*ssa.MakeClosure instructions create copy edges to Captures.
A Go value of type 'func' (i.e. a pointer to one or more functions)
is a pointer whose pts() contains function objects. The valueNode()
for an *ssa.Function returns a singleton for that function.
Interfaces
An expression of type 'interface{...}' is a kind of pointer that
points exclusively to tagged objects. All tagged objects pointed to
by an interface are direct (the otIndirect flag is clear) and
concrete (the tag type T is not itself an interface type). The
associated ssa.Value for an interface's tagged objects may be an
*ssa.MakeInterface instruction, or nil if the tagged object was
created by an instrinsic (e.g. reflection).
Constructing an interface value causes generation of constraints for
all of the concrete type's methods; we can't tell a priori which
ones may be called.
TypeAssert y = x.(T) is implemented by a dynamic constraint
triggered by each tagged object O added to pts(x): a typeFilter
constraint if T is an interface type, or an untag constraint if T is
a concrete type. A typeFilter tests whether O.typ implements T; if
so, O is added to pts(y). An untagFilter tests whether O.typ is
assignable to T,and if so, a copy edge O.v -> y is added.
ChangeInterface is a simple copy because the representation of
tagged objects is independent of the interface type (in contrast
to the "method tables" approach used by the gc runtime).
y := Invoke x.m(...) is implemented by allocating contiguous P/R
blocks for the callsite and adding a dynamic rule triggered by each
tagged object added to pts(x). The rule adds param/results copy
edges to/from each discovered concrete method.
(Q. Why do we model an interface as a pointer to a pair of type and
value, rather than as a pair of a pointer to type and a pointer to
value?
A. Control-flow joins would merge interfaces ({T1}, {V1}) and ({T2},
{V2}) to make ({T1,T2}, {V1,V2}), leading to the infeasible and
type-unsafe combination (T1,V2). Treating the value and its concrete
type as inseparable makes the analysis type-safe.)
reflect.Value
A reflect.Value is modelled very similar to an interface{}, i.e. as
a pointer exclusively to tagged objects, but with two generalizations.
1) a reflect.Value that represents an lvalue points to an indirect
(obj.flags {otIndirect}) tagged object, which has a similar
layout to an tagged object except that the value is a pointer to
the dynamic type. Indirect tagged objects preserve the correct
aliasing so that mutations made by (reflect.Value).Set can be
observed.
Indirect objects only arise when an lvalue is derived from an
rvalue by indirection, e.g. the following code:
type S struct { X T }
var s S
var i interface{} = &s // i points to a *S-tagged object (from MakeInterface)
v1 := reflect.ValueOf(i) // v1 points to same *S-tagged object as i
v2 := v1.Elem() // v2 points to an indirect S-tagged object, pointing to s
v3 := v2.FieldByName("X") // v3 points to an indirect int-tagged object, pointing to s.X
v3.Set(y) // pts(s.X) ⊇ pts(y)
Whether indirect or not, the concrete type of the tagged object
corresponds to the user-visible dynamic type, and the existence
of a pointer is an implementation detail.
(NB: indirect tagged objects are not yet implemented)
2) The dynamic type tag of a tagged object pointed to by a
reflect.Value may be an interface type; it need not be concrete.
This arises in code such as this:
tEface := reflect.TypeOf(new(interface{}).Elem() // interface{}
eface := reflect.Zero(tEface)
pts(eface) is a singleton containing an interface{}-tagged
object. That tagged object's payload is an interface{} value,
i.e. the pts of the payload contains only concrete-tagged
objects, although in this example it's the zero interface{} value,
so its pts is empty.
reflect.Type
Just as in the real "reflect" library, we represent a reflect.Type
as an interface whose sole implementation is the concrete type,
*reflect.rtype. (This choice is forced on us by go/types: clients
cannot fabricate types with arbitrary method sets.)
rtype instances are canonical: there is at most one per dynamic
type. (rtypes are in fact large structs but since identity is all
that matters, we represent them by a single node.)
The payload of each *rtype-tagged object is an *rtype pointer that
points to exactly one such canonical rtype object. We exploit this
by setting the node.typ of the payload to the dynamic type, not
'*rtype'. This saves us an indirection in each resolution rule. As
an optimisation, *rtype-tagged objects are canonicalized too.
Aggregate types:
Aggregate types are treated as if all directly contained
aggregates are recursively flattened out.
Structs
*ssa.Field y = x.f creates a simple edge to y from x's node at f's offset.
*ssa.FieldAddr y = &x->f requires a dynamic closure rule to create
simple edges for each struct discovered in pts(x).
The nodes of a struct consist of a special 'identity' node (whose
type is that of the struct itself), followed by the nodes for all
the struct's fields, recursively flattened out. A pointer to the
struct is a pointer to its identity node. That node allows us to
distinguish a pointer to a struct from a pointer to its first field.
Field offsets are logical field offsets (plus one for the identity
node), so the sizes of the fields can be ignored by the analysis.
(The identity node is non-traditional but enables the distiction
described above, which is valuable for code comprehension tools.
Typical pointer analyses for C, whose purpose is compiler
optimization, must soundly model unsafe.Pointer (void*) conversions,
and this requires fidelity to the actual memory layout using physical
field offsets.)
*ssa.Field y = x.f creates a simple edge to y from x's node at f's offset.
*ssa.FieldAddr y = &x->f requires a dynamic closure rule to create
simple edges for each struct discovered in pts(x).
Arrays
We model an array by an identity node (whose type is that of the
array itself) followed by a node representing all the elements of
the array; the analysis does not distinguish elements with different
indices. Effectively, an array is treated like struct{elem T}, a
load y=x[i] like y=x.elem, and a store x[i]=y like x.elem=y; the
index i is ignored.
A pointer to an array is pointer to its identity node. (A slice is
also a pointer to an array's identity node.) The identity node
allows us to distinguish a pointer to an array from a pointer to one
of its elements, but it is rather costly because it introduces more
offset constraints into the system. Furthermore, sound treatment of
unsafe.Pointer would require us to dispense with this node.
Arrays may be allocated by Alloc, by make([]T), by calls to append,
and via reflection.
Tuples (T, ...)
Tuples are treated like structs with naturally numbered fields.
*ssa.Extract is analogous to *ssa.Field.
However, tuples have no identity field since by construction, they
cannot be address-taken.
FUNCTION CALLS
There are three kinds of function call:
(1) static "call"-mode calls of functions.
(2) dynamic "call"-mode calls of functions.
(3) dynamic "invoke"-mode calls of interface methods.
Cases 1 and 2 apply equally to methods and standalone functions.
Static calls.
A static call consists three steps:
- finding the function object of the callee;
- creating copy edges from the actual parameter value nodes to the
P-block in the function object (this includes the receiver if
the callee is a method);
- creating copy edges from the R-block in the function object to
the value nodes for the result of the call.
A static function call is little more than two struct value copies
between the P/R blocks of caller and callee:
callee.P = caller.P
caller.R = callee.R
Context sensitivity
Static calls (alone) may be treated context sensitively,
i.e. each callsite may cause a distinct re-analysis of the
callee, improving precision. Our current context-sensitivity
policy treats all intrinsics and getter/setter methods in this
manner since such functions are small and seem like an obvious
source of spurious confluences, though this has not yet been
evaluated.
Dynamic function calls
Dynamic calls work in a similar manner except that the creation of
copy edges occurs dynamically, in a similar fashion to a pair of
struct copies in which the callee is indirect:
callee->P = caller.P
caller.R = callee->R
(Recall that the function object's P- and R-blocks are contiguous.)
Interface method invocation
For invoke-mode calls, we create a params/results block for the
callsite and attach a dynamic closure rule to the interface. For
each new tagged object that flows to the interface, we look up
the concrete method, find its function object, and connect its P/R
blocks to the callsite's P/R blocks, adding copy edges to the graph
during solving.
Recording call targets
The analysis notifies its clients of each callsite it encounters,
passing a CallSite interface. Among other things, the CallSite
contains a synthetic constraint variable ("targets") whose
points-to solution includes the set of all function objects to
which the call may dispatch.
It is via this mechanism that the callgraph is made available.
Clients may also elect to be notified of callgraph edges directly;
internally this just iterates all "targets" variables' pts(·)s.
PRESOLVER
We implement Hash-Value Numbering (HVN), a pre-solver constraint
optimization described in Hardekopf & Lin, SAS'07. This is documented
in more detail in hvn.go. We intend to add its cousins HR and HU in
future.
SOLVER
The solver is currently a naive Andersen-style implementation; it does
not perform online cycle detection, though we plan to add solver
optimisations such as Hybrid- and Lazy- Cycle Detection from (Hardekopf
& Lin, PLDI'07).
It uses difference propagation (Pearce et al, SQC'04) to avoid
redundant re-triggering of closure rules for values already seen.
Points-to sets are represented using sparse bit vectors (similar to
those used in LLVM and gcc), which are more space- and time-efficient
than sets based on Go's built-in map type or dense bit vectors.
Nodes are permuted prior to solving so that object nodes (which may
appear in points-to sets) are lower numbered than non-object (var)
nodes. This improves the density of the set over which the PTSs
range, and thus the efficiency of the representation.
Partly thanks to avoiding map iteration, the execution of the solver is
100% deterministic, a great help during debugging.
FURTHER READING
Andersen, L. O. 1994. Program analysis and specialization for the C
programming language. Ph.D. dissertation. DIKU, University of
Copenhagen.
David J. Pearce, Paul H. J. Kelly, and Chris Hankin. 2004. Efficient
field-sensitive pointer analysis for C. In Proceedings of the 5th ACM
SIGPLAN-SIGSOFT workshop on Program analysis for software tools and
engineering (PASTE '04). ACM, New York, NY, USA, 37-42.
http://doi.acm.org/10.1145/996821.996835
David J. Pearce, Paul H. J. Kelly, and Chris Hankin. 2004. Online
Cycle Detection and Difference Propagation: Applications to Pointer
Analysis. Software Quality Control 12, 4 (December 2004), 311-337.
http://dx.doi.org/10.1023/B:SQJO.0000039791.93071.a2
David Grove and Craig Chambers. 2001. A framework for call graph
construction algorithms. ACM Trans. Program. Lang. Syst. 23, 6
(November 2001), 685-746.
http://doi.acm.org/10.1145/506315.506316
Ben Hardekopf and Calvin Lin. 2007. The ant and the grasshopper: fast
and accurate pointer analysis for millions of lines of code. In
Proceedings of the 2007 ACM SIGPLAN conference on Programming language
design and implementation (PLDI '07). ACM, New York, NY, USA, 290-299.
http://doi.acm.org/10.1145/1250734.1250767
Ben Hardekopf and Calvin Lin. 2007. Exploiting pointer and location
equivalence to optimize pointer analysis. In Proceedings of the 14th
international conference on Static Analysis (SAS'07), Hanne Riis
Nielson and Gilberto Filé (Eds.). Springer-Verlag, Berlin, Heidelberg,
265-280.
Atanas Rountev and Satish Chandra. 2000. Off-line variable substitution
for scaling points-to analysis. In Proceedings of the ACM SIGPLAN 2000
conference on Programming language design and implementation (PLDI '00).
ACM, New York, NY, USA, 47-56. DOI=10.1145/349299.349310
http://doi.acm.org/10.1145/349299.349310
*/
package pointer // import "golang.org/x/tools/go/pointer"

1292
vendor/golang.org/x/tools/go/pointer/gen.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

969
vendor/golang.org/x/tools/go/pointer/hvn.go generated vendored Normal file
View File

@ -0,0 +1,969 @@
package pointer
// This file implements Hash-Value Numbering (HVN), a pre-solver
// constraint optimization described in Hardekopf & Lin, SAS'07 (see
// doc.go) that analyses the graph topology to determine which sets of
// variables are "pointer equivalent" (PE), i.e. must have identical
// points-to sets in the solution.
//
// A separate ("offline") graph is constructed. Its nodes are those of
// the main-graph, plus an additional node *X for each pointer node X.
// With this graph we can reason about the unknown points-to set of
// dereferenced pointers. (We do not generalize this to represent
// unknown fields x->f, perhaps because such fields would be numerous,
// though it might be worth an experiment.)
//
// Nodes whose points-to relations are not entirely captured by the
// graph are marked as "indirect": the *X nodes, the parameters of
// address-taken functions (which includes all functions in method
// sets), or nodes updated by the solver rules for reflection, etc.
//
// All addr (y=&x) nodes are initially assigned a pointer-equivalence
// (PE) label equal to x's nodeid in the main graph. (These are the
// only PE labels that are less than len(a.nodes).)
//
// All offsetAddr (y=&x.f) constraints are initially assigned a PE
// label; such labels are memoized, keyed by (x, f), so that equivalent
// nodes y as assigned the same label.
//
// Then we process each strongly connected component (SCC) of the graph
// in topological order, assigning it a PE label based on the set P of
// PE labels that flow to it from its immediate dependencies.
//
// If any node in P is "indirect", the entire SCC is assigned a fresh PE
// label. Otherwise:
//
// |P|=0 if P is empty, all nodes in the SCC are non-pointers (e.g.
// uninitialized variables, or formal params of dead functions)
// and the SCC is assigned the PE label of zero.
//
// |P|=1 if P is a singleton, the SCC is assigned the same label as the
// sole element of P.
//
// |P|>1 if P contains multiple labels, a unique label representing P is
// invented and recorded in an hash table, so that other
// equivalent SCCs may also be assigned this label, akin to
// conventional hash-value numbering in a compiler.
//
// Finally, a renumbering is computed such that each node is replaced by
// the lowest-numbered node with the same PE label. All constraints are
// renumbered, and any resulting duplicates are eliminated.
//
// The only nodes that are not renumbered are the objects x in addr
// (y=&x) constraints, since the ids of these nodes (and fields derived
// from them via offsetAddr rules) are the elements of all points-to
// sets, so they must remain as they are if we want the same solution.
//
// The solverStates (node.solve) for nodes in the same equivalence class
// are linked together so that all nodes in the class have the same
// solution. This avoids the need to renumber nodeids buried in
// Queries, cgnodes, etc (like (*analysis).renumber() does) since only
// the solution is needed.
//
// The result of HVN is that the number of distinct nodes and
// constraints is reduced, but the solution is identical (almost---see
// CROSS-CHECK below). In particular, both linear and cyclic chains of
// copies are each replaced by a single node.
//
// Nodes and constraints created "online" (e.g. while solving reflection
// constraints) are not subject to this optimization.
//
// PERFORMANCE
//
// In two benchmarks (oracle and godoc), HVN eliminates about two thirds
// of nodes, the majority accounted for by non-pointers: nodes of
// non-pointer type, pointers that remain nil, formal parameters of dead
// functions, nodes of untracked types, etc. It also reduces the number
// of constraints, also by about two thirds, and the solving time by
// 30--42%, although we must pay about 15% for the running time of HVN
// itself. The benefit is greater for larger applications.
//
// There are many possible optimizations to improve the performance:
// * Use fewer than 1:1 onodes to main graph nodes: many of the onodes
// we create are not needed.
// * HU (HVN with Union---see paper): coalesce "union" peLabels when
// their expanded-out sets are equal.
// * HR (HVN with deReference---see paper): this will require that we
// apply HVN until fixed point, which may need more bookkeeping of the
// correspondance of main nodes to onodes.
// * Location Equivalence (see paper): have points-to sets contain not
// locations but location-equivalence class labels, each representing
// a set of locations.
// * HVN with field-sensitive ref: model each of the fields of a
// pointer-to-struct.
//
// CROSS-CHECK
//
// To verify the soundness of the optimization, when the
// debugHVNCrossCheck option is enabled, we run the solver twice, once
// before and once after running HVN, dumping the solution to disk, and
// then we compare the results. If they are not identical, the analysis
// panics.
//
// The solution dumped to disk includes only the N*N submatrix of the
// complete solution where N is the number of nodes after generation.
// In other words, we ignore pointer variables and objects created by
// the solver itself, since their numbering depends on the solver order,
// which is affected by the optimization. In any case, that's the only
// part the client cares about.
//
// The cross-check is too strict and may fail spuriously. Although the
// H&L paper describing HVN states that the solutions obtained should be
// identical, this is not the case in practice because HVN can collapse
// cycles involving *p even when pts(p)={}. Consider this example
// distilled from testdata/hello.go:
//
// var x T
// func f(p **T) {
// t0 = *p
// ...
// t1 = φ(t0, &x)
// *p = t1
// }
//
// If f is dead code, we get:
// unoptimized: pts(p)={} pts(t0)={} pts(t1)={&x}
// optimized: pts(p)={} pts(t0)=pts(t1)=pts(*p)={&x}
//
// It's hard to argue that this is a bug: the result is sound and the
// loss of precision is inconsequential---f is dead code, after all.
// But unfortunately it limits the usefulness of the cross-check since
// failures must be carefully analyzed. Ben Hardekopf suggests (in
// personal correspondence) some approaches to mitigating it:
//
// If there is a node with an HVN points-to set that is a superset
// of the NORM points-to set, then either it's a bug or it's a
// result of this issue. If it's a result of this issue, then in
// the offline constraint graph there should be a REF node inside
// some cycle that reaches this node, and in the NORM solution the
// pointer being dereferenced by that REF node should be the empty
// set. If that isn't true then this is a bug. If it is true, then
// you can further check that in the NORM solution the "extra"
// points-to info in the HVN solution does in fact come from that
// purported cycle (if it doesn't, then this is still a bug). If
// you're doing the further check then you'll need to do it for
// each "extra" points-to element in the HVN points-to set.
//
// There are probably ways to optimize these checks by taking
// advantage of graph properties. For example, extraneous points-to
// info will flow through the graph and end up in many
// nodes. Rather than checking every node with extra info, you
// could probably work out the "origin point" of the extra info and
// just check there. Note that the check in the first bullet is
// looking for soundness bugs, while the check in the second bullet
// is looking for precision bugs; depending on your needs, you may
// care more about one than the other.
//
// which we should evaluate. The cross-check is nonetheless invaluable
// for all but one of the programs in the pointer_test suite.
import (
"fmt"
"io"
"reflect"
"golang.org/x/tools/container/intsets"
"golang.org/x/tools/go/types"
)
// A peLabel is a pointer-equivalence label: two nodes with the same
// peLabel have identical points-to solutions.
//
// The numbers are allocated consecutively like so:
// 0 not a pointer
// 1..N-1 addrConstraints (equals the constraint's .src field, hence sparse)
// ... offsetAddr constraints
// ... SCCs (with indirect nodes or multiple inputs)
//
// Each PE label denotes a set of pointers containing a single addr, a
// single offsetAddr, or some set of other PE labels.
//
type peLabel int
type hvn struct {
a *analysis
N int // len(a.nodes) immediately after constraint generation
log io.Writer // (optional) log of HVN lemmas
onodes []*onode // nodes of the offline graph
label peLabel // the next available PE label
hvnLabel map[string]peLabel // hash-value numbering (PE label) for each set of onodeids
stack []onodeid // DFS stack
index int32 // next onode.index, from Tarjan's SCC algorithm
// For each distinct offsetAddrConstraint (src, offset) pair,
// offsetAddrLabels records a unique PE label >= N.
offsetAddrLabels map[offsetAddr]peLabel
}
// The index of an node in the offline graph.
// (Currently the first N align with the main nodes,
// but this may change with HRU.)
type onodeid uint32
// An onode is a node in the offline constraint graph.
// (Where ambiguous, members of analysis.nodes are referred to as
// "main graph" nodes.)
//
// Edges in the offline constraint graph (edges and implicit) point to
// the source, i.e. against the flow of values: they are dependencies.
// Implicit edges are used for SCC computation, but not for gathering
// incoming labels.
//
type onode struct {
rep onodeid // index of representative of SCC in offline constraint graph
edges intsets.Sparse // constraint edges X-->Y (this onode is X)
implicit intsets.Sparse // implicit edges *X-->*Y (this onode is X)
peLabels intsets.Sparse // set of peLabels are pointer-equivalent to this one
indirect bool // node has points-to relations not represented in graph
// Tarjan's SCC algorithm
index, lowlink int32 // Tarjan numbering
scc int32 // -ve => on stack; 0 => unvisited; +ve => node is root of a found SCC
}
type offsetAddr struct {
ptr nodeid
offset uint32
}
// nextLabel issues the next unused pointer-equivalence label.
func (h *hvn) nextLabel() peLabel {
h.label++
return h.label
}
// ref(X) returns the index of the onode for *X.
func (h *hvn) ref(id onodeid) onodeid {
return id + onodeid(len(h.a.nodes))
}
// hvn computes pointer-equivalence labels (peLabels) using the Hash-based
// Value Numbering (HVN) algorithm described in Hardekopf & Lin, SAS'07.
//
func (a *analysis) hvn() {
start("HVN")
if a.log != nil {
fmt.Fprintf(a.log, "\n\n==== Pointer equivalence optimization\n\n")
}
h := hvn{
a: a,
N: len(a.nodes),
log: a.log,
hvnLabel: make(map[string]peLabel),
offsetAddrLabels: make(map[offsetAddr]peLabel),
}
if h.log != nil {
fmt.Fprintf(h.log, "\nCreating offline graph nodes...\n")
}
// Create offline nodes. The first N nodes correspond to main
// graph nodes; the next N are their corresponding ref() nodes.
h.onodes = make([]*onode, 2*h.N)
for id := range a.nodes {
id := onodeid(id)
h.onodes[id] = &onode{}
h.onodes[h.ref(id)] = &onode{indirect: true}
}
// Each node initially represents just itself.
for id, o := range h.onodes {
o.rep = onodeid(id)
}
h.markIndirectNodes()
// Reserve the first N PE labels for addrConstraints.
h.label = peLabel(h.N)
// Add offline constraint edges.
if h.log != nil {
fmt.Fprintf(h.log, "\nAdding offline graph edges...\n")
}
for _, c := range a.constraints {
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "; %s\n", c)
}
c.presolve(&h)
}
// Find and collapse SCCs.
if h.log != nil {
fmt.Fprintf(h.log, "\nFinding SCCs...\n")
}
h.index = 1
for id, o := range h.onodes {
if id > 0 && o.index == 0 {
// Start depth-first search at each unvisited node.
h.visit(onodeid(id))
}
}
// Dump the solution
// (NB: somewhat redundant with logging from simplify().)
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "\nPointer equivalences:\n")
for id, o := range h.onodes {
if id == 0 {
continue
}
if id == int(h.N) {
fmt.Fprintf(h.log, "---\n")
}
fmt.Fprintf(h.log, "o%d\t", id)
if o.rep != onodeid(id) {
fmt.Fprintf(h.log, "rep=o%d", o.rep)
} else {
fmt.Fprintf(h.log, "p%d", o.peLabels.Min())
if o.indirect {
fmt.Fprint(h.log, " indirect")
}
}
fmt.Fprintln(h.log)
}
}
// Simplify the main constraint graph
h.simplify()
a.showCounts()
stop("HVN")
}
// ---- constraint-specific rules ----
// dst := &src
func (c *addrConstraint) presolve(h *hvn) {
// Each object (src) is an initial PE label.
label := peLabel(c.src) // label < N
if debugHVNVerbose && h.log != nil {
// duplicate log messages are possible
fmt.Fprintf(h.log, "\tcreate p%d: {&n%d}\n", label, c.src)
}
odst := onodeid(c.dst)
osrc := onodeid(c.src)
// Assign dst this label.
h.onodes[odst].peLabels.Insert(int(label))
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "\to%d has p%d\n", odst, label)
}
h.addImplicitEdge(h.ref(odst), osrc) // *dst ~~> src.
}
// dst = src
func (c *copyConstraint) presolve(h *hvn) {
odst := onodeid(c.dst)
osrc := onodeid(c.src)
h.addEdge(odst, osrc) // dst --> src
h.addImplicitEdge(h.ref(odst), h.ref(osrc)) // *dst ~~> *src
}
// dst = *src + offset
func (c *loadConstraint) presolve(h *hvn) {
odst := onodeid(c.dst)
osrc := onodeid(c.src)
if c.offset == 0 {
h.addEdge(odst, h.ref(osrc)) // dst --> *src
} else {
// We don't interpret load-with-offset, e.g. results
// of map value lookup, R-block of dynamic call, slice
// copy/append, reflection.
h.markIndirect(odst, "load with offset")
}
}
// *dst + offset = src
func (c *storeConstraint) presolve(h *hvn) {
odst := onodeid(c.dst)
osrc := onodeid(c.src)
if c.offset == 0 {
h.onodes[h.ref(odst)].edges.Insert(int(osrc)) // *dst --> src
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "\to%d --> o%d\n", h.ref(odst), osrc)
}
} else {
// We don't interpret store-with-offset.
// See discussion of soundness at markIndirectNodes.
}
}
// dst = &src.offset
func (c *offsetAddrConstraint) presolve(h *hvn) {
// Give each distinct (addr, offset) pair a fresh PE label.
// The cache performs CSE, effectively.
key := offsetAddr{c.src, c.offset}
label, ok := h.offsetAddrLabels[key]
if !ok {
label = h.nextLabel()
h.offsetAddrLabels[key] = label
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "\tcreate p%d: {&n%d.#%d}\n",
label, c.src, c.offset)
}
}
// Assign dst this label.
h.onodes[c.dst].peLabels.Insert(int(label))
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "\to%d has p%d\n", c.dst, label)
}
}
// dst = src.(typ) where typ is an interface
func (c *typeFilterConstraint) presolve(h *hvn) {
h.markIndirect(onodeid(c.dst), "typeFilter result")
}
// dst = src.(typ) where typ is concrete
func (c *untagConstraint) presolve(h *hvn) {
odst := onodeid(c.dst)
for end := odst + onodeid(h.a.sizeof(c.typ)); odst < end; odst++ {
h.markIndirect(odst, "untag result")
}
}
// dst = src.method(c.params...)
func (c *invokeConstraint) presolve(h *hvn) {
// All methods are address-taken functions, so
// their formal P-blocks were already marked indirect.
// Mark the caller's targets node as indirect.
sig := c.method.Type().(*types.Signature)
id := c.params
h.markIndirect(onodeid(c.params), "invoke targets node")
id++
id += nodeid(h.a.sizeof(sig.Params()))
// Mark the caller's R-block as indirect.
end := id + nodeid(h.a.sizeof(sig.Results()))
for id < end {
h.markIndirect(onodeid(id), "invoke R-block")
id++
}
}
// markIndirectNodes marks as indirect nodes whose points-to relations
// are not entirely captured by the offline graph, including:
//
// (a) All address-taken nodes (including the following nodes within
// the same object). This is described in the paper.
//
// The most subtle cause of indirect nodes is the generation of
// store-with-offset constraints since the offline graph doesn't
// represent them. A global audit of constraint generation reveals the
// following uses of store-with-offset:
//
// (b) genDynamicCall, for P-blocks of dynamically called functions,
// to which dynamic copy edges will be added to them during
// solving: from storeConstraint for standalone functions,
// and from invokeConstraint for methods.
// All such P-blocks must be marked indirect.
// (c) MakeUpdate, to update the value part of a map object.
// All MakeMap objects's value parts must be marked indirect.
// (d) copyElems, to update the destination array.
// All array elements must be marked indirect.
//
// Not all indirect marking happens here. ref() nodes are marked
// indirect at construction, and each constraint's presolve() method may
// mark additional nodes.
//
func (h *hvn) markIndirectNodes() {
// (a) all address-taken nodes, plus all nodes following them
// within the same object, since these may be indirectly
// stored or address-taken.
for _, c := range h.a.constraints {
if c, ok := c.(*addrConstraint); ok {
start := h.a.enclosingObj(c.src)
end := start + nodeid(h.a.nodes[start].obj.size)
for id := c.src; id < end; id++ {
h.markIndirect(onodeid(id), "A-T object")
}
}
}
// (b) P-blocks of all address-taken functions.
for id := 0; id < h.N; id++ {
obj := h.a.nodes[id].obj
// TODO(adonovan): opt: if obj.cgn.fn is a method and
// obj.cgn is not its shared contour, this is an
// "inlined" static method call. We needn't consider it
// address-taken since no invokeConstraint will affect it.
if obj != nil && obj.flags&otFunction != 0 && h.a.atFuncs[obj.cgn.fn] {
// address-taken function
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "n%d is address-taken: %s\n", id, obj.cgn.fn)
}
h.markIndirect(onodeid(id), "A-T func identity")
id++
sig := obj.cgn.fn.Signature
psize := h.a.sizeof(sig.Params())
if sig.Recv() != nil {
psize += h.a.sizeof(sig.Recv().Type())
}
for end := id + int(psize); id < end; id++ {
h.markIndirect(onodeid(id), "A-T func P-block")
}
id--
continue
}
}
// (c) all map objects' value fields.
for _, id := range h.a.mapValues {
h.markIndirect(onodeid(id), "makemap.value")
}
// (d) all array element objects.
// TODO(adonovan): opt: can we do better?
for id := 0; id < h.N; id++ {
// Identity node for an object of array type?
if tArray, ok := h.a.nodes[id].typ.(*types.Array); ok {
// Mark the array element nodes indirect.
// (Skip past the identity field.)
for _ = range h.a.flatten(tArray.Elem()) {
id++
h.markIndirect(onodeid(id), "array elem")
}
}
}
}
func (h *hvn) markIndirect(oid onodeid, comment string) {
h.onodes[oid].indirect = true
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "\to%d is indirect: %s\n", oid, comment)
}
}
// Adds an edge dst-->src.
// Note the unusual convention: edges are dependency (contraflow) edges.
func (h *hvn) addEdge(odst, osrc onodeid) {
h.onodes[odst].edges.Insert(int(osrc))
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "\to%d --> o%d\n", odst, osrc)
}
}
func (h *hvn) addImplicitEdge(odst, osrc onodeid) {
h.onodes[odst].implicit.Insert(int(osrc))
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "\to%d ~~> o%d\n", odst, osrc)
}
}
// visit implements the depth-first search of Tarjan's SCC algorithm.
// Precondition: x is canonical.
func (h *hvn) visit(x onodeid) {
h.checkCanonical(x)
xo := h.onodes[x]
xo.index = h.index
xo.lowlink = h.index
h.index++
h.stack = append(h.stack, x) // push
assert(xo.scc == 0, "node revisited")
xo.scc = -1
var deps []int
deps = xo.edges.AppendTo(deps)
deps = xo.implicit.AppendTo(deps)
for _, y := range deps {
// Loop invariant: x is canonical.
y := h.find(onodeid(y))
if x == y {
continue // nodes already coalesced
}
xo := h.onodes[x]
yo := h.onodes[y]
switch {
case yo.scc > 0:
// y is already a collapsed SCC
case yo.scc < 0:
// y is on the stack, and thus in the current SCC.
if yo.index < xo.lowlink {
xo.lowlink = yo.index
}
default:
// y is unvisited; visit it now.
h.visit(y)
// Note: x and y are now non-canonical.
x = h.find(onodeid(x))
if yo.lowlink < xo.lowlink {
xo.lowlink = yo.lowlink
}
}
}
h.checkCanonical(x)
// Is x the root of an SCC?
if xo.lowlink == xo.index {
// Coalesce all nodes in the SCC.
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "scc o%d\n", x)
}
for {
// Pop y from stack.
i := len(h.stack) - 1
y := h.stack[i]
h.stack = h.stack[:i]
h.checkCanonical(x)
xo := h.onodes[x]
h.checkCanonical(y)
yo := h.onodes[y]
if xo == yo {
// SCC is complete.
xo.scc = 1
h.labelSCC(x)
break
}
h.coalesce(x, y)
}
}
}
// Precondition: x is canonical.
func (h *hvn) labelSCC(x onodeid) {
h.checkCanonical(x)
xo := h.onodes[x]
xpe := &xo.peLabels
// All indirect nodes get new labels.
if xo.indirect {
label := h.nextLabel()
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "\tcreate p%d: indirect SCC\n", label)
fmt.Fprintf(h.log, "\to%d has p%d\n", x, label)
}
// Remove pre-labeling, in case a direct pre-labeled node was
// merged with an indirect one.
xpe.Clear()
xpe.Insert(int(label))
return
}
// Invariant: all peLabels sets are non-empty.
// Those that are logically empty contain zero as their sole element.
// No other sets contains zero.
// Find all labels coming in to the coalesced SCC node.
for _, y := range xo.edges.AppendTo(nil) {
y := h.find(onodeid(y))
if y == x {
continue // already coalesced
}
ype := &h.onodes[y].peLabels
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "\tedge from o%d = %s\n", y, ype)
}
if ype.IsEmpty() {
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "\tnode has no PE label\n")
}
}
assert(!ype.IsEmpty(), "incoming node has no PE label")
if ype.Has(0) {
// {0} represents a non-pointer.
assert(ype.Len() == 1, "PE set contains {0, ...}")
} else {
xpe.UnionWith(ype)
}
}
switch xpe.Len() {
case 0:
// SCC has no incoming non-zero PE labels: it is a non-pointer.
xpe.Insert(0)
case 1:
// already a singleton
default:
// SCC has multiple incoming non-zero PE labels.
// Find the canonical label representing this set.
// We use String() as a fingerprint consistent with Equals().
key := xpe.String()
label, ok := h.hvnLabel[key]
if !ok {
label = h.nextLabel()
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "\tcreate p%d: union %s\n", label, xpe.String())
}
h.hvnLabel[key] = label
}
xpe.Clear()
xpe.Insert(int(label))
}
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "\to%d has p%d\n", x, xpe.Min())
}
}
// coalesce combines two nodes in the offline constraint graph.
// Precondition: x and y are canonical.
func (h *hvn) coalesce(x, y onodeid) {
xo := h.onodes[x]
yo := h.onodes[y]
// x becomes y's canonical representative.
yo.rep = x
if debugHVNVerbose && h.log != nil {
fmt.Fprintf(h.log, "\tcoalesce o%d into o%d\n", y, x)
}
// x accumulates y's edges.
xo.edges.UnionWith(&yo.edges)
yo.edges.Clear()
// x accumulates y's implicit edges.
xo.implicit.UnionWith(&yo.implicit)
yo.implicit.Clear()
// x accumulates y's pointer-equivalence labels.
xo.peLabels.UnionWith(&yo.peLabels)
yo.peLabels.Clear()
// x accumulates y's indirect flag.
if yo.indirect {
xo.indirect = true
}
}
// simplify computes a degenerate renumbering of nodeids from the PE
// labels assigned by the hvn, and uses it to simplify the main
// constraint graph, eliminating non-pointer nodes and duplicate
// constraints.
//
func (h *hvn) simplify() {
// canon maps each peLabel to its canonical main node.
canon := make([]nodeid, h.label)
for i := range canon {
canon[i] = nodeid(h.N) // indicates "unset"
}
// mapping maps each main node index to the index of the canonical node.
mapping := make([]nodeid, len(h.a.nodes))
for id := range h.a.nodes {
id := nodeid(id)
if id == 0 {
canon[0] = 0
mapping[0] = 0
continue
}
oid := h.find(onodeid(id))
peLabels := &h.onodes[oid].peLabels
assert(peLabels.Len() == 1, "PE class is not a singleton")
label := peLabel(peLabels.Min())
canonId := canon[label]
if canonId == nodeid(h.N) {
// id becomes the representative of the PE label.
canonId = id
canon[label] = canonId
if h.a.log != nil {
fmt.Fprintf(h.a.log, "\tpts(n%d) is canonical : \t(%s)\n",
id, h.a.nodes[id].typ)
}
} else {
// Link the solver states for the two nodes.
assert(h.a.nodes[canonId].solve != nil, "missing solver state")
h.a.nodes[id].solve = h.a.nodes[canonId].solve
if h.a.log != nil {
// TODO(adonovan): debug: reorganize the log so it prints
// one line:
// pe y = x1, ..., xn
// for each canonical y. Requires allocation.
fmt.Fprintf(h.a.log, "\tpts(n%d) = pts(n%d) : %s\n",
id, canonId, h.a.nodes[id].typ)
}
}
mapping[id] = canonId
}
// Renumber the constraints, eliminate duplicates, and eliminate
// any containing non-pointers (n0).
addrs := make(map[addrConstraint]bool)
copys := make(map[copyConstraint]bool)
loads := make(map[loadConstraint]bool)
stores := make(map[storeConstraint]bool)
offsetAddrs := make(map[offsetAddrConstraint]bool)
untags := make(map[untagConstraint]bool)
typeFilters := make(map[typeFilterConstraint]bool)
invokes := make(map[invokeConstraint]bool)
nbefore := len(h.a.constraints)
cc := h.a.constraints[:0] // in-situ compaction
for _, c := range h.a.constraints {
// Renumber.
switch c := c.(type) {
case *addrConstraint:
// Don't renumber c.src since it is the label of
// an addressable object and will appear in PT sets.
c.dst = mapping[c.dst]
default:
c.renumber(mapping)
}
if c.ptr() == 0 {
continue // skip: constraint attached to non-pointer
}
var dup bool
switch c := c.(type) {
case *addrConstraint:
_, dup = addrs[*c]
addrs[*c] = true
case *copyConstraint:
if c.src == c.dst {
continue // skip degenerate copies
}
if c.src == 0 {
continue // skip copy from non-pointer
}
_, dup = copys[*c]
copys[*c] = true
case *loadConstraint:
if c.src == 0 {
continue // skip load from non-pointer
}
_, dup = loads[*c]
loads[*c] = true
case *storeConstraint:
if c.src == 0 {
continue // skip store from non-pointer
}
_, dup = stores[*c]
stores[*c] = true
case *offsetAddrConstraint:
if c.src == 0 {
continue // skip offset from non-pointer
}
_, dup = offsetAddrs[*c]
offsetAddrs[*c] = true
case *untagConstraint:
if c.src == 0 {
continue // skip untag of non-pointer
}
_, dup = untags[*c]
untags[*c] = true
case *typeFilterConstraint:
if c.src == 0 {
continue // skip filter of non-pointer
}
_, dup = typeFilters[*c]
typeFilters[*c] = true
case *invokeConstraint:
if c.params == 0 {
panic("non-pointer invoke.params")
}
if c.iface == 0 {
continue // skip invoke on non-pointer
}
_, dup = invokes[*c]
invokes[*c] = true
default:
// We don't bother de-duping advanced constraints
// (e.g. reflection) since they are uncommon.
// Eliminate constraints containing non-pointer nodeids.
//
// We use reflection to find the fields to avoid
// adding yet another method to constraint.
//
// TODO(adonovan): experiment with a constraint
// method that returns a slice of pointers to
// nodeids fields to enable uniform iteration;
// the renumber() method could be removed and
// implemented using the new one.
//
// TODO(adonovan): opt: this is unsound since
// some constraints still have an effect if one
// of the operands is zero: rVCall, rVMapIndex,
// rvSetMapIndex. Handle them specially.
rtNodeid := reflect.TypeOf(nodeid(0))
x := reflect.ValueOf(c).Elem()
for i, nf := 0, x.NumField(); i < nf; i++ {
f := x.Field(i)
if f.Type() == rtNodeid {
if f.Uint() == 0 {
dup = true // skip it
break
}
}
}
}
if dup {
continue // skip duplicates
}
cc = append(cc, c)
}
h.a.constraints = cc
if h.log != nil {
fmt.Fprintf(h.log, "#constraints: was %d, now %d\n", nbefore, len(h.a.constraints))
}
}
// find returns the canonical onodeid for x.
// (The onodes form a disjoint set forest.)
func (h *hvn) find(x onodeid) onodeid {
// TODO(adonovan): opt: this is a CPU hotspot. Try "union by rank".
xo := h.onodes[x]
rep := xo.rep
if rep != x {
rep = h.find(rep) // simple path compression
xo.rep = rep
}
return rep
}
func (h *hvn) checkCanonical(x onodeid) {
if debugHVN {
assert(x == h.find(x), "not canonical")
}
}
func assert(p bool, msg string) {
if debugHVN && !p {
panic("assertion failed: " + msg)
}
}

380
vendor/golang.org/x/tools/go/pointer/intrinsics.go generated vendored Normal file
View File

@ -0,0 +1,380 @@
// Copyright 2013 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 pointer
// This package defines the treatment of intrinsics, i.e. library
// functions requiring special analytical treatment.
//
// Most of these are C or assembly functions, but even some Go
// functions require may special treatment if the analysis completely
// replaces the implementation of an API such as reflection.
// TODO(adonovan): support a means of writing analytic summaries in
// the target code, so that users can summarise the effects of their
// own C functions using a snippet of Go.
import (
"fmt"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
)
// Instances of 'intrinsic' generate analysis constraints for calls to
// intrinsic functions.
// Implementations may exploit information from the calling site
// via cgn.callersite; for shared contours this is nil.
type intrinsic func(a *analysis, cgn *cgnode)
// Initialized in explicit init() to defeat (spurious) initialization
// cycle error.
var intrinsicsByName = make(map[string]intrinsic)
func init() {
// Key strings are from Function.String().
// That little dot ۰ is an Arabic zero numeral (U+06F0),
// categories [Nd].
for name, fn := range map[string]intrinsic{
// Other packages.
"bytes.Equal": ext۰NoEffect,
"bytes.IndexByte": ext۰NoEffect,
"crypto/aes.decryptBlockAsm": ext۰NoEffect,
"crypto/aes.encryptBlockAsm": ext۰NoEffect,
"crypto/aes.expandKeyAsm": ext۰NoEffect,
"crypto/aes.hasAsm": ext۰NoEffect,
"crypto/md5.block": ext۰NoEffect,
"crypto/rc4.xorKeyStream": ext۰NoEffect,
"crypto/sha1.block": ext۰NoEffect,
"crypto/sha256.block": ext۰NoEffect,
"hash/crc32.castagnoliSSE42": ext۰NoEffect,
"hash/crc32.haveSSE42": ext۰NoEffect,
"math.Abs": ext۰NoEffect,
"math.Acos": ext۰NoEffect,
"math.Asin": ext۰NoEffect,
"math.Atan": ext۰NoEffect,
"math.Atan2": ext۰NoEffect,
"math.Ceil": ext۰NoEffect,
"math.Cos": ext۰NoEffect,
"math.Dim": ext۰NoEffect,
"math.Exp": ext۰NoEffect,
"math.Exp2": ext۰NoEffect,
"math.Expm1": ext۰NoEffect,
"math.Float32bits": ext۰NoEffect,
"math.Float32frombits": ext۰NoEffect,
"math.Float64bits": ext۰NoEffect,
"math.Float64frombits": ext۰NoEffect,
"math.Floor": ext۰NoEffect,
"math.Frexp": ext۰NoEffect,
"math.Hypot": ext۰NoEffect,
"math.Ldexp": ext۰NoEffect,
"math.Log": ext۰NoEffect,
"math.Log10": ext۰NoEffect,
"math.Log1p": ext۰NoEffect,
"math.Log2": ext۰NoEffect,
"math.Max": ext۰NoEffect,
"math.Min": ext۰NoEffect,
"math.Mod": ext۰NoEffect,
"math.Modf": ext۰NoEffect,
"math.Remainder": ext۰NoEffect,
"math.Sin": ext۰NoEffect,
"math.Sincos": ext۰NoEffect,
"math.Sqrt": ext۰NoEffect,
"math.Tan": ext۰NoEffect,
"math.Trunc": ext۰NoEffect,
"math/big.addMulVVW": ext۰NoEffect,
"math/big.addVV": ext۰NoEffect,
"math/big.addVW": ext۰NoEffect,
"math/big.bitLen": ext۰NoEffect,
"math/big.divWVW": ext۰NoEffect,
"math/big.divWW": ext۰NoEffect,
"math/big.mulAddVWW": ext۰NoEffect,
"math/big.mulWW": ext۰NoEffect,
"math/big.shlVU": ext۰NoEffect,
"math/big.shrVU": ext۰NoEffect,
"math/big.subVV": ext۰NoEffect,
"math/big.subVW": ext۰NoEffect,
"net.runtime_Semacquire": ext۰NoEffect,
"net.runtime_Semrelease": ext۰NoEffect,
"net.runtime_pollClose": ext۰NoEffect,
"net.runtime_pollOpen": ext۰NoEffect,
"net.runtime_pollReset": ext۰NoEffect,
"net.runtime_pollServerInit": ext۰NoEffect,
"net.runtime_pollSetDeadline": ext۰NoEffect,
"net.runtime_pollUnblock": ext۰NoEffect,
"net.runtime_pollWait": ext۰NoEffect,
"net.runtime_pollWaitCanceled": ext۰NoEffect,
"os.epipecheck": ext۰NoEffect,
"runtime.BlockProfile": ext۰NoEffect,
"runtime.Breakpoint": ext۰NoEffect,
"runtime.CPUProfile": ext۰NoEffect, // good enough
"runtime.Caller": ext۰NoEffect,
"runtime.Callers": ext۰NoEffect, // good enough
"runtime.FuncForPC": ext۰NoEffect,
"runtime.GC": ext۰NoEffect,
"runtime.GOMAXPROCS": ext۰NoEffect,
"runtime.Goexit": ext۰NoEffect,
"runtime.GoroutineProfile": ext۰NoEffect,
"runtime.Gosched": ext۰NoEffect,
"runtime.MemProfile": ext۰NoEffect,
"runtime.NumCPU": ext۰NoEffect,
"runtime.NumGoroutine": ext۰NoEffect,
"runtime.ReadMemStats": ext۰NoEffect,
"runtime.SetBlockProfileRate": ext۰NoEffect,
"runtime.SetCPUProfileRate": ext۰NoEffect,
"runtime.SetFinalizer": ext۰runtime۰SetFinalizer,
"runtime.Stack": ext۰NoEffect,
"runtime.ThreadCreateProfile": ext۰NoEffect,
"runtime.cstringToGo": ext۰NoEffect,
"runtime.funcentry_go": ext۰NoEffect,
"runtime.funcline_go": ext۰NoEffect,
"runtime.funcname_go": ext۰NoEffect,
"runtime.getgoroot": ext۰NoEffect,
"runtime/pprof.runtime_cyclesPerSecond": ext۰NoEffect,
"strings.IndexByte": ext۰NoEffect,
"sync.runtime_Semacquire": ext۰NoEffect,
"sync.runtime_Semrelease": ext۰NoEffect,
"sync.runtime_Syncsemacquire": ext۰NoEffect,
"sync.runtime_Syncsemcheck": ext۰NoEffect,
"sync.runtime_Syncsemrelease": ext۰NoEffect,
"sync.runtime_procPin": ext۰NoEffect,
"sync.runtime_procUnpin": ext۰NoEffect,
"sync.runtime_registerPool": ext۰NoEffect,
"sync/atomic.AddInt32": ext۰NoEffect,
"sync/atomic.AddInt64": ext۰NoEffect,
"sync/atomic.AddUint32": ext۰NoEffect,
"sync/atomic.AddUint64": ext۰NoEffect,
"sync/atomic.AddUintptr": ext۰NoEffect,
"sync/atomic.CompareAndSwapInt32": ext۰NoEffect,
"sync/atomic.CompareAndSwapUint32": ext۰NoEffect,
"sync/atomic.CompareAndSwapUint64": ext۰NoEffect,
"sync/atomic.CompareAndSwapUintptr": ext۰NoEffect,
"sync/atomic.LoadInt32": ext۰NoEffect,
"sync/atomic.LoadInt64": ext۰NoEffect,
"sync/atomic.LoadPointer": ext۰NoEffect, // ignore unsafe.Pointers
"sync/atomic.LoadUint32": ext۰NoEffect,
"sync/atomic.LoadUint64": ext۰NoEffect,
"sync/atomic.LoadUintptr": ext۰NoEffect,
"sync/atomic.StoreInt32": ext۰NoEffect,
"sync/atomic.StorePointer": ext۰NoEffect, // ignore unsafe.Pointers
"sync/atomic.StoreUint32": ext۰NoEffect,
"sync/atomic.StoreUintptr": ext۰NoEffect,
"syscall.Close": ext۰NoEffect,
"syscall.Exit": ext۰NoEffect,
"syscall.Getpid": ext۰NoEffect,
"syscall.Getwd": ext۰NoEffect,
"syscall.Kill": ext۰NoEffect,
"syscall.RawSyscall": ext۰NoEffect,
"syscall.RawSyscall6": ext۰NoEffect,
"syscall.Syscall": ext۰NoEffect,
"syscall.Syscall6": ext۰NoEffect,
"syscall.runtime_AfterFork": ext۰NoEffect,
"syscall.runtime_BeforeFork": ext۰NoEffect,
"syscall.setenv_c": ext۰NoEffect,
"time.Sleep": ext۰NoEffect,
"time.now": ext۰NoEffect,
"time.startTimer": ext۰time۰startTimer,
"time.stopTimer": ext۰NoEffect,
} {
intrinsicsByName[name] = fn
}
}
// findIntrinsic returns the constraint generation function for an
// intrinsic function fn, or nil if the function should be handled normally.
//
func (a *analysis) findIntrinsic(fn *ssa.Function) intrinsic {
// Consult the *Function-keyed cache.
// A cached nil indicates a normal non-intrinsic function.
impl, ok := a.intrinsics[fn]
if !ok {
impl = intrinsicsByName[fn.String()] // may be nil
if a.isReflect(fn) {
if !a.config.Reflection {
impl = ext۰NoEffect // reflection disabled
} else if impl == nil {
// Ensure all "reflect" code is treated intrinsically.
impl = ext۰NotYetImplemented
}
}
a.intrinsics[fn] = impl
}
return impl
}
// isReflect reports whether fn belongs to the "reflect" package.
func (a *analysis) isReflect(fn *ssa.Function) bool {
if a.reflectValueObj == nil {
return false // "reflect" package not loaded
}
reflectPackage := a.reflectValueObj.Pkg()
if fn.Pkg != nil && fn.Pkg.Object == reflectPackage {
return true
}
// Synthetic wrappers have a nil Pkg, so they slip through the
// previous check. Check the receiver package.
// TODO(adonovan): should synthetic wrappers have a non-nil Pkg?
if recv := fn.Signature.Recv(); recv != nil {
if named, ok := deref(recv.Type()).(*types.Named); ok {
if named.Obj().Pkg() == reflectPackage {
return true // e.g. wrapper of (reflect.Value).f
}
}
}
return false
}
// A trivial intrinsic suitable for any function that does not:
// 1) induce aliases between its arguments or any global variables;
// 2) call any functions; or
// 3) create any labels.
//
// Many intrinsics (such as CompareAndSwapInt32) have a fourth kind of
// effect: loading or storing through a pointer. Though these could
// be significant, we deliberately ignore them because they are
// generally not worth the effort.
//
// We sometimes violate condition #3 if the function creates only
// non-function labels, as the control-flow graph is still sound.
//
func ext۰NoEffect(a *analysis, cgn *cgnode) {}
func ext۰NotYetImplemented(a *analysis, cgn *cgnode) {
fn := cgn.fn
a.warnf(fn.Pos(), "unsound: intrinsic treatment of %s not yet implemented", fn)
}
// ---------- func runtime.SetFinalizer(x, f interface{}) ----------
// runtime.SetFinalizer(x, f)
type runtimeSetFinalizerConstraint struct {
targets nodeid // (indirect)
f nodeid // (ptr)
x nodeid
}
func (c *runtimeSetFinalizerConstraint) ptr() nodeid { return c.f }
func (c *runtimeSetFinalizerConstraint) presolve(h *hvn) {
h.markIndirect(onodeid(c.targets), "SetFinalizer.targets")
}
func (c *runtimeSetFinalizerConstraint) renumber(mapping []nodeid) {
c.targets = mapping[c.targets]
c.f = mapping[c.f]
c.x = mapping[c.x]
}
func (c *runtimeSetFinalizerConstraint) String() string {
return fmt.Sprintf("runtime.SetFinalizer(n%d, n%d)", c.x, c.f)
}
func (c *runtimeSetFinalizerConstraint) solve(a *analysis, delta *nodeset) {
for _, fObj := range delta.AppendTo(a.deltaSpace) {
tDyn, f, indirect := a.taggedValue(nodeid(fObj))
if indirect {
// TODO(adonovan): we'll need to implement this
// when we start creating indirect tagged objects.
panic("indirect tagged object")
}
tSig, ok := tDyn.Underlying().(*types.Signature)
if !ok {
continue // not a function
}
if tSig.Recv() != nil {
panic(tSig)
}
if tSig.Params().Len() != 1 {
continue // not a unary function
}
// Extract x to tmp.
tx := tSig.Params().At(0).Type()
tmp := a.addNodes(tx, "SetFinalizer.tmp")
a.typeAssert(tx, tmp, c.x, false)
// Call f(tmp).
a.store(f, tmp, 1, a.sizeof(tx))
// Add dynamic call target.
if a.onlineCopy(c.targets, f) {
a.addWork(c.targets)
}
}
}
func ext۰runtime۰SetFinalizer(a *analysis, cgn *cgnode) {
// This is the shared contour, used for dynamic calls.
targets := a.addOneNode(tInvalid, "SetFinalizer.targets", nil)
cgn.sites = append(cgn.sites, &callsite{targets: targets})
params := a.funcParams(cgn.obj)
a.addConstraint(&runtimeSetFinalizerConstraint{
targets: targets,
x: params,
f: params + 1,
})
}
// ---------- func time.startTimer(t *runtimeTimer) ----------
// time.StartTimer(t)
type timeStartTimerConstraint struct {
targets nodeid // (indirect)
t nodeid // (ptr)
}
func (c *timeStartTimerConstraint) ptr() nodeid { return c.t }
func (c *timeStartTimerConstraint) presolve(h *hvn) {
h.markIndirect(onodeid(c.targets), "StartTimer.targets")
}
func (c *timeStartTimerConstraint) renumber(mapping []nodeid) {
c.targets = mapping[c.targets]
c.t = mapping[c.t]
}
func (c *timeStartTimerConstraint) String() string {
return fmt.Sprintf("time.startTimer(n%d)", c.t)
}
func (c *timeStartTimerConstraint) solve(a *analysis, delta *nodeset) {
for _, tObj := range delta.AppendTo(a.deltaSpace) {
t := nodeid(tObj)
// We model startTimer as if it was defined thus:
// func startTimer(t *runtimeTimer) { t.f(t.arg) }
// We hard-code the field offsets of time.runtimeTimer:
// type runtimeTimer struct {
// 0 __identity__
// 1 i int32
// 2 when int64
// 3 period int64
// 4 f func(int64, interface{})
// 5 arg interface{}
// }
f := t + 4
arg := t + 5
// store t.arg to t.f.params[0]
// (offset 1 => skip identity)
a.store(f, arg, 1, 1)
// Add dynamic call target.
if a.onlineCopy(c.targets, f) {
a.addWork(c.targets)
}
}
}
func ext۰time۰startTimer(a *analysis, cgn *cgnode) {
// This is the shared contour, used for dynamic calls.
targets := a.addOneNode(tInvalid, "startTimer.targets", nil)
cgn.sites = append(cgn.sites, &callsite{targets: targets})
params := a.funcParams(cgn.obj)
a.addConstraint(&timeStartTimerConstraint{
targets: targets,
t: params,
})
}

152
vendor/golang.org/x/tools/go/pointer/labels.go generated vendored Normal file
View File

@ -0,0 +1,152 @@
// Copyright 2013 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 pointer
import (
"fmt"
"go/token"
"strings"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
)
// A Label is an entity that may be pointed to by a pointer, map,
// channel, 'func', slice or interface.
//
// Labels include:
// - functions
// - globals
// - tagged objects, representing interfaces and reflect.Values
// - arrays created by conversions (e.g. []byte("foo"), []byte(s))
// - stack- and heap-allocated variables (including composite literals)
// - channels, maps and arrays created by make()
// - intrinsic or reflective operations that allocate (e.g. append, reflect.New)
// - intrinsic objects, e.g. the initial array behind os.Args.
// - and their subelements, e.g. "alloc.y[*].z"
//
// Labels are so varied that they defy good generalizations;
// some have no value, no callgraph node, or no position.
// Many objects have types that are inexpressible in Go:
// maps, channels, functions, tagged objects.
//
// At most one of Value() or ReflectType() may return non-nil.
//
type Label struct {
obj *object // the addressable memory location containing this label
subelement *fieldInfo // subelement path within obj, e.g. ".a.b[*].c"
}
// Value returns the ssa.Value that allocated this label's object, if any.
func (l Label) Value() ssa.Value {
val, _ := l.obj.data.(ssa.Value)
return val
}
// ReflectType returns the type represented by this label if it is an
// reflect.rtype instance object or *reflect.rtype-tagged object.
//
func (l Label) ReflectType() types.Type {
rtype, _ := l.obj.data.(types.Type)
return rtype
}
// Path returns the path to the subelement of the object containing
// this label. For example, ".x[*].y".
//
func (l Label) Path() string {
return l.subelement.path()
}
// Pos returns the position of this label, if known, zero otherwise.
func (l Label) Pos() token.Pos {
switch data := l.obj.data.(type) {
case ssa.Value:
return data.Pos()
case types.Type:
if nt, ok := deref(data).(*types.Named); ok {
return nt.Obj().Pos()
}
}
if cgn := l.obj.cgn; cgn != nil {
return cgn.fn.Pos()
}
return token.NoPos
}
// String returns the printed form of this label.
//
// Examples: Object type:
// x (a variable)
// (sync.Mutex).Lock (a function)
// convert (array created by conversion)
// makemap (map allocated via make)
// makechan (channel allocated via make)
// makeinterface (tagged object allocated by makeinterface)
// <alloc in reflect.Zero> (allocation in instrinsic)
// sync.Mutex (a reflect.rtype instance)
// <command-line arguments> (an intrinsic object)
//
// Labels within compound objects have subelement paths:
// x.y[*].z (a struct variable, x)
// append.y[*].z (array allocated by append)
// makeslice.y[*].z (array allocated via make)
//
// TODO(adonovan): expose func LabelString(*types.Package, Label).
//
func (l Label) String() string {
var s string
switch v := l.obj.data.(type) {
case types.Type:
return v.String()
case string:
s = v // an intrinsic object (e.g. os.Args[*])
case nil:
if l.obj.cgn != nil {
// allocation by intrinsic or reflective operation
s = fmt.Sprintf("<alloc in %s>", l.obj.cgn.fn)
} else {
s = "<unknown>" // should be unreachable
}
case *ssa.Function:
s = v.String()
case *ssa.Global:
s = v.String()
case *ssa.Const:
s = v.Name()
case *ssa.Alloc:
s = v.Comment
if s == "" {
s = "alloc"
}
case *ssa.Call:
// Currently only calls to append can allocate objects.
if v.Call.Value.(*ssa.Builtin).Object().Name() != "append" {
panic("unhandled *ssa.Call label: " + v.Name())
}
s = "append"
case *ssa.MakeMap, *ssa.MakeChan, *ssa.MakeSlice, *ssa.Convert:
s = strings.ToLower(strings.TrimPrefix(fmt.Sprintf("%T", v), "*ssa."))
case *ssa.MakeInterface:
// MakeInterface is usually implicit in Go source (so
// Pos()==0), and tagged objects may be allocated
// synthetically (so no *MakeInterface data).
s = "makeinterface:" + v.X.Type().String()
default:
panic(fmt.Sprintf("unhandled object data type: %T", v))
}
return s + l.subelement.path()
}

125
vendor/golang.org/x/tools/go/pointer/opt.go generated vendored Normal file
View File

@ -0,0 +1,125 @@
// Copyright 2013 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 pointer
// This file implements renumbering, a pre-solver optimization to
// improve the efficiency of the solver's points-to set representation.
//
// TODO(adonovan): rename file "renumber.go"
import "fmt"
// renumber permutes a.nodes so that all nodes within an addressable
// object appear before all non-addressable nodes, maintaining the
// order of nodes within the same object (as required by offsetAddr).
//
// renumber must update every nodeid in the analysis (constraints,
// Pointers, callgraph, etc) to reflect the new ordering.
//
// This is an optimisation to increase the locality and efficiency of
// sparse representations of points-to sets. (Typically only about
// 20% of nodes are within an object.)
//
// NB: nodes added during solving (e.g. for reflection, SetFinalizer)
// will be appended to the end.
//
// Renumbering makes the PTA log inscrutable. To aid debugging, later
// phases (e.g. HVN) must not rely on it having occurred.
//
func (a *analysis) renumber() {
if a.log != nil {
fmt.Fprintf(a.log, "\n\n==== Renumbering\n\n")
}
N := nodeid(len(a.nodes))
newNodes := make([]*node, N, N)
renumbering := make([]nodeid, N, N) // maps old to new
var i, j nodeid
// The zero node is special.
newNodes[j] = a.nodes[i]
renumbering[i] = j
i++
j++
// Pass 1: object nodes.
for i < N {
obj := a.nodes[i].obj
if obj == nil {
i++
continue
}
end := i + nodeid(obj.size)
for i < end {
newNodes[j] = a.nodes[i]
renumbering[i] = j
i++
j++
}
}
nobj := j
// Pass 2: non-object nodes.
for i = 1; i < N; {
obj := a.nodes[i].obj
if obj != nil {
i += nodeid(obj.size)
continue
}
newNodes[j] = a.nodes[i]
renumbering[i] = j
i++
j++
}
if j != N {
panic(fmt.Sprintf("internal error: j=%d, N=%d", j, N))
}
// Log the remapping table.
if a.log != nil {
fmt.Fprintf(a.log, "Renumbering nodes to improve density:\n")
fmt.Fprintf(a.log, "(%d object nodes of %d total)\n", nobj, N)
for old, new := range renumbering {
fmt.Fprintf(a.log, "\tn%d -> n%d\n", old, new)
}
}
// Now renumber all existing nodeids to use the new node permutation.
// It is critical that all reachable nodeids are accounted for!
// Renumber nodeids in queried Pointers.
for v, ptr := range a.result.Queries {
ptr.n = renumbering[ptr.n]
a.result.Queries[v] = ptr
}
for v, ptr := range a.result.IndirectQueries {
ptr.n = renumbering[ptr.n]
a.result.IndirectQueries[v] = ptr
}
// Renumber nodeids in global objects.
for v, id := range a.globalobj {
a.globalobj[v] = renumbering[id]
}
// Renumber nodeids in constraints.
for _, c := range a.constraints {
c.renumber(renumbering)
}
// Renumber nodeids in the call graph.
for _, cgn := range a.cgnodes {
cgn.obj = renumbering[cgn.obj]
for _, site := range cgn.sites {
site.targets = renumbering[site.targets]
}
}
a.nodes = newNodes
}

43
vendor/golang.org/x/tools/go/pointer/print.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
// Copyright 2013 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 pointer
import "fmt"
func (c *addrConstraint) String() string {
return fmt.Sprintf("addr n%d <- {&n%d}", c.dst, c.src)
}
func (c *copyConstraint) String() string {
return fmt.Sprintf("copy n%d <- n%d", c.dst, c.src)
}
func (c *loadConstraint) String() string {
return fmt.Sprintf("load n%d <- n%d[%d]", c.dst, c.src, c.offset)
}
func (c *storeConstraint) String() string {
return fmt.Sprintf("store n%d[%d] <- n%d", c.dst, c.offset, c.src)
}
func (c *offsetAddrConstraint) String() string {
return fmt.Sprintf("offsetAddr n%d <- n%d.#%d", c.dst, c.src, c.offset)
}
func (c *typeFilterConstraint) String() string {
return fmt.Sprintf("typeFilter n%d <- n%d.(%s)", c.dst, c.src, c.typ)
}
func (c *untagConstraint) String() string {
return fmt.Sprintf("untag n%d <- n%d.(%s)", c.dst, c.src, c.typ)
}
func (c *invokeConstraint) String() string {
return fmt.Sprintf("invoke n%d.%s(n%d ...)", c.iface, c.method.Name(), c.params)
}
func (n nodeid) String() string {
return fmt.Sprintf("n%d", n)
}

1971
vendor/golang.org/x/tools/go/pointer/reflect.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

371
vendor/golang.org/x/tools/go/pointer/solve.go generated vendored Normal file
View File

@ -0,0 +1,371 @@
// Copyright 2013 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 pointer
// This file defines a naive Andersen-style solver for the inclusion
// constraint system.
import (
"fmt"
"golang.org/x/tools/go/types"
)
type solverState struct {
complex []constraint // complex constraints attached to this node
copyTo nodeset // simple copy constraint edges
pts nodeset // points-to set of this node
prevPTS nodeset // pts(n) in previous iteration (for difference propagation)
}
func (a *analysis) solve() {
start("Solving")
if a.log != nil {
fmt.Fprintf(a.log, "\n\n==== Solving constraints\n\n")
}
// Solver main loop.
var delta nodeset
for {
// Add new constraints to the graph:
// static constraints from SSA on round 1,
// dynamic constraints from reflection thereafter.
a.processNewConstraints()
var x int
if !a.work.TakeMin(&x) {
break // empty
}
id := nodeid(x)
if a.log != nil {
fmt.Fprintf(a.log, "\tnode n%d\n", id)
}
n := a.nodes[id]
// Difference propagation.
delta.Difference(&n.solve.pts.Sparse, &n.solve.prevPTS.Sparse)
if delta.IsEmpty() {
continue
}
if a.log != nil {
fmt.Fprintf(a.log, "\t\tpts(n%d : %s) = %s + %s\n",
id, n.typ, &delta, &n.solve.prevPTS)
}
n.solve.prevPTS.Copy(&n.solve.pts.Sparse)
// Apply all resolution rules attached to n.
a.solveConstraints(n, &delta)
if a.log != nil {
fmt.Fprintf(a.log, "\t\tpts(n%d) = %s\n", id, &n.solve.pts)
}
}
if !a.nodes[0].solve.pts.IsEmpty() {
panic(fmt.Sprintf("pts(0) is nonempty: %s", &a.nodes[0].solve.pts))
}
// Release working state (but keep final PTS).
for _, n := range a.nodes {
n.solve.complex = nil
n.solve.copyTo.Clear()
n.solve.prevPTS.Clear()
}
if a.log != nil {
fmt.Fprintf(a.log, "Solver done\n")
// Dump solution.
for i, n := range a.nodes {
if !n.solve.pts.IsEmpty() {
fmt.Fprintf(a.log, "pts(n%d) = %s : %s\n", i, &n.solve.pts, n.typ)
}
}
}
stop("Solving")
}
// processNewConstraints takes the new constraints from a.constraints
// and adds them to the graph, ensuring
// that new constraints are applied to pre-existing labels and
// that pre-existing constraints are applied to new labels.
//
func (a *analysis) processNewConstraints() {
// Take the slice of new constraints.
// (May grow during call to solveConstraints.)
constraints := a.constraints
a.constraints = nil
// Initialize points-to sets from addr-of (base) constraints.
for _, c := range constraints {
if c, ok := c.(*addrConstraint); ok {
dst := a.nodes[c.dst]
dst.solve.pts.add(c.src)
// Populate the worklist with nodes that point to
// something initially (due to addrConstraints) and
// have other constraints attached.
// (A no-op in round 1.)
if !dst.solve.copyTo.IsEmpty() || len(dst.solve.complex) > 0 {
a.addWork(c.dst)
}
}
}
// Attach simple (copy) and complex constraints to nodes.
var stale nodeset
for _, c := range constraints {
var id nodeid
switch c := c.(type) {
case *addrConstraint:
// base constraints handled in previous loop
continue
case *copyConstraint:
// simple (copy) constraint
id = c.src
a.nodes[id].solve.copyTo.add(c.dst)
default:
// complex constraint
id = c.ptr()
solve := a.nodes[id].solve
solve.complex = append(solve.complex, c)
}
if n := a.nodes[id]; !n.solve.pts.IsEmpty() {
if !n.solve.prevPTS.IsEmpty() {
stale.add(id)
}
a.addWork(id)
}
}
// Apply new constraints to pre-existing PTS labels.
var space [50]int
for _, id := range stale.AppendTo(space[:0]) {
n := a.nodes[nodeid(id)]
a.solveConstraints(n, &n.solve.prevPTS)
}
}
// solveConstraints applies each resolution rule attached to node n to
// the set of labels delta. It may generate new constraints in
// a.constraints.
//
func (a *analysis) solveConstraints(n *node, delta *nodeset) {
if delta.IsEmpty() {
return
}
// Process complex constraints dependent on n.
for _, c := range n.solve.complex {
if a.log != nil {
fmt.Fprintf(a.log, "\t\tconstraint %s\n", c)
}
c.solve(a, delta)
}
// Process copy constraints.
var copySeen nodeset
for _, x := range n.solve.copyTo.AppendTo(a.deltaSpace) {
mid := nodeid(x)
if copySeen.add(mid) {
if a.nodes[mid].solve.pts.addAll(delta) {
a.addWork(mid)
}
}
}
}
// addLabel adds label to the points-to set of ptr and reports whether the set grew.
func (a *analysis) addLabel(ptr, label nodeid) bool {
b := a.nodes[ptr].solve.pts.add(label)
if b && a.log != nil {
fmt.Fprintf(a.log, "\t\tpts(n%d) += n%d\n", ptr, label)
}
return b
}
func (a *analysis) addWork(id nodeid) {
a.work.Insert(int(id))
if a.log != nil {
fmt.Fprintf(a.log, "\t\twork: n%d\n", id)
}
}
// onlineCopy adds a copy edge. It is called online, i.e. during
// solving, so it adds edges and pts members directly rather than by
// instantiating a 'constraint'.
//
// The size of the copy is implicitly 1.
// It returns true if pts(dst) changed.
//
func (a *analysis) onlineCopy(dst, src nodeid) bool {
if dst != src {
if nsrc := a.nodes[src]; nsrc.solve.copyTo.add(dst) {
if a.log != nil {
fmt.Fprintf(a.log, "\t\t\tdynamic copy n%d <- n%d\n", dst, src)
}
// TODO(adonovan): most calls to onlineCopy
// are followed by addWork, possibly batched
// via a 'changed' flag; see if there's a
// noticeable penalty to calling addWork here.
return a.nodes[dst].solve.pts.addAll(&nsrc.solve.pts)
}
}
return false
}
// Returns sizeof.
// Implicitly adds nodes to worklist.
//
// TODO(adonovan): now that we support a.copy() during solving, we
// could eliminate onlineCopyN, but it's much slower. Investigate.
//
func (a *analysis) onlineCopyN(dst, src nodeid, sizeof uint32) uint32 {
for i := uint32(0); i < sizeof; i++ {
if a.onlineCopy(dst, src) {
a.addWork(dst)
}
src++
dst++
}
return sizeof
}
func (c *loadConstraint) solve(a *analysis, delta *nodeset) {
var changed bool
for _, x := range delta.AppendTo(a.deltaSpace) {
k := nodeid(x)
koff := k + nodeid(c.offset)
if a.onlineCopy(c.dst, koff) {
changed = true
}
}
if changed {
a.addWork(c.dst)
}
}
func (c *storeConstraint) solve(a *analysis, delta *nodeset) {
for _, x := range delta.AppendTo(a.deltaSpace) {
k := nodeid(x)
koff := k + nodeid(c.offset)
if a.onlineCopy(koff, c.src) {
a.addWork(koff)
}
}
}
func (c *offsetAddrConstraint) solve(a *analysis, delta *nodeset) {
dst := a.nodes[c.dst]
for _, x := range delta.AppendTo(a.deltaSpace) {
k := nodeid(x)
if dst.solve.pts.add(k + nodeid(c.offset)) {
a.addWork(c.dst)
}
}
}
func (c *typeFilterConstraint) solve(a *analysis, delta *nodeset) {
for _, x := range delta.AppendTo(a.deltaSpace) {
ifaceObj := nodeid(x)
tDyn, _, indirect := a.taggedValue(ifaceObj)
if indirect {
// TODO(adonovan): we'll need to implement this
// when we start creating indirect tagged objects.
panic("indirect tagged object")
}
if types.AssignableTo(tDyn, c.typ) {
if a.addLabel(c.dst, ifaceObj) {
a.addWork(c.dst)
}
}
}
}
func (c *untagConstraint) solve(a *analysis, delta *nodeset) {
predicate := types.AssignableTo
if c.exact {
predicate = types.Identical
}
for _, x := range delta.AppendTo(a.deltaSpace) {
ifaceObj := nodeid(x)
tDyn, v, indirect := a.taggedValue(ifaceObj)
if indirect {
// TODO(adonovan): we'll need to implement this
// when we start creating indirect tagged objects.
panic("indirect tagged object")
}
if predicate(tDyn, c.typ) {
// Copy payload sans tag to dst.
//
// TODO(adonovan): opt: if tDyn is
// nonpointerlike we can skip this entire
// constraint, perhaps. We only care about
// pointers among the fields.
a.onlineCopyN(c.dst, v, a.sizeof(tDyn))
}
}
}
func (c *invokeConstraint) solve(a *analysis, delta *nodeset) {
for _, x := range delta.AppendTo(a.deltaSpace) {
ifaceObj := nodeid(x)
tDyn, v, indirect := a.taggedValue(ifaceObj)
if indirect {
// TODO(adonovan): we may need to implement this if
// we ever apply invokeConstraints to reflect.Value PTSs,
// e.g. for (reflect.Value).Call.
panic("indirect tagged object")
}
// Look up the concrete method.
fn := a.prog.LookupMethod(tDyn, c.method.Pkg(), c.method.Name())
if fn == nil {
panic(fmt.Sprintf("n%d: no ssa.Function for %s", c.iface, c.method))
}
sig := fn.Signature
fnObj := a.globalobj[fn] // dynamic calls use shared contour
if fnObj == 0 {
// a.objectNode(fn) was not called during gen phase.
panic(fmt.Sprintf("a.globalobj[%s]==nil", fn))
}
// Make callsite's fn variable point to identity of
// concrete method. (There's no need to add it to
// worklist since it never has attached constraints.)
a.addLabel(c.params, fnObj)
// Extract value and connect to method's receiver.
// Copy payload to method's receiver param (arg0).
arg0 := a.funcParams(fnObj)
recvSize := a.sizeof(sig.Recv().Type())
a.onlineCopyN(arg0, v, recvSize)
src := c.params + 1 // skip past identity
dst := arg0 + nodeid(recvSize)
// Copy caller's argument block to method formal parameters.
paramsSize := a.sizeof(sig.Params())
a.onlineCopyN(dst, src, paramsSize)
src += nodeid(paramsSize)
dst += nodeid(paramsSize)
// Copy method results to caller's result block.
resultsSize := a.sizeof(sig.Results())
a.onlineCopyN(src, dst, resultsSize)
}
}
func (c *addrConstraint) solve(a *analysis, delta *nodeset) {
panic("addr is not a complex constraint")
}
func (c *copyConstraint) solve(a *analysis, delta *nodeset) {
panic("copy is not a complex constraint")
}

314
vendor/golang.org/x/tools/go/pointer/util.go generated vendored Normal file
View File

@ -0,0 +1,314 @@
// Copyright 2013 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 pointer
import (
"bytes"
"fmt"
"log"
"os"
"os/exec"
"runtime"
"time"
"golang.org/x/tools/container/intsets"
"golang.org/x/tools/go/types"
)
// CanPoint reports whether the type T is pointerlike,
// for the purposes of this analysis.
func CanPoint(T types.Type) bool {
switch T := T.(type) {
case *types.Named:
if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" {
return true // treat reflect.Value like interface{}
}
return CanPoint(T.Underlying())
case *types.Pointer, *types.Interface, *types.Map, *types.Chan, *types.Signature, *types.Slice:
return true
}
return false // array struct tuple builtin basic
}
// CanHaveDynamicTypes reports whether the type T can "hold" dynamic types,
// i.e. is an interface (incl. reflect.Type) or a reflect.Value.
//
func CanHaveDynamicTypes(T types.Type) bool {
switch T := T.(type) {
case *types.Named:
if obj := T.Obj(); obj.Name() == "Value" && obj.Pkg().Path() == "reflect" {
return true // reflect.Value
}
return CanHaveDynamicTypes(T.Underlying())
case *types.Interface:
return true
}
return false
}
func isInterface(T types.Type) bool { return types.IsInterface(T) }
// mustDeref returns the element type of its argument, which must be a
// pointer; panic ensues otherwise.
func mustDeref(typ types.Type) types.Type {
return typ.Underlying().(*types.Pointer).Elem()
}
// deref returns a pointer's element type; otherwise it returns typ.
func deref(typ types.Type) types.Type {
if p, ok := typ.Underlying().(*types.Pointer); ok {
return p.Elem()
}
return typ
}
// A fieldInfo describes one subelement (node) of the flattening-out
// of a type T: the subelement's type and its path from the root of T.
//
// For example, for this type:
// type line struct{ points []struct{x, y int} }
// flatten() of the inner struct yields the following []fieldInfo:
// struct{ x, y int } ""
// int ".x"
// int ".y"
// and flatten(line) yields:
// struct{ points []struct{x, y int} } ""
// struct{ x, y int } ".points[*]"
// int ".points[*].x
// int ".points[*].y"
//
type fieldInfo struct {
typ types.Type
// op and tail describe the path to the element (e.g. ".a#2.b[*].c").
op interface{} // *Array: true; *Tuple: int; *Struct: *types.Var; *Named: nil
tail *fieldInfo
}
// path returns a user-friendly string describing the subelement path.
//
func (fi *fieldInfo) path() string {
var buf bytes.Buffer
for p := fi; p != nil; p = p.tail {
switch op := p.op.(type) {
case bool:
fmt.Fprintf(&buf, "[*]")
case int:
fmt.Fprintf(&buf, "#%d", op)
case *types.Var:
fmt.Fprintf(&buf, ".%s", op.Name())
}
}
return buf.String()
}
// flatten returns a list of directly contained fields in the preorder
// traversal of the type tree of t. The resulting elements are all
// scalars (basic types or pointerlike types), except for struct/array
// "identity" nodes, whose type is that of the aggregate.
//
// reflect.Value is considered pointerlike, similar to interface{}.
//
// Callers must not mutate the result.
//
func (a *analysis) flatten(t types.Type) []*fieldInfo {
fl, ok := a.flattenMemo[t]
if !ok {
switch t := t.(type) {
case *types.Named:
u := t.Underlying()
if isInterface(u) {
// Debuggability hack: don't remove
// the named type from interfaces as
// they're very verbose.
fl = append(fl, &fieldInfo{typ: t})
} else {
fl = a.flatten(u)
}
case *types.Basic,
*types.Signature,
*types.Chan,
*types.Map,
*types.Interface,
*types.Slice,
*types.Pointer:
fl = append(fl, &fieldInfo{typ: t})
case *types.Array:
fl = append(fl, &fieldInfo{typ: t}) // identity node
for _, fi := range a.flatten(t.Elem()) {
fl = append(fl, &fieldInfo{typ: fi.typ, op: true, tail: fi})
}
case *types.Struct:
fl = append(fl, &fieldInfo{typ: t}) // identity node
for i, n := 0, t.NumFields(); i < n; i++ {
f := t.Field(i)
for _, fi := range a.flatten(f.Type()) {
fl = append(fl, &fieldInfo{typ: fi.typ, op: f, tail: fi})
}
}
case *types.Tuple:
// No identity node: tuples are never address-taken.
n := t.Len()
if n == 1 {
// Don't add a fieldInfo link for singletons,
// e.g. in params/results.
fl = append(fl, a.flatten(t.At(0).Type())...)
} else {
for i := 0; i < n; i++ {
f := t.At(i)
for _, fi := range a.flatten(f.Type()) {
fl = append(fl, &fieldInfo{typ: fi.typ, op: i, tail: fi})
}
}
}
default:
panic(t)
}
a.flattenMemo[t] = fl
}
return fl
}
// sizeof returns the number of pointerlike abstractions (nodes) in the type t.
func (a *analysis) sizeof(t types.Type) uint32 {
return uint32(len(a.flatten(t)))
}
// shouldTrack reports whether object type T contains (recursively)
// any fields whose addresses should be tracked.
func (a *analysis) shouldTrack(T types.Type) bool {
if a.track == trackAll {
return true // fast path
}
track, ok := a.trackTypes[T]
if !ok {
a.trackTypes[T] = true // break cycles conservatively
// NB: reflect.Value, reflect.Type are pre-populated to true.
for _, fi := range a.flatten(T) {
switch ft := fi.typ.Underlying().(type) {
case *types.Interface, *types.Signature:
track = true // needed for callgraph
case *types.Basic:
// no-op
case *types.Chan:
track = a.track&trackChan != 0 || a.shouldTrack(ft.Elem())
case *types.Map:
track = a.track&trackMap != 0 || a.shouldTrack(ft.Key()) || a.shouldTrack(ft.Elem())
case *types.Slice:
track = a.track&trackSlice != 0 || a.shouldTrack(ft.Elem())
case *types.Pointer:
track = a.track&trackPtr != 0 || a.shouldTrack(ft.Elem())
case *types.Array, *types.Struct:
// No need to look at field types since they will follow (flattened).
default:
// Includes *types.Tuple, which are never address-taken.
panic(ft)
}
if track {
break
}
}
a.trackTypes[T] = track
if !track && a.log != nil {
fmt.Fprintf(a.log, "\ttype not tracked: %s\n", T)
}
}
return track
}
// offsetOf returns the (abstract) offset of field index within struct
// or tuple typ.
func (a *analysis) offsetOf(typ types.Type, index int) uint32 {
var offset uint32
switch t := typ.Underlying().(type) {
case *types.Tuple:
for i := 0; i < index; i++ {
offset += a.sizeof(t.At(i).Type())
}
case *types.Struct:
offset++ // the node for the struct itself
for i := 0; i < index; i++ {
offset += a.sizeof(t.Field(i).Type())
}
default:
panic(fmt.Sprintf("offsetOf(%s : %T)", typ, typ))
}
return offset
}
// sliceToArray returns the type representing the arrays to which
// slice type slice points.
func sliceToArray(slice types.Type) *types.Array {
return types.NewArray(slice.Underlying().(*types.Slice).Elem(), 1)
}
// Node set -------------------------------------------------------------------
type nodeset struct {
intsets.Sparse
}
func (ns *nodeset) String() string {
var buf bytes.Buffer
buf.WriteRune('{')
var space [50]int
for i, n := range ns.AppendTo(space[:0]) {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteRune('n')
fmt.Fprintf(&buf, "%d", n)
}
buf.WriteRune('}')
return buf.String()
}
func (ns *nodeset) add(n nodeid) bool {
return ns.Sparse.Insert(int(n))
}
func (x *nodeset) addAll(y *nodeset) bool {
return x.UnionWith(&y.Sparse)
}
// Profiling & debugging -------------------------------------------------------
var timers = make(map[string]time.Time)
func start(name string) {
if debugTimers {
timers[name] = time.Now()
log.Printf("%s...\n", name)
}
}
func stop(name string) {
if debugTimers {
log.Printf("%s took %s\n", name, time.Since(timers[name]))
}
}
// diff runs the command "diff a b" and reports its success.
func diff(a, b string) bool {
var cmd *exec.Cmd
switch runtime.GOOS {
case "plan9":
cmd = exec.Command("/bin/diff", "-c", a, b)
default:
cmd = exec.Command("/usr/bin/diff", "-u", a, b)
}
cmd.Stdout = os.Stderr
cmd.Stderr = os.Stderr
return cmd.Run() == nil
}

187
vendor/golang.org/x/tools/go/ssa/blockopt.go generated vendored Normal file
View File

@ -0,0 +1,187 @@
// Copyright 2013 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 ssa
// Simple block optimizations to simplify the control flow graph.
// TODO(adonovan): opt: instead of creating several "unreachable" blocks
// per function in the Builder, reuse a single one (e.g. at Blocks[1])
// to reduce garbage.
import (
"fmt"
"os"
)
// If true, perform sanity checking and show progress at each
// successive iteration of optimizeBlocks. Very verbose.
const debugBlockOpt = false
// markReachable sets Index=-1 for all blocks reachable from b.
func markReachable(b *BasicBlock) {
b.Index = -1
for _, succ := range b.Succs {
if succ.Index == 0 {
markReachable(succ)
}
}
}
// deleteUnreachableBlocks marks all reachable blocks of f and
// eliminates (nils) all others, including possibly cyclic subgraphs.
//
func deleteUnreachableBlocks(f *Function) {
const white, black = 0, -1
// We borrow b.Index temporarily as the mark bit.
for _, b := range f.Blocks {
b.Index = white
}
markReachable(f.Blocks[0])
if f.Recover != nil {
markReachable(f.Recover)
}
for i, b := range f.Blocks {
if b.Index == white {
for _, c := range b.Succs {
if c.Index == black {
c.removePred(b) // delete white->black edge
}
}
if debugBlockOpt {
fmt.Fprintln(os.Stderr, "unreachable", b)
}
f.Blocks[i] = nil // delete b
}
}
f.removeNilBlocks()
}
// jumpThreading attempts to apply simple jump-threading to block b,
// in which a->b->c become a->c if b is just a Jump.
// The result is true if the optimization was applied.
//
func jumpThreading(f *Function, b *BasicBlock) bool {
if b.Index == 0 {
return false // don't apply to entry block
}
if b.Instrs == nil {
return false
}
if _, ok := b.Instrs[0].(*Jump); !ok {
return false // not just a jump
}
c := b.Succs[0]
if c == b {
return false // don't apply to degenerate jump-to-self.
}
if c.hasPhi() {
return false // not sound without more effort
}
for j, a := range b.Preds {
a.replaceSucc(b, c)
// If a now has two edges to c, replace its degenerate If by Jump.
if len(a.Succs) == 2 && a.Succs[0] == c && a.Succs[1] == c {
jump := new(Jump)
jump.setBlock(a)
a.Instrs[len(a.Instrs)-1] = jump
a.Succs = a.Succs[:1]
c.removePred(b)
} else {
if j == 0 {
c.replacePred(b, a)
} else {
c.Preds = append(c.Preds, a)
}
}
if debugBlockOpt {
fmt.Fprintln(os.Stderr, "jumpThreading", a, b, c)
}
}
f.Blocks[b.Index] = nil // delete b
return true
}
// fuseBlocks attempts to apply the block fusion optimization to block
// a, in which a->b becomes ab if len(a.Succs)==len(b.Preds)==1.
// The result is true if the optimization was applied.
//
func fuseBlocks(f *Function, a *BasicBlock) bool {
if len(a.Succs) != 1 {
return false
}
b := a.Succs[0]
if len(b.Preds) != 1 {
return false
}
// Degenerate &&/|| ops may result in a straight-line CFG
// containing φ-nodes. (Ideally we'd replace such them with
// their sole operand but that requires Referrers, built later.)
if b.hasPhi() {
return false // not sound without further effort
}
// Eliminate jump at end of A, then copy all of B across.
a.Instrs = append(a.Instrs[:len(a.Instrs)-1], b.Instrs...)
for _, instr := range b.Instrs {
instr.setBlock(a)
}
// A inherits B's successors
a.Succs = append(a.succs2[:0], b.Succs...)
// Fix up Preds links of all successors of B.
for _, c := range b.Succs {
c.replacePred(b, a)
}
if debugBlockOpt {
fmt.Fprintln(os.Stderr, "fuseBlocks", a, b)
}
f.Blocks[b.Index] = nil // delete b
return true
}
// optimizeBlocks() performs some simple block optimizations on a
// completed function: dead block elimination, block fusion, jump
// threading.
//
func optimizeBlocks(f *Function) {
deleteUnreachableBlocks(f)
// Loop until no further progress.
changed := true
for changed {
changed = false
if debugBlockOpt {
f.WriteTo(os.Stderr)
mustSanityCheck(f, nil)
}
for _, b := range f.Blocks {
// f.Blocks will temporarily contain nils to indicate
// deleted blocks; we remove them at the end.
if b == nil {
continue
}
// Fuse blocks. b->c becomes bc.
if fuseBlocks(f, b) {
changed = true
}
// a->b->c becomes a->c if b contains only a Jump.
if jumpThreading(f, b) {
changed = true
continue // (b was disconnected)
}
}
}
f.removeNilBlocks()
}

2370
vendor/golang.org/x/tools/go/ssa/builder.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

168
vendor/golang.org/x/tools/go/ssa/const.go generated vendored Normal file
View File

@ -0,0 +1,168 @@
// Copyright 2013 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 ssa
// This file defines the Const SSA value type.
import (
"fmt"
"go/token"
"strconv"
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/types"
)
// NewConst returns a new constant of the specified value and type.
// val must be valid according to the specification of Const.Value.
//
func NewConst(val exact.Value, typ types.Type) *Const {
return &Const{typ, val}
}
// intConst returns an 'int' constant that evaluates to i.
// (i is an int64 in case the host is narrower than the target.)
func intConst(i int64) *Const {
return NewConst(exact.MakeInt64(i), tInt)
}
// nilConst returns a nil constant of the specified type, which may
// be any reference type, including interfaces.
//
func nilConst(typ types.Type) *Const {
return NewConst(nil, typ)
}
// stringConst returns a 'string' constant that evaluates to s.
func stringConst(s string) *Const {
return NewConst(exact.MakeString(s), tString)
}
// zeroConst returns a new "zero" constant of the specified type,
// which must not be an array or struct type: the zero values of
// aggregates are well-defined but cannot be represented by Const.
//
func zeroConst(t types.Type) *Const {
switch t := t.(type) {
case *types.Basic:
switch {
case t.Info()&types.IsBoolean != 0:
return NewConst(exact.MakeBool(false), t)
case t.Info()&types.IsNumeric != 0:
return NewConst(exact.MakeInt64(0), t)
case t.Info()&types.IsString != 0:
return NewConst(exact.MakeString(""), t)
case t.Kind() == types.UnsafePointer:
fallthrough
case t.Kind() == types.UntypedNil:
return nilConst(t)
default:
panic(fmt.Sprint("zeroConst for unexpected type:", t))
}
case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature:
return nilConst(t)
case *types.Named:
return NewConst(zeroConst(t.Underlying()).Value, t)
case *types.Array, *types.Struct, *types.Tuple:
panic(fmt.Sprint("zeroConst applied to aggregate:", t))
}
panic(fmt.Sprint("zeroConst: unexpected ", t))
}
func (c *Const) RelString(from *types.Package) string {
var s string
if c.Value == nil {
s = "nil"
} else if c.Value.Kind() == exact.String {
s = exact.StringVal(c.Value)
const max = 20
// TODO(adonovan): don't cut a rune in half.
if len(s) > max {
s = s[:max-3] + "..." // abbreviate
}
s = strconv.Quote(s)
} else {
s = c.Value.String()
}
return s + ":" + relType(c.Type(), from)
}
func (c *Const) Name() string {
return c.RelString(nil)
}
func (c *Const) String() string {
return c.Name()
}
func (c *Const) Type() types.Type {
return c.typ
}
func (c *Const) Referrers() *[]Instruction {
return nil
}
func (c *Const) Parent() *Function { return nil }
func (c *Const) Pos() token.Pos {
return token.NoPos
}
// IsNil returns true if this constant represents a typed or untyped nil value.
func (c *Const) IsNil() bool {
return c.Value == nil
}
// Int64 returns the numeric value of this constant truncated to fit
// a signed 64-bit integer.
//
func (c *Const) Int64() int64 {
switch x := c.Value; x.Kind() {
case exact.Int:
if i, ok := exact.Int64Val(x); ok {
return i
}
return 0
case exact.Float:
f, _ := exact.Float64Val(x)
return int64(f)
}
panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
}
// Uint64 returns the numeric value of this constant truncated to fit
// an unsigned 64-bit integer.
//
func (c *Const) Uint64() uint64 {
switch x := c.Value; x.Kind() {
case exact.Int:
if u, ok := exact.Uint64Val(x); ok {
return u
}
return 0
case exact.Float:
f, _ := exact.Float64Val(x)
return uint64(f)
}
panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
}
// Float64 returns the numeric value of this constant truncated to fit
// a float64.
//
func (c *Const) Float64() float64 {
f, _ := exact.Float64Val(c.Value)
return f
}
// Complex128 returns the complex value of this constant truncated to
// fit a complex128.
//
func (c *Const) Complex128() complex128 {
re, _ := exact.Float64Val(exact.Real(c.Value))
im, _ := exact.Float64Val(exact.Imag(c.Value))
return complex(re, im)
}

257
vendor/golang.org/x/tools/go/ssa/create.go generated vendored Normal file
View File

@ -0,0 +1,257 @@
// Copyright 2013 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 ssa
// This file implements the CREATE phase of SSA construction.
// See builder.go for explanation.
import (
"fmt"
"go/ast"
"go/token"
"os"
"sync"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
// NewProgram returns a new SSA Program.
//
// mode controls diagnostics and checking during SSA construction.
//
func NewProgram(fset *token.FileSet, mode BuilderMode) *Program {
prog := &Program{
Fset: fset,
imported: make(map[string]*Package),
packages: make(map[*types.Package]*Package),
thunks: make(map[selectionKey]*Function),
bounds: make(map[*types.Func]*Function),
mode: mode,
}
h := typeutil.MakeHasher() // protected by methodsMu, in effect
prog.methodSets.SetHasher(h)
prog.canon.SetHasher(h)
return prog
}
// memberFromObject populates package pkg with a member for the
// typechecker object obj.
//
// For objects from Go source code, syntax is the associated syntax
// tree (for funcs and vars only); it will be used during the build
// phase.
//
func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
name := obj.Name()
switch obj := obj.(type) {
case *types.TypeName:
pkg.Members[name] = &Type{
object: obj,
pkg: pkg,
}
case *types.Const:
c := &NamedConst{
object: obj,
Value: NewConst(obj.Val(), obj.Type()),
pkg: pkg,
}
pkg.values[obj] = c.Value
pkg.Members[name] = c
case *types.Var:
g := &Global{
Pkg: pkg,
name: name,
object: obj,
typ: types.NewPointer(obj.Type()), // address
pos: obj.Pos(),
}
pkg.values[obj] = g
pkg.Members[name] = g
case *types.Func:
sig := obj.Type().(*types.Signature)
if sig.Recv() == nil && name == "init" {
pkg.ninit++
name = fmt.Sprintf("init#%d", pkg.ninit)
}
fn := &Function{
name: name,
object: obj,
Signature: sig,
syntax: syntax,
pos: obj.Pos(),
Pkg: pkg,
Prog: pkg.Prog,
}
if syntax == nil {
fn.Synthetic = "loaded from gc object file"
}
pkg.values[obj] = fn
if sig.Recv() == nil {
pkg.Members[name] = fn // package-level function
}
default: // (incl. *types.Package)
panic("unexpected Object type: " + obj.String())
}
}
// membersFromDecl populates package pkg with members for each
// typechecker object (var, func, const or type) associated with the
// specified decl.
//
func membersFromDecl(pkg *Package, decl ast.Decl) {
switch decl := decl.(type) {
case *ast.GenDecl: // import, const, type or var
switch decl.Tok {
case token.CONST:
for _, spec := range decl.Specs {
for _, id := range spec.(*ast.ValueSpec).Names {
if !isBlankIdent(id) {
memberFromObject(pkg, pkg.info.Defs[id], nil)
}
}
}
case token.VAR:
for _, spec := range decl.Specs {
for _, id := range spec.(*ast.ValueSpec).Names {
if !isBlankIdent(id) {
memberFromObject(pkg, pkg.info.Defs[id], spec)
}
}
}
case token.TYPE:
for _, spec := range decl.Specs {
id := spec.(*ast.TypeSpec).Name
if !isBlankIdent(id) {
memberFromObject(pkg, pkg.info.Defs[id], nil)
}
}
}
case *ast.FuncDecl:
id := decl.Name
if !isBlankIdent(id) {
memberFromObject(pkg, pkg.info.Defs[id], decl)
}
}
}
// CreatePackage constructs and returns an SSA Package from the
// specified type-checked, error-free file ASTs, and populates its
// Members mapping.
//
// importable determines whether this package should be returned by a
// subsequent call to ImportedPackage(pkg.Path()).
//
// The real work of building SSA form for each function is not done
// until a subsequent call to Package.Build().
//
func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *types.Info, importable bool) *Package {
p := &Package{
Prog: prog,
Members: make(map[string]Member),
values: make(map[types.Object]Value),
Object: pkg,
info: info, // transient (CREATE and BUILD phases)
files: files, // transient (CREATE and BUILD phases)
}
// Add init() function.
p.init = &Function{
name: "init",
Signature: new(types.Signature),
Synthetic: "package initializer",
Pkg: p,
Prog: prog,
}
p.Members[p.init.name] = p.init
// CREATE phase.
// Allocate all package members: vars, funcs, consts and types.
if len(files) > 0 {
// Go source package.
for _, file := range files {
for _, decl := range file.Decls {
membersFromDecl(p, decl)
}
}
} else {
// GC-compiled binary package.
// No code.
// No position information.
scope := p.Object.Scope()
for _, name := range scope.Names() {
obj := scope.Lookup(name)
memberFromObject(p, obj, nil)
if obj, ok := obj.(*types.TypeName); ok {
named := obj.Type().(*types.Named)
for i, n := 0, named.NumMethods(); i < n; i++ {
memberFromObject(p, named.Method(i), nil)
}
}
}
}
if prog.mode&BareInits == 0 {
// Add initializer guard variable.
initguard := &Global{
Pkg: p,
name: "init$guard",
typ: types.NewPointer(tBool),
}
p.Members[initguard.Name()] = initguard
}
if prog.mode&GlobalDebug != 0 {
p.SetDebugMode(true)
}
if prog.mode&PrintPackages != 0 {
printMu.Lock()
p.WriteTo(os.Stdout)
printMu.Unlock()
}
if importable {
prog.imported[p.Object.Path()] = p
}
prog.packages[p.Object] = p
return p
}
// printMu serializes printing of Packages/Functions to stdout.
var printMu sync.Mutex
// AllPackages returns a new slice containing all packages in the
// program prog in unspecified order.
//
func (prog *Program) AllPackages() []*Package {
pkgs := make([]*Package, 0, len(prog.packages))
for _, pkg := range prog.packages {
pkgs = append(pkgs, pkg)
}
return pkgs
}
// ImportedPackage returns the importable SSA Package whose import
// path is path, or nil if no such SSA package has been created.
//
// Not all packages are importable. For example, no import
// declaration can resolve to the x_test package created by 'go test'
// or the ad-hoc main package created 'go build foo.go'.
//
func (prog *Program) ImportedPackage(path string) *Package {
return prog.imported[path]
}

123
vendor/golang.org/x/tools/go/ssa/doc.go generated vendored Normal file
View File

@ -0,0 +1,123 @@
// Copyright 2013 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 ssa defines a representation of the elements of Go programs
// (packages, types, functions, variables and constants) using a
// static single-assignment (SSA) form intermediate representation
// (IR) for the bodies of functions.
//
// THIS INTERFACE IS EXPERIMENTAL AND IS LIKELY TO CHANGE.
//
// For an introduction to SSA form, see
// http://en.wikipedia.org/wiki/Static_single_assignment_form.
// This page provides a broader reading list:
// http://www.dcs.gla.ac.uk/~jsinger/ssa.html.
//
// The level of abstraction of the SSA form is intentionally close to
// the source language to facilitate construction of source analysis
// tools. It is not intended for machine code generation.
//
// All looping, branching and switching constructs are replaced with
// unstructured control flow. Higher-level control flow constructs
// such as multi-way branch can be reconstructed as needed; see
// ssautil.Switches() for an example.
//
// To construct an SSA-form program, call ssautil.CreateProgram on a
// loader.Program, a set of type-checked packages created from
// parsed Go source files. The resulting ssa.Program contains all the
// packages and their members, but SSA code is not created for
// function bodies until a subsequent call to (*Package).Build.
//
// The builder initially builds a naive SSA form in which all local
// variables are addresses of stack locations with explicit loads and
// stores. Registerisation of eligible locals and φ-node insertion
// using dominance and dataflow are then performed as a second pass
// called "lifting" to improve the accuracy and performance of
// subsequent analyses; this pass can be skipped by setting the
// NaiveForm builder flag.
//
// The primary interfaces of this package are:
//
// - Member: a named member of a Go package.
// - Value: an expression that yields a value.
// - Instruction: a statement that consumes values and performs computation.
// - Node: a Value or Instruction (emphasizing its membership in the SSA value graph)
//
// A computation that yields a result implements both the Value and
// Instruction interfaces. The following table shows for each
// concrete type which of these interfaces it implements.
//
// Value? Instruction? Member?
// *Alloc ✔ ✔
// *BinOp ✔ ✔
// *Builtin ✔
// *Call ✔ ✔
// *ChangeInterface ✔ ✔
// *ChangeType ✔ ✔
// *Const ✔
// *Convert ✔ ✔
// *DebugRef ✔
// *Defer ✔
// *Extract ✔ ✔
// *Field ✔ ✔
// *FieldAddr ✔ ✔
// *FreeVar ✔
// *Function ✔ ✔ (func)
// *Global ✔ ✔ (var)
// *Go ✔
// *If ✔
// *Index ✔ ✔
// *IndexAddr ✔ ✔
// *Jump ✔
// *Lookup ✔ ✔
// *MakeChan ✔ ✔
// *MakeClosure ✔ ✔
// *MakeInterface ✔ ✔
// *MakeMap ✔ ✔
// *MakeSlice ✔ ✔
// *MapUpdate ✔
// *NamedConst ✔ (const)
// *Next ✔ ✔
// *Panic ✔
// *Parameter ✔
// *Phi ✔ ✔
// *Range ✔ ✔
// *Return ✔
// *RunDefers ✔
// *Select ✔ ✔
// *Send ✔
// *Slice ✔ ✔
// *Store ✔
// *Type ✔ (type)
// *TypeAssert ✔ ✔
// *UnOp ✔ ✔
//
// Other key types in this package include: Program, Package, Function
// and BasicBlock.
//
// The program representation constructed by this package is fully
// resolved internally, i.e. it does not rely on the names of Values,
// Packages, Functions, Types or BasicBlocks for the correct
// interpretation of the program. Only the identities of objects and
// the topology of the SSA and type graphs are semantically
// significant. (There is one exception: Ids, used to identify field
// and method names, contain strings.) Avoidance of name-based
// operations simplifies the implementation of subsequent passes and
// can make them very efficient. Many objects are nonetheless named
// to aid in debugging, but it is not essential that the names be
// either accurate or unambiguous. The public API exposes a number of
// name-based maps for client convenience.
//
// The ssa/ssautil package provides various utilities that depend only
// on the public API of this package.
//
// TODO(adonovan): Consider the exceptional control-flow implications
// of defer and recover().
//
// TODO(adonovan): write a how-to document for all the various cases
// of trying to determine corresponding elements across the four
// domains of source locations, ast.Nodes, types.Objects,
// ssa.Values/Instructions.
//
package ssa // import "golang.org/x/tools/go/ssa"

341
vendor/golang.org/x/tools/go/ssa/dom.go generated vendored Normal file
View File

@ -0,0 +1,341 @@
// Copyright 2013 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 ssa
// This file defines algorithms related to dominance.
// Dominator tree construction ----------------------------------------
//
// We use the algorithm described in Lengauer & Tarjan. 1979. A fast
// algorithm for finding dominators in a flowgraph.
// http://doi.acm.org/10.1145/357062.357071
//
// We also apply the optimizations to SLT described in Georgiadis et
// al, Finding Dominators in Practice, JGAA 2006,
// http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
// to avoid the need for buckets of size > 1.
import (
"bytes"
"fmt"
"math/big"
"os"
"sort"
)
// Idom returns the block that immediately dominates b:
// its parent in the dominator tree, if any.
// Neither the entry node (b.Index==0) nor recover node
// (b==b.Parent().Recover()) have a parent.
//
func (b *BasicBlock) Idom() *BasicBlock { return b.dom.idom }
// Dominees returns the list of blocks that b immediately dominates:
// its children in the dominator tree.
//
func (b *BasicBlock) Dominees() []*BasicBlock { return b.dom.children }
// Dominates reports whether b dominates c.
func (b *BasicBlock) Dominates(c *BasicBlock) bool {
return b.dom.pre <= c.dom.pre && c.dom.post <= b.dom.post
}
type byDomPreorder []*BasicBlock
func (a byDomPreorder) Len() int { return len(a) }
func (a byDomPreorder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byDomPreorder) Less(i, j int) bool { return a[i].dom.pre < a[j].dom.pre }
// DomPreorder returns a new slice containing the blocks of f in
// dominator tree preorder.
//
func (f *Function) DomPreorder() []*BasicBlock {
n := len(f.Blocks)
order := make(byDomPreorder, n, n)
copy(order, f.Blocks)
sort.Sort(order)
return order
}
// domInfo contains a BasicBlock's dominance information.
type domInfo struct {
idom *BasicBlock // immediate dominator (parent in domtree)
children []*BasicBlock // nodes immediately dominated by this one
pre, post int32 // pre- and post-order numbering within domtree
}
// ltState holds the working state for Lengauer-Tarjan algorithm
// (during which domInfo.pre is repurposed for CFG DFS preorder number).
type ltState struct {
// Each slice is indexed by b.Index.
sdom []*BasicBlock // b's semidominator
parent []*BasicBlock // b's parent in DFS traversal of CFG
ancestor []*BasicBlock // b's ancestor with least sdom
}
// dfs implements the depth-first search part of the LT algorithm.
func (lt *ltState) dfs(v *BasicBlock, i int32, preorder []*BasicBlock) int32 {
preorder[i] = v
v.dom.pre = i // For now: DFS preorder of spanning tree of CFG
i++
lt.sdom[v.Index] = v
lt.link(nil, v)
for _, w := range v.Succs {
if lt.sdom[w.Index] == nil {
lt.parent[w.Index] = v
i = lt.dfs(w, i, preorder)
}
}
return i
}
// eval implements the EVAL part of the LT algorithm.
func (lt *ltState) eval(v *BasicBlock) *BasicBlock {
// TODO(adonovan): opt: do path compression per simple LT.
u := v
for ; lt.ancestor[v.Index] != nil; v = lt.ancestor[v.Index] {
if lt.sdom[v.Index].dom.pre < lt.sdom[u.Index].dom.pre {
u = v
}
}
return u
}
// link implements the LINK part of the LT algorithm.
func (lt *ltState) link(v, w *BasicBlock) {
lt.ancestor[w.Index] = v
}
// buildDomTree computes the dominator tree of f using the LT algorithm.
// Precondition: all blocks are reachable (e.g. optimizeBlocks has been run).
//
func buildDomTree(f *Function) {
// The step numbers refer to the original LT paper; the
// reordering is due to Georgiadis.
// Clear any previous domInfo.
for _, b := range f.Blocks {
b.dom = domInfo{}
}
n := len(f.Blocks)
// Allocate space for 5 contiguous [n]*BasicBlock arrays:
// sdom, parent, ancestor, preorder, buckets.
space := make([]*BasicBlock, 5*n, 5*n)
lt := ltState{
sdom: space[0:n],
parent: space[n : 2*n],
ancestor: space[2*n : 3*n],
}
// Step 1. Number vertices by depth-first preorder.
preorder := space[3*n : 4*n]
root := f.Blocks[0]
prenum := lt.dfs(root, 0, preorder)
recover := f.Recover
if recover != nil {
lt.dfs(recover, prenum, preorder)
}
buckets := space[4*n : 5*n]
copy(buckets, preorder)
// In reverse preorder...
for i := int32(n) - 1; i > 0; i-- {
w := preorder[i]
// Step 3. Implicitly define the immediate dominator of each node.
for v := buckets[i]; v != w; v = buckets[v.dom.pre] {
u := lt.eval(v)
if lt.sdom[u.Index].dom.pre < i {
v.dom.idom = u
} else {
v.dom.idom = w
}
}
// Step 2. Compute the semidominators of all nodes.
lt.sdom[w.Index] = lt.parent[w.Index]
for _, v := range w.Preds {
u := lt.eval(v)
if lt.sdom[u.Index].dom.pre < lt.sdom[w.Index].dom.pre {
lt.sdom[w.Index] = lt.sdom[u.Index]
}
}
lt.link(lt.parent[w.Index], w)
if lt.parent[w.Index] == lt.sdom[w.Index] {
w.dom.idom = lt.parent[w.Index]
} else {
buckets[i] = buckets[lt.sdom[w.Index].dom.pre]
buckets[lt.sdom[w.Index].dom.pre] = w
}
}
// The final 'Step 3' is now outside the loop.
for v := buckets[0]; v != root; v = buckets[v.dom.pre] {
v.dom.idom = root
}
// Step 4. Explicitly define the immediate dominator of each
// node, in preorder.
for _, w := range preorder[1:] {
if w == root || w == recover {
w.dom.idom = nil
} else {
if w.dom.idom != lt.sdom[w.Index] {
w.dom.idom = w.dom.idom.dom.idom
}
// Calculate Children relation as inverse of Idom.
w.dom.idom.dom.children = append(w.dom.idom.dom.children, w)
}
}
pre, post := numberDomTree(root, 0, 0)
if recover != nil {
numberDomTree(recover, pre, post)
}
// printDomTreeDot(os.Stderr, f) // debugging
// printDomTreeText(os.Stderr, root, 0) // debugging
if f.Prog.mode&SanityCheckFunctions != 0 {
sanityCheckDomTree(f)
}
}
// numberDomTree sets the pre- and post-order numbers of a depth-first
// traversal of the dominator tree rooted at v. These are used to
// answer dominance queries in constant time.
//
func numberDomTree(v *BasicBlock, pre, post int32) (int32, int32) {
v.dom.pre = pre
pre++
for _, child := range v.dom.children {
pre, post = numberDomTree(child, pre, post)
}
v.dom.post = post
post++
return pre, post
}
// Testing utilities ----------------------------------------
// sanityCheckDomTree checks the correctness of the dominator tree
// computed by the LT algorithm by comparing against the dominance
// relation computed by a naive Kildall-style forward dataflow
// analysis (Algorithm 10.16 from the "Dragon" book).
//
func sanityCheckDomTree(f *Function) {
n := len(f.Blocks)
// D[i] is the set of blocks that dominate f.Blocks[i],
// represented as a bit-set of block indices.
D := make([]big.Int, n)
one := big.NewInt(1)
// all is the set of all blocks; constant.
var all big.Int
all.Set(one).Lsh(&all, uint(n)).Sub(&all, one)
// Initialization.
for i, b := range f.Blocks {
if i == 0 || b == f.Recover {
// A root is dominated only by itself.
D[i].SetBit(&D[0], 0, 1)
} else {
// All other blocks are (initially) dominated
// by every block.
D[i].Set(&all)
}
}
// Iteration until fixed point.
for changed := true; changed; {
changed = false
for i, b := range f.Blocks {
if i == 0 || b == f.Recover {
continue
}
// Compute intersection across predecessors.
var x big.Int
x.Set(&all)
for _, pred := range b.Preds {
x.And(&x, &D[pred.Index])
}
x.SetBit(&x, i, 1) // a block always dominates itself.
if D[i].Cmp(&x) != 0 {
D[i].Set(&x)
changed = true
}
}
}
// Check the entire relation. O(n^2).
// The Recover block (if any) must be treated specially so we skip it.
ok := true
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
b, c := f.Blocks[i], f.Blocks[j]
if c == f.Recover {
continue
}
actual := b.Dominates(c)
expected := D[j].Bit(i) == 1
if actual != expected {
fmt.Fprintf(os.Stderr, "dominates(%s, %s)==%t, want %t\n", b, c, actual, expected)
ok = false
}
}
}
preorder := f.DomPreorder()
for _, b := range f.Blocks {
if got := preorder[b.dom.pre]; got != b {
fmt.Fprintf(os.Stderr, "preorder[%d]==%s, want %s\n", b.dom.pre, got, b)
ok = false
}
}
if !ok {
panic("sanityCheckDomTree failed for " + f.String())
}
}
// Printing functions ----------------------------------------
// printDomTree prints the dominator tree as text, using indentation.
func printDomTreeText(buf *bytes.Buffer, v *BasicBlock, indent int) {
fmt.Fprintf(buf, "%*s%s\n", 4*indent, "", v)
for _, child := range v.dom.children {
printDomTreeText(buf, child, indent+1)
}
}
// printDomTreeDot prints the dominator tree of f in AT&T GraphViz
// (.dot) format.
func printDomTreeDot(buf *bytes.Buffer, f *Function) {
fmt.Fprintln(buf, "//", f)
fmt.Fprintln(buf, "digraph domtree {")
for i, b := range f.Blocks {
v := b.dom
fmt.Fprintf(buf, "\tn%d [label=\"%s (%d, %d)\",shape=\"rectangle\"];\n", v.pre, b, v.pre, v.post)
// TODO(adonovan): improve appearance of edges
// belonging to both dominator tree and CFG.
// Dominator tree edge.
if i != 0 {
fmt.Fprintf(buf, "\tn%d -> n%d [style=\"solid\",weight=100];\n", v.idom.dom.pre, v.pre)
}
// CFG edges.
for _, pred := range b.Preds {
fmt.Fprintf(buf, "\tn%d -> n%d [style=\"dotted\",weight=0];\n", pred.dom.pre, v.pre)
}
}
fmt.Fprintln(buf, "}")
}

469
vendor/golang.org/x/tools/go/ssa/emit.go generated vendored Normal file
View File

@ -0,0 +1,469 @@
// Copyright 2013 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 ssa
// Helpers for emitting SSA instructions.
import (
"fmt"
"go/ast"
"go/token"
"golang.org/x/tools/go/types"
)
// emitNew emits to f a new (heap Alloc) instruction allocating an
// object of type typ. pos is the optional source location.
//
func emitNew(f *Function, typ types.Type, pos token.Pos) *Alloc {
v := &Alloc{Heap: true}
v.setType(types.NewPointer(typ))
v.setPos(pos)
f.emit(v)
return v
}
// emitLoad emits to f an instruction to load the address addr into a
// new temporary, and returns the value so defined.
//
func emitLoad(f *Function, addr Value) *UnOp {
v := &UnOp{Op: token.MUL, X: addr}
v.setType(deref(addr.Type()))
f.emit(v)
return v
}
// emitDebugRef emits to f a DebugRef pseudo-instruction associating
// expression e with value v.
//
func emitDebugRef(f *Function, e ast.Expr, v Value, isAddr bool) {
if !f.debugInfo() {
return // debugging not enabled
}
if v == nil || e == nil {
panic("nil")
}
var obj types.Object
e = unparen(e)
if id, ok := e.(*ast.Ident); ok {
if isBlankIdent(id) {
return
}
obj = f.Pkg.objectOf(id)
switch obj.(type) {
case *types.Nil, *types.Const, *types.Builtin:
return
}
}
f.emit(&DebugRef{
X: v,
Expr: e,
IsAddr: isAddr,
object: obj,
})
}
// emitArith emits to f code to compute the binary operation op(x, y)
// where op is an eager shift, logical or arithmetic operation.
// (Use emitCompare() for comparisons and Builder.logicalBinop() for
// non-eager operations.)
//
func emitArith(f *Function, op token.Token, x, y Value, t types.Type, pos token.Pos) Value {
switch op {
case token.SHL, token.SHR:
x = emitConv(f, x, t)
// y may be signed or an 'untyped' constant.
// TODO(adonovan): whence signed values?
if b, ok := y.Type().Underlying().(*types.Basic); ok && b.Info()&types.IsUnsigned == 0 {
y = emitConv(f, y, types.Typ[types.Uint64])
}
case token.ADD, token.SUB, token.MUL, token.QUO, token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
x = emitConv(f, x, t)
y = emitConv(f, y, t)
default:
panic("illegal op in emitArith: " + op.String())
}
v := &BinOp{
Op: op,
X: x,
Y: y,
}
v.setPos(pos)
v.setType(t)
return f.emit(v)
}
// emitCompare emits to f code compute the boolean result of
// comparison comparison 'x op y'.
//
func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value {
xt := x.Type().Underlying()
yt := y.Type().Underlying()
// Special case to optimise a tagless SwitchStmt so that
// these are equivalent
// switch { case e: ...}
// switch true { case e: ... }
// if e==true { ... }
// even in the case when e's type is an interface.
// TODO(adonovan): opt: generalise to x==true, false!=y, etc.
if x == vTrue && op == token.EQL {
if yt, ok := yt.(*types.Basic); ok && yt.Info()&types.IsBoolean != 0 {
return y
}
}
if types.Identical(xt, yt) {
// no conversion necessary
} else if _, ok := xt.(*types.Interface); ok {
y = emitConv(f, y, x.Type())
} else if _, ok := yt.(*types.Interface); ok {
x = emitConv(f, x, y.Type())
} else if _, ok := x.(*Const); ok {
x = emitConv(f, x, y.Type())
} else if _, ok := y.(*Const); ok {
y = emitConv(f, y, x.Type())
} else {
// other cases, e.g. channels. No-op.
}
v := &BinOp{
Op: op,
X: x,
Y: y,
}
v.setPos(pos)
v.setType(tBool)
return f.emit(v)
}
// isValuePreserving returns true if a conversion from ut_src to
// ut_dst is value-preserving, i.e. just a change of type.
// Precondition: neither argument is a named type.
//
func isValuePreserving(ut_src, ut_dst types.Type) bool {
// Identical underlying types?
if types.Identical(ut_dst, ut_src) {
return true
}
switch ut_dst.(type) {
case *types.Chan:
// Conversion between channel types?
_, ok := ut_src.(*types.Chan)
return ok
case *types.Pointer:
// Conversion between pointers with identical base types?
_, ok := ut_src.(*types.Pointer)
return ok
}
return false
}
// emitConv emits to f code to convert Value val to exactly type typ,
// and returns the converted value. Implicit conversions are required
// by language assignability rules in assignments, parameter passing,
// etc. Conversions cannot fail dynamically.
//
func emitConv(f *Function, val Value, typ types.Type) Value {
t_src := val.Type()
// Identical types? Conversion is a no-op.
if types.Identical(t_src, typ) {
return val
}
ut_dst := typ.Underlying()
ut_src := t_src.Underlying()
// Just a change of type, but not value or representation?
if isValuePreserving(ut_src, ut_dst) {
c := &ChangeType{X: val}
c.setType(typ)
return f.emit(c)
}
// Conversion to, or construction of a value of, an interface type?
if _, ok := ut_dst.(*types.Interface); ok {
// Assignment from one interface type to another?
if _, ok := ut_src.(*types.Interface); ok {
c := &ChangeInterface{X: val}
c.setType(typ)
return f.emit(c)
}
// Untyped nil constant? Return interface-typed nil constant.
if ut_src == tUntypedNil {
return nilConst(typ)
}
// Convert (non-nil) "untyped" literals to their default type.
if t, ok := ut_src.(*types.Basic); ok && t.Info()&types.IsUntyped != 0 {
val = emitConv(f, val, DefaultType(ut_src))
}
f.Pkg.Prog.needMethodsOf(val.Type())
mi := &MakeInterface{X: val}
mi.setType(typ)
return f.emit(mi)
}
// Conversion of a compile-time constant value?
if c, ok := val.(*Const); ok {
if _, ok := ut_dst.(*types.Basic); ok || c.IsNil() {
// Conversion of a compile-time constant to
// another constant type results in a new
// constant of the destination type and
// (initially) the same abstract value.
// We don't truncate the value yet.
return NewConst(c.Value, typ)
}
// We're converting from constant to non-constant type,
// e.g. string -> []byte/[]rune.
}
// A representation-changing conversion?
// At least one of {ut_src,ut_dst} must be *Basic.
// (The other may be []byte or []rune.)
_, ok1 := ut_src.(*types.Basic)
_, ok2 := ut_dst.(*types.Basic)
if ok1 || ok2 {
c := &Convert{X: val}
c.setType(typ)
return f.emit(c)
}
panic(fmt.Sprintf("in %s: cannot convert %s (%s) to %s", f, val, val.Type(), typ))
}
// emitStore emits to f an instruction to store value val at location
// addr, applying implicit conversions as required by assignability rules.
//
func emitStore(f *Function, addr, val Value, pos token.Pos) *Store {
s := &Store{
Addr: addr,
Val: emitConv(f, val, deref(addr.Type())),
pos: pos,
}
f.emit(s)
return s
}
// emitJump emits to f a jump to target, and updates the control-flow graph.
// Postcondition: f.currentBlock is nil.
//
func emitJump(f *Function, target *BasicBlock) {
b := f.currentBlock
b.emit(new(Jump))
addEdge(b, target)
f.currentBlock = nil
}
// emitIf emits to f a conditional jump to tblock or fblock based on
// cond, and updates the control-flow graph.
// Postcondition: f.currentBlock is nil.
//
func emitIf(f *Function, cond Value, tblock, fblock *BasicBlock) {
b := f.currentBlock
b.emit(&If{Cond: cond})
addEdge(b, tblock)
addEdge(b, fblock)
f.currentBlock = nil
}
// emitExtract emits to f an instruction to extract the index'th
// component of tuple. It returns the extracted value.
//
func emitExtract(f *Function, tuple Value, index int) Value {
e := &Extract{Tuple: tuple, Index: index}
e.setType(tuple.Type().(*types.Tuple).At(index).Type())
return f.emit(e)
}
// emitTypeAssert emits to f a type assertion value := x.(t) and
// returns the value. x.Type() must be an interface.
//
func emitTypeAssert(f *Function, x Value, t types.Type, pos token.Pos) Value {
a := &TypeAssert{X: x, AssertedType: t}
a.setPos(pos)
a.setType(t)
return f.emit(a)
}
// emitTypeTest emits to f a type test value,ok := x.(t) and returns
// a (value, ok) tuple. x.Type() must be an interface.
//
func emitTypeTest(f *Function, x Value, t types.Type, pos token.Pos) Value {
a := &TypeAssert{
X: x,
AssertedType: t,
CommaOk: true,
}
a.setPos(pos)
a.setType(types.NewTuple(
newVar("value", t),
varOk,
))
return f.emit(a)
}
// emitTailCall emits to f a function call in tail position. The
// caller is responsible for all fields of 'call' except its type.
// Intended for wrapper methods.
// Precondition: f does/will not use deferred procedure calls.
// Postcondition: f.currentBlock is nil.
//
func emitTailCall(f *Function, call *Call) {
tresults := f.Signature.Results()
nr := tresults.Len()
if nr == 1 {
call.typ = tresults.At(0).Type()
} else {
call.typ = tresults
}
tuple := f.emit(call)
var ret Return
switch nr {
case 0:
// no-op
case 1:
ret.Results = []Value{tuple}
default:
for i := 0; i < nr; i++ {
v := emitExtract(f, tuple, i)
// TODO(adonovan): in principle, this is required:
// v = emitConv(f, o.Type, f.Signature.Results[i].Type)
// but in practice emitTailCall is only used when
// the types exactly match.
ret.Results = append(ret.Results, v)
}
}
f.emit(&ret)
f.currentBlock = nil
}
// emitImplicitSelections emits to f code to apply the sequence of
// implicit field selections specified by indices to base value v, and
// returns the selected value.
//
// If v is the address of a struct, the result will be the address of
// a field; if it is the value of a struct, the result will be the
// value of a field.
//
func emitImplicitSelections(f *Function, v Value, indices []int) Value {
for _, index := range indices {
fld := deref(v.Type()).Underlying().(*types.Struct).Field(index)
if isPointer(v.Type()) {
instr := &FieldAddr{
X: v,
Field: index,
}
instr.setType(types.NewPointer(fld.Type()))
v = f.emit(instr)
// Load the field's value iff indirectly embedded.
if isPointer(fld.Type()) {
v = emitLoad(f, v)
}
} else {
instr := &Field{
X: v,
Field: index,
}
instr.setType(fld.Type())
v = f.emit(instr)
}
}
return v
}
// emitFieldSelection emits to f code to select the index'th field of v.
//
// If wantAddr, the input must be a pointer-to-struct and the result
// will be the field's address; otherwise the result will be the
// field's value.
// Ident id is used for position and debug info.
//
func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value {
fld := deref(v.Type()).Underlying().(*types.Struct).Field(index)
if isPointer(v.Type()) {
instr := &FieldAddr{
X: v,
Field: index,
}
instr.setPos(id.Pos())
instr.setType(types.NewPointer(fld.Type()))
v = f.emit(instr)
// Load the field's value iff we don't want its address.
if !wantAddr {
v = emitLoad(f, v)
}
} else {
instr := &Field{
X: v,
Field: index,
}
instr.setPos(id.Pos())
instr.setType(fld.Type())
v = f.emit(instr)
}
emitDebugRef(f, id, v, wantAddr)
return v
}
// zeroValue emits to f code to produce a zero value of type t,
// and returns it.
//
func zeroValue(f *Function, t types.Type) Value {
switch t.Underlying().(type) {
case *types.Struct, *types.Array:
return emitLoad(f, f.addLocal(t, token.NoPos))
default:
return zeroConst(t)
}
}
// createRecoverBlock emits to f a block of code to return after a
// recovered panic, and sets f.Recover to it.
//
// If f's result parameters are named, the code loads and returns
// their current values, otherwise it returns the zero values of their
// type.
//
// Idempotent.
//
func createRecoverBlock(f *Function) {
if f.Recover != nil {
return // already created
}
saved := f.currentBlock
f.Recover = f.newBasicBlock("recover")
f.currentBlock = f.Recover
var results []Value
if f.namedResults != nil {
// Reload NRPs to form value tuple.
for _, r := range f.namedResults {
results = append(results, emitLoad(f, r))
}
} else {
R := f.Signature.Results()
for i, n := 0, R.Len(); i < n; i++ {
T := R.At(i).Type()
// Return zero value of each result type.
results = append(results, zeroValue(f, T))
}
}
f.emit(&Return{Results: results})
f.currentBlock = saved
}

690
vendor/golang.org/x/tools/go/ssa/func.go generated vendored Normal file
View File

@ -0,0 +1,690 @@
// Copyright 2013 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 ssa
// This file implements the Function and BasicBlock types.
import (
"bytes"
"fmt"
"go/ast"
"go/token"
"io"
"os"
"strings"
"golang.org/x/tools/go/types"
)
// addEdge adds a control-flow graph edge from from to to.
func addEdge(from, to *BasicBlock) {
from.Succs = append(from.Succs, to)
to.Preds = append(to.Preds, from)
}
// Parent returns the function that contains block b.
func (b *BasicBlock) Parent() *Function { return b.parent }
// String returns a human-readable label of this block.
// It is not guaranteed unique within the function.
//
func (b *BasicBlock) String() string {
return fmt.Sprintf("%d", b.Index)
}
// emit appends an instruction to the current basic block.
// If the instruction defines a Value, it is returned.
//
func (b *BasicBlock) emit(i Instruction) Value {
i.setBlock(b)
b.Instrs = append(b.Instrs, i)
v, _ := i.(Value)
return v
}
// predIndex returns the i such that b.Preds[i] == c or panics if
// there is none.
func (b *BasicBlock) predIndex(c *BasicBlock) int {
for i, pred := range b.Preds {
if pred == c {
return i
}
}
panic(fmt.Sprintf("no edge %s -> %s", c, b))
}
// hasPhi returns true if b.Instrs contains φ-nodes.
func (b *BasicBlock) hasPhi() bool {
_, ok := b.Instrs[0].(*Phi)
return ok
}
// phis returns the prefix of b.Instrs containing all the block's φ-nodes.
func (b *BasicBlock) phis() []Instruction {
for i, instr := range b.Instrs {
if _, ok := instr.(*Phi); !ok {
return b.Instrs[:i]
}
}
return nil // unreachable in well-formed blocks
}
// replacePred replaces all occurrences of p in b's predecessor list with q.
// Ordinarily there should be at most one.
//
func (b *BasicBlock) replacePred(p, q *BasicBlock) {
for i, pred := range b.Preds {
if pred == p {
b.Preds[i] = q
}
}
}
// replaceSucc replaces all occurrences of p in b's successor list with q.
// Ordinarily there should be at most one.
//
func (b *BasicBlock) replaceSucc(p, q *BasicBlock) {
for i, succ := range b.Succs {
if succ == p {
b.Succs[i] = q
}
}
}
// removePred removes all occurrences of p in b's
// predecessor list and φ-nodes.
// Ordinarily there should be at most one.
//
func (b *BasicBlock) removePred(p *BasicBlock) {
phis := b.phis()
// We must preserve edge order for φ-nodes.
j := 0
for i, pred := range b.Preds {
if pred != p {
b.Preds[j] = b.Preds[i]
// Strike out φ-edge too.
for _, instr := range phis {
phi := instr.(*Phi)
phi.Edges[j] = phi.Edges[i]
}
j++
}
}
// Nil out b.Preds[j:] and φ-edges[j:] to aid GC.
for i := j; i < len(b.Preds); i++ {
b.Preds[i] = nil
for _, instr := range phis {
instr.(*Phi).Edges[i] = nil
}
}
b.Preds = b.Preds[:j]
for _, instr := range phis {
phi := instr.(*Phi)
phi.Edges = phi.Edges[:j]
}
}
// Destinations associated with unlabelled for/switch/select stmts.
// We push/pop one of these as we enter/leave each construct and for
// each BranchStmt we scan for the innermost target of the right type.
//
type targets struct {
tail *targets // rest of stack
_break *BasicBlock
_continue *BasicBlock
_fallthrough *BasicBlock
}
// Destinations associated with a labelled block.
// We populate these as labels are encountered in forward gotos or
// labelled statements.
//
type lblock struct {
_goto *BasicBlock
_break *BasicBlock
_continue *BasicBlock
}
// labelledBlock returns the branch target associated with the
// specified label, creating it if needed.
//
func (f *Function) labelledBlock(label *ast.Ident) *lblock {
lb := f.lblocks[label.Obj]
if lb == nil {
lb = &lblock{_goto: f.newBasicBlock(label.Name)}
if f.lblocks == nil {
f.lblocks = make(map[*ast.Object]*lblock)
}
f.lblocks[label.Obj] = lb
}
return lb
}
// addParam adds a (non-escaping) parameter to f.Params of the
// specified name, type and source position.
//
func (f *Function) addParam(name string, typ types.Type, pos token.Pos) *Parameter {
v := &Parameter{
name: name,
typ: typ,
pos: pos,
parent: f,
}
f.Params = append(f.Params, v)
return v
}
func (f *Function) addParamObj(obj types.Object) *Parameter {
name := obj.Name()
if name == "" {
name = fmt.Sprintf("arg%d", len(f.Params))
}
param := f.addParam(name, obj.Type(), obj.Pos())
param.object = obj
return param
}
// addSpilledParam declares a parameter that is pre-spilled to the
// stack; the function body will load/store the spilled location.
// Subsequent lifting will eliminate spills where possible.
//
func (f *Function) addSpilledParam(obj types.Object) {
param := f.addParamObj(obj)
spill := &Alloc{Comment: obj.Name()}
spill.setType(types.NewPointer(obj.Type()))
spill.setPos(obj.Pos())
f.objects[obj] = spill
f.Locals = append(f.Locals, spill)
f.emit(spill)
f.emit(&Store{Addr: spill, Val: param})
}
// startBody initializes the function prior to generating SSA code for its body.
// Precondition: f.Type() already set.
//
func (f *Function) startBody() {
f.currentBlock = f.newBasicBlock("entry")
f.objects = make(map[types.Object]Value) // needed for some synthetics, e.g. init
}
// createSyntacticParams populates f.Params and generates code (spills
// and named result locals) for all the parameters declared in the
// syntax. In addition it populates the f.objects mapping.
//
// Preconditions:
// f.startBody() was called.
// Postcondition:
// len(f.Params) == len(f.Signature.Params) + (f.Signature.Recv() ? 1 : 0)
//
func (f *Function) createSyntacticParams(recv *ast.FieldList, functype *ast.FuncType) {
// Receiver (at most one inner iteration).
if recv != nil {
for _, field := range recv.List {
for _, n := range field.Names {
f.addSpilledParam(f.Pkg.info.Defs[n])
}
// Anonymous receiver? No need to spill.
if field.Names == nil {
f.addParamObj(f.Signature.Recv())
}
}
}
// Parameters.
if functype.Params != nil {
n := len(f.Params) // 1 if has recv, 0 otherwise
for _, field := range functype.Params.List {
for _, n := range field.Names {
f.addSpilledParam(f.Pkg.info.Defs[n])
}
// Anonymous parameter? No need to spill.
if field.Names == nil {
f.addParamObj(f.Signature.Params().At(len(f.Params) - n))
}
}
}
// Named results.
if functype.Results != nil {
for _, field := range functype.Results.List {
// Implicit "var" decl of locals for named results.
for _, n := range field.Names {
f.namedResults = append(f.namedResults, f.addLocalForIdent(n))
}
}
}
}
// numberRegisters assigns numbers to all SSA registers
// (value-defining Instructions) in f, to aid debugging.
// (Non-Instruction Values are named at construction.)
//
func numberRegisters(f *Function) {
v := 0
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
switch instr.(type) {
case Value:
instr.(interface {
setNum(int)
}).setNum(v)
v++
}
}
}
}
// buildReferrers populates the def/use information in all non-nil
// Value.Referrers slice.
// Precondition: all such slices are initially empty.
func buildReferrers(f *Function) {
var rands []*Value
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
rands = instr.Operands(rands[:0]) // recycle storage
for _, rand := range rands {
if r := *rand; r != nil {
if ref := r.Referrers(); ref != nil {
*ref = append(*ref, instr)
}
}
}
}
}
}
// finishBody() finalizes the function after SSA code generation of its body.
func (f *Function) finishBody() {
f.objects = nil
f.currentBlock = nil
f.lblocks = nil
// Don't pin the AST in memory (except in debug mode).
if n := f.syntax; n != nil && !f.debugInfo() {
f.syntax = extentNode{n.Pos(), n.End()}
}
// Remove from f.Locals any Allocs that escape to the heap.
j := 0
for _, l := range f.Locals {
if !l.Heap {
f.Locals[j] = l
j++
}
}
// Nil out f.Locals[j:] to aid GC.
for i := j; i < len(f.Locals); i++ {
f.Locals[i] = nil
}
f.Locals = f.Locals[:j]
optimizeBlocks(f)
buildReferrers(f)
buildDomTree(f)
if f.Prog.mode&NaiveForm == 0 {
// For debugging pre-state of lifting pass:
// numberRegisters(f)
// f.WriteTo(os.Stderr)
lift(f)
}
f.namedResults = nil // (used by lifting)
numberRegisters(f)
if f.Prog.mode&PrintFunctions != 0 {
printMu.Lock()
f.WriteTo(os.Stdout)
printMu.Unlock()
}
if f.Prog.mode&SanityCheckFunctions != 0 {
mustSanityCheck(f, nil)
}
}
// removeNilBlocks eliminates nils from f.Blocks and updates each
// BasicBlock.Index. Use this after any pass that may delete blocks.
//
func (f *Function) removeNilBlocks() {
j := 0
for _, b := range f.Blocks {
if b != nil {
b.Index = j
f.Blocks[j] = b
j++
}
}
// Nil out f.Blocks[j:] to aid GC.
for i := j; i < len(f.Blocks); i++ {
f.Blocks[i] = nil
}
f.Blocks = f.Blocks[:j]
}
// SetDebugMode sets the debug mode for package pkg. If true, all its
// functions will include full debug info. This greatly increases the
// size of the instruction stream, and causes Functions to depend upon
// the ASTs, potentially keeping them live in memory for longer.
//
func (pkg *Package) SetDebugMode(debug bool) {
// TODO(adonovan): do we want ast.File granularity?
pkg.debug = debug
}
// debugInfo reports whether debug info is wanted for this function.
func (f *Function) debugInfo() bool {
return f.Pkg != nil && f.Pkg.debug
}
// addNamedLocal creates a local variable, adds it to function f and
// returns it. Its name and type are taken from obj. Subsequent
// calls to f.lookup(obj) will return the same local.
//
func (f *Function) addNamedLocal(obj types.Object) *Alloc {
l := f.addLocal(obj.Type(), obj.Pos())
l.Comment = obj.Name()
f.objects[obj] = l
return l
}
func (f *Function) addLocalForIdent(id *ast.Ident) *Alloc {
return f.addNamedLocal(f.Pkg.info.Defs[id])
}
// addLocal creates an anonymous local variable of type typ, adds it
// to function f and returns it. pos is the optional source location.
//
func (f *Function) addLocal(typ types.Type, pos token.Pos) *Alloc {
v := &Alloc{}
v.setType(types.NewPointer(typ))
v.setPos(pos)
f.Locals = append(f.Locals, v)
f.emit(v)
return v
}
// lookup returns the address of the named variable identified by obj
// that is local to function f or one of its enclosing functions.
// If escaping, the reference comes from a potentially escaping pointer
// expression and the referent must be heap-allocated.
//
func (f *Function) lookup(obj types.Object, escaping bool) Value {
if v, ok := f.objects[obj]; ok {
if alloc, ok := v.(*Alloc); ok && escaping {
alloc.Heap = true
}
return v // function-local var (address)
}
// Definition must be in an enclosing function;
// plumb it through intervening closures.
if f.parent == nil {
panic("no ssa.Value for " + obj.String())
}
outer := f.parent.lookup(obj, true) // escaping
v := &FreeVar{
name: obj.Name(),
typ: outer.Type(),
pos: outer.Pos(),
outer: outer,
parent: f,
}
f.objects[obj] = v
f.FreeVars = append(f.FreeVars, v)
return v
}
// emit emits the specified instruction to function f.
func (f *Function) emit(instr Instruction) Value {
return f.currentBlock.emit(instr)
}
// RelString returns the full name of this function, qualified by
// package name, receiver type, etc.
//
// The specific formatting rules are not guaranteed and may change.
//
// Examples:
// "math.IsNaN" // a package-level function
// "(*bytes.Buffer).Bytes" // a declared method or a wrapper
// "(*bytes.Buffer).Bytes$thunk" // thunk (func wrapping method; receiver is param 0)
// "(*bytes.Buffer).Bytes$bound" // bound (func wrapping method; receiver supplied by closure)
// "main.main$1" // an anonymous function in main
// "main.init#1" // a declared init function
// "main.init" // the synthesized package initializer
//
// When these functions are referred to from within the same package
// (i.e. from == f.Pkg.Object), they are rendered without the package path.
// For example: "IsNaN", "(*Buffer).Bytes", etc.
//
// All non-synthetic functions have distinct package-qualified names.
// (But two methods may have the same name "(T).f" if one is a synthetic
// wrapper promoting a non-exported method "f" from another package; in
// that case, the strings are equal but the identifiers "f" are distinct.)
//
func (f *Function) RelString(from *types.Package) string {
// Anonymous?
if f.parent != nil {
// An anonymous function's Name() looks like "parentName$1",
// but its String() should include the type/package/etc.
parent := f.parent.RelString(from)
for i, anon := range f.parent.AnonFuncs {
if anon == f {
return fmt.Sprintf("%s$%d", parent, 1+i)
}
}
return f.name // should never happen
}
// Method (declared or wrapper)?
if recv := f.Signature.Recv(); recv != nil {
return f.relMethod(from, recv.Type())
}
// Thunk?
if f.method != nil {
return f.relMethod(from, f.method.Recv())
}
// Bound?
if len(f.FreeVars) == 1 && strings.HasSuffix(f.name, "$bound") {
return f.relMethod(from, f.FreeVars[0].Type())
}
// Package-level function?
// Prefix with package name for cross-package references only.
if p := f.pkgobj(); p != nil && p != from {
return fmt.Sprintf("%s.%s", p.Path(), f.name)
}
// Unknown.
return f.name
}
func (f *Function) relMethod(from *types.Package, recv types.Type) string {
return fmt.Sprintf("(%s).%s", relType(recv, from), f.name)
}
// writeSignature writes to buf the signature sig in declaration syntax.
func writeSignature(buf *bytes.Buffer, from *types.Package, name string, sig *types.Signature, params []*Parameter) {
buf.WriteString("func ")
if recv := sig.Recv(); recv != nil {
buf.WriteString("(")
if n := params[0].Name(); n != "" {
buf.WriteString(n)
buf.WriteString(" ")
}
types.WriteType(buf, params[0].Type(), types.RelativeTo(from))
buf.WriteString(") ")
}
buf.WriteString(name)
types.WriteSignature(buf, sig, types.RelativeTo(from))
}
func (f *Function) pkgobj() *types.Package {
if f.Pkg != nil {
return f.Pkg.Object
}
return nil
}
var _ io.WriterTo = (*Function)(nil) // *Function implements io.Writer
func (f *Function) WriteTo(w io.Writer) (int64, error) {
var buf bytes.Buffer
WriteFunction(&buf, f)
n, err := w.Write(buf.Bytes())
return int64(n), err
}
// WriteFunction writes to buf a human-readable "disassembly" of f.
func WriteFunction(buf *bytes.Buffer, f *Function) {
fmt.Fprintf(buf, "# Name: %s\n", f.String())
if f.Pkg != nil {
fmt.Fprintf(buf, "# Package: %s\n", f.Pkg.Object.Path())
}
if syn := f.Synthetic; syn != "" {
fmt.Fprintln(buf, "# Synthetic:", syn)
}
if pos := f.Pos(); pos.IsValid() {
fmt.Fprintf(buf, "# Location: %s\n", f.Prog.Fset.Position(pos))
}
if f.parent != nil {
fmt.Fprintf(buf, "# Parent: %s\n", f.parent.Name())
}
if f.Recover != nil {
fmt.Fprintf(buf, "# Recover: %s\n", f.Recover)
}
from := f.pkgobj()
if f.FreeVars != nil {
buf.WriteString("# Free variables:\n")
for i, fv := range f.FreeVars {
fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, fv.Name(), relType(fv.Type(), from))
}
}
if len(f.Locals) > 0 {
buf.WriteString("# Locals:\n")
for i, l := range f.Locals {
fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), from))
}
}
writeSignature(buf, from, f.Name(), f.Signature, f.Params)
buf.WriteString(":\n")
if f.Blocks == nil {
buf.WriteString("\t(external)\n")
}
// NB. column calculations are confused by non-ASCII
// characters and assume 8-space tabs.
const punchcard = 80 // for old time's sake.
const tabwidth = 8
for _, b := range f.Blocks {
if b == nil {
// Corrupt CFG.
fmt.Fprintf(buf, ".nil:\n")
continue
}
n, _ := fmt.Fprintf(buf, "%d:", b.Index)
bmsg := fmt.Sprintf("%s P:%d S:%d", b.Comment, len(b.Preds), len(b.Succs))
fmt.Fprintf(buf, "%*s%s\n", punchcard-1-n-len(bmsg), "", bmsg)
if false { // CFG debugging
fmt.Fprintf(buf, "\t# CFG: %s --> %s --> %s\n", b.Preds, b, b.Succs)
}
for _, instr := range b.Instrs {
buf.WriteString("\t")
switch v := instr.(type) {
case Value:
l := punchcard - tabwidth
// Left-align the instruction.
if name := v.Name(); name != "" {
n, _ := fmt.Fprintf(buf, "%s = ", name)
l -= n
}
n, _ := buf.WriteString(instr.String())
l -= n
// Right-align the type if there's space.
if t := v.Type(); t != nil {
buf.WriteByte(' ')
ts := relType(t, from)
l -= len(ts) + len(" ") // (spaces before and after type)
if l > 0 {
fmt.Fprintf(buf, "%*s", l, "")
}
buf.WriteString(ts)
}
case nil:
// Be robust against bad transforms.
buf.WriteString("<deleted>")
default:
buf.WriteString(instr.String())
}
buf.WriteString("\n")
}
}
fmt.Fprintf(buf, "\n")
}
// newBasicBlock adds to f a new basic block and returns it. It does
// not automatically become the current block for subsequent calls to emit.
// comment is an optional string for more readable debugging output.
//
func (f *Function) newBasicBlock(comment string) *BasicBlock {
b := &BasicBlock{
Index: len(f.Blocks),
Comment: comment,
parent: f,
}
b.Succs = b.succs2[:0]
f.Blocks = append(f.Blocks, b)
return b
}
// NewFunction returns a new synthetic Function instance belonging to
// prog, with its name and signature fields set as specified.
//
// The caller is responsible for initializing the remaining fields of
// the function object, e.g. Pkg, Params, Blocks.
//
// It is practically impossible for clients to construct well-formed
// SSA functions/packages/programs directly, so we assume this is the
// job of the Builder alone. NewFunction exists to provide clients a
// little flexibility. For example, analysis tools may wish to
// construct fake Functions for the root of the callgraph, a fake
// "reflect" package, etc.
//
// TODO(adonovan): think harder about the API here.
//
func (prog *Program) NewFunction(name string, sig *types.Signature, provenance string) *Function {
return &Function{Prog: prog, name: name, Signature: sig, Synthetic: provenance}
}
type extentNode [2]token.Pos
func (n extentNode) Pos() token.Pos { return n[0] }
func (n extentNode) End() token.Pos { return n[1] }
// Syntax returns an ast.Node whose Pos/End methods provide the
// lexical extent of the function if it was defined by Go source code
// (f.Synthetic==""), or nil otherwise.
//
// If f was built with debug information (see Package.SetDebugRef),
// the result is the *ast.FuncDecl or *ast.FuncLit that declared the
// function. Otherwise, it is an opaque Node providing only position
// information; this avoids pinning the AST in memory.
//
func (f *Function) Syntax() ast.Node { return f.syntax }

599
vendor/golang.org/x/tools/go/ssa/lift.go generated vendored Normal file
View File

@ -0,0 +1,599 @@
// Copyright 2013 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 ssa
// This file defines the lifting pass which tries to "lift" Alloc
// cells (new/local variables) into SSA registers, replacing loads
// with the dominating stored value, eliminating loads and stores, and
// inserting φ-nodes as needed.
// Cited papers and resources:
//
// Ron Cytron et al. 1991. Efficiently computing SSA form...
// http://doi.acm.org/10.1145/115372.115320
//
// Cooper, Harvey, Kennedy. 2001. A Simple, Fast Dominance Algorithm.
// Software Practice and Experience 2001, 4:1-10.
// http://www.hipersoft.rice.edu/grads/publications/dom14.pdf
//
// Daniel Berlin, llvmdev mailing list, 2012.
// http://lists.cs.uiuc.edu/pipermail/llvmdev/2012-January/046638.html
// (Be sure to expand the whole thread.)
// TODO(adonovan): opt: there are many optimizations worth evaluating, and
// the conventional wisdom for SSA construction is that a simple
// algorithm well engineered often beats those of better asymptotic
// complexity on all but the most egregious inputs.
//
// Danny Berlin suggests that the Cooper et al. algorithm for
// computing the dominance frontier is superior to Cytron et al.
// Furthermore he recommends that rather than computing the DF for the
// whole function then renaming all alloc cells, it may be cheaper to
// compute the DF for each alloc cell separately and throw it away.
//
// Consider exploiting liveness information to avoid creating dead
// φ-nodes which we then immediately remove.
//
// Integrate lifting with scalar replacement of aggregates (SRA) since
// the two are synergistic.
//
// Also see many other "TODO: opt" suggestions in the code.
import (
"fmt"
"go/token"
"math/big"
"os"
"golang.org/x/tools/go/types"
)
// If true, perform sanity checking and show diagnostic information at
// each step of lifting. Very verbose.
const debugLifting = false
// domFrontier maps each block to the set of blocks in its dominance
// frontier. The outer slice is conceptually a map keyed by
// Block.Index. The inner slice is conceptually a set, possibly
// containing duplicates.
//
// TODO(adonovan): opt: measure impact of dups; consider a packed bit
// representation, e.g. big.Int, and bitwise parallel operations for
// the union step in the Children loop.
//
// domFrontier's methods mutate the slice's elements but not its
// length, so their receivers needn't be pointers.
//
type domFrontier [][]*BasicBlock
func (df domFrontier) add(u, v *BasicBlock) {
p := &df[u.Index]
*p = append(*p, v)
}
// build builds the dominance frontier df for the dominator (sub)tree
// rooted at u, using the Cytron et al. algorithm.
//
// TODO(adonovan): opt: consider Berlin approach, computing pruned SSA
// by pruning the entire IDF computation, rather than merely pruning
// the DF -> IDF step.
func (df domFrontier) build(u *BasicBlock) {
// Encounter each node u in postorder of dom tree.
for _, child := range u.dom.children {
df.build(child)
}
for _, vb := range u.Succs {
if v := vb.dom; v.idom != u {
df.add(u, vb)
}
}
for _, w := range u.dom.children {
for _, vb := range df[w.Index] {
// TODO(adonovan): opt: use word-parallel bitwise union.
if v := vb.dom; v.idom != u {
df.add(u, vb)
}
}
}
}
func buildDomFrontier(fn *Function) domFrontier {
df := make(domFrontier, len(fn.Blocks))
df.build(fn.Blocks[0])
if fn.Recover != nil {
df.build(fn.Recover)
}
return df
}
func removeInstr(refs []Instruction, instr Instruction) []Instruction {
i := 0
for _, ref := range refs {
if ref == instr {
continue
}
refs[i] = ref
i++
}
for j := i; j != len(refs); j++ {
refs[j] = nil // aid GC
}
return refs[:i]
}
// lift attempts to replace local and new Allocs accessed only with
// load/store by SSA registers, inserting φ-nodes where necessary.
// The result is a program in classical pruned SSA form.
//
// Preconditions:
// - fn has no dead blocks (blockopt has run).
// - Def/use info (Operands and Referrers) is up-to-date.
// - The dominator tree is up-to-date.
//
func lift(fn *Function) {
// TODO(adonovan): opt: lots of little optimizations may be
// worthwhile here, especially if they cause us to avoid
// buildDomFrontier. For example:
//
// - Alloc never loaded? Eliminate.
// - Alloc never stored? Replace all loads with a zero constant.
// - Alloc stored once? Replace loads with dominating store;
// don't forget that an Alloc is itself an effective store
// of zero.
// - Alloc used only within a single block?
// Use degenerate algorithm avoiding φ-nodes.
// - Consider synergy with scalar replacement of aggregates (SRA).
// e.g. *(&x.f) where x is an Alloc.
// Perhaps we'd get better results if we generated this as x.f
// i.e. Field(x, .f) instead of Load(FieldIndex(x, .f)).
// Unclear.
//
// But we will start with the simplest correct code.
df := buildDomFrontier(fn)
if debugLifting {
title := false
for i, blocks := range df {
if blocks != nil {
if !title {
fmt.Fprintf(os.Stderr, "Dominance frontier of %s:\n", fn)
title = true
}
fmt.Fprintf(os.Stderr, "\t%s: %s\n", fn.Blocks[i], blocks)
}
}
}
newPhis := make(newPhiMap)
// During this pass we will replace some BasicBlock.Instrs
// (allocs, loads and stores) with nil, keeping a count in
// BasicBlock.gaps. At the end we will reset Instrs to the
// concatenation of all non-dead newPhis and non-nil Instrs
// for the block, reusing the original array if space permits.
// While we're here, we also eliminate 'rundefers'
// instructions in functions that contain no 'defer'
// instructions.
usesDefer := false
// Determine which allocs we can lift and number them densely.
// The renaming phase uses this numbering for compact maps.
numAllocs := 0
for _, b := range fn.Blocks {
b.gaps = 0
b.rundefers = 0
for _, instr := range b.Instrs {
switch instr := instr.(type) {
case *Alloc:
index := -1
if liftAlloc(df, instr, newPhis) {
index = numAllocs
numAllocs++
}
instr.index = index
case *Defer:
usesDefer = true
case *RunDefers:
b.rundefers++
}
}
}
// renaming maps an alloc (keyed by index) to its replacement
// value. Initially the renaming contains nil, signifying the
// zero constant of the appropriate type; we construct the
// Const lazily at most once on each path through the domtree.
// TODO(adonovan): opt: cache per-function not per subtree.
renaming := make([]Value, numAllocs)
// Renaming.
rename(fn.Blocks[0], renaming, newPhis)
// Eliminate dead new phis, then prepend the live ones to each block.
for _, b := range fn.Blocks {
// Compress the newPhis slice to eliminate unused phis.
// TODO(adonovan): opt: compute liveness to avoid
// placing phis in blocks for which the alloc cell is
// not live.
nps := newPhis[b]
j := 0
for _, np := range nps {
if !phiIsLive(np.phi) {
// discard it, first removing it from referrers
for _, newval := range np.phi.Edges {
if refs := newval.Referrers(); refs != nil {
*refs = removeInstr(*refs, np.phi)
}
}
continue
}
nps[j] = np
j++
}
nps = nps[:j]
rundefersToKill := b.rundefers
if usesDefer {
rundefersToKill = 0
}
if j+b.gaps+rundefersToKill == 0 {
continue // fast path: no new phis or gaps
}
// Compact nps + non-nil Instrs into a new slice.
// TODO(adonovan): opt: compact in situ if there is
// sufficient space or slack in the slice.
dst := make([]Instruction, len(b.Instrs)+j-b.gaps-rundefersToKill)
for i, np := range nps {
dst[i] = np.phi
}
for _, instr := range b.Instrs {
if instr == nil {
continue
}
if !usesDefer {
if _, ok := instr.(*RunDefers); ok {
continue
}
}
dst[j] = instr
j++
}
for i, np := range nps {
dst[i] = np.phi
}
b.Instrs = dst
}
// Remove any fn.Locals that were lifted.
j := 0
for _, l := range fn.Locals {
if l.index < 0 {
fn.Locals[j] = l
j++
}
}
// Nil out fn.Locals[j:] to aid GC.
for i := j; i < len(fn.Locals); i++ {
fn.Locals[i] = nil
}
fn.Locals = fn.Locals[:j]
}
func phiIsLive(phi *Phi) bool {
for _, instr := range *phi.Referrers() {
if instr == phi {
continue // self-refs don't count
}
if _, ok := instr.(*DebugRef); ok {
continue // debug refs don't count
}
return true
}
return false
}
type blockSet struct{ big.Int } // (inherit methods from Int)
// add adds b to the set and returns true if the set changed.
func (s *blockSet) add(b *BasicBlock) bool {
i := b.Index
if s.Bit(i) != 0 {
return false
}
s.SetBit(&s.Int, i, 1)
return true
}
// take removes an arbitrary element from a set s and
// returns its index, or returns -1 if empty.
func (s *blockSet) take() int {
l := s.BitLen()
for i := 0; i < l; i++ {
if s.Bit(i) == 1 {
s.SetBit(&s.Int, i, 0)
return i
}
}
return -1
}
// newPhi is a pair of a newly introduced φ-node and the lifted Alloc
// it replaces.
type newPhi struct {
phi *Phi
alloc *Alloc
}
// newPhiMap records for each basic block, the set of newPhis that
// must be prepended to the block.
type newPhiMap map[*BasicBlock][]newPhi
// liftAlloc determines whether alloc can be lifted into registers,
// and if so, it populates newPhis with all the φ-nodes it may require
// and returns true.
//
func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap) bool {
// Don't lift aggregates into registers, because we don't have
// a way to express their zero-constants.
switch deref(alloc.Type()).Underlying().(type) {
case *types.Array, *types.Struct:
return false
}
// Don't lift named return values in functions that defer
// calls that may recover from panic.
if fn := alloc.Parent(); fn.Recover != nil {
for _, nr := range fn.namedResults {
if nr == alloc {
return false
}
}
}
// Compute defblocks, the set of blocks containing a
// definition of the alloc cell.
var defblocks blockSet
for _, instr := range *alloc.Referrers() {
// Bail out if we discover the alloc is not liftable;
// the only operations permitted to use the alloc are
// loads/stores into the cell, and DebugRef.
switch instr := instr.(type) {
case *Store:
if instr.Val == alloc {
return false // address used as value
}
if instr.Addr != alloc {
panic("Alloc.Referrers is inconsistent")
}
defblocks.add(instr.Block())
case *UnOp:
if instr.Op != token.MUL {
return false // not a load
}
if instr.X != alloc {
panic("Alloc.Referrers is inconsistent")
}
case *DebugRef:
// ok
default:
return false // some other instruction
}
}
// The Alloc itself counts as a (zero) definition of the cell.
defblocks.add(alloc.Block())
if debugLifting {
fmt.Fprintln(os.Stderr, "\tlifting ", alloc, alloc.Name())
}
fn := alloc.Parent()
// Φ-insertion.
//
// What follows is the body of the main loop of the insert-φ
// function described by Cytron et al, but instead of using
// counter tricks, we just reset the 'hasAlready' and 'work'
// sets each iteration. These are bitmaps so it's pretty cheap.
//
// TODO(adonovan): opt: recycle slice storage for W,
// hasAlready, defBlocks across liftAlloc calls.
var hasAlready blockSet
// Initialize W and work to defblocks.
var work blockSet = defblocks // blocks seen
var W blockSet // blocks to do
W.Set(&defblocks.Int)
// Traverse iterated dominance frontier, inserting φ-nodes.
for i := W.take(); i != -1; i = W.take() {
u := fn.Blocks[i]
for _, v := range df[u.Index] {
if hasAlready.add(v) {
// Create φ-node.
// It will be prepended to v.Instrs later, if needed.
phi := &Phi{
Edges: make([]Value, len(v.Preds)),
Comment: alloc.Comment,
}
phi.pos = alloc.Pos()
phi.setType(deref(alloc.Type()))
phi.block = v
if debugLifting {
fmt.Fprintf(os.Stderr, "\tplace %s = %s at block %s\n", phi.Name(), phi, v)
}
newPhis[v] = append(newPhis[v], newPhi{phi, alloc})
if work.add(v) {
W.add(v)
}
}
}
}
return true
}
// replaceAll replaces all intraprocedural uses of x with y,
// updating x.Referrers and y.Referrers.
// Precondition: x.Referrers() != nil, i.e. x must be local to some function.
//
func replaceAll(x, y Value) {
var rands []*Value
pxrefs := x.Referrers()
pyrefs := y.Referrers()
for _, instr := range *pxrefs {
rands = instr.Operands(rands[:0]) // recycle storage
for _, rand := range rands {
if *rand != nil {
if *rand == x {
*rand = y
}
}
}
if pyrefs != nil {
*pyrefs = append(*pyrefs, instr) // dups ok
}
}
*pxrefs = nil // x is now unreferenced
}
// renamed returns the value to which alloc is being renamed,
// constructing it lazily if it's the implicit zero initialization.
//
func renamed(renaming []Value, alloc *Alloc) Value {
v := renaming[alloc.index]
if v == nil {
v = zeroConst(deref(alloc.Type()))
renaming[alloc.index] = v
}
return v
}
// rename implements the (Cytron et al) SSA renaming algorithm, a
// preorder traversal of the dominator tree replacing all loads of
// Alloc cells with the value stored to that cell by the dominating
// store instruction. For lifting, we need only consider loads,
// stores and φ-nodes.
//
// renaming is a map from *Alloc (keyed by index number) to its
// dominating stored value; newPhis[x] is the set of new φ-nodes to be
// prepended to block x.
//
func rename(u *BasicBlock, renaming []Value, newPhis newPhiMap) {
// Each φ-node becomes the new name for its associated Alloc.
for _, np := range newPhis[u] {
phi := np.phi
alloc := np.alloc
renaming[alloc.index] = phi
}
// Rename loads and stores of allocs.
for i, instr := range u.Instrs {
switch instr := instr.(type) {
case *Alloc:
if instr.index >= 0 { // store of zero to Alloc cell
// Replace dominated loads by the zero value.
renaming[instr.index] = nil
if debugLifting {
fmt.Fprintf(os.Stderr, "\tkill alloc %s\n", instr)
}
// Delete the Alloc.
u.Instrs[i] = nil
u.gaps++
}
case *Store:
if alloc, ok := instr.Addr.(*Alloc); ok && alloc.index >= 0 { // store to Alloc cell
// Replace dominated loads by the stored value.
renaming[alloc.index] = instr.Val
if debugLifting {
fmt.Fprintf(os.Stderr, "\tkill store %s; new value: %s\n",
instr, instr.Val.Name())
}
// Remove the store from the referrer list of the stored value.
if refs := instr.Val.Referrers(); refs != nil {
*refs = removeInstr(*refs, instr)
}
// Delete the Store.
u.Instrs[i] = nil
u.gaps++
}
case *UnOp:
if instr.Op == token.MUL {
if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // load of Alloc cell
newval := renamed(renaming, alloc)
if debugLifting {
fmt.Fprintf(os.Stderr, "\tupdate load %s = %s with %s\n",
instr.Name(), instr, newval.Name())
}
// Replace all references to
// the loaded value by the
// dominating stored value.
replaceAll(instr, newval)
// Delete the Load.
u.Instrs[i] = nil
u.gaps++
}
}
case *DebugRef:
if alloc, ok := instr.X.(*Alloc); ok && alloc.index >= 0 { // ref of Alloc cell
if instr.IsAddr {
instr.X = renamed(renaming, alloc)
instr.IsAddr = false
// Add DebugRef to instr.X's referrers.
if refs := instr.X.Referrers(); refs != nil {
*refs = append(*refs, instr)
}
} else {
// A source expression denotes the address
// of an Alloc that was optimized away.
instr.X = nil
// Delete the DebugRef.
u.Instrs[i] = nil
u.gaps++
}
}
}
}
// For each φ-node in a CFG successor, rename the edge.
for _, v := range u.Succs {
phis := newPhis[v]
if len(phis) == 0 {
continue
}
i := v.predIndex(u)
for _, np := range phis {
phi := np.phi
alloc := np.alloc
newval := renamed(renaming, alloc)
if debugLifting {
fmt.Fprintf(os.Stderr, "\tsetphi %s edge %s -> %s (#%d) (alloc=%s) := %s\n",
phi.Name(), u, v, i, alloc.Name(), newval.Name())
}
phi.Edges[i] = newval
if prefs := newval.Referrers(); prefs != nil {
*prefs = append(*prefs, phi)
}
}
}
// Continue depth-first recursion over domtree, pushing a
// fresh copy of the renaming map for each subtree.
for _, v := range u.dom.children {
// TODO(adonovan): opt: avoid copy on final iteration; use destructive update.
r := make([]Value, len(renaming))
copy(r, renaming)
rename(v, r, newPhis)
}
}

121
vendor/golang.org/x/tools/go/ssa/lvalue.go generated vendored Normal file
View File

@ -0,0 +1,121 @@
// Copyright 2013 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 ssa
// lvalues are the union of addressable expressions and map-index
// expressions.
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/types"
)
// An lvalue represents an assignable location that may appear on the
// left-hand side of an assignment. This is a generalization of a
// pointer to permit updates to elements of maps.
//
type lvalue interface {
store(fn *Function, v Value) // stores v into the location
load(fn *Function) Value // loads the contents of the location
address(fn *Function) Value // address of the location
typ() types.Type // returns the type of the location
}
// An address is an lvalue represented by a true pointer.
type address struct {
addr Value
pos token.Pos // source position
expr ast.Expr // source syntax of the value (not address) [debug mode]
}
func (a *address) load(fn *Function) Value {
load := emitLoad(fn, a.addr)
load.pos = a.pos
return load
}
func (a *address) store(fn *Function, v Value) {
store := emitStore(fn, a.addr, v, a.pos)
if a.expr != nil {
// store.Val is v, converted for assignability.
emitDebugRef(fn, a.expr, store.Val, false)
}
}
func (a *address) address(fn *Function) Value {
if a.expr != nil {
emitDebugRef(fn, a.expr, a.addr, true)
}
return a.addr
}
func (a *address) typ() types.Type {
return deref(a.addr.Type())
}
// An element is an lvalue represented by m[k], the location of an
// element of a map or string. These locations are not addressable
// since pointers cannot be formed from them, but they do support
// load(), and in the case of maps, store().
//
type element struct {
m, k Value // map or string
t types.Type // map element type or string byte type
pos token.Pos // source position of colon ({k:v}) or lbrack (m[k]=v)
}
func (e *element) load(fn *Function) Value {
l := &Lookup{
X: e.m,
Index: e.k,
}
l.setPos(e.pos)
l.setType(e.t)
return fn.emit(l)
}
func (e *element) store(fn *Function, v Value) {
up := &MapUpdate{
Map: e.m,
Key: e.k,
Value: emitConv(fn, v, e.t),
}
up.pos = e.pos
fn.emit(up)
}
func (e *element) address(fn *Function) Value {
panic("map/string elements are not addressable")
}
func (e *element) typ() types.Type {
return e.t
}
// A blank is a dummy variable whose name is "_".
// It is not reified: loads are illegal and stores are ignored.
//
type blank struct{}
func (bl blank) load(fn *Function) Value {
panic("blank.load is illegal")
}
func (bl blank) store(fn *Function, v Value) {
// no-op
}
func (bl blank) address(fn *Function) Value {
panic("blank var is not addressable")
}
func (bl blank) typ() types.Type {
// This should be the type of the blank Ident; the typechecker
// doesn't provide this yet, but fortunately, we don't need it
// yet either.
panic("blank.typ is unimplemented")
}

243
vendor/golang.org/x/tools/go/ssa/methods.go generated vendored Normal file
View File

@ -0,0 +1,243 @@
// Copyright 2013 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 ssa
// This file defines utilities for population of method sets.
import (
"fmt"
"golang.org/x/tools/go/types"
)
// Method returns the Function implementing method sel, building
// wrapper methods on demand. It returns nil if sel denotes an
// abstract (interface) method.
//
// Precondition: sel.Kind() == MethodVal.
//
// TODO(adonovan): rename this to MethodValue because of the
// precondition, and for consistency with functions in source.go.
//
// Thread-safe.
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
func (prog *Program) Method(sel *types.Selection) *Function {
if sel.Kind() != types.MethodVal {
panic(fmt.Sprintf("Method(%s) kind != MethodVal", sel))
}
T := sel.Recv()
if isInterface(T) {
return nil // abstract method
}
if prog.mode&LogSource != 0 {
defer logStack("Method %s %v", T, sel)()
}
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
return prog.addMethod(prog.createMethodSet(T), sel)
}
// LookupMethod returns the implementation of the method of type T
// identified by (pkg, name). It returns nil if the method exists but
// is abstract, and panics if T has no such method.
//
func (prog *Program) LookupMethod(T types.Type, pkg *types.Package, name string) *Function {
sel := prog.MethodSets.MethodSet(T).Lookup(pkg, name)
if sel == nil {
panic(fmt.Sprintf("%s has no method %s", T, types.Id(pkg, name)))
}
return prog.Method(sel)
}
// methodSet contains the (concrete) methods of a non-interface type.
type methodSet struct {
mapping map[string]*Function // populated lazily
complete bool // mapping contains all methods
}
// Precondition: !isInterface(T).
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
func (prog *Program) createMethodSet(T types.Type) *methodSet {
mset, ok := prog.methodSets.At(T).(*methodSet)
if !ok {
mset = &methodSet{mapping: make(map[string]*Function)}
prog.methodSets.Set(T, mset)
}
return mset
}
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
func (prog *Program) addMethod(mset *methodSet, sel *types.Selection) *Function {
if sel.Kind() == types.MethodExpr {
panic(sel)
}
id := sel.Obj().Id()
fn := mset.mapping[id]
if fn == nil {
obj := sel.Obj().(*types.Func)
needsPromotion := len(sel.Index()) > 1
needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.Recv())
if needsPromotion || needsIndirection {
fn = makeWrapper(prog, sel)
} else {
fn = prog.declaredFunc(obj)
}
if fn.Signature.Recv() == nil {
panic(fn) // missing receiver
}
mset.mapping[id] = fn
}
return fn
}
// RuntimeTypes returns a new unordered slice containing all
// concrete types in the program for which a complete (non-empty)
// method set is required at run-time.
//
// Thread-safe.
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
func (prog *Program) RuntimeTypes() []types.Type {
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
var res []types.Type
prog.methodSets.Iterate(func(T types.Type, v interface{}) {
if v.(*methodSet).complete {
res = append(res, T)
}
})
return res
}
// declaredFunc returns the concrete function/method denoted by obj.
// Panic ensues if there is none.
//
func (prog *Program) declaredFunc(obj *types.Func) *Function {
if v := prog.packageLevelValue(obj); v != nil {
return v.(*Function)
}
panic("no concrete method: " + obj.String())
}
// needMethodsOf ensures that runtime type information (including the
// complete method set) is available for the specified type T and all
// its subcomponents.
//
// needMethodsOf must be called for at least every type that is an
// operand of some MakeInterface instruction, and for the type of
// every exported package member.
//
// Precondition: T is not a method signature (*Signature with Recv()!=nil).
//
// Thread-safe. (Called via emitConv from multiple builder goroutines.)
//
// TODO(adonovan): make this faster. It accounts for 20% of SSA build time.
//
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
//
func (prog *Program) needMethodsOf(T types.Type) {
prog.methodsMu.Lock()
prog.needMethods(T, false)
prog.methodsMu.Unlock()
}
// Precondition: T is not a method signature (*Signature with Recv()!=nil).
// Recursive case: skip => don't create methods for T.
//
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
//
func (prog *Program) needMethods(T types.Type, skip bool) {
// Each package maintains its own set of types it has visited.
if prevSkip, ok := prog.runtimeTypes.At(T).(bool); ok {
// needMethods(T) was previously called
if !prevSkip || skip {
return // already seen, with same or false 'skip' value
}
}
prog.runtimeTypes.Set(T, skip)
tmset := prog.MethodSets.MethodSet(T)
if !skip && !isInterface(T) && tmset.Len() > 0 {
// Create methods of T.
mset := prog.createMethodSet(T)
if !mset.complete {
mset.complete = true
n := tmset.Len()
for i := 0; i < n; i++ {
prog.addMethod(mset, tmset.At(i))
}
}
}
// Recursion over signatures of each method.
for i := 0; i < tmset.Len(); i++ {
sig := tmset.At(i).Type().(*types.Signature)
prog.needMethods(sig.Params(), false)
prog.needMethods(sig.Results(), false)
}
switch t := T.(type) {
case *types.Basic:
// nop
case *types.Interface:
// nop---handled by recursion over method set.
case *types.Pointer:
prog.needMethods(t.Elem(), false)
case *types.Slice:
prog.needMethods(t.Elem(), false)
case *types.Chan:
prog.needMethods(t.Elem(), false)
case *types.Map:
prog.needMethods(t.Key(), false)
prog.needMethods(t.Elem(), false)
case *types.Signature:
if t.Recv() != nil {
panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
}
prog.needMethods(t.Params(), false)
prog.needMethods(t.Results(), false)
case *types.Named:
// A pointer-to-named type can be derived from a named
// type via reflection. It may have methods too.
prog.needMethods(types.NewPointer(T), false)
// Consider 'type T struct{S}' where S has methods.
// Reflection provides no way to get from T to struct{S},
// only to S, so the method set of struct{S} is unwanted,
// so set 'skip' flag during recursion.
prog.needMethods(t.Underlying(), true)
case *types.Array:
prog.needMethods(t.Elem(), false)
case *types.Struct:
for i, n := 0, t.NumFields(); i < n; i++ {
prog.needMethods(t.Field(i).Type(), false)
}
case *types.Tuple:
for i, n := 0, t.Len(); i < n; i++ {
prog.needMethods(t.At(i).Type(), false)
}
default:
panic(T)
}
}

107
vendor/golang.org/x/tools/go/ssa/mode.go generated vendored Normal file
View File

@ -0,0 +1,107 @@
// 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 ssa
// This file defines the BuilderMode type and its command-line flag.
import (
"bytes"
"flag"
"fmt"
)
// BuilderMode is a bitmask of options for diagnostics and checking.
type BuilderMode uint
const (
PrintPackages BuilderMode = 1 << iota // Print package inventory to stdout
PrintFunctions // Print function SSA code to stdout
LogSource // Log source locations as SSA builder progresses
SanityCheckFunctions // Perform sanity checking of function bodies
NaiveForm // Build naïve SSA form: don't replace local loads/stores with registers
BuildSerially // Build packages serially, not in parallel.
GlobalDebug // Enable debug info for all packages
BareInits // Build init functions without guards or calls to dependent inits
)
const modeFlagUsage = `Options controlling the SSA builder.
The value is a sequence of zero or more of these letters:
C perform sanity [C]hecking of the SSA form.
D include [D]ebug info for every function.
P print [P]ackage inventory.
F print [F]unction SSA code.
S log [S]ource locations as SSA builder progresses.
L build distinct packages seria[L]ly instead of in parallel.
N build [N]aive SSA form: don't replace local loads/stores with registers.
I build bare [I]nit functions: no init guards or calls to dependent inits.
`
// BuilderModeFlag creates a new command line flag of type BuilderMode,
// adds it to the specified flag set, and returns it.
//
// Example:
// var ssabuild = BuilderModeFlag(flag.CommandLine, "ssabuild", 0)
//
func BuilderModeFlag(set *flag.FlagSet, name string, value BuilderMode) *BuilderMode {
set.Var((*builderModeValue)(&value), name, modeFlagUsage)
return &value
}
type builderModeValue BuilderMode // satisfies flag.Value and flag.Getter.
func (v *builderModeValue) Set(s string) error {
var mode BuilderMode
for _, c := range s {
switch c {
case 'D':
mode |= GlobalDebug
case 'P':
mode |= PrintPackages
case 'F':
mode |= PrintFunctions
case 'S':
mode |= LogSource | BuildSerially
case 'C':
mode |= SanityCheckFunctions
case 'N':
mode |= NaiveForm
case 'L':
mode |= BuildSerially
default:
return fmt.Errorf("unknown BuilderMode option: %q", c)
}
}
*v = builderModeValue(mode)
return nil
}
func (v *builderModeValue) Get() interface{} { return BuilderMode(*v) }
func (v *builderModeValue) String() string {
mode := BuilderMode(*v)
var buf bytes.Buffer
if mode&GlobalDebug != 0 {
buf.WriteByte('D')
}
if mode&PrintPackages != 0 {
buf.WriteByte('P')
}
if mode&PrintFunctions != 0 {
buf.WriteByte('F')
}
if mode&LogSource != 0 {
buf.WriteByte('S')
}
if mode&SanityCheckFunctions != 0 {
buf.WriteByte('C')
}
if mode&NaiveForm != 0 {
buf.WriteByte('N')
}
if mode&BuildSerially != 0 {
buf.WriteByte('L')
}
return buf.String()
}

427
vendor/golang.org/x/tools/go/ssa/print.go generated vendored Normal file
View File

@ -0,0 +1,427 @@
// Copyright 2013 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 ssa
// This file implements the String() methods for all Value and
// Instruction types.
import (
"bytes"
"fmt"
"io"
"reflect"
"sort"
"golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
// relName returns the name of v relative to i.
// In most cases, this is identical to v.Name(), but references to
// Functions (including methods) and Globals use RelString and
// all types are displayed with relType, so that only cross-package
// references are package-qualified.
//
func relName(v Value, i Instruction) string {
var from *types.Package
if i != nil {
from = i.Parent().pkgobj()
}
switch v := v.(type) {
case Member: // *Function or *Global
return v.RelString(from)
case *Const:
return v.RelString(from)
}
return v.Name()
}
func relType(t types.Type, from *types.Package) string {
return types.TypeString(t, types.RelativeTo(from))
}
func relString(m Member, from *types.Package) string {
// NB: not all globals have an Object (e.g. init$guard),
// so use Package().Object not Object.Package().
if obj := m.Package().Object; obj != nil && obj != from {
return fmt.Sprintf("%s.%s", obj.Path(), m.Name())
}
return m.Name()
}
// Value.String()
//
// This method is provided only for debugging.
// It never appears in disassembly, which uses Value.Name().
func (v *Parameter) String() string {
from := v.Parent().pkgobj()
return fmt.Sprintf("parameter %s : %s", v.Name(), relType(v.Type(), from))
}
func (v *FreeVar) String() string {
from := v.Parent().pkgobj()
return fmt.Sprintf("freevar %s : %s", v.Name(), relType(v.Type(), from))
}
func (v *Builtin) String() string {
return fmt.Sprintf("builtin %s", v.Name())
}
// Instruction.String()
func (v *Alloc) String() string {
op := "local"
if v.Heap {
op = "new"
}
from := v.Parent().pkgobj()
return fmt.Sprintf("%s %s (%s)", op, relType(deref(v.Type()), from), v.Comment)
}
func (v *Phi) String() string {
var b bytes.Buffer
b.WriteString("phi [")
for i, edge := range v.Edges {
if i > 0 {
b.WriteString(", ")
}
// Be robust against malformed CFG.
block := -1
if v.block != nil && i < len(v.block.Preds) {
block = v.block.Preds[i].Index
}
fmt.Fprintf(&b, "%d: ", block)
edgeVal := "<nil>" // be robust
if edge != nil {
edgeVal = relName(edge, v)
}
b.WriteString(edgeVal)
}
b.WriteString("]")
if v.Comment != "" {
b.WriteString(" #")
b.WriteString(v.Comment)
}
return b.String()
}
func printCall(v *CallCommon, prefix string, instr Instruction) string {
var b bytes.Buffer
b.WriteString(prefix)
if !v.IsInvoke() {
b.WriteString(relName(v.Value, instr))
} else {
fmt.Fprintf(&b, "invoke %s.%s", relName(v.Value, instr), v.Method.Name())
}
b.WriteString("(")
for i, arg := range v.Args {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(relName(arg, instr))
}
if v.Signature().Variadic() {
b.WriteString("...")
}
b.WriteString(")")
return b.String()
}
func (c *CallCommon) String() string {
return printCall(c, "", nil)
}
func (v *Call) String() string {
return printCall(&v.Call, "", v)
}
func (v *BinOp) String() string {
return fmt.Sprintf("%s %s %s", relName(v.X, v), v.Op.String(), relName(v.Y, v))
}
func (v *UnOp) String() string {
return fmt.Sprintf("%s%s%s", v.Op, relName(v.X, v), commaOk(v.CommaOk))
}
func printConv(prefix string, v, x Value) string {
from := v.Parent().pkgobj()
return fmt.Sprintf("%s %s <- %s (%s)",
prefix,
relType(v.Type(), from),
relType(x.Type(), from),
relName(x, v.(Instruction)))
}
func (v *ChangeType) String() string { return printConv("changetype", v, v.X) }
func (v *Convert) String() string { return printConv("convert", v, v.X) }
func (v *ChangeInterface) String() string { return printConv("change interface", v, v.X) }
func (v *MakeInterface) String() string { return printConv("make", v, v.X) }
func (v *MakeClosure) String() string {
var b bytes.Buffer
fmt.Fprintf(&b, "make closure %s", relName(v.Fn, v))
if v.Bindings != nil {
b.WriteString(" [")
for i, c := range v.Bindings {
if i > 0 {
b.WriteString(", ")
}
b.WriteString(relName(c, v))
}
b.WriteString("]")
}
return b.String()
}
func (v *MakeSlice) String() string {
from := v.Parent().pkgobj()
return fmt.Sprintf("make %s %s %s",
relType(v.Type(), from),
relName(v.Len, v),
relName(v.Cap, v))
}
func (v *Slice) String() string {
var b bytes.Buffer
b.WriteString("slice ")
b.WriteString(relName(v.X, v))
b.WriteString("[")
if v.Low != nil {
b.WriteString(relName(v.Low, v))
}
b.WriteString(":")
if v.High != nil {
b.WriteString(relName(v.High, v))
}
if v.Max != nil {
b.WriteString(":")
b.WriteString(relName(v.Max, v))
}
b.WriteString("]")
return b.String()
}
func (v *MakeMap) String() string {
res := ""
if v.Reserve != nil {
res = relName(v.Reserve, v)
}
from := v.Parent().pkgobj()
return fmt.Sprintf("make %s %s", relType(v.Type(), from), res)
}
func (v *MakeChan) String() string {
from := v.Parent().pkgobj()
return fmt.Sprintf("make %s %s", relType(v.Type(), from), relName(v.Size, v))
}
func (v *FieldAddr) String() string {
st := deref(v.X.Type()).Underlying().(*types.Struct)
// Be robust against a bad index.
name := "?"
if 0 <= v.Field && v.Field < st.NumFields() {
name = st.Field(v.Field).Name()
}
return fmt.Sprintf("&%s.%s [#%d]", relName(v.X, v), name, v.Field)
}
func (v *Field) String() string {
st := v.X.Type().Underlying().(*types.Struct)
// Be robust against a bad index.
name := "?"
if 0 <= v.Field && v.Field < st.NumFields() {
name = st.Field(v.Field).Name()
}
return fmt.Sprintf("%s.%s [#%d]", relName(v.X, v), name, v.Field)
}
func (v *IndexAddr) String() string {
return fmt.Sprintf("&%s[%s]", relName(v.X, v), relName(v.Index, v))
}
func (v *Index) String() string {
return fmt.Sprintf("%s[%s]", relName(v.X, v), relName(v.Index, v))
}
func (v *Lookup) String() string {
return fmt.Sprintf("%s[%s]%s", relName(v.X, v), relName(v.Index, v), commaOk(v.CommaOk))
}
func (v *Range) String() string {
return "range " + relName(v.X, v)
}
func (v *Next) String() string {
return "next " + relName(v.Iter, v)
}
func (v *TypeAssert) String() string {
from := v.Parent().pkgobj()
return fmt.Sprintf("typeassert%s %s.(%s)", commaOk(v.CommaOk), relName(v.X, v), relType(v.AssertedType, from))
}
func (v *Extract) String() string {
return fmt.Sprintf("extract %s #%d", relName(v.Tuple, v), v.Index)
}
func (s *Jump) String() string {
// Be robust against malformed CFG.
block := -1
if s.block != nil && len(s.block.Succs) == 1 {
block = s.block.Succs[0].Index
}
return fmt.Sprintf("jump %d", block)
}
func (s *If) String() string {
// Be robust against malformed CFG.
tblock, fblock := -1, -1
if s.block != nil && len(s.block.Succs) == 2 {
tblock = s.block.Succs[0].Index
fblock = s.block.Succs[1].Index
}
return fmt.Sprintf("if %s goto %d else %d", relName(s.Cond, s), tblock, fblock)
}
func (s *Go) String() string {
return printCall(&s.Call, "go ", s)
}
func (s *Panic) String() string {
return "panic " + relName(s.X, s)
}
func (s *Return) String() string {
var b bytes.Buffer
b.WriteString("return")
for i, r := range s.Results {
if i == 0 {
b.WriteString(" ")
} else {
b.WriteString(", ")
}
b.WriteString(relName(r, s))
}
return b.String()
}
func (*RunDefers) String() string {
return "rundefers"
}
func (s *Send) String() string {
return fmt.Sprintf("send %s <- %s", relName(s.Chan, s), relName(s.X, s))
}
func (s *Defer) String() string {
return printCall(&s.Call, "defer ", s)
}
func (s *Select) String() string {
var b bytes.Buffer
for i, st := range s.States {
if i > 0 {
b.WriteString(", ")
}
if st.Dir == types.RecvOnly {
b.WriteString("<-")
b.WriteString(relName(st.Chan, s))
} else {
b.WriteString(relName(st.Chan, s))
b.WriteString("<-")
b.WriteString(relName(st.Send, s))
}
}
non := ""
if !s.Blocking {
non = "non"
}
return fmt.Sprintf("select %sblocking [%s]", non, b.String())
}
func (s *Store) String() string {
return fmt.Sprintf("*%s = %s", relName(s.Addr, s), relName(s.Val, s))
}
func (s *MapUpdate) String() string {
return fmt.Sprintf("%s[%s] = %s", relName(s.Map, s), relName(s.Key, s), relName(s.Value, s))
}
func (s *DebugRef) String() string {
p := s.Parent().Prog.Fset.Position(s.Pos())
var descr interface{}
if s.object != nil {
descr = s.object // e.g. "var x int"
} else {
descr = reflect.TypeOf(s.Expr) // e.g. "*ast.CallExpr"
}
var addr string
if s.IsAddr {
addr = "address of "
}
return fmt.Sprintf("; %s%s @ %d:%d is %s", addr, descr, p.Line, p.Column, s.X.Name())
}
func (p *Package) String() string {
return "package " + p.Object.Path()
}
var _ io.WriterTo = (*Package)(nil) // *Package implements io.Writer
func (p *Package) WriteTo(w io.Writer) (int64, error) {
var buf bytes.Buffer
WritePackage(&buf, p)
n, err := w.Write(buf.Bytes())
return int64(n), err
}
// WritePackage writes to buf a human-readable summary of p.
func WritePackage(buf *bytes.Buffer, p *Package) {
fmt.Fprintf(buf, "%s:\n", p)
var names []string
maxname := 0
for name := range p.Members {
if l := len(name); l > maxname {
maxname = l
}
names = append(names, name)
}
from := p.Object
sort.Strings(names)
for _, name := range names {
switch mem := p.Members[name].(type) {
case *NamedConst:
fmt.Fprintf(buf, " const %-*s %s = %s\n",
maxname, name, mem.Name(), mem.Value.RelString(from))
case *Function:
fmt.Fprintf(buf, " func %-*s %s\n",
maxname, name, relType(mem.Type(), from))
case *Type:
fmt.Fprintf(buf, " type %-*s %s\n",
maxname, name, relType(mem.Type().Underlying(), from))
for _, meth := range typeutil.IntuitiveMethodSet(mem.Type(), &p.Prog.MethodSets) {
fmt.Fprintf(buf, " %s\n", types.SelectionString(meth, types.RelativeTo(from)))
}
case *Global:
fmt.Fprintf(buf, " var %-*s %s\n",
maxname, name, relType(mem.Type().(*types.Pointer).Elem(), from))
}
}
fmt.Fprintf(buf, "\n")
}
func commaOk(x bool) string {
if x {
return ",ok"
}
return ""
}

520
vendor/golang.org/x/tools/go/ssa/sanity.go generated vendored Normal file
View File

@ -0,0 +1,520 @@
// Copyright 2013 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 ssa
// An optional pass for sanity-checking invariants of the SSA representation.
// Currently it checks CFG invariants but little at the instruction level.
import (
"fmt"
"io"
"os"
"strings"
"golang.org/x/tools/go/types"
)
type sanity struct {
reporter io.Writer
fn *Function
block *BasicBlock
instrs map[Instruction]struct{}
insane bool
}
// sanityCheck performs integrity checking of the SSA representation
// of the function fn and returns true if it was valid. Diagnostics
// are written to reporter if non-nil, os.Stderr otherwise. Some
// diagnostics are only warnings and do not imply a negative result.
//
// Sanity-checking is intended to facilitate the debugging of code
// transformation passes.
//
func sanityCheck(fn *Function, reporter io.Writer) bool {
if reporter == nil {
reporter = os.Stderr
}
return (&sanity{reporter: reporter}).checkFunction(fn)
}
// mustSanityCheck is like sanityCheck but panics instead of returning
// a negative result.
//
func mustSanityCheck(fn *Function, reporter io.Writer) {
if !sanityCheck(fn, reporter) {
fn.WriteTo(os.Stderr)
panic("SanityCheck failed")
}
}
func (s *sanity) diagnostic(prefix, format string, args ...interface{}) {
fmt.Fprintf(s.reporter, "%s: function %s", prefix, s.fn)
if s.block != nil {
fmt.Fprintf(s.reporter, ", block %s", s.block)
}
io.WriteString(s.reporter, ": ")
fmt.Fprintf(s.reporter, format, args...)
io.WriteString(s.reporter, "\n")
}
func (s *sanity) errorf(format string, args ...interface{}) {
s.insane = true
s.diagnostic("Error", format, args...)
}
func (s *sanity) warnf(format string, args ...interface{}) {
s.diagnostic("Warning", format, args...)
}
// findDuplicate returns an arbitrary basic block that appeared more
// than once in blocks, or nil if all were unique.
func findDuplicate(blocks []*BasicBlock) *BasicBlock {
if len(blocks) < 2 {
return nil
}
if blocks[0] == blocks[1] {
return blocks[0]
}
// Slow path:
m := make(map[*BasicBlock]bool)
for _, b := range blocks {
if m[b] {
return b
}
m[b] = true
}
return nil
}
func (s *sanity) checkInstr(idx int, instr Instruction) {
switch instr := instr.(type) {
case *If, *Jump, *Return, *Panic:
s.errorf("control flow instruction not at end of block")
case *Phi:
if idx == 0 {
// It suffices to apply this check to just the first phi node.
if dup := findDuplicate(s.block.Preds); dup != nil {
s.errorf("phi node in block with duplicate predecessor %s", dup)
}
} else {
prev := s.block.Instrs[idx-1]
if _, ok := prev.(*Phi); !ok {
s.errorf("Phi instruction follows a non-Phi: %T", prev)
}
}
if ne, np := len(instr.Edges), len(s.block.Preds); ne != np {
s.errorf("phi node has %d edges but %d predecessors", ne, np)
} else {
for i, e := range instr.Edges {
if e == nil {
s.errorf("phi node '%s' has no value for edge #%d from %s", instr.Comment, i, s.block.Preds[i])
}
}
}
case *Alloc:
if !instr.Heap {
found := false
for _, l := range s.fn.Locals {
if l == instr {
found = true
break
}
}
if !found {
s.errorf("local alloc %s = %s does not appear in Function.Locals", instr.Name(), instr)
}
}
case *BinOp:
case *Call:
case *ChangeInterface:
case *ChangeType:
case *Convert:
if _, ok := instr.X.Type().Underlying().(*types.Basic); !ok {
if _, ok := instr.Type().Underlying().(*types.Basic); !ok {
s.errorf("convert %s -> %s: at least one type must be basic", instr.X.Type(), instr.Type())
}
}
case *Defer:
case *Extract:
case *Field:
case *FieldAddr:
case *Go:
case *Index:
case *IndexAddr:
case *Lookup:
case *MakeChan:
case *MakeClosure:
numFree := len(instr.Fn.(*Function).FreeVars)
numBind := len(instr.Bindings)
if numFree != numBind {
s.errorf("MakeClosure has %d Bindings for function %s with %d free vars",
numBind, instr.Fn, numFree)
}
if recv := instr.Type().(*types.Signature).Recv(); recv != nil {
s.errorf("MakeClosure's type includes receiver %s", recv.Type())
}
case *MakeInterface:
case *MakeMap:
case *MakeSlice:
case *MapUpdate:
case *Next:
case *Range:
case *RunDefers:
case *Select:
case *Send:
case *Slice:
case *Store:
case *TypeAssert:
case *UnOp:
case *DebugRef:
// TODO(adonovan): implement checks.
default:
panic(fmt.Sprintf("Unknown instruction type: %T", instr))
}
if call, ok := instr.(CallInstruction); ok {
if call.Common().Signature() == nil {
s.errorf("nil signature: %s", call)
}
}
// Check that value-defining instructions have valid types
// and a valid referrer list.
if v, ok := instr.(Value); ok {
t := v.Type()
if t == nil {
s.errorf("no type: %s = %s", v.Name(), v)
} else if t == tRangeIter {
// not a proper type; ignore.
} else if b, ok := t.Underlying().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
s.errorf("instruction has 'untyped' result: %s = %s : %s", v.Name(), v, t)
}
s.checkReferrerList(v)
}
// Untyped constants are legal as instruction Operands(),
// for example:
// _ = "foo"[0]
// or:
// if wordsize==64 {...}
// All other non-Instruction Values can be found via their
// enclosing Function or Package.
}
func (s *sanity) checkFinalInstr(idx int, instr Instruction) {
switch instr := instr.(type) {
case *If:
if nsuccs := len(s.block.Succs); nsuccs != 2 {
s.errorf("If-terminated block has %d successors; expected 2", nsuccs)
return
}
if s.block.Succs[0] == s.block.Succs[1] {
s.errorf("If-instruction has same True, False target blocks: %s", s.block.Succs[0])
return
}
case *Jump:
if nsuccs := len(s.block.Succs); nsuccs != 1 {
s.errorf("Jump-terminated block has %d successors; expected 1", nsuccs)
return
}
case *Return:
if nsuccs := len(s.block.Succs); nsuccs != 0 {
s.errorf("Return-terminated block has %d successors; expected none", nsuccs)
return
}
if na, nf := len(instr.Results), s.fn.Signature.Results().Len(); nf != na {
s.errorf("%d-ary return in %d-ary function", na, nf)
}
case *Panic:
if nsuccs := len(s.block.Succs); nsuccs != 0 {
s.errorf("Panic-terminated block has %d successors; expected none", nsuccs)
return
}
default:
s.errorf("non-control flow instruction at end of block")
}
}
func (s *sanity) checkBlock(b *BasicBlock, index int) {
s.block = b
if b.Index != index {
s.errorf("block has incorrect Index %d", b.Index)
}
if b.parent != s.fn {
s.errorf("block has incorrect parent %s", b.parent)
}
// Check all blocks are reachable.
// (The entry block is always implicitly reachable,
// as is the Recover block, if any.)
if (index > 0 && b != b.parent.Recover) && len(b.Preds) == 0 {
s.warnf("unreachable block")
if b.Instrs == nil {
// Since this block is about to be pruned,
// tolerating transient problems in it
// simplifies other optimizations.
return
}
}
// Check predecessor and successor relations are dual,
// and that all blocks in CFG belong to same function.
for _, a := range b.Preds {
found := false
for _, bb := range a.Succs {
if bb == b {
found = true
break
}
}
if !found {
s.errorf("expected successor edge in predecessor %s; found only: %s", a, a.Succs)
}
if a.parent != s.fn {
s.errorf("predecessor %s belongs to different function %s", a, a.parent)
}
}
for _, c := range b.Succs {
found := false
for _, bb := range c.Preds {
if bb == b {
found = true
break
}
}
if !found {
s.errorf("expected predecessor edge in successor %s; found only: %s", c, c.Preds)
}
if c.parent != s.fn {
s.errorf("successor %s belongs to different function %s", c, c.parent)
}
}
// Check each instruction is sane.
n := len(b.Instrs)
if n == 0 {
s.errorf("basic block contains no instructions")
}
var rands [10]*Value // reuse storage
for j, instr := range b.Instrs {
if instr == nil {
s.errorf("nil instruction at index %d", j)
continue
}
if b2 := instr.Block(); b2 == nil {
s.errorf("nil Block() for instruction at index %d", j)
continue
} else if b2 != b {
s.errorf("wrong Block() (%s) for instruction at index %d ", b2, j)
continue
}
if j < n-1 {
s.checkInstr(j, instr)
} else {
s.checkFinalInstr(j, instr)
}
// Check Instruction.Operands.
operands:
for i, op := range instr.Operands(rands[:0]) {
if op == nil {
s.errorf("nil operand pointer %d of %s", i, instr)
continue
}
val := *op
if val == nil {
continue // a nil operand is ok
}
// Check that "untyped" types only appear on constant operands.
if _, ok := (*op).(*Const); !ok {
if basic, ok := (*op).Type().(*types.Basic); ok {
if basic.Info()&types.IsUntyped != 0 {
s.errorf("operand #%d of %s is untyped: %s", i, instr, basic)
}
}
}
// Check that Operands that are also Instructions belong to same function.
// TODO(adonovan): also check their block dominates block b.
if val, ok := val.(Instruction); ok {
if val.Parent() != s.fn {
s.errorf("operand %d of %s is an instruction (%s) from function %s", i, instr, val, val.Parent())
}
}
// Check that each function-local operand of
// instr refers back to instr. (NB: quadratic)
switch val := val.(type) {
case *Const, *Global, *Builtin:
continue // not local
case *Function:
if val.parent == nil {
continue // only anon functions are local
}
}
// TODO(adonovan): check val.Parent() != nil <=> val.Referrers() is defined.
if refs := val.Referrers(); refs != nil {
for _, ref := range *refs {
if ref == instr {
continue operands
}
}
s.errorf("operand %d of %s (%s) does not refer to us", i, instr, val)
} else {
s.errorf("operand %d of %s (%s) has no referrers", i, instr, val)
}
}
}
}
func (s *sanity) checkReferrerList(v Value) {
refs := v.Referrers()
if refs == nil {
s.errorf("%s has missing referrer list", v.Name())
return
}
for i, ref := range *refs {
if _, ok := s.instrs[ref]; !ok {
s.errorf("%s.Referrers()[%d] = %s is not an instruction belonging to this function", v.Name(), i, ref)
}
}
}
func (s *sanity) checkFunction(fn *Function) bool {
// TODO(adonovan): check Function invariants:
// - check params match signature
// - check transient fields are nil
// - warn if any fn.Locals do not appear among block instructions.
s.fn = fn
if fn.Prog == nil {
s.errorf("nil Prog")
}
fn.String() // must not crash
fn.RelString(fn.pkgobj()) // must not crash
// All functions have a package, except delegates (which are
// shared across packages, or duplicated as weak symbols in a
// separate-compilation model), and error.Error.
if fn.Pkg == nil {
if strings.HasPrefix(fn.Synthetic, "wrapper ") ||
strings.HasPrefix(fn.Synthetic, "bound ") ||
strings.HasPrefix(fn.Synthetic, "thunk ") ||
strings.HasSuffix(fn.name, "Error") {
// ok
} else {
s.errorf("nil Pkg")
}
}
if src, syn := fn.Synthetic == "", fn.Syntax() != nil; src != syn {
s.errorf("got fromSource=%t, hasSyntax=%t; want same values", src, syn)
}
for i, l := range fn.Locals {
if l.Parent() != fn {
s.errorf("Local %s at index %d has wrong parent", l.Name(), i)
}
if l.Heap {
s.errorf("Local %s at index %d has Heap flag set", l.Name(), i)
}
}
// Build the set of valid referrers.
s.instrs = make(map[Instruction]struct{})
for _, b := range fn.Blocks {
for _, instr := range b.Instrs {
s.instrs[instr] = struct{}{}
}
}
for i, p := range fn.Params {
if p.Parent() != fn {
s.errorf("Param %s at index %d has wrong parent", p.Name(), i)
}
s.checkReferrerList(p)
}
for i, fv := range fn.FreeVars {
if fv.Parent() != fn {
s.errorf("FreeVar %s at index %d has wrong parent", fv.Name(), i)
}
s.checkReferrerList(fv)
}
if fn.Blocks != nil && len(fn.Blocks) == 0 {
// Function _had_ blocks (so it's not external) but
// they were "optimized" away, even the entry block.
s.errorf("Blocks slice is non-nil but empty")
}
for i, b := range fn.Blocks {
if b == nil {
s.warnf("nil *BasicBlock at f.Blocks[%d]", i)
continue
}
s.checkBlock(b, i)
}
if fn.Recover != nil && fn.Blocks[fn.Recover.Index] != fn.Recover {
s.errorf("Recover block is not in Blocks slice")
}
s.block = nil
for i, anon := range fn.AnonFuncs {
if anon.Parent() != fn {
s.errorf("AnonFuncs[%d]=%s but %s.Parent()=%s", i, anon, anon, anon.Parent())
}
}
s.fn = nil
return !s.insane
}
// sanityCheckPackage checks invariants of packages upon creation.
// It does not require that the package is built.
// Unlike sanityCheck (for functions), it just panics at the first error.
func sanityCheckPackage(pkg *Package) {
if pkg.Object == nil {
panic(fmt.Sprintf("Package %s has no Object", pkg))
}
pkg.String() // must not crash
for name, mem := range pkg.Members {
if name != mem.Name() {
panic(fmt.Sprintf("%s: %T.Name() = %s, want %s",
pkg.Object.Path(), mem, mem.Name(), name))
}
obj := mem.Object()
if obj == nil {
// This check is sound because fields
// {Global,Function}.object have type
// types.Object. (If they were declared as
// *types.{Var,Func}, we'd have a non-empty
// interface containing a nil pointer.)
continue // not all members have typechecker objects
}
if obj.Name() != name {
if obj.Name() == "init" && strings.HasPrefix(mem.Name(), "init#") {
// Ok. The name of a declared init function varies between
// its types.Func ("init") and its ssa.Function ("init#%d").
} else {
panic(fmt.Sprintf("%s: %T.Object().Name() = %s, want %s",
pkg.Object.Path(), mem, obj.Name(), name))
}
}
if obj.Pos() != mem.Pos() {
panic(fmt.Sprintf("%s Pos=%d obj.Pos=%d", mem, mem.Pos(), obj.Pos()))
}
}
}

294
vendor/golang.org/x/tools/go/ssa/source.go generated vendored Normal file
View File

@ -0,0 +1,294 @@
// Copyright 2013 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 ssa
// This file defines utilities for working with source positions
// or source-level named entities ("objects").
// TODO(adonovan): test that {Value,Instruction}.Pos() positions match
// the originating syntax, as specified.
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/types"
)
// EnclosingFunction returns the function that contains the syntax
// node denoted by path.
//
// Syntax associated with package-level variable specifications is
// enclosed by the package's init() function.
//
// Returns nil if not found; reasons might include:
// - the node is not enclosed by any function.
// - the node is within an anonymous function (FuncLit) and
// its SSA function has not been created yet
// (pkg.Build() has not yet been called).
//
func EnclosingFunction(pkg *Package, path []ast.Node) *Function {
// Start with package-level function...
fn := findEnclosingPackageLevelFunction(pkg, path)
if fn == nil {
return nil // not in any function
}
// ...then walk down the nested anonymous functions.
n := len(path)
outer:
for i := range path {
if lit, ok := path[n-1-i].(*ast.FuncLit); ok {
for _, anon := range fn.AnonFuncs {
if anon.Pos() == lit.Type.Func {
fn = anon
continue outer
}
}
// SSA function not found:
// - package not yet built, or maybe
// - builder skipped FuncLit in dead block
// (in principle; but currently the Builder
// generates even dead FuncLits).
return nil
}
}
return fn
}
// HasEnclosingFunction returns true if the AST node denoted by path
// is contained within the declaration of some function or
// package-level variable.
//
// Unlike EnclosingFunction, the behaviour of this function does not
// depend on whether SSA code for pkg has been built, so it can be
// used to quickly reject check inputs that will cause
// EnclosingFunction to fail, prior to SSA building.
//
func HasEnclosingFunction(pkg *Package, path []ast.Node) bool {
return findEnclosingPackageLevelFunction(pkg, path) != nil
}
// findEnclosingPackageLevelFunction returns the Function
// corresponding to the package-level function enclosing path.
//
func findEnclosingPackageLevelFunction(pkg *Package, path []ast.Node) *Function {
if n := len(path); n >= 2 { // [... {Gen,Func}Decl File]
switch decl := path[n-2].(type) {
case *ast.GenDecl:
if decl.Tok == token.VAR && n >= 3 {
// Package-level 'var' initializer.
return pkg.init
}
case *ast.FuncDecl:
if decl.Recv == nil && decl.Name.Name == "init" {
// Explicit init() function.
for _, b := range pkg.init.Blocks {
for _, instr := range b.Instrs {
if instr, ok := instr.(*Call); ok {
if callee, ok := instr.Call.Value.(*Function); ok && callee.Pkg == pkg && callee.Pos() == decl.Name.NamePos {
return callee
}
}
}
}
// Hack: return non-nil when SSA is not yet
// built so that HasEnclosingFunction works.
return pkg.init
}
// Declared function/method.
return findNamedFunc(pkg, decl.Name.NamePos)
}
}
return nil // not in any function
}
// findNamedFunc returns the named function whose FuncDecl.Ident is at
// position pos.
//
func findNamedFunc(pkg *Package, pos token.Pos) *Function {
// Look at all package members and method sets of named types.
// Not very efficient.
for _, mem := range pkg.Members {
switch mem := mem.(type) {
case *Function:
if mem.Pos() == pos {
return mem
}
case *Type:
mset := pkg.Prog.MethodSets.MethodSet(types.NewPointer(mem.Type()))
for i, n := 0, mset.Len(); i < n; i++ {
// Don't call Program.Method: avoid creating wrappers.
obj := mset.At(i).Obj().(*types.Func)
if obj.Pos() == pos {
return pkg.values[obj].(*Function)
}
}
}
}
return nil
}
// ValueForExpr returns the SSA Value that corresponds to non-constant
// expression e.
//
// It returns nil if no value was found, e.g.
// - the expression is not lexically contained within f;
// - f was not built with debug information; or
// - e is a constant expression. (For efficiency, no debug
// information is stored for constants. Use
// go/types.Info.Types[e].Value instead.)
// - e is a reference to nil or a built-in function.
// - the value was optimised away.
//
// If e is an addressable expression used in an lvalue context,
// value is the address denoted by e, and isAddr is true.
//
// The types of e (or &e, if isAddr) and the result are equal
// (modulo "untyped" bools resulting from comparisons).
//
// (Tip: to find the ssa.Value given a source position, use
// importer.PathEnclosingInterval to locate the ast.Node, then
// EnclosingFunction to locate the Function, then ValueForExpr to find
// the ssa.Value.)
//
func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) {
if f.debugInfo() { // (opt)
e = unparen(e)
for _, b := range f.Blocks {
for _, instr := range b.Instrs {
if ref, ok := instr.(*DebugRef); ok {
if ref.Expr == e {
return ref.X, ref.IsAddr
}
}
}
}
}
return
}
// --- Lookup functions for source-level named entities (types.Objects) ---
// Package returns the SSA Package corresponding to the specified
// type-checker package object.
// It returns nil if no such SSA package has been created.
//
func (prog *Program) Package(obj *types.Package) *Package {
return prog.packages[obj]
}
// packageLevelValue returns the package-level value corresponding to
// the specified named object, which may be a package-level const
// (*Const), var (*Global) or func (*Function) of some package in
// prog. It returns nil if the object is not found.
//
func (prog *Program) packageLevelValue(obj types.Object) Value {
if pkg, ok := prog.packages[obj.Pkg()]; ok {
return pkg.values[obj]
}
return nil
}
// FuncValue returns the concrete Function denoted by the source-level
// named function obj, or nil if obj denotes an interface method.
//
// TODO(adonovan): check the invariant that obj.Type() matches the
// result's Signature, both in the params/results and in the receiver.
//
func (prog *Program) FuncValue(obj *types.Func) *Function {
fn, _ := prog.packageLevelValue(obj).(*Function)
return fn
}
// ConstValue returns the SSA Value denoted by the source-level named
// constant obj.
//
func (prog *Program) ConstValue(obj *types.Const) *Const {
// TODO(adonovan): opt: share (don't reallocate)
// Consts for const objects and constant ast.Exprs.
// Universal constant? {true,false,nil}
if obj.Parent() == types.Universe {
return NewConst(obj.Val(), obj.Type())
}
// Package-level named constant?
if v := prog.packageLevelValue(obj); v != nil {
return v.(*Const)
}
return NewConst(obj.Val(), obj.Type())
}
// VarValue returns the SSA Value that corresponds to a specific
// identifier denoting the source-level named variable obj.
//
// VarValue returns nil if a local variable was not found, perhaps
// because its package was not built, the debug information was not
// requested during SSA construction, or the value was optimized away.
//
// ref is the path to an ast.Ident (e.g. from PathEnclosingInterval),
// and that ident must resolve to obj.
//
// pkg is the package enclosing the reference. (A reference to a var
// always occurs within a function, so we need to know where to find it.)
//
// If the identifier is a field selector and its base expression is
// non-addressable, then VarValue returns the value of that field.
// For example:
// func f() struct {x int}
// f().x // VarValue(x) returns a *Field instruction of type int
//
// All other identifiers denote addressable locations (variables).
// For them, VarValue may return either the variable's address or its
// value, even when the expression is evaluated only for its value; the
// situation is reported by isAddr, the second component of the result.
//
// If !isAddr, the returned value is the one associated with the
// specific identifier. For example,
// var x int // VarValue(x) returns Const 0 here
// x = 1 // VarValue(x) returns Const 1 here
//
// It is not specified whether the value or the address is returned in
// any particular case, as it may depend upon optimizations performed
// during SSA code generation, such as registerization, constant
// folding, avoidance of materialization of subexpressions, etc.
//
func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) {
// All references to a var are local to some function, possibly init.
fn := EnclosingFunction(pkg, ref)
if fn == nil {
return // e.g. def of struct field; SSA not built?
}
id := ref[0].(*ast.Ident)
// Defining ident of a parameter?
if id.Pos() == obj.Pos() {
for _, param := range fn.Params {
if param.Object() == obj {
return param, false
}
}
}
// Other ident?
for _, b := range fn.Blocks {
for _, instr := range b.Instrs {
if dr, ok := instr.(*DebugRef); ok {
if dr.Pos() == id.Pos() {
return dr.X, dr.IsAddr
}
}
}
}
// Defining ident of package-level var?
if v := prog.packageLevelValue(obj); v != nil {
return v.(*Global), true
}
return // e.g. debug info not requested, or var optimized away
}

1700
vendor/golang.org/x/tools/go/ssa/ssa.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

95
vendor/golang.org/x/tools/go/ssa/ssautil/load.go generated vendored Normal file
View File

@ -0,0 +1,95 @@
// 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 ssautil
// This file defines utility functions for constructing programs in SSA form.
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
)
// CreateProgram returns a new program in SSA form, given a program
// loaded from source. An SSA package is created for each transitively
// error-free package of lprog.
//
// Code for bodies of functions is not built until BuildAll() is called
// on the result.
//
// mode controls diagnostics and checking during SSA construction.
//
func CreateProgram(lprog *loader.Program, mode ssa.BuilderMode) *ssa.Program {
prog := ssa.NewProgram(lprog.Fset, mode)
for _, info := range lprog.AllPackages {
if info.TransitivelyErrorFree {
prog.CreatePackage(info.Pkg, info.Files, &info.Info, info.Importable)
}
}
return prog
}
// BuildPackage builds an SSA program with IR for a single package.
//
// It populates pkg by type-checking the specified file ASTs. All
// dependencies are loaded using the importer specified by tc, which
// typically loads compiler export data; SSA code cannot be built for
// those packages. BuildPackage then constructs an ssa.Program with all
// dependency packages created, and builds and returns the SSA package
// corresponding to pkg.
//
// The caller must have set pkg.Path() to the import path.
//
// The operation fails if there were any type-checking or import errors.
//
// See ../ssa/example_test.go for an example.
//
func BuildPackage(tc *types.Config, fset *token.FileSet, pkg *types.Package, files []*ast.File, mode ssa.BuilderMode) (*ssa.Package, *types.Info, error) {
if fset == nil {
panic("no token.FileSet")
}
if pkg.Path() == "" {
panic("package has no import path")
}
info := &types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
}
if err := types.NewChecker(tc, fset, pkg, info).Files(files); err != nil {
return nil, nil, err
}
prog := ssa.NewProgram(fset, mode)
// Create SSA packages for all imports.
// Order is not significant.
created := make(map[*types.Package]bool)
var createAll func(pkgs []*types.Package)
createAll = func(pkgs []*types.Package) {
for _, p := range pkgs {
if !created[p] {
created[p] = true
prog.CreatePackage(p, nil, nil, true)
createAll(p.Imports())
}
}
}
createAll(pkg.Imports())
// Create and build the primary package.
ssapkg := prog.CreatePackage(pkg, files, info, false)
ssapkg.Build()
return ssapkg, info, nil
}

234
vendor/golang.org/x/tools/go/ssa/ssautil/switch.go generated vendored Normal file
View File

@ -0,0 +1,234 @@
// Copyright 2013 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 ssautil
// This file implements discovery of switch and type-switch constructs
// from low-level control flow.
//
// Many techniques exist for compiling a high-level switch with
// constant cases to efficient machine code. The optimal choice will
// depend on the data type, the specific case values, the code in the
// body of each case, and the hardware.
// Some examples:
// - a lookup table (for a switch that maps constants to constants)
// - a computed goto
// - a binary tree
// - a perfect hash
// - a two-level switch (to partition constant strings by their first byte).
import (
"bytes"
"fmt"
"go/token"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/types"
)
// A ConstCase represents a single constant comparison.
// It is part of a Switch.
type ConstCase struct {
Block *ssa.BasicBlock // block performing the comparison
Body *ssa.BasicBlock // body of the case
Value *ssa.Const // case comparand
}
// A TypeCase represents a single type assertion.
// It is part of a Switch.
type TypeCase struct {
Block *ssa.BasicBlock // block performing the type assert
Body *ssa.BasicBlock // body of the case
Type types.Type // case type
Binding ssa.Value // value bound by this case
}
// A Switch is a logical high-level control flow operation
// (a multiway branch) discovered by analysis of a CFG containing
// only if/else chains. It is not part of the ssa.Instruction set.
//
// One of ConstCases and TypeCases has length >= 2;
// the other is nil.
//
// In a value switch, the list of cases may contain duplicate constants.
// A type switch may contain duplicate types, or types assignable
// to an interface type also in the list.
// TODO(adonovan): eliminate such duplicates.
//
type Switch struct {
Start *ssa.BasicBlock // block containing start of if/else chain
X ssa.Value // the switch operand
ConstCases []ConstCase // ordered list of constant comparisons
TypeCases []TypeCase // ordered list of type assertions
Default *ssa.BasicBlock // successor if all comparisons fail
}
func (sw *Switch) String() string {
// We represent each block by the String() of its
// first Instruction, e.g. "print(42:int)".
var buf bytes.Buffer
if sw.ConstCases != nil {
fmt.Fprintf(&buf, "switch %s {\n", sw.X.Name())
for _, c := range sw.ConstCases {
fmt.Fprintf(&buf, "case %s: %s\n", c.Value, c.Body.Instrs[0])
}
} else {
fmt.Fprintf(&buf, "switch %s.(type) {\n", sw.X.Name())
for _, c := range sw.TypeCases {
fmt.Fprintf(&buf, "case %s %s: %s\n",
c.Binding.Name(), c.Type, c.Body.Instrs[0])
}
}
if sw.Default != nil {
fmt.Fprintf(&buf, "default: %s\n", sw.Default.Instrs[0])
}
fmt.Fprintf(&buf, "}")
return buf.String()
}
// Switches examines the control-flow graph of fn and returns the
// set of inferred value and type switches. A value switch tests an
// ssa.Value for equality against two or more compile-time constant
// values. Switches involving link-time constants (addresses) are
// ignored. A type switch type-asserts an ssa.Value against two or
// more types.
//
// The switches are returned in dominance order.
//
// The resulting switches do not necessarily correspond to uses of the
// 'switch' keyword in the source: for example, a single source-level
// switch statement with non-constant cases may result in zero, one or
// many Switches, one per plural sequence of constant cases.
// Switches may even be inferred from if/else- or goto-based control flow.
// (In general, the control flow constructs of the source program
// cannot be faithfully reproduced from the SSA representation.)
//
func Switches(fn *ssa.Function) []Switch {
// Traverse the CFG in dominance order, so we don't
// enter an if/else-chain in the middle.
var switches []Switch
seen := make(map[*ssa.BasicBlock]bool) // TODO(adonovan): opt: use ssa.blockSet
for _, b := range fn.DomPreorder() {
if x, k := isComparisonBlock(b); x != nil {
// Block b starts a switch.
sw := Switch{Start: b, X: x}
valueSwitch(&sw, k, seen)
if len(sw.ConstCases) > 1 {
switches = append(switches, sw)
}
}
if y, x, T := isTypeAssertBlock(b); y != nil {
// Block b starts a type switch.
sw := Switch{Start: b, X: x}
typeSwitch(&sw, y, T, seen)
if len(sw.TypeCases) > 1 {
switches = append(switches, sw)
}
}
}
return switches
}
func valueSwitch(sw *Switch, k *ssa.Const, seen map[*ssa.BasicBlock]bool) {
b := sw.Start
x := sw.X
for x == sw.X {
if seen[b] {
break
}
seen[b] = true
sw.ConstCases = append(sw.ConstCases, ConstCase{
Block: b,
Body: b.Succs[0],
Value: k,
})
b = b.Succs[1]
if len(b.Instrs) > 2 {
// Block b contains not just 'if x == k',
// so it may have side effects that
// make it unsafe to elide.
break
}
if len(b.Preds) != 1 {
// Block b has multiple predecessors,
// so it cannot be treated as a case.
break
}
x, k = isComparisonBlock(b)
}
sw.Default = b
}
func typeSwitch(sw *Switch, y ssa.Value, T types.Type, seen map[*ssa.BasicBlock]bool) {
b := sw.Start
x := sw.X
for x == sw.X {
if seen[b] {
break
}
seen[b] = true
sw.TypeCases = append(sw.TypeCases, TypeCase{
Block: b,
Body: b.Succs[0],
Type: T,
Binding: y,
})
b = b.Succs[1]
if len(b.Instrs) > 4 {
// Block b contains not just
// {TypeAssert; Extract #0; Extract #1; If}
// so it may have side effects that
// make it unsafe to elide.
break
}
if len(b.Preds) != 1 {
// Block b has multiple predecessors,
// so it cannot be treated as a case.
break
}
y, x, T = isTypeAssertBlock(b)
}
sw.Default = b
}
// isComparisonBlock returns the operands (v, k) if a block ends with
// a comparison v==k, where k is a compile-time constant.
//
func isComparisonBlock(b *ssa.BasicBlock) (v ssa.Value, k *ssa.Const) {
if n := len(b.Instrs); n >= 2 {
if i, ok := b.Instrs[n-1].(*ssa.If); ok {
if binop, ok := i.Cond.(*ssa.BinOp); ok && binop.Block() == b && binop.Op == token.EQL {
if k, ok := binop.Y.(*ssa.Const); ok {
return binop.X, k
}
if k, ok := binop.X.(*ssa.Const); ok {
return binop.Y, k
}
}
}
}
return
}
// isTypeAssertBlock returns the operands (y, x, T) if a block ends with
// a type assertion "if y, ok := x.(T); ok {".
//
func isTypeAssertBlock(b *ssa.BasicBlock) (y, x ssa.Value, T types.Type) {
if n := len(b.Instrs); n >= 4 {
if i, ok := b.Instrs[n-1].(*ssa.If); ok {
if ext1, ok := i.Cond.(*ssa.Extract); ok && ext1.Block() == b && ext1.Index == 1 {
if ta, ok := ext1.Tuple.(*ssa.TypeAssert); ok && ta.Block() == b {
// hack: relies upon instruction ordering.
if ext0, ok := b.Instrs[n-3].(*ssa.Extract); ok {
return ext0, ta.X, ta.AssertedType
}
}
}
}
}
return
}

66
vendor/golang.org/x/tools/go/ssa/ssautil/visit.go generated vendored Normal file
View File

@ -0,0 +1,66 @@
// Copyright 2013 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 ssautil // import "golang.org/x/tools/go/ssa/ssautil"
import "golang.org/x/tools/go/ssa"
// This file defines utilities for visiting the SSA representation of
// a Program.
//
// TODO(adonovan): test coverage.
// AllFunctions finds and returns the set of functions potentially
// needed by program prog, as determined by a simple linker-style
// reachability algorithm starting from the members and method-sets of
// each package. The result may include anonymous functions and
// synthetic wrappers.
//
// Precondition: all packages are built.
//
func AllFunctions(prog *ssa.Program) map[*ssa.Function]bool {
visit := visitor{
prog: prog,
seen: make(map[*ssa.Function]bool),
}
visit.program()
return visit.seen
}
type visitor struct {
prog *ssa.Program
seen map[*ssa.Function]bool
}
func (visit *visitor) program() {
for _, pkg := range visit.prog.AllPackages() {
for _, mem := range pkg.Members {
if fn, ok := mem.(*ssa.Function); ok {
visit.function(fn)
}
}
}
for _, T := range visit.prog.RuntimeTypes() {
mset := visit.prog.MethodSets.MethodSet(T)
for i, n := 0, mset.Len(); i < n; i++ {
visit.function(visit.prog.Method(mset.At(i)))
}
}
}
func (visit *visitor) function(fn *ssa.Function) {
if !visit.seen[fn] {
visit.seen[fn] = true
var buf [10]*ssa.Value // avoid alloc in common case
for _, b := range fn.Blocks {
for _, instr := range b.Instrs {
for _, op := range instr.Operands(buf[:0]) {
if fn, ok := (*op).(*ssa.Function); ok {
visit.function(fn)
}
}
}
}
}
}

296
vendor/golang.org/x/tools/go/ssa/testmain.go generated vendored Normal file
View File

@ -0,0 +1,296 @@
// Copyright 2013 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 ssa
// CreateTestMainPackage synthesizes a main package that runs all the
// tests of the supplied packages.
// It is closely coupled to $GOROOT/src/cmd/go/test.go and $GOROOT/src/testing.
import (
"go/ast"
"go/token"
"os"
"sort"
"strings"
"golang.org/x/tools/go/exact"
"golang.org/x/tools/go/types"
)
// FindTests returns the list of packages that define at least one Test,
// Example or Benchmark function (as defined by "go test"), and the
// lists of all such functions.
//
func FindTests(pkgs []*Package) (testpkgs []*Package, tests, benchmarks, examples []*Function) {
if len(pkgs) == 0 {
return
}
prog := pkgs[0].Prog
// The first two of these may be nil: if the program doesn't import "testing",
// it can't contain any tests, but it may yet contain Examples.
var testSig *types.Signature // func(*testing.T)
var benchmarkSig *types.Signature // func(*testing.B)
var exampleSig = types.NewSignature(nil, nil, nil, false) // func()
// Obtain the types from the parameters of testing.Main().
if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil {
params := testingPkg.Func("Main").Signature.Params()
testSig = funcField(params.At(1).Type())
benchmarkSig = funcField(params.At(2).Type())
}
seen := make(map[*Package]bool)
for _, pkg := range pkgs {
if pkg.Prog != prog {
panic("wrong Program")
}
// TODO(adonovan): use a stable order, e.g. lexical.
for _, mem := range pkg.Members {
if f, ok := mem.(*Function); ok &&
ast.IsExported(f.Name()) &&
strings.HasSuffix(prog.Fset.Position(f.Pos()).Filename, "_test.go") {
switch {
case testSig != nil && isTestSig(f, "Test", testSig):
tests = append(tests, f)
case benchmarkSig != nil && isTestSig(f, "Benchmark", benchmarkSig):
benchmarks = append(benchmarks, f)
case isTestSig(f, "Example", exampleSig):
examples = append(examples, f)
default:
continue
}
if !seen[pkg] {
seen[pkg] = true
testpkgs = append(testpkgs, pkg)
}
}
}
}
return
}
// Like isTest, but checks the signature too.
func isTestSig(f *Function, prefix string, sig *types.Signature) bool {
return isTest(f.Name(), prefix) && types.Identical(f.Signature, sig)
}
// If non-nil, testMainStartBodyHook is called immediately after
// startBody for main.init and main.main, making it easy for users to
// add custom imports and initialization steps for proprietary build
// systems that don't exactly follow 'go test' conventions.
var testMainStartBodyHook func(*Function)
// CreateTestMainPackage creates and returns a synthetic "main"
// package that runs all the tests of the supplied packages, similar
// to the one that would be created by the 'go test' tool.
//
// It returns nil if the program contains no tests.
//
func (prog *Program) CreateTestMainPackage(pkgs ...*Package) *Package {
pkgs, tests, benchmarks, examples := FindTests(pkgs)
if len(pkgs) == 0 {
return nil
}
testmain := &Package{
Prog: prog,
Members: make(map[string]Member),
values: make(map[types.Object]Value),
Object: types.NewPackage("test$main", "main"),
}
// Build package's init function.
init := &Function{
name: "init",
Signature: new(types.Signature),
Synthetic: "package initializer",
Pkg: testmain,
Prog: prog,
}
init.startBody()
if testMainStartBodyHook != nil {
testMainStartBodyHook(init)
}
// Initialize packages to test.
var pkgpaths []string
for _, pkg := range pkgs {
var v Call
v.Call.Value = pkg.init
v.setType(types.NewTuple())
init.emit(&v)
pkgpaths = append(pkgpaths, pkg.Object.Path())
}
sort.Strings(pkgpaths)
init.emit(new(Return))
init.finishBody()
testmain.init = init
testmain.Object.MarkComplete()
testmain.Members[init.name] = init
// For debugging convenience, define an unexported const
// that enumerates the packages.
packagesConst := types.NewConst(token.NoPos, testmain.Object, "packages", tString,
exact.MakeString(strings.Join(pkgpaths, " ")))
memberFromObject(testmain, packagesConst, nil)
// Create main *types.Func and *ssa.Function
mainFunc := types.NewFunc(token.NoPos, testmain.Object, "main", new(types.Signature))
memberFromObject(testmain, mainFunc, nil)
main := testmain.Func("main")
main.Synthetic = "test main function"
main.startBody()
if testMainStartBodyHook != nil {
testMainStartBodyHook(main)
}
if testingPkg := prog.ImportedPackage("testing"); testingPkg != nil {
testingMain := testingPkg.Func("Main")
testingMainParams := testingMain.Signature.Params()
// The generated code is as if compiled from this:
//
// func main() {
// match := func(_, _ string) (bool, error) { return true, nil }
// tests := []testing.InternalTest{{"TestFoo", TestFoo}, ...}
// benchmarks := []testing.InternalBenchmark{...}
// examples := []testing.InternalExample{...}
// testing.Main(match, tests, benchmarks, examples)
// }
matcher := &Function{
name: "matcher",
Signature: testingMainParams.At(0).Type().(*types.Signature),
Synthetic: "test matcher predicate",
parent: main,
Pkg: testmain,
Prog: prog,
}
main.AnonFuncs = append(main.AnonFuncs, matcher)
matcher.startBody()
matcher.emit(&Return{Results: []Value{vTrue, nilConst(types.Universe.Lookup("error").Type())}})
matcher.finishBody()
// Emit call: testing.Main(matcher, tests, benchmarks, examples).
var c Call
c.Call.Value = testingMain
c.Call.Args = []Value{
matcher,
testMainSlice(main, tests, testingMainParams.At(1).Type()),
testMainSlice(main, benchmarks, testingMainParams.At(2).Type()),
testMainSlice(main, examples, testingMainParams.At(3).Type()),
}
emitTailCall(main, &c)
} else {
// The program does not import "testing", but FindTests
// returned non-nil, which must mean there were Examples
// but no Tests or Benchmarks.
// We'll simply call them from testmain.main; this will
// ensure they don't panic, but will not check any
// "Output:" comments.
for _, eg := range examples {
var c Call
c.Call.Value = eg
c.setType(types.NewTuple())
main.emit(&c)
}
main.emit(&Return{})
main.currentBlock = nil
}
main.finishBody()
testmain.Members["main"] = main
if prog.mode&PrintPackages != 0 {
printMu.Lock()
testmain.WriteTo(os.Stdout)
printMu.Unlock()
}
if prog.mode&SanityCheckFunctions != 0 {
sanityCheckPackage(testmain)
}
prog.packages[testmain.Object] = testmain
return testmain
}
// testMainSlice emits to fn code to construct a slice of type slice
// (one of []testing.Internal{Test,Benchmark,Example}) for all
// functions in testfuncs. It returns the slice value.
//
func testMainSlice(fn *Function, testfuncs []*Function, slice types.Type) Value {
if testfuncs == nil {
return nilConst(slice)
}
tElem := slice.(*types.Slice).Elem()
tPtrString := types.NewPointer(tString)
tPtrElem := types.NewPointer(tElem)
tPtrFunc := types.NewPointer(funcField(slice))
// Emit: array = new [n]testing.InternalTest
tArray := types.NewArray(tElem, int64(len(testfuncs)))
array := emitNew(fn, tArray, token.NoPos)
array.Comment = "test main"
for i, testfunc := range testfuncs {
// Emit: pitem = &array[i]
ia := &IndexAddr{X: array, Index: intConst(int64(i))}
ia.setType(tPtrElem)
pitem := fn.emit(ia)
// Emit: pname = &pitem.Name
fa := &FieldAddr{X: pitem, Field: 0} // .Name
fa.setType(tPtrString)
pname := fn.emit(fa)
// Emit: *pname = "testfunc"
emitStore(fn, pname, stringConst(testfunc.Name()), token.NoPos)
// Emit: pfunc = &pitem.F
fa = &FieldAddr{X: pitem, Field: 1} // .F
fa.setType(tPtrFunc)
pfunc := fn.emit(fa)
// Emit: *pfunc = testfunc
emitStore(fn, pfunc, testfunc, token.NoPos)
}
// Emit: slice array[:]
sl := &Slice{X: array}
sl.setType(slice)
return fn.emit(sl)
}
// Given the type of one of the three slice parameters of testing.Main,
// returns the function type.
func funcField(slice types.Type) *types.Signature {
return slice.(*types.Slice).Elem().Underlying().(*types.Struct).Field(1).Type().(*types.Signature)
}
// Plundered from $GOROOT/src/cmd/go/test.go
// isTest tells whether name looks like a test (or benchmark, according to prefix).
// It is a Test (say) if there is a character after Test that is not a lower-case letter.
// We don't want TesticularCancer.
func isTest(name, prefix string) bool {
if !strings.HasPrefix(name, prefix) {
return false
}
if len(name) == len(prefix) { // "Test" is ok
return true
}
return ast.IsExported(name[len(prefix):])
}

119
vendor/golang.org/x/tools/go/ssa/util.go generated vendored Normal file
View File

@ -0,0 +1,119 @@
// Copyright 2013 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 ssa
// This file defines a number of miscellaneous utility functions.
import (
"fmt"
"go/ast"
"go/token"
"io"
"os"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/types"
)
//// AST utilities
func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
// isBlankIdent returns true iff e is an Ident with name "_".
// They have no associated types.Object, and thus no type.
//
func isBlankIdent(e ast.Expr) bool {
id, ok := e.(*ast.Ident)
return ok && id.Name == "_"
}
//// Type utilities. Some of these belong in go/types.
// isPointer returns true for types whose underlying type is a pointer.
func isPointer(typ types.Type) bool {
_, ok := typ.Underlying().(*types.Pointer)
return ok
}
func isInterface(T types.Type) bool { return types.IsInterface(T) }
// deref returns a pointer's element type; otherwise it returns typ.
func deref(typ types.Type) types.Type {
if p, ok := typ.Underlying().(*types.Pointer); ok {
return p.Elem()
}
return typ
}
// recvType returns the receiver type of method obj.
func recvType(obj *types.Func) types.Type {
return obj.Type().(*types.Signature).Recv().Type()
}
// DefaultType returns the default "typed" type for an "untyped" type;
// it returns the incoming type for all other types. The default type
// for untyped nil is untyped nil.
//
// Exported to ssa/interp.
//
// TODO(gri): this is a copy of go/types.defaultType; export that function.
//
func DefaultType(typ types.Type) types.Type {
if t, ok := typ.(*types.Basic); ok {
k := t.Kind()
switch k {
case types.UntypedBool:
k = types.Bool
case types.UntypedInt:
k = types.Int
case types.UntypedRune:
k = types.Rune
case types.UntypedFloat:
k = types.Float64
case types.UntypedComplex:
k = types.Complex128
case types.UntypedString:
k = types.String
}
typ = types.Typ[k]
}
return typ
}
// logStack prints the formatted "start" message to stderr and
// returns a closure that prints the corresponding "end" message.
// Call using 'defer logStack(...)()' to show builder stack on panic.
// Don't forget trailing parens!
//
func logStack(format string, args ...interface{}) func() {
msg := fmt.Sprintf(format, args...)
io.WriteString(os.Stderr, msg)
io.WriteString(os.Stderr, "\n")
return func() {
io.WriteString(os.Stderr, msg)
io.WriteString(os.Stderr, " end\n")
}
}
// newVar creates a 'var' for use in a types.Tuple.
func newVar(name string, typ types.Type) *types.Var {
return types.NewParam(token.NoPos, nil, name, typ)
}
// anonVar creates an anonymous 'var' for use in a types.Tuple.
func anonVar(typ types.Type) *types.Var {
return newVar("", typ)
}
var lenResults = types.NewTuple(anonVar(tInt))
// makeLen returns the len builtin specialized to type func(T)int.
func makeLen(T types.Type) *Builtin {
lenParams := types.NewTuple(anonVar(T))
return &Builtin{
name: "len",
sig: types.NewSignature(nil, lenParams, lenResults, false),
}
}

294
vendor/golang.org/x/tools/go/ssa/wrappers.go generated vendored Normal file
View File

@ -0,0 +1,294 @@
// Copyright 2013 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 ssa
// This file defines synthesis of Functions that delegate to declared
// methods; they come in three kinds:
//
// (1) wrappers: methods that wrap declared methods, performing
// implicit pointer indirections and embedded field selections.
//
// (2) thunks: funcs that wrap declared methods. Like wrappers,
// thunks perform indirections and field selections. The thunk's
// first parameter is used as the receiver for the method call.
//
// (3) bounds: funcs that wrap declared methods. The bound's sole
// free variable, supplied by a closure, is used as the receiver
// for the method call. No indirections or field selections are
// performed since they can be done before the call.
import (
"fmt"
"golang.org/x/tools/go/types"
)
// -- wrappers -----------------------------------------------------------
// makeWrapper returns a synthetic method that delegates to the
// declared method denoted by meth.Obj(), first performing any
// necessary pointer indirections or field selections implied by meth.
//
// The resulting method's receiver type is meth.Recv().
//
// This function is versatile but quite subtle! Consider the
// following axes of variation when making changes:
// - optional receiver indirection
// - optional implicit field selections
// - meth.Obj() may denote a concrete or an interface method
// - the result may be a thunk or a wrapper.
//
// EXCLUSIVE_LOCKS_REQUIRED(prog.methodsMu)
//
func makeWrapper(prog *Program, sel *types.Selection) *Function {
obj := sel.Obj().(*types.Func) // the declared function
sig := sel.Type().(*types.Signature) // type of this wrapper
var recv *types.Var // wrapper's receiver or thunk's params[0]
name := obj.Name()
var description string
var start int // first regular param
if sel.Kind() == types.MethodExpr {
name += "$thunk"
description = "thunk"
recv = sig.Params().At(0)
start = 1
} else {
description = "wrapper"
recv = sig.Recv()
}
description = fmt.Sprintf("%s for %s", description, sel.Obj())
if prog.mode&LogSource != 0 {
defer logStack("make %s to (%s)", description, recv.Type())()
}
fn := &Function{
name: name,
method: sel,
object: obj,
Signature: sig,
Synthetic: description,
Prog: prog,
pos: obj.Pos(),
}
fn.startBody()
fn.addSpilledParam(recv)
createParams(fn, start)
indices := sel.Index()
var v Value = fn.Locals[0] // spilled receiver
if isPointer(sel.Recv()) {
v = emitLoad(fn, v)
// For simple indirection wrappers, perform an informative nil-check:
// "value method (T).f called using nil *T pointer"
if len(indices) == 1 && !isPointer(recvType(obj)) {
var c Call
c.Call.Value = &Builtin{
name: "ssa:wrapnilchk",
sig: types.NewSignature(nil,
types.NewTuple(anonVar(sel.Recv()), anonVar(tString), anonVar(tString)),
types.NewTuple(anonVar(sel.Recv())), false),
}
c.Call.Args = []Value{
v,
stringConst(deref(sel.Recv()).String()),
stringConst(sel.Obj().Name()),
}
c.setType(v.Type())
v = fn.emit(&c)
}
}
// Invariant: v is a pointer, either
// value of *A receiver param, or
// address of A spilled receiver.
// We use pointer arithmetic (FieldAddr possibly followed by
// Load) in preference to value extraction (Field possibly
// preceded by Load).
v = emitImplicitSelections(fn, v, indices[:len(indices)-1])
// Invariant: v is a pointer, either
// value of implicit *C field, or
// address of implicit C field.
var c Call
if r := recvType(obj); !isInterface(r) { // concrete method
if !isPointer(r) {
v = emitLoad(fn, v)
}
c.Call.Value = prog.declaredFunc(obj)
c.Call.Args = append(c.Call.Args, v)
} else {
c.Call.Method = obj
c.Call.Value = emitLoad(fn, v)
}
for _, arg := range fn.Params[1:] {
c.Call.Args = append(c.Call.Args, arg)
}
emitTailCall(fn, &c)
fn.finishBody()
return fn
}
// createParams creates parameters for wrapper method fn based on its
// Signature.Params, which do not include the receiver.
// start is the index of the first regular parameter to use.
//
func createParams(fn *Function, start int) {
var last *Parameter
tparams := fn.Signature.Params()
for i, n := start, tparams.Len(); i < n; i++ {
last = fn.addParamObj(tparams.At(i))
}
if fn.Signature.Variadic() {
last.typ = types.NewSlice(last.typ)
}
}
// -- bounds -----------------------------------------------------------
// makeBound returns a bound method wrapper (or "bound"), a synthetic
// function that delegates to a concrete or interface method denoted
// by obj. The resulting function has no receiver, but has one free
// variable which will be used as the method's receiver in the
// tail-call.
//
// Use MakeClosure with such a wrapper to construct a bound method
// closure. e.g.:
//
// type T int or: type T interface { meth() }
// func (t T) meth()
// var t T
// f := t.meth
// f() // calls t.meth()
//
// f is a closure of a synthetic wrapper defined as if by:
//
// f := func() { return t.meth() }
//
// Unlike makeWrapper, makeBound need perform no indirection or field
// selections because that can be done before the closure is
// constructed.
//
// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
//
func makeBound(prog *Program, obj *types.Func) *Function {
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
fn, ok := prog.bounds[obj]
if !ok {
description := fmt.Sprintf("bound method wrapper for %s", obj)
if prog.mode&LogSource != 0 {
defer logStack("%s", description)()
}
fn = &Function{
name: obj.Name() + "$bound",
object: obj,
Signature: changeRecv(obj.Type().(*types.Signature), nil), // drop receiver
Synthetic: description,
Prog: prog,
pos: obj.Pos(),
}
fv := &FreeVar{name: "recv", typ: recvType(obj), parent: fn}
fn.FreeVars = []*FreeVar{fv}
fn.startBody()
createParams(fn, 0)
var c Call
if !isInterface(recvType(obj)) { // concrete
c.Call.Value = prog.declaredFunc(obj)
c.Call.Args = []Value{fv}
} else {
c.Call.Value = fv
c.Call.Method = obj
}
for _, arg := range fn.Params {
c.Call.Args = append(c.Call.Args, arg)
}
emitTailCall(fn, &c)
fn.finishBody()
prog.bounds[obj] = fn
}
return fn
}
// -- thunks -----------------------------------------------------------
// makeThunk returns a thunk, a synthetic function that delegates to a
// concrete or interface method denoted by sel.Obj(). The resulting
// function has no receiver, but has an additional (first) regular
// parameter.
//
// Precondition: sel.Kind() == types.MethodExpr.
//
// type T int or: type T interface { meth() }
// func (t T) meth()
// f := T.meth
// var t T
// f(t) // calls t.meth()
//
// f is a synthetic wrapper defined as if by:
//
// f := func(t T) { return t.meth() }
//
// TODO(adonovan): opt: currently the stub is created even when used
// directly in a function call: C.f(i, 0). This is less efficient
// than inlining the stub.
//
// EXCLUSIVE_LOCKS_ACQUIRED(meth.Prog.methodsMu)
//
func makeThunk(prog *Program, sel *types.Selection) *Function {
if sel.Kind() != types.MethodExpr {
panic(sel)
}
key := selectionKey{
kind: sel.Kind(),
recv: sel.Recv(),
obj: sel.Obj(),
index: fmt.Sprint(sel.Index()),
indirect: sel.Indirect(),
}
prog.methodsMu.Lock()
defer prog.methodsMu.Unlock()
// Canonicalize key.recv to avoid constructing duplicate thunks.
canonRecv, ok := prog.canon.At(key.recv).(types.Type)
if !ok {
canonRecv = key.recv
prog.canon.Set(key.recv, canonRecv)
}
key.recv = canonRecv
fn, ok := prog.thunks[key]
if !ok {
fn = makeWrapper(prog, sel)
if fn.Signature.Recv() != nil {
panic(fn) // unexpected receiver
}
prog.thunks[key] = fn
}
return fn
}
func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
return types.NewSignature(recv, s.Params(), s.Results(), s.Variadic())
}
// selectionKey is like types.Selection but a usable map key.
type selectionKey struct {
kind types.SelectionKind
recv types.Type // canonicalized via Program.canon
obj types.Object
index string
indirect bool
}

365
vendor/golang.org/x/tools/go/types/api.go generated vendored Normal file
View File

@ -0,0 +1,365 @@
// Copyright 2012 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 declares the data types and implements
// the algorithms for type-checking of Go packages.
// Use Check and Config.Check to invoke the type-checker.
//
// Type-checking consists of several interdependent phases:
//
// Name resolution maps each identifier (ast.Ident) in the program to the
// language object (Object) it denotes.
// Use Info.{Defs,Uses,Implicits} for the results of name resolution.
//
// Constant folding computes the exact constant value (exact.Value) for
// every expression (ast.Expr) that is a compile-time constant.
// Use Info.Types[expr].Value for the results of constant folding.
//
// Type inference computes the type (Type) of every expression (ast.Expr)
// and checks for compliance with the language specification.
// Use Info.Types[expr].Type for the results of type inference.
//
package types // import "golang.org/x/tools/go/types"
import (
"bytes"
"fmt"
"go/ast"
"go/token"
"golang.org/x/tools/go/exact"
)
// Check type-checks a package and returns the resulting complete package
// object, or a nil package and the first error. The package is specified
// by a list of *ast.Files and corresponding file set, and the import path
// the package is identified with. The clean path must not be empty or dot (".").
//
// For more control over type-checking and results, use Config.Check.
func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error) {
var conf Config
pkg, err := conf.Check(path, fset, files, nil)
if err != nil {
return nil, err
}
return pkg, nil
}
// An Error describes a type-checking error; it implements the error interface.
// A "soft" error is an error that still permits a valid interpretation of a
// package (such as "unused variable"); "hard" errors may lead to unpredictable
// behavior if ignored.
type Error struct {
Fset *token.FileSet // file set for interpretation of Pos
Pos token.Pos // error position
Msg string // error message
Soft bool // if set, error is "soft"
}
// Error returns an error string formatted as follows:
// filename:line:column: message
func (err Error) Error() string {
return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg)
}
// An importer resolves import paths to Packages.
// The imports map records packages already known,
// indexed by package path. The type-checker
// will invoke Import with Config.Packages.
// An importer must determine the canonical package path and
// check imports to see if it is already present in the map.
// If so, the Importer can return the map entry. Otherwise,
// the importer must load the package data for the given path
// into a new *Package, record it in imports map, and return
// the package.
// TODO(gri) Need to be clearer about requirements of completeness.
type Importer func(map[string]*Package, string) (*Package, error)
// A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
// If IgnoreFuncBodies is set, function bodies are not
// type-checked.
IgnoreFuncBodies bool
// If FakeImportC is set, `import "C"` (for packages requiring Cgo)
// declares an empty "C" package and errors are omitted for qualified
// identifiers referring to package C (which won't find an object).
// This feature is intended for the standard library cmd/api tool.
//
// Caution: Effects may be unpredictable due to follow-up errors.
// Do not use casually!
FakeImportC bool
// Packages is used to look up (and thus canonicalize) packages by
// package path. If Packages is nil, it is set to a new empty map.
// During type-checking, imported packages are added to the map.
Packages map[string]*Package
// If Error != nil, it is called with each error found
// during type checking; err has dynamic type Error.
// Secondary errors (for instance, to enumerate all types
// involved in an invalid recursive type declaration) have
// error strings that start with a '\t' character.
// If Error == nil, type-checking stops with the first
// error found.
Error func(err error)
// If Import != nil, it is called for each imported package.
// Otherwise, DefaultImport is called.
Import Importer
// If Sizes != nil, it provides the sizing functions for package unsafe.
// Otherwise &StdSizes{WordSize: 8, MaxAlign: 8} is used instead.
Sizes Sizes
// If DisableUnusedImportCheck is set, packages are not checked
// for unused imports.
DisableUnusedImportCheck bool
}
// DefaultImport is the default importer invoked if Config.Import == nil.
// The declaration:
//
// import _ "golang.org/x/tools/go/gcimporter"
//
// in a client of go/types will initialize DefaultImport to gcimporter.Import.
var DefaultImport Importer
// Info holds result type information for a type-checked package.
// Only the information for which a map is provided is collected.
// If the package has type errors, the collected information may
// be incomplete.
type Info struct {
// Types maps expressions to their types, and for constant
// expressions, their values. Invalid expressions are omitted.
//
// For (possibly parenthesized) identifiers denoting built-in
// functions, the recorded signatures are call-site specific:
// if the call result is not a constant, the recorded type is
// an argument-specific signature. Otherwise, the recorded type
// is invalid.
//
// Identifiers on the lhs of declarations (i.e., the identifiers
// which are being declared) are collected in the Defs map.
// Identifiers denoting packages are collected in the Uses maps.
Types map[ast.Expr]TypeAndValue
// Defs maps identifiers to the objects they define (including
// package names, dots "." of dot-imports, and blank "_" identifiers).
// For identifiers that do not denote objects (e.g., the package name
// in package clauses, or symbolic variables t in t := x.(type) of
// type switch headers), the corresponding objects are nil.
//
// For an anonymous field, Defs returns the field *Var it defines.
//
// Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
Defs map[*ast.Ident]Object
// Uses maps identifiers to the objects they denote.
//
// For an anonymous field, Uses returns the *TypeName it denotes.
//
// Invariant: Uses[id].Pos() != id.Pos()
Uses map[*ast.Ident]Object
// Implicits maps nodes to their implicitly declared objects, if any.
// The following node and object types may appear:
//
// node declared object
//
// *ast.ImportSpec *PkgName for dot-imports and imports without renames
// *ast.CaseClause type-specific *Var for each type switch case clause (incl. default)
// *ast.Field anonymous struct field or parameter *Var
//
Implicits map[ast.Node]Object
// Selections maps selector expressions (excluding qualified identifiers)
// to their corresponding selections.
Selections map[*ast.SelectorExpr]*Selection
// Scopes maps ast.Nodes to the scopes they define. Package scopes are not
// associated with a specific node but with all files belonging to a package.
// Thus, the package scope can be found in the type-checked Package object.
// Scopes nest, with the Universe scope being the outermost scope, enclosing
// the package scope, which contains (one or more) files scopes, which enclose
// function scopes which in turn enclose statement and function literal scopes.
// Note that even though package-level functions are declared in the package
// scope, the function scopes are embedded in the file scope of the file
// containing the function declaration.
//
// The following node types may appear in Scopes:
//
// *ast.File
// *ast.FuncType
// *ast.BlockStmt
// *ast.IfStmt
// *ast.SwitchStmt
// *ast.TypeSwitchStmt
// *ast.CaseClause
// *ast.CommClause
// *ast.ForStmt
// *ast.RangeStmt
//
Scopes map[ast.Node]*Scope
// InitOrder is the list of package-level initializers in the order in which
// they must be executed. Initializers referring to variables related by an
// initialization dependency appear in topological order, the others appear
// in source order. Variables without an initialization expression do not
// appear in this list.
InitOrder []*Initializer
}
// TypeOf returns the type of expression e, or nil if not found.
// Precondition: the Types, Uses and Defs maps are populated.
//
func (info *Info) TypeOf(e ast.Expr) Type {
if t, ok := info.Types[e]; ok {
return t.Type
}
if id, _ := e.(*ast.Ident); id != nil {
if obj := info.ObjectOf(id); obj != nil {
return obj.Type()
}
}
return nil
}
// ObjectOf returns the object denoted by the specified id,
// or nil if not found.
//
// If id is an anonymous struct field, ObjectOf returns the field (*Var)
// it uses, not the type (*TypeName) it defines.
//
// Precondition: the Uses and Defs maps are populated.
//
func (info *Info) ObjectOf(id *ast.Ident) Object {
if obj, _ := info.Defs[id]; obj != nil {
return obj
}
return info.Uses[id]
}
// TypeAndValue reports the type and value (for constants)
// of the corresponding expression.
type TypeAndValue struct {
mode operandMode
Type Type
Value exact.Value
}
// TODO(gri) Consider eliminating the IsVoid predicate. Instead, report
// "void" values as regular values but with the empty tuple type.
// IsVoid reports whether the corresponding expression
// is a function call without results.
func (tv TypeAndValue) IsVoid() bool {
return tv.mode == novalue
}
// IsType reports whether the corresponding expression specifies a type.
func (tv TypeAndValue) IsType() bool {
return tv.mode == typexpr
}
// IsBuiltin reports whether the corresponding expression denotes
// a (possibly parenthesized) built-in function.
func (tv TypeAndValue) IsBuiltin() bool {
return tv.mode == builtin
}
// IsValue reports whether the corresponding expression is a value.
// Builtins are not considered values. Constant values have a non-
// nil Value.
func (tv TypeAndValue) IsValue() bool {
switch tv.mode {
case constant, variable, mapindex, value, commaok:
return true
}
return false
}
// IsNil reports whether the corresponding expression denotes the
// predeclared value nil.
func (tv TypeAndValue) IsNil() bool {
return tv.mode == value && tv.Type == Typ[UntypedNil]
}
// Addressable reports whether the corresponding expression
// is addressable (http://golang.org/ref/spec#Address_operators).
func (tv TypeAndValue) Addressable() bool {
return tv.mode == variable
}
// Assignable reports whether the corresponding expression
// is assignable to (provided a value of the right type).
func (tv TypeAndValue) Assignable() bool {
return tv.mode == variable || tv.mode == mapindex
}
// HasOk reports whether the corresponding expression may be
// used on the lhs of a comma-ok assignment.
func (tv TypeAndValue) HasOk() bool {
return tv.mode == commaok || tv.mode == mapindex
}
// An Initializer describes a package-level variable, or a list of variables in case
// of a multi-valued initialization expression, and the corresponding initialization
// expression.
type Initializer struct {
Lhs []*Var // var Lhs = Rhs
Rhs ast.Expr
}
func (init *Initializer) String() string {
var buf bytes.Buffer
for i, lhs := range init.Lhs {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(lhs.Name())
}
buf.WriteString(" = ")
WriteExpr(&buf, init.Rhs)
return buf.String()
}
// Check type-checks a package and returns the resulting package object,
// the first error if any, and if info != nil, additional type information.
// The package is marked as complete if no errors occurred, otherwise it is
// incomplete. See Config.Error for controlling behavior in the presence of
// errors.
//
// The package is specified by a list of *ast.Files and corresponding
// file set, and the package path the package is identified with.
// The clean path must not be empty or dot (".").
func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) {
pkg := NewPackage(path, "")
return pkg, NewChecker(conf, fset, pkg, info).Files(files)
}
// AssertableTo reports whether a value of type V can be asserted to have type T.
func AssertableTo(V *Interface, T Type) bool {
m, _ := assertableTo(V, T)
return m == nil
}
// AssignableTo reports whether a value of type V is assignable to a variable of type T.
func AssignableTo(V, T Type) bool {
x := operand{mode: value, typ: V}
return x.assignableTo(nil, T) // config not needed for non-constant x
}
// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
func ConvertibleTo(V, T Type) bool {
x := operand{mode: value, typ: V}
return x.convertibleTo(nil, T) // config not needed for non-constant x
}
// Implements reports whether type V implements interface T.
func Implements(V Type, T *Interface) bool {
f, _ := MissingMethod(V, T, true)
return f == nil
}

328
vendor/golang.org/x/tools/go/types/assignments.go generated vendored Normal file
View File

@ -0,0 +1,328 @@
// Copyright 2013 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.
// This file implements initialization and assignment checks.
package types
import (
"go/ast"
"go/token"
)
// assignment reports whether x can be assigned to a variable of type T,
// if necessary by attempting to convert untyped values to the appropriate
// type. If x.mode == invalid upon return, then assignment has already
// issued an error message and the caller doesn't have to report another.
// Use T == nil to indicate assignment to an untyped blank identifier.
//
// TODO(gri) Should find a better way to handle in-band errors.
//
func (check *Checker) assignment(x *operand, T Type) bool {
switch x.mode {
case invalid:
return true // error reported before
case constant, variable, mapindex, value, commaok:
// ok
default:
unreachable()
}
// x must be a single value
// (tuple types are never named - no need for underlying type)
if t, _ := x.typ.(*Tuple); t != nil {
assert(t.Len() > 1)
check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x)
x.mode = invalid
return false
}
if isUntyped(x.typ) {
target := T
// spec: "If an untyped constant is assigned to a variable of interface
// type or the blank identifier, the constant is first converted to type
// bool, rune, int, float64, complex128 or string respectively, depending
// on whether the value is a boolean, rune, integer, floating-point, complex,
// or string constant."
if T == nil || IsInterface(T) {
if T == nil && x.typ == Typ[UntypedNil] {
check.errorf(x.pos(), "use of untyped nil")
x.mode = invalid
return false
}
target = defaultType(x.typ)
}
check.convertUntyped(x, target)
if x.mode == invalid {
return false
}
}
// spec: "If a left-hand side is the blank identifier, any typed or
// non-constant value except for the predeclared identifier nil may
// be assigned to it."
return T == nil || x.assignableTo(check.conf, T)
}
func (check *Checker) initConst(lhs *Const, x *operand) {
if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
if lhs.typ == nil {
lhs.typ = Typ[Invalid]
}
return
}
// rhs must be a constant
if x.mode != constant {
check.errorf(x.pos(), "%s is not constant", x)
if lhs.typ == nil {
lhs.typ = Typ[Invalid]
}
return
}
assert(isConstType(x.typ))
// If the lhs doesn't have a type yet, use the type of x.
if lhs.typ == nil {
lhs.typ = x.typ
}
if !check.assignment(x, lhs.typ) {
if x.mode != invalid {
check.errorf(x.pos(), "cannot define constant %s (type %s) as %s", lhs.Name(), lhs.typ, x)
}
return
}
lhs.val = x.val
}
// If result is set, lhs is a function result parameter and x is a return result.
func (check *Checker) initVar(lhs *Var, x *operand, result bool) Type {
if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
if lhs.typ == nil {
lhs.typ = Typ[Invalid]
}
return nil
}
// If the lhs doesn't have a type yet, use the type of x.
if lhs.typ == nil {
typ := x.typ
if isUntyped(typ) {
// convert untyped types to default types
if typ == Typ[UntypedNil] {
check.errorf(x.pos(), "use of untyped nil")
lhs.typ = Typ[Invalid]
return nil
}
typ = defaultType(typ)
}
lhs.typ = typ
}
if !check.assignment(x, lhs.typ) {
if x.mode != invalid {
if result {
// don't refer to lhs.name because it may be an anonymous result parameter
check.errorf(x.pos(), "cannot return %s as value of type %s", x, lhs.typ)
} else {
check.errorf(x.pos(), "cannot initialize %s with %s", lhs, x)
}
}
return nil
}
return x.typ
}
func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
if x.mode == invalid || x.typ == Typ[Invalid] {
return nil
}
// Determine if the lhs is a (possibly parenthesized) identifier.
ident, _ := unparen(lhs).(*ast.Ident)
// Don't evaluate lhs if it is the blank identifier.
if ident != nil && ident.Name == "_" {
check.recordDef(ident, nil)
if !check.assignment(x, nil) {
assert(x.mode == invalid)
x.typ = nil
}
return x.typ
}
// If the lhs is an identifier denoting a variable v, this assignment
// is not a 'use' of v. Remember current value of v.used and restore
// after evaluating the lhs via check.expr.
var v *Var
var v_used bool
if ident != nil {
if _, obj := check.scope.LookupParent(ident.Name, token.NoPos); obj != nil {
v, _ = obj.(*Var)
if v != nil {
v_used = v.used
}
}
}
var z operand
check.expr(&z, lhs)
if v != nil {
v.used = v_used // restore v.used
}
if z.mode == invalid || z.typ == Typ[Invalid] {
return nil
}
// spec: "Each left-hand side operand must be addressable, a map index
// expression, or the blank identifier. Operands may be parenthesized."
switch z.mode {
case invalid:
return nil
case variable, mapindex:
// ok
default:
check.errorf(z.pos(), "cannot assign to %s", &z)
return nil
}
if !check.assignment(x, z.typ) {
if x.mode != invalid {
check.errorf(x.pos(), "cannot assign %s to %s", x, &z)
}
return nil
}
return x.typ
}
// If returnPos is valid, initVars is called to type-check the assignment of
// return expressions, and returnPos is the position of the return statement.
func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) {
l := len(lhs)
get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid())
if get == nil || l != r {
// invalidate lhs and use rhs
for _, obj := range lhs {
if obj.typ == nil {
obj.typ = Typ[Invalid]
}
}
if get == nil {
return // error reported by unpack
}
check.useGetter(get, r)
if returnPos.IsValid() {
check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r)
return
}
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
return
}
var x operand
if commaOk {
var a [2]Type
for i := range a {
get(&x, i)
a[i] = check.initVar(lhs[i], &x, returnPos.IsValid())
}
check.recordCommaOkTypes(rhs[0], a)
return
}
for i, lhs := range lhs {
get(&x, i)
check.initVar(lhs, &x, returnPos.IsValid())
}
}
func (check *Checker) assignVars(lhs, rhs []ast.Expr) {
l := len(lhs)
get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2)
if get == nil {
return // error reported by unpack
}
if l != r {
check.useGetter(get, r)
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
return
}
var x operand
if commaOk {
var a [2]Type
for i := range a {
get(&x, i)
a[i] = check.assignVar(lhs[i], &x)
}
check.recordCommaOkTypes(rhs[0], a)
return
}
for i, lhs := range lhs {
get(&x, i)
check.assignVar(lhs, &x)
}
}
func (check *Checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) {
scope := check.scope
// collect lhs variables
var newVars []*Var
var lhsVars = make([]*Var, len(lhs))
for i, lhs := range lhs {
var obj *Var
if ident, _ := lhs.(*ast.Ident); ident != nil {
// Use the correct obj if the ident is redeclared. The
// variable's scope starts after the declaration; so we
// must use Scope.Lookup here and call Scope.Insert
// (via check.declare) later.
name := ident.Name
if alt := scope.Lookup(name); alt != nil {
// redeclared object must be a variable
if alt, _ := alt.(*Var); alt != nil {
obj = alt
} else {
check.errorf(lhs.Pos(), "cannot assign to %s", lhs)
}
check.recordUse(ident, alt)
} else {
// declare new variable, possibly a blank (_) variable
obj = NewVar(ident.Pos(), check.pkg, name, nil)
if name != "_" {
newVars = append(newVars, obj)
}
check.recordDef(ident, obj)
}
} else {
check.errorf(lhs.Pos(), "cannot declare %s", lhs)
}
if obj == nil {
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
}
lhsVars[i] = obj
}
check.initVars(lhsVars, rhs, token.NoPos)
// declare new variables
if len(newVars) > 0 {
// spec: "The scope of a constant or variable identifier declared inside
// a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
// for short variable declarations) and ends at the end of the innermost
// containing block."
scopePos := rhs[len(rhs)-1].End()
for _, obj := range newVars {
check.declare(scope, nil, obj, scopePos) // recordObject already called
}
} else {
check.softErrorf(pos, "no new variables on left side of :=")
}
}

628
vendor/golang.org/x/tools/go/types/builtins.go generated vendored Normal file
View File

@ -0,0 +1,628 @@
// Copyright 2012 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.
// This file implements typechecking of builtin function calls.
package types
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/exact"
)
// builtin type-checks a call to the built-in specified by id and
// returns true if the call is valid, with *x holding the result;
// but x.expr is not set. If the call is invalid, the result is
// false, and *x is undefined.
//
func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ bool) {
// append is the only built-in that permits the use of ... for the last argument
bin := predeclaredFuncs[id]
if call.Ellipsis.IsValid() && id != _Append {
check.invalidOp(call.Ellipsis, "invalid use of ... with built-in %s", bin.name)
check.use(call.Args...)
return
}
// For len(x) and cap(x) we need to know if x contains any function calls or
// receive operations. Save/restore current setting and set hasCallOrRecv to
// false for the evaluation of x so that we can check it afterwards.
// Note: We must do this _before_ calling unpack because unpack evaluates the
// first argument before we even call arg(x, 0)!
if id == _Len || id == _Cap {
defer func(b bool) {
check.hasCallOrRecv = b
}(check.hasCallOrRecv)
check.hasCallOrRecv = false
}
// determine actual arguments
var arg getter
nargs := len(call.Args)
switch id {
default:
// make argument getter
arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false)
if arg == nil {
return
}
// evaluate first argument, if present
if nargs > 0 {
arg(x, 0)
if x.mode == invalid {
return
}
}
case _Make, _New, _Offsetof, _Trace:
// arguments require special handling
}
// check argument count
{
msg := ""
if nargs < bin.nargs {
msg = "not enough"
} else if !bin.variadic && nargs > bin.nargs {
msg = "too many"
}
if msg != "" {
check.invalidOp(call.Rparen, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs)
return
}
}
switch id {
case _Append:
// append(s S, x ...T) S, where T is the element type of S
// spec: "The variadic function append appends zero or more values x to s of type
// S, which must be a slice type, and returns the resulting slice, also of type S.
// The values x are passed to a parameter of type ...T where T is the element type
// of S and the respective parameter passing rules apply."
S := x.typ
var T Type
if s, _ := S.Underlying().(*Slice); s != nil {
T = s.elem
} else {
check.invalidArg(x.pos(), "%s is not a slice", x)
return
}
// remember arguments that have been evaluated already
alist := []operand{*x}
// spec: "As a special case, append also accepts a first argument assignable
// to type []byte with a second argument of string type followed by ... .
// This form appends the bytes of the string.
if nargs == 2 && call.Ellipsis.IsValid() && x.assignableTo(check.conf, NewSlice(universeByte)) {
arg(x, 1)
if x.mode == invalid {
return
}
if isString(x.typ) {
if check.Types != nil {
sig := makeSig(S, S, x.typ)
sig.variadic = true
check.recordBuiltinType(call.Fun, sig)
}
x.mode = value
x.typ = S
break
}
alist = append(alist, *x)
// fallthrough
}
// check general case by creating custom signature
sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature
sig.variadic = true
check.arguments(x, call, sig, func(x *operand, i int) {
// only evaluate arguments that have not been evaluated before
if i < len(alist) {
*x = alist[i]
return
}
arg(x, i)
}, nargs)
// ok to continue even if check.arguments reported errors
x.mode = value
x.typ = S
if check.Types != nil {
check.recordBuiltinType(call.Fun, sig)
}
case _Cap, _Len:
// cap(x)
// len(x)
mode := invalid
var typ Type
var val exact.Value
switch typ = implicitArrayDeref(x.typ.Underlying()); t := typ.(type) {
case *Basic:
if isString(t) && id == _Len {
if x.mode == constant {
mode = constant
val = exact.MakeInt64(int64(len(exact.StringVal(x.val))))
} else {
mode = value
}
}
case *Array:
mode = value
// spec: "The expressions len(s) and cap(s) are constants
// if the type of s is an array or pointer to an array and
// the expression s does not contain channel receives or
// function calls; in this case s is not evaluated."
if !check.hasCallOrRecv {
mode = constant
val = exact.MakeInt64(t.len)
}
case *Slice, *Chan:
mode = value
case *Map:
if id == _Len {
mode = value
}
}
if mode == invalid {
check.invalidArg(x.pos(), "%s for %s", x, bin.name)
return
}
x.mode = mode
x.typ = Typ[Int]
x.val = val
if check.Types != nil && mode != constant {
check.recordBuiltinType(call.Fun, makeSig(x.typ, typ))
}
case _Close:
// close(c)
c, _ := x.typ.Underlying().(*Chan)
if c == nil {
check.invalidArg(x.pos(), "%s is not a channel", x)
return
}
if c.dir == RecvOnly {
check.invalidArg(x.pos(), "%s must not be a receive-only channel", x)
return
}
x.mode = novalue
if check.Types != nil {
check.recordBuiltinType(call.Fun, makeSig(nil, c))
}
case _Complex:
// complex(x, y realT) complexT
if !check.complexArg(x) {
return
}
var y operand
arg(&y, 1)
if y.mode == invalid {
return
}
if !check.complexArg(&y) {
return
}
check.convertUntyped(x, y.typ)
if x.mode == invalid {
return
}
check.convertUntyped(&y, x.typ)
if y.mode == invalid {
return
}
if !Identical(x.typ, y.typ) {
check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
return
}
if x.mode == constant && y.mode == constant {
x.val = exact.BinaryOp(x.val, token.ADD, exact.MakeImag(y.val))
} else {
x.mode = value
}
realT := x.typ
complexT := Typ[Invalid]
switch realT.Underlying().(*Basic).kind {
case Float32:
complexT = Typ[Complex64]
case Float64:
complexT = Typ[Complex128]
case UntypedInt, UntypedRune, UntypedFloat:
if x.mode == constant {
realT = defaultType(realT).(*Basic)
complexT = Typ[UntypedComplex]
} else {
// untyped but not constant; probably because one
// operand is a non-constant shift of untyped lhs
realT = Typ[Float64]
complexT = Typ[Complex128]
}
default:
check.invalidArg(x.pos(), "float32 or float64 arguments expected")
return
}
x.typ = complexT
if check.Types != nil && x.mode != constant {
check.recordBuiltinType(call.Fun, makeSig(complexT, realT, realT))
}
if x.mode != constant {
// The arguments have now their final types, which at run-
// time will be materialized. Update the expression trees.
// If the current types are untyped, the materialized type
// is the respective default type.
// (If the result is constant, the arguments are never
// materialized and there is nothing to do.)
check.updateExprType(x.expr, realT, true)
check.updateExprType(y.expr, realT, true)
}
case _Copy:
// copy(x, y []T) int
var dst Type
if t, _ := x.typ.Underlying().(*Slice); t != nil {
dst = t.elem
}
var y operand
arg(&y, 1)
if y.mode == invalid {
return
}
var src Type
switch t := y.typ.Underlying().(type) {
case *Basic:
if isString(y.typ) {
src = universeByte
}
case *Slice:
src = t.elem
}
if dst == nil || src == nil {
check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y)
return
}
if !Identical(dst, src) {
check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
return
}
if check.Types != nil {
check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ, y.typ))
}
x.mode = value
x.typ = Typ[Int]
case _Delete:
// delete(m, k)
m, _ := x.typ.Underlying().(*Map)
if m == nil {
check.invalidArg(x.pos(), "%s is not a map", x)
return
}
arg(x, 1) // k
if x.mode == invalid {
return
}
if !x.assignableTo(check.conf, m.key) {
check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key)
return
}
x.mode = novalue
if check.Types != nil {
check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key))
}
case _Imag, _Real:
// imag(complexT) realT
// real(complexT) realT
if !isComplex(x.typ) {
check.invalidArg(x.pos(), "%s must be a complex number", x)
return
}
if x.mode == constant {
if id == _Real {
x.val = exact.Real(x.val)
} else {
x.val = exact.Imag(x.val)
}
} else {
x.mode = value
}
var k BasicKind
switch x.typ.Underlying().(*Basic).kind {
case Complex64:
k = Float32
case Complex128:
k = Float64
case UntypedComplex:
k = UntypedFloat
default:
unreachable()
}
if check.Types != nil && x.mode != constant {
check.recordBuiltinType(call.Fun, makeSig(Typ[k], x.typ))
}
x.typ = Typ[k]
case _Make:
// make(T, n)
// make(T, n, m)
// (no argument evaluated yet)
arg0 := call.Args[0]
T := check.typ(arg0)
if T == Typ[Invalid] {
return
}
var min int // minimum number of arguments
switch T.Underlying().(type) {
case *Slice:
min = 2
case *Map, *Chan:
min = 1
default:
check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0)
return
}
if nargs < min || min+1 < nargs {
check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, nargs)
return
}
var sizes []int64 // constant integer arguments, if any
for _, arg := range call.Args[1:] {
if s, ok := check.index(arg, -1); ok && s >= 0 {
sizes = append(sizes, s)
}
}
if len(sizes) == 2 && sizes[0] > sizes[1] {
check.invalidArg(call.Args[1].Pos(), "length and capacity swapped")
// safe to continue
}
x.mode = value
x.typ = T
if check.Types != nil {
params := [...]Type{T, Typ[Int], Typ[Int]}
check.recordBuiltinType(call.Fun, makeSig(x.typ, params[:1+len(sizes)]...))
}
case _New:
// new(T)
// (no argument evaluated yet)
T := check.typ(call.Args[0])
if T == Typ[Invalid] {
return
}
x.mode = value
x.typ = &Pointer{base: T}
if check.Types != nil {
check.recordBuiltinType(call.Fun, makeSig(x.typ, T))
}
case _Panic:
// panic(x)
T := new(Interface)
if !check.assignment(x, T) {
assert(x.mode == invalid)
return
}
x.mode = novalue
if check.Types != nil {
check.recordBuiltinType(call.Fun, makeSig(nil, T))
}
case _Print, _Println:
// print(x, y, ...)
// println(x, y, ...)
var params []Type
if nargs > 0 {
params = make([]Type, nargs)
for i := 0; i < nargs; i++ {
if i > 0 {
arg(x, i) // first argument already evaluated
}
if !check.assignment(x, nil) {
assert(x.mode == invalid)
return
}
params[i] = x.typ
}
}
x.mode = novalue
if check.Types != nil {
check.recordBuiltinType(call.Fun, makeSig(nil, params...))
}
case _Recover:
// recover() interface{}
x.mode = value
x.typ = new(Interface)
if check.Types != nil {
check.recordBuiltinType(call.Fun, makeSig(x.typ))
}
case _Alignof:
// unsafe.Alignof(x T) uintptr
if !check.assignment(x, nil) {
assert(x.mode == invalid)
return
}
x.mode = constant
x.val = exact.MakeInt64(check.conf.alignof(x.typ))
x.typ = Typ[Uintptr]
// result is constant - no need to record signature
case _Offsetof:
// unsafe.Offsetof(x T) uintptr, where x must be a selector
// (no argument evaluated yet)
arg0 := call.Args[0]
selx, _ := unparen(arg0).(*ast.SelectorExpr)
if selx == nil {
check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0)
check.use(arg0)
return
}
check.expr(x, selx.X)
if x.mode == invalid {
return
}
base := derefStructPtr(x.typ)
sel := selx.Sel.Name
obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel)
switch obj.(type) {
case nil:
check.invalidArg(x.pos(), "%s has no single field %s", base, sel)
return
case *Func:
// TODO(gri) Using derefStructPtr may result in methods being found
// that don't actually exist. An error either way, but the error
// message is confusing. See: http://play.golang.org/p/al75v23kUy ,
// but go/types reports: "invalid argument: x.m is a method value".
check.invalidArg(arg0.Pos(), "%s is a method value", arg0)
return
}
if indirect {
check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base)
return
}
// TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)?
check.recordSelection(selx, FieldVal, base, obj, index, false)
offs := check.conf.offsetof(base, index)
x.mode = constant
x.val = exact.MakeInt64(offs)
x.typ = Typ[Uintptr]
// result is constant - no need to record signature
case _Sizeof:
// unsafe.Sizeof(x T) uintptr
if !check.assignment(x, nil) {
assert(x.mode == invalid)
return
}
x.mode = constant
x.val = exact.MakeInt64(check.conf.sizeof(x.typ))
x.typ = Typ[Uintptr]
// result is constant - no need to record signature
case _Assert:
// assert(pred) causes a typechecker error if pred is false.
// The result of assert is the value of pred if there is no error.
// Note: assert is only available in self-test mode.
if x.mode != constant || !isBoolean(x.typ) {
check.invalidArg(x.pos(), "%s is not a boolean constant", x)
return
}
if x.val.Kind() != exact.Bool {
check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x)
return
}
if !exact.BoolVal(x.val) {
check.errorf(call.Pos(), "%s failed", call)
// compile-time assertion failure - safe to continue
}
// result is constant - no need to record signature
case _Trace:
// trace(x, y, z, ...) dumps the positions, expressions, and
// values of its arguments. The result of trace is the value
// of the first argument.
// Note: trace is only available in self-test mode.
// (no argument evaluated yet)
if nargs == 0 {
check.dump("%s: trace() without arguments", call.Pos())
x.mode = novalue
break
}
var t operand
x1 := x
for _, arg := range call.Args {
check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T))
check.dump("%s: %s", x1.pos(), x1)
x1 = &t // use incoming x only for first argument
}
// trace is only available in test mode - no need to record signature
default:
unreachable()
}
return true
}
// makeSig makes a signature for the given argument and result types.
// Default types are used for untyped arguments, and res may be nil.
func makeSig(res Type, args ...Type) *Signature {
list := make([]*Var, len(args))
for i, param := range args {
list[i] = NewVar(token.NoPos, nil, "", defaultType(param))
}
params := NewTuple(list...)
var result *Tuple
if res != nil {
assert(!isUntyped(res))
result = NewTuple(NewVar(token.NoPos, nil, "", res))
}
return &Signature{params: params, results: result}
}
// implicitArrayDeref returns A if typ is of the form *A and A is an array;
// otherwise it returns typ.
//
func implicitArrayDeref(typ Type) Type {
if p, ok := typ.(*Pointer); ok {
if a, ok := p.base.Underlying().(*Array); ok {
return a
}
}
return typ
}
// unparen returns e with any enclosing parentheses stripped.
func unparen(e ast.Expr) ast.Expr {
for {
p, ok := e.(*ast.ParenExpr)
if !ok {
return e
}
e = p.X
}
}
func (check *Checker) complexArg(x *operand) bool {
t, _ := x.typ.Underlying().(*Basic)
if t != nil && (t.info&IsFloat != 0 || t.kind == UntypedInt || t.kind == UntypedRune) {
return true
}
check.invalidArg(x.pos(), "%s must be a float32, float64, or an untyped non-complex numeric constant", x)
return false
}

441
vendor/golang.org/x/tools/go/types/call.go generated vendored Normal file
View File

@ -0,0 +1,441 @@
// Copyright 2013 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.
// This file implements typechecking of call and selector expressions.
package types
import (
"go/ast"
"go/token"
)
func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
check.exprOrType(x, e.Fun)
switch x.mode {
case invalid:
check.use(e.Args...)
x.mode = invalid
x.expr = e
return statement
case typexpr:
// conversion
T := x.typ
x.mode = invalid
switch n := len(e.Args); n {
case 0:
check.errorf(e.Rparen, "missing argument in conversion to %s", T)
case 1:
check.expr(x, e.Args[0])
if x.mode != invalid {
check.conversion(x, T)
}
default:
check.errorf(e.Args[n-1].Pos(), "too many arguments in conversion to %s", T)
}
x.expr = e
return conversion
case builtin:
id := x.id
if !check.builtin(x, e, id) {
x.mode = invalid
}
x.expr = e
// a non-constant result implies a function call
if x.mode != invalid && x.mode != constant {
check.hasCallOrRecv = true
}
return predeclaredFuncs[id].kind
default:
// function/method call
sig, _ := x.typ.Underlying().(*Signature)
if sig == nil {
check.invalidOp(x.pos(), "cannot call non-function %s", x)
x.mode = invalid
x.expr = e
return statement
}
arg, n, _ := unpack(func(x *operand, i int) { check.expr(x, e.Args[i]) }, len(e.Args), false)
if arg == nil {
x.mode = invalid
x.expr = e
return statement
}
check.arguments(x, e, sig, arg, n)
// determine result
switch sig.results.Len() {
case 0:
x.mode = novalue
case 1:
x.mode = value
x.typ = sig.results.vars[0].typ // unpack tuple
default:
x.mode = value
x.typ = sig.results
}
x.expr = e
check.hasCallOrRecv = true
return statement
}
}
// use type-checks each argument.
// Useful to make sure expressions are evaluated
// (and variables are "used") in the presence of other errors.
func (check *Checker) use(arg ...ast.Expr) {
var x operand
for _, e := range arg {
check.rawExpr(&x, e, nil)
}
}
// useGetter is like use, but takes a getter instead of a list of expressions.
// It should be called instead of use if a getter is present to avoid repeated
// evaluation of the first argument (since the getter was likely obtained via
// unpack, which may have evaluated the first argument already).
func (check *Checker) useGetter(get getter, n int) {
var x operand
for i := 0; i < n; i++ {
get(&x, i)
}
}
// A getter sets x as the i'th operand, where 0 <= i < n and n is the total
// number of operands (context-specific, and maintained elsewhere). A getter
// type-checks the i'th operand; the details of the actual check are getter-
// specific.
type getter func(x *operand, i int)
// unpack takes a getter get and a number of operands n. If n == 1, unpack
// calls the incoming getter for the first operand. If that operand is
// invalid, unpack returns (nil, 0, false). Otherwise, if that operand is a
// function call, or a comma-ok expression and allowCommaOk is set, the result
// is a new getter and operand count providing access to the function results,
// or comma-ok values, respectively. The third result value reports if it
// is indeed the comma-ok case. In all other cases, the incoming getter and
// operand count are returned unchanged, and the third result value is false.
//
// In other words, if there's exactly one operand that - after type-checking
// by calling get - stands for multiple operands, the resulting getter provides
// access to those operands instead.
//
// If the returned getter is called at most once for a given operand index i
// (including i == 0), that operand is guaranteed to cause only one call of
// the incoming getter with that i.
//
func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) {
if n == 1 {
// possibly result of an n-valued function call or comma,ok value
var x0 operand
get(&x0, 0)
if x0.mode == invalid {
return nil, 0, false
}
if t, ok := x0.typ.(*Tuple); ok {
// result of an n-valued function call
return func(x *operand, i int) {
x.mode = value
x.expr = x0.expr
x.typ = t.At(i).typ
}, t.Len(), false
}
if x0.mode == mapindex || x0.mode == commaok {
// comma-ok value
if allowCommaOk {
a := [2]Type{x0.typ, Typ[UntypedBool]}
return func(x *operand, i int) {
x.mode = value
x.expr = x0.expr
x.typ = a[i]
}, 2, true
}
x0.mode = value
}
// single value
return func(x *operand, i int) {
if i != 0 {
unreachable()
}
*x = x0
}, 1, false
}
// zero or multiple values
return get, n, false
}
// arguments checks argument passing for the call with the given signature.
// The arg function provides the operand for the i'th argument.
func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, arg getter, n int) {
if call.Ellipsis.IsValid() {
// last argument is of the form x...
if len(call.Args) == 1 && n > 1 {
// f()... is not permitted if f() is multi-valued
check.errorf(call.Ellipsis, "cannot use ... with %d-valued expression %s", n, call.Args[0])
check.useGetter(arg, n)
return
}
if !sig.variadic {
check.errorf(call.Ellipsis, "cannot use ... in call to non-variadic %s", call.Fun)
check.useGetter(arg, n)
return
}
}
// evaluate arguments
for i := 0; i < n; i++ {
arg(x, i)
if x.mode != invalid {
var ellipsis token.Pos
if i == n-1 && call.Ellipsis.IsValid() {
ellipsis = call.Ellipsis
}
check.argument(sig, i, x, ellipsis)
}
}
// check argument count
if sig.variadic {
// a variadic function accepts an "empty"
// last argument: count one extra
n++
}
if n < sig.params.Len() {
check.errorf(call.Rparen, "too few arguments in call to %s", call.Fun)
// ok to continue
}
}
// argument checks passing of argument x to the i'th parameter of the given signature.
// If ellipsis is valid, the argument is followed by ... at that position in the call.
func (check *Checker) argument(sig *Signature, i int, x *operand, ellipsis token.Pos) {
n := sig.params.Len()
// determine parameter type
var typ Type
switch {
case i < n:
typ = sig.params.vars[i].typ
case sig.variadic:
typ = sig.params.vars[n-1].typ
if debug {
if _, ok := typ.(*Slice); !ok {
check.dump("%s: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), typ)
}
}
default:
check.errorf(x.pos(), "too many arguments")
return
}
if ellipsis.IsValid() {
// argument is of the form x...
if i != n-1 {
check.errorf(ellipsis, "can only use ... with matching parameter")
return
}
switch t := x.typ.Underlying().(type) {
case *Slice:
// ok
case *Tuple:
check.errorf(ellipsis, "cannot use ... with %d-valued expression %s", t.Len(), x)
return
default:
check.errorf(x.pos(), "cannot use %s as parameter of type %s", x, typ)
return
}
} else if sig.variadic && i >= n-1 {
// use the variadic parameter slice's element type
typ = typ.(*Slice).elem
}
if !check.assignment(x, typ) && x.mode != invalid {
check.errorf(x.pos(), "cannot pass argument %s to parameter of type %s", x, typ)
}
}
func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
// these must be declared before the "goto Error" statements
var (
obj Object
index []int
indirect bool
)
sel := e.Sel.Name
// If the identifier refers to a package, handle everything here
// so we don't need a "package" mode for operands: package names
// can only appear in qualified identifiers which are mapped to
// selector expressions.
if ident, ok := e.X.(*ast.Ident); ok {
_, obj := check.scope.LookupParent(ident.Name, check.pos)
if pkg, _ := obj.(*PkgName); pkg != nil {
assert(pkg.pkg == check.pkg)
check.recordUse(ident, pkg)
pkg.used = true
exp := pkg.imported.scope.Lookup(sel)
if exp == nil {
if !pkg.imported.fake {
check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
}
goto Error
}
if !exp.Exported() {
check.errorf(e.Pos(), "%s not exported by package %s", sel, ident)
// ok to continue
}
check.recordUse(e.Sel, exp)
// Simplified version of the code for *ast.Idents:
// - imported objects are always fully initialized
switch exp := exp.(type) {
case *Const:
assert(exp.Val() != nil)
x.mode = constant
x.typ = exp.typ
x.val = exp.val
case *TypeName:
x.mode = typexpr
x.typ = exp.typ
case *Var:
x.mode = variable
x.typ = exp.typ
case *Func:
x.mode = value
x.typ = exp.typ
case *Builtin:
x.mode = builtin
x.typ = exp.typ
x.id = exp.id
default:
unreachable()
}
x.expr = e
return
}
}
check.exprOrType(x, e.X)
if x.mode == invalid {
goto Error
}
obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
if obj == nil {
switch {
case index != nil:
// TODO(gri) should provide actual type where the conflict happens
check.invalidOp(e.Pos(), "ambiguous selector %s", sel)
case indirect:
check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ)
default:
check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel)
}
goto Error
}
if x.mode == typexpr {
// method expression
m, _ := obj.(*Func)
if m == nil {
check.invalidOp(e.Pos(), "%s has no method %s", x, sel)
goto Error
}
check.recordSelection(e, MethodExpr, x.typ, m, index, indirect)
// the receiver type becomes the type of the first function
// argument of the method expression's function type
var params []*Var
sig := m.typ.(*Signature)
if sig.params != nil {
params = sig.params.vars
}
x.mode = value
x.typ = &Signature{
params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "", x.typ)}, params...)...),
results: sig.results,
variadic: sig.variadic,
}
check.addDeclDep(m)
} else {
// regular selector
switch obj := obj.(type) {
case *Var:
check.recordSelection(e, FieldVal, x.typ, obj, index, indirect)
if x.mode == variable || indirect {
x.mode = variable
} else {
x.mode = value
}
x.typ = obj.typ
case *Func:
// TODO(gri) If we needed to take into account the receiver's
// addressability, should we report the type &(x.typ) instead?
check.recordSelection(e, MethodVal, x.typ, obj, index, indirect)
if debug {
// Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
typ := x.typ
if x.mode == variable {
// If typ is not an (unnamed) pointer or an interface,
// use *typ instead, because the method set of *typ
// includes the methods of typ.
// Variables are addressable, so we can always take their
// address.
if _, ok := typ.(*Pointer); !ok && !IsInterface(typ) {
typ = &Pointer{base: typ}
}
}
// If we created a synthetic pointer type above, we will throw
// away the method set computed here after use.
// TODO(gri) Method set computation should probably always compute
// both, the value and the pointer receiver method set and represent
// them in a single structure.
// TODO(gri) Consider also using a method set cache for the lifetime
// of checker once we rely on MethodSet lookup instead of individual
// lookup.
mset := NewMethodSet(typ)
if m := mset.Lookup(check.pkg, sel); m == nil || m.obj != obj {
check.dump("%s: (%s).%v -> %s", e.Pos(), typ, obj.name, m)
check.dump("%s\n", mset)
panic("method sets and lookup don't agree")
}
}
x.mode = value
// remove receiver
sig := *obj.typ.(*Signature)
sig.recv = nil
x.typ = &sig
check.addDeclDep(obj)
default:
unreachable()
}
}
// everything went well
x.expr = e
return
Error:
x.mode = invalid
x.expr = e
}

364
vendor/golang.org/x/tools/go/types/check.go generated vendored Normal file
View File

@ -0,0 +1,364 @@
// Copyright 2011 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.
// This file implements the Check function, which drives type-checking.
package types
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/exact"
)
// debugging/development support
const (
debug = false // leave on during development
trace = false // turn on for detailed type resolution traces
)
// If Strict is set, the type-checker enforces additional
// rules not specified by the Go 1 spec, but which will
// catch guaranteed run-time errors if the respective
// code is executed. In other words, programs passing in
// Strict mode are Go 1 compliant, but not all Go 1 programs
// will pass in Strict mode. The additional rules are:
//
// - A type assertion x.(T) where T is an interface type
// is invalid if any (statically known) method that exists
// for both x and T have different signatures.
//
const strict = false
// exprInfo stores information about an untyped expression.
type exprInfo struct {
isLhs bool // expression is lhs operand of a shift with delayed type-check
mode operandMode
typ *Basic
val exact.Value // constant value; or nil (if not a constant)
}
// funcInfo stores the information required for type-checking a function.
type funcInfo struct {
name string // for debugging/tracing only
decl *declInfo // for cycle detection
sig *Signature
body *ast.BlockStmt
}
// A context represents the context within which an object is type-checked.
type context struct {
decl *declInfo // package-level declaration whose init expression/function body is checked
scope *Scope // top-most scope for lookups
iota exact.Value // value of iota in a constant declaration; nil otherwise
sig *Signature // function signature if inside a function; nil otherwise
hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions
hasCallOrRecv bool // set if an expression contains a function call or channel receive operation
}
// A Checker maintains the state of the type checker.
// It must be created with NewChecker.
type Checker struct {
// package information
// (initialized by NewChecker, valid for the life-time of checker)
conf *Config
fset *token.FileSet
pkg *Package
*Info
objMap map[Object]*declInfo // maps package-level object to declaration info
// information collected during type-checking of a set of package files
// (initialized by Files, valid only for the duration of check.Files;
// maps and lists are allocated on demand)
files []*ast.File // package files
unusedDotImports map[*Scope]map[*Package]token.Pos // positions of unused dot-imported packages for each file scope
firstErr error // first error encountered
methods map[string][]*Func // maps type names to associated methods
untyped map[ast.Expr]exprInfo // map of expressions without final type
funcs []funcInfo // list of functions to type-check
delayed []func() // delayed checks requiring fully setup types
// context within which the current object is type-checked
// (valid only for the duration of type-checking a specific object)
context
pos token.Pos // if valid, identifiers are looked up as if at position pos (used by Eval)
// debugging
indent int // indentation for tracing
}
// addUnusedImport adds the position of a dot-imported package
// pkg to the map of dot imports for the given file scope.
func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, pos token.Pos) {
mm := check.unusedDotImports
if mm == nil {
mm = make(map[*Scope]map[*Package]token.Pos)
check.unusedDotImports = mm
}
m := mm[scope]
if m == nil {
m = make(map[*Package]token.Pos)
mm[scope] = m
}
m[pkg] = pos
}
// addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists
func (check *Checker) addDeclDep(to Object) {
from := check.decl
if from == nil {
return // not in a package-level init expression
}
if _, found := check.objMap[to]; !found {
return // to is not a package-level object
}
from.addDep(to)
}
func (check *Checker) assocMethod(tname string, meth *Func) {
m := check.methods
if m == nil {
m = make(map[string][]*Func)
check.methods = m
}
m[tname] = append(m[tname], meth)
}
func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val exact.Value) {
m := check.untyped
if m == nil {
m = make(map[ast.Expr]exprInfo)
check.untyped = m
}
m[e] = exprInfo{lhs, mode, typ, val}
}
func (check *Checker) later(name string, decl *declInfo, sig *Signature, body *ast.BlockStmt) {
check.funcs = append(check.funcs, funcInfo{name, decl, sig, body})
}
func (check *Checker) delay(f func()) {
check.delayed = append(check.delayed, f)
}
// NewChecker returns a new Checker instance for a given package.
// Package files may be added incrementally via checker.Files.
func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Checker {
// make sure we have a configuration
if conf == nil {
conf = new(Config)
}
// make sure we have a package canonicalization map
if conf.Packages == nil {
conf.Packages = make(map[string]*Package)
}
// make sure we have an info struct
if info == nil {
info = new(Info)
}
return &Checker{
conf: conf,
fset: fset,
pkg: pkg,
Info: info,
objMap: make(map[Object]*declInfo),
}
}
// initFiles initializes the files-specific portion of checker.
// The provided files must all belong to the same package.
func (check *Checker) initFiles(files []*ast.File) {
// start with a clean slate (check.Files may be called multiple times)
check.files = nil
check.unusedDotImports = nil
check.firstErr = nil
check.methods = nil
check.untyped = nil
check.funcs = nil
check.delayed = nil
// determine package name and collect valid files
pkg := check.pkg
for _, file := range files {
switch name := file.Name.Name; pkg.name {
case "":
if name != "_" {
pkg.name = name
} else {
check.errorf(file.Name.Pos(), "invalid package name _")
}
fallthrough
case name:
check.files = append(check.files, file)
default:
check.errorf(file.Package, "package %s; expected %s", name, pkg.name)
// ignore this file
}
}
}
// A bailout panic is used for early termination.
type bailout struct{}
func (check *Checker) handleBailout(err *error) {
switch p := recover().(type) {
case nil, bailout:
// normal return or early exit
*err = check.firstErr
default:
// re-panic
panic(p)
}
}
// Files checks the provided files as part of the checker's package.
func (check *Checker) Files(files []*ast.File) (err error) {
defer check.handleBailout(&err)
check.initFiles(files)
check.collectObjects()
check.packageObjects(check.resolveOrder())
check.functionBodies()
check.initOrder()
if !check.conf.DisableUnusedImportCheck {
check.unusedImports()
}
// perform delayed checks
for _, f := range check.delayed {
f()
}
check.recordUntyped()
check.pkg.complete = true
return
}
func (check *Checker) recordUntyped() {
if !debug && check.Types == nil {
return // nothing to do
}
for x, info := range check.untyped {
if debug && isTyped(info.typ) {
check.dump("%s: %s (type %s) is typed", x.Pos(), x, info.typ)
unreachable()
}
check.recordTypeAndValue(x, info.mode, info.typ, info.val)
}
}
func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type, val exact.Value) {
assert(x != nil)
assert(typ != nil)
if mode == invalid {
return // omit
}
assert(typ != nil)
if mode == constant {
assert(val != nil)
assert(typ == Typ[Invalid] || isConstType(typ))
}
if m := check.Types; m != nil {
m[x] = TypeAndValue{mode, typ, val}
}
}
func (check *Checker) recordBuiltinType(f ast.Expr, sig *Signature) {
// f must be a (possibly parenthesized) identifier denoting a built-in
// (built-ins in package unsafe always produce a constant result and
// we don't record their signatures, so we don't see qualified idents
// here): record the signature for f and possible children.
for {
check.recordTypeAndValue(f, builtin, sig, nil)
switch p := f.(type) {
case *ast.Ident:
return // we're done
case *ast.ParenExpr:
f = p.X
default:
unreachable()
}
}
}
func (check *Checker) recordCommaOkTypes(x ast.Expr, a [2]Type) {
assert(x != nil)
if a[0] == nil || a[1] == nil {
return
}
assert(isTyped(a[0]) && isTyped(a[1]) && isBoolean(a[1]))
if m := check.Types; m != nil {
for {
tv := m[x]
assert(tv.Type != nil) // should have been recorded already
pos := x.Pos()
tv.Type = NewTuple(
NewVar(pos, check.pkg, "", a[0]),
NewVar(pos, check.pkg, "", a[1]),
)
m[x] = tv
// if x is a parenthesized expression (p.X), update p.X
p, _ := x.(*ast.ParenExpr)
if p == nil {
break
}
x = p.X
}
}
}
func (check *Checker) recordDef(id *ast.Ident, obj Object) {
assert(id != nil)
if m := check.Defs; m != nil {
m[id] = obj
}
}
func (check *Checker) recordUse(id *ast.Ident, obj Object) {
assert(id != nil)
assert(obj != nil)
if m := check.Uses; m != nil {
m[id] = obj
}
}
func (check *Checker) recordImplicit(node ast.Node, obj Object) {
assert(node != nil)
assert(obj != nil)
if m := check.Implicits; m != nil {
m[node] = obj
}
}
func (check *Checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
assert(obj != nil && (recv == nil || len(index) > 0))
check.recordUse(x.Sel, obj)
// TODO(gri) Should we also call recordTypeAndValue?
if m := check.Selections; m != nil {
m[x] = &Selection{kind, recv, obj, index, indirect}
}
}
func (check *Checker) recordScope(node ast.Node, scope *Scope) {
assert(node != nil)
assert(scope != nil)
if m := check.Scopes; m != nil {
m[node] = scope
}
}

146
vendor/golang.org/x/tools/go/types/conversions.go generated vendored Normal file
View File

@ -0,0 +1,146 @@
// Copyright 2012 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.
// This file implements typechecking of conversions.
package types
import "golang.org/x/tools/go/exact"
// Conversion type-checks the conversion T(x).
// The result is in x.
func (check *Checker) conversion(x *operand, T Type) {
constArg := x.mode == constant
var ok bool
switch {
case constArg && isConstType(T):
// constant conversion
switch t := T.Underlying().(*Basic); {
case representableConst(x.val, check.conf, t.kind, &x.val):
ok = true
case isInteger(x.typ) && isString(t):
codepoint := int64(-1)
if i, ok := exact.Int64Val(x.val); ok {
codepoint = i
}
// If codepoint < 0 the absolute value is too large (or unknown) for
// conversion. This is the same as converting any other out-of-range
// value - let string(codepoint) do the work.
x.val = exact.MakeString(string(codepoint))
ok = true
}
case x.convertibleTo(check.conf, T):
// non-constant conversion
x.mode = value
ok = true
}
if !ok {
check.errorf(x.pos(), "cannot convert %s to %s", x, T)
x.mode = invalid
return
}
// The conversion argument types are final. For untyped values the
// conversion provides the type, per the spec: "A constant may be
// given a type explicitly by a constant declaration or conversion,...".
final := x.typ
if isUntyped(x.typ) {
final = T
// - For conversions to interfaces, use the argument's default type.
// - For conversions of untyped constants to non-constant types, also
// use the default type (e.g., []byte("foo") should report string
// not []byte as type for the constant "foo").
// - Keep untyped nil for untyped nil arguments.
if IsInterface(T) || constArg && !isConstType(T) {
final = defaultType(x.typ)
}
check.updateExprType(x.expr, final, true)
}
x.typ = T
}
func (x *operand) convertibleTo(conf *Config, T Type) bool {
// "x is assignable to T"
if x.assignableTo(conf, T) {
return true
}
// "x's type and T have identical underlying types"
V := x.typ
Vu := V.Underlying()
Tu := T.Underlying()
if Identical(Vu, Tu) {
return true
}
// "x's type and T are unnamed pointer types and their pointer base types have identical underlying types"
if V, ok := V.(*Pointer); ok {
if T, ok := T.(*Pointer); ok {
if Identical(V.base.Underlying(), T.base.Underlying()) {
return true
}
}
}
// "x's type and T are both integer or floating point types"
if (isInteger(V) || isFloat(V)) && (isInteger(T) || isFloat(T)) {
return true
}
// "x's type and T are both complex types"
if isComplex(V) && isComplex(T) {
return true
}
// "x is an integer or a slice of bytes or runes and T is a string type"
if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
return true
}
// "x is a string and T is a slice of bytes or runes"
if isString(V) && isBytesOrRunes(Tu) {
return true
}
// package unsafe:
// "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) {
return true
}
// "and vice versa"
if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) {
return true
}
return false
}
func isUintptr(typ Type) bool {
t, ok := typ.Underlying().(*Basic)
return ok && t.kind == Uintptr
}
func isUnsafePointer(typ Type) bool {
// TODO(gri): Is this (typ.Underlying() instead of just typ) correct?
// The spec does not say so, but gc claims it is. See also
// issue 6326.
t, ok := typ.Underlying().(*Basic)
return ok && t.kind == UnsafePointer
}
func isPointer(typ Type) bool {
_, ok := typ.Underlying().(*Pointer)
return ok
}
func isBytesOrRunes(typ Type) bool {
if s, ok := typ.(*Slice); ok {
t, ok := s.elem.Underlying().(*Basic)
return ok && (t.kind == Byte || t.kind == Rune)
}
return false
}

431
vendor/golang.org/x/tools/go/types/decl.go generated vendored Normal file
View File

@ -0,0 +1,431 @@
// Copyright 2014 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
import (
"go/ast"
"go/token"
"golang.org/x/tools/go/exact"
)
func (check *Checker) reportAltDecl(obj Object) {
if pos := obj.Pos(); pos.IsValid() {
// We use "other" rather than "previous" here because
// the first declaration seen may not be textually
// earlier in the source.
check.errorf(pos, "\tother declaration of %s", obj.Name()) // secondary error, \t indented
}
}
func (check *Checker) declare(scope *Scope, id *ast.Ident, obj Object, pos token.Pos) {
// spec: "The blank identifier, represented by the underscore
// character _, may be used in a declaration like any other
// identifier but the declaration does not introduce a new
// binding."
if obj.Name() != "_" {
if alt := scope.Insert(obj); alt != nil {
check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
check.reportAltDecl(alt)
return
}
obj.setScopePos(pos)
}
if id != nil {
check.recordDef(id, obj)
}
}
// objDecl type-checks the declaration of obj in its respective (file) context.
// See check.typ for the details on def and path.
func (check *Checker) objDecl(obj Object, def *Named, path []*TypeName) {
if obj.Type() != nil {
return // already checked - nothing to do
}
if trace {
check.trace(obj.Pos(), "-- declaring %s", obj.Name())
check.indent++
defer func() {
check.indent--
check.trace(obj.Pos(), "=> %s", obj)
}()
}
d := check.objMap[obj]
if d == nil {
check.dump("%s: %s should have been declared", obj.Pos(), obj.Name())
unreachable()
}
// save/restore current context and setup object context
defer func(ctxt context) {
check.context = ctxt
}(check.context)
check.context = context{
scope: d.file,
}
// Const and var declarations must not have initialization
// cycles. We track them by remembering the current declaration
// in check.decl. Initialization expressions depending on other
// consts, vars, or functions, add dependencies to the current
// check.decl.
switch obj := obj.(type) {
case *Const:
check.decl = d // new package-level const decl
check.constDecl(obj, d.typ, d.init)
case *Var:
check.decl = d // new package-level var decl
check.varDecl(obj, d.lhs, d.typ, d.init)
case *TypeName:
// invalid recursive types are detected via path
check.typeDecl(obj, d.typ, def, path)
case *Func:
// functions may be recursive - no need to track dependencies
check.funcDecl(obj, d)
default:
unreachable()
}
}
func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) {
assert(obj.typ == nil)
if obj.visited {
obj.typ = Typ[Invalid]
return
}
obj.visited = true
// use the correct value of iota
assert(check.iota == nil)
check.iota = obj.val
defer func() { check.iota = nil }()
// provide valid constant value under all circumstances
obj.val = exact.MakeUnknown()
// determine type, if any
if typ != nil {
t := check.typ(typ)
if !isConstType(t) {
check.errorf(typ.Pos(), "invalid constant type %s", t)
obj.typ = Typ[Invalid]
return
}
obj.typ = t
}
// check initialization
var x operand
if init != nil {
check.expr(&x, init)
}
check.initConst(obj, &x)
}
func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) {
assert(obj.typ == nil)
if obj.visited {
obj.typ = Typ[Invalid]
return
}
obj.visited = true
// var declarations cannot use iota
assert(check.iota == nil)
// determine type, if any
if typ != nil {
obj.typ = check.typ(typ)
}
// check initialization
if init == nil {
if typ == nil {
// error reported before by arityMatch
obj.typ = Typ[Invalid]
}
return
}
if lhs == nil || len(lhs) == 1 {
assert(lhs == nil || lhs[0] == obj)
var x operand
check.expr(&x, init)
check.initVar(obj, &x, false)
return
}
if debug {
// obj must be one of lhs
found := false
for _, lhs := range lhs {
if obj == lhs {
found = true
break
}
}
if !found {
panic("inconsistent lhs")
}
}
check.initVars(lhs, []ast.Expr{init}, token.NoPos)
}
// underlying returns the underlying type of typ; possibly by following
// forward chains of named types. Such chains only exist while named types
// are incomplete.
func underlying(typ Type) Type {
for {
n, _ := typ.(*Named)
if n == nil {
break
}
typ = n.underlying
}
return typ
}
func (n *Named) setUnderlying(typ Type) {
if n != nil {
n.underlying = typ
}
}
func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, path []*TypeName) {
assert(obj.typ == nil)
// type declarations cannot use iota
assert(check.iota == nil)
named := &Named{obj: obj}
def.setUnderlying(named)
obj.typ = named // make sure recursive type declarations terminate
// determine underlying type of named
check.typExpr(typ, named, append(path, obj))
// The underlying type of named may be itself a named type that is
// incomplete:
//
// type (
// A B
// B *C
// C A
// )
//
// The type of C is the (named) type of A which is incomplete,
// and which has as its underlying type the named type B.
// Determine the (final, unnamed) underlying type by resolving
// any forward chain (they always end in an unnamed type).
named.underlying = underlying(named.underlying)
// check and add associated methods
// TODO(gri) It's easy to create pathological cases where the
// current approach is incorrect: In general we need to know
// and add all methods _before_ type-checking the type.
// See http://play.golang.org/p/WMpE0q2wK8
check.addMethodDecls(obj)
}
func (check *Checker) addMethodDecls(obj *TypeName) {
// get associated methods
methods := check.methods[obj.name]
if len(methods) == 0 {
return // no methods
}
delete(check.methods, obj.name)
// use an objset to check for name conflicts
var mset objset
// spec: "If the base type is a struct type, the non-blank method
// and field names must be distinct."
base := obj.typ.(*Named)
if t, _ := base.underlying.(*Struct); t != nil {
for _, fld := range t.fields {
if fld.name != "_" {
assert(mset.insert(fld) == nil)
}
}
}
// Checker.Files may be called multiple times; additional package files
// may add methods to already type-checked types. Add pre-existing methods
// so that we can detect redeclarations.
for _, m := range base.methods {
assert(m.name != "_")
assert(mset.insert(m) == nil)
}
// type-check methods
for _, m := range methods {
// spec: "For a base type, the non-blank names of methods bound
// to it must be unique."
if m.name != "_" {
if alt := mset.insert(m); alt != nil {
switch alt.(type) {
case *Var:
check.errorf(m.pos, "field and method with the same name %s", m.name)
case *Func:
check.errorf(m.pos, "method %s already declared for %s", m.name, base)
default:
unreachable()
}
check.reportAltDecl(alt)
continue
}
}
check.objDecl(m, nil, nil)
// methods with blank _ names cannot be found - don't keep them
if m.name != "_" {
base.methods = append(base.methods, m)
}
}
}
func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
assert(obj.typ == nil)
// func declarations cannot use iota
assert(check.iota == nil)
sig := new(Signature)
obj.typ = sig // guard against cycles
fdecl := decl.fdecl
check.funcType(sig, fdecl.Recv, fdecl.Type)
if sig.recv == nil && obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) {
check.errorf(fdecl.Pos(), "func init must have no arguments and no return values")
// ok to continue
}
// function body must be type-checked after global declarations
// (functions implemented elsewhere have no body)
if !check.conf.IgnoreFuncBodies && fdecl.Body != nil {
check.later(obj.name, decl, sig, fdecl.Body)
}
}
func (check *Checker) declStmt(decl ast.Decl) {
pkg := check.pkg
switch d := decl.(type) {
case *ast.BadDecl:
// ignore
case *ast.GenDecl:
var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
for iota, spec := range d.Specs {
switch s := spec.(type) {
case *ast.ValueSpec:
switch d.Tok {
case token.CONST:
// determine which init exprs to use
switch {
case s.Type != nil || len(s.Values) > 0:
last = s
case last == nil:
last = new(ast.ValueSpec) // make sure last exists
}
// declare all constants
lhs := make([]*Const, len(s.Names))
for i, name := range s.Names {
obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota)))
lhs[i] = obj
var init ast.Expr
if i < len(last.Values) {
init = last.Values[i]
}
check.constDecl(obj, last.Type, init)
}
check.arityMatch(s, last)
// spec: "The scope of a constant or variable identifier declared
// inside a function begins at the end of the ConstSpec or VarSpec
// (ShortVarDecl for short variable declarations) and ends at the
// end of the innermost containing block."
scopePos := s.End()
for i, name := range s.Names {
check.declare(check.scope, name, lhs[i], scopePos)
}
case token.VAR:
lhs0 := make([]*Var, len(s.Names))
for i, name := range s.Names {
lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
}
// initialize all variables
for i, obj := range lhs0 {
var lhs []*Var
var init ast.Expr
switch len(s.Values) {
case len(s.Names):
// lhs and rhs match
init = s.Values[i]
case 1:
// rhs is expected to be a multi-valued expression
lhs = lhs0
init = s.Values[0]
default:
if i < len(s.Values) {
init = s.Values[i]
}
}
check.varDecl(obj, lhs, s.Type, init)
if len(s.Values) == 1 {
// If we have a single lhs variable we are done either way.
// If we have a single rhs expression, it must be a multi-
// valued expression, in which case handling the first lhs
// variable will cause all lhs variables to have a type
// assigned, and we are done as well.
if debug {
for _, obj := range lhs0 {
assert(obj.typ != nil)
}
}
break
}
}
check.arityMatch(s, nil)
// declare all variables
// (only at this point are the variable scopes (parents) set)
scopePos := s.End() // see constant declarations
for i, name := range s.Names {
// see constant declarations
check.declare(check.scope, name, lhs0[i], scopePos)
}
default:
check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
}
case *ast.TypeSpec:
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
// spec: "The scope of a type identifier declared inside a function
// begins at the identifier in the TypeSpec and ends at the end of
// the innermost containing block."
scopePos := s.Name.Pos()
check.declare(check.scope, s.Name, obj, scopePos)
check.typeDecl(obj, s.Type, nil, nil)
default:
check.invalidAST(s.Pos(), "const, type, or var declaration expected")
}
}
default:
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
}
}

103
vendor/golang.org/x/tools/go/types/errors.go generated vendored Normal file
View File

@ -0,0 +1,103 @@
// Copyright 2012 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.
// This file implements various error reporters.
package types
import (
"fmt"
"go/ast"
"go/token"
"strings"
)
func assert(p bool) {
if !p {
panic("assertion failed")
}
}
func unreachable() {
panic("unreachable")
}
func (check *Checker) qualifier(pkg *Package) string {
if pkg != check.pkg {
return pkg.path
}
return ""
}
func (check *Checker) sprintf(format string, args ...interface{}) string {
for i, arg := range args {
switch a := arg.(type) {
case nil:
arg = "<nil>"
case operand:
panic("internal error: should always pass *operand")
case *operand:
arg = operandString(a, check.qualifier)
case token.Pos:
arg = check.fset.Position(a).String()
case ast.Expr:
arg = ExprString(a)
case Object:
arg = ObjectString(a, check.qualifier)
case Type:
arg = TypeString(a, check.qualifier)
}
args[i] = arg
}
return fmt.Sprintf(format, args...)
}
func (check *Checker) trace(pos token.Pos, format string, args ...interface{}) {
fmt.Printf("%s:\t%s%s\n",
check.fset.Position(pos),
strings.Repeat(". ", check.indent),
check.sprintf(format, args...),
)
}
// dump is only needed for debugging
func (check *Checker) dump(format string, args ...interface{}) {
fmt.Println(check.sprintf(format, args...))
}
func (check *Checker) err(pos token.Pos, msg string, soft bool) {
err := Error{check.fset, pos, msg, soft}
if check.firstErr == nil {
check.firstErr = err
}
f := check.conf.Error
if f == nil {
panic(bailout{}) // report only first error
}
f(err)
}
func (check *Checker) error(pos token.Pos, msg string) {
check.err(pos, msg, false)
}
func (check *Checker) errorf(pos token.Pos, format string, args ...interface{}) {
check.err(pos, check.sprintf(format, args...), false)
}
func (check *Checker) softErrorf(pos token.Pos, format string, args ...interface{}) {
check.err(pos, check.sprintf(format, args...), true)
}
func (check *Checker) invalidAST(pos token.Pos, format string, args ...interface{}) {
check.errorf(pos, "invalid AST: "+format, args...)
}
func (check *Checker) invalidArg(pos token.Pos, format string, args ...interface{}) {
check.errorf(pos, "invalid argument: "+format, args...)
}
func (check *Checker) invalidOp(pos token.Pos, format string, args ...interface{}) {
check.errorf(pos, "invalid operation: "+format, args...)
}

87
vendor/golang.org/x/tools/go/types/eval.go generated vendored Normal file
View File

@ -0,0 +1,87 @@
// Copyright 2013 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
import (
"fmt"
"go/parser"
"go/token"
)
// Eval returns the type and, if constant, the value for the
// expression expr, evaluated at position pos of package pkg,
// which must have been derived from type-checking an AST with
// complete position information relative to the provided file
// set.
//
// If the expression contains function literals, their bodies
// are ignored (i.e., the bodies are not type-checked).
//
// If pkg == nil, the Universe scope is used and the provided
// position pos is ignored. If pkg != nil, and pos is invalid,
// the package scope is used. Otherwise, pos must belong to the
// package.
//
// An error is returned if pos is not within the package or
// if the node cannot be evaluated.
//
// Note: Eval should not be used instead of running Check to compute
// types and values, but in addition to Check. Eval will re-evaluate
// its argument each time, and it also does not know about the context
// in which an expression is used (e.g., an assignment). Thus, top-
// level untyped constants will return an untyped type rather then the
// respective context-specific type.
//
func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (tv TypeAndValue, err error) {
// determine scope
var scope *Scope
if pkg == nil {
scope = Universe
pos = token.NoPos
} else if !pos.IsValid() {
scope = pkg.scope
} else {
// The package scope extent (position information) may be
// incorrect (files spread accross a wide range of fset
// positions) - ignore it and just consider its children
// (file scopes).
for _, fscope := range pkg.scope.children {
if scope = fscope.Innermost(pos); scope != nil {
break
}
}
if scope == nil || debug {
s := scope
for s != nil && s != pkg.scope {
s = s.parent
}
// s == nil || s == pkg.scope
if s == nil {
return TypeAndValue{}, fmt.Errorf("no position %s found in package %s", fset.Position(pos), pkg.name)
}
}
}
// parse expressions
// BUG(gri) In case of type-checking errors below, the type checker
// doesn't have the correct file set for expr. The correct
// solution requires a ParseExpr that uses the incoming
// file set fset.
node, err := parser.ParseExpr(expr)
if err != nil {
return TypeAndValue{}, err
}
// initialize checker
check := NewChecker(nil, fset, pkg, nil)
check.scope = scope
check.pos = pos
defer check.handleBailout(&err)
// evaluate node
var x operand
check.rawExpr(&x, node, nil)
return TypeAndValue{x.mode, x.typ, x.val}, err
}

1497
vendor/golang.org/x/tools/go/types/expr.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

220
vendor/golang.org/x/tools/go/types/exprstring.go generated vendored Normal file
View File

@ -0,0 +1,220 @@
// Copyright 2013 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.
// This file implements printing of expressions.
package types
import (
"bytes"
"go/ast"
)
// ExprString returns the (possibly simplified) string representation for x.
func ExprString(x ast.Expr) string {
var buf bytes.Buffer
WriteExpr(&buf, x)
return buf.String()
}
// WriteExpr writes the (possibly simplified) string representation for x to buf.
func WriteExpr(buf *bytes.Buffer, x ast.Expr) {
// The AST preserves source-level parentheses so there is
// no need to introduce them here to correct for different
// operator precedences. (This assumes that the AST was
// generated by a Go parser.)
switch x := x.(type) {
default:
buf.WriteString("(bad expr)") // nil, ast.BadExpr, ast.KeyValueExpr
case *ast.Ident:
buf.WriteString(x.Name)
case *ast.Ellipsis:
buf.WriteString("...")
if x.Elt != nil {
WriteExpr(buf, x.Elt)
}
case *ast.BasicLit:
buf.WriteString(x.Value)
case *ast.FuncLit:
buf.WriteByte('(')
WriteExpr(buf, x.Type)
buf.WriteString(" literal)") // simplified
case *ast.CompositeLit:
buf.WriteByte('(')
WriteExpr(buf, x.Type)
buf.WriteString(" literal)") // simplified
case *ast.ParenExpr:
buf.WriteByte('(')
WriteExpr(buf, x.X)
buf.WriteByte(')')
case *ast.SelectorExpr:
WriteExpr(buf, x.X)
buf.WriteByte('.')
buf.WriteString(x.Sel.Name)
case *ast.IndexExpr:
WriteExpr(buf, x.X)
buf.WriteByte('[')
WriteExpr(buf, x.Index)
buf.WriteByte(']')
case *ast.SliceExpr:
WriteExpr(buf, x.X)
buf.WriteByte('[')
if x.Low != nil {
WriteExpr(buf, x.Low)
}
buf.WriteByte(':')
if x.High != nil {
WriteExpr(buf, x.High)
}
if x.Slice3 {
buf.WriteByte(':')
if x.Max != nil {
WriteExpr(buf, x.Max)
}
}
buf.WriteByte(']')
case *ast.TypeAssertExpr:
WriteExpr(buf, x.X)
buf.WriteString(".(")
WriteExpr(buf, x.Type)
buf.WriteByte(')')
case *ast.CallExpr:
WriteExpr(buf, x.Fun)
buf.WriteByte('(')
for i, arg := range x.Args {
if i > 0 {
buf.WriteString(", ")
}
WriteExpr(buf, arg)
}
if x.Ellipsis.IsValid() {
buf.WriteString("...")
}
buf.WriteByte(')')
case *ast.StarExpr:
buf.WriteByte('*')
WriteExpr(buf, x.X)
case *ast.UnaryExpr:
buf.WriteString(x.Op.String())
WriteExpr(buf, x.X)
case *ast.BinaryExpr:
WriteExpr(buf, x.X)
buf.WriteByte(' ')
buf.WriteString(x.Op.String())
buf.WriteByte(' ')
WriteExpr(buf, x.Y)
case *ast.ArrayType:
buf.WriteByte('[')
if x.Len != nil {
WriteExpr(buf, x.Len)
}
buf.WriteByte(']')
WriteExpr(buf, x.Elt)
case *ast.StructType:
buf.WriteString("struct{")
writeFieldList(buf, x.Fields, "; ", false)
buf.WriteByte('}')
case *ast.FuncType:
buf.WriteString("func")
writeSigExpr(buf, x)
case *ast.InterfaceType:
buf.WriteString("interface{")
writeFieldList(buf, x.Methods, "; ", true)
buf.WriteByte('}')
case *ast.MapType:
buf.WriteString("map[")
WriteExpr(buf, x.Key)
buf.WriteByte(']')
WriteExpr(buf, x.Value)
case *ast.ChanType:
var s string
switch x.Dir {
case ast.SEND:
s = "chan<- "
case ast.RECV:
s = "<-chan "
default:
s = "chan "
}
buf.WriteString(s)
WriteExpr(buf, x.Value)
}
}
func writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) {
buf.WriteByte('(')
writeFieldList(buf, sig.Params, ", ", false)
buf.WriteByte(')')
res := sig.Results
n := res.NumFields()
if n == 0 {
// no result
return
}
buf.WriteByte(' ')
if n == 1 && len(res.List[0].Names) == 0 {
// single unnamed result
WriteExpr(buf, res.List[0].Type)
return
}
// multiple or named result(s)
buf.WriteByte('(')
writeFieldList(buf, res, ", ", false)
buf.WriteByte(')')
}
func writeFieldList(buf *bytes.Buffer, fields *ast.FieldList, sep string, iface bool) {
for i, f := range fields.List {
if i > 0 {
buf.WriteString(sep)
}
// field list names
for i, name := range f.Names {
if i > 0 {
buf.WriteString(", ")
}
buf.WriteString(name.Name)
}
// types of interface methods consist of signatures only
if sig, _ := f.Type.(*ast.FuncType); sig != nil && iface {
writeSigExpr(buf, sig)
continue
}
// named fields are separated with a blank from the field type
if len(f.Names) > 0 {
buf.WriteByte(' ')
}
WriteExpr(buf, f.Type)
// ignore tag
}
}

17
vendor/golang.org/x/tools/go/types/go11.go generated vendored Normal file
View File

@ -0,0 +1,17 @@
// Copyright 2013 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.
// +build !go1.2
package types
import "go/ast"
func slice3(x *ast.SliceExpr) bool {
return false
}
func sliceMax(x *ast.SliceExpr) ast.Expr {
return nil
}

17
vendor/golang.org/x/tools/go/types/go12.go generated vendored Normal file
View File

@ -0,0 +1,17 @@
// Copyright 2013 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.
// +build go1.2
package types
import "go/ast"
func slice3(x *ast.SliceExpr) bool {
return x.Slice3
}
func sliceMax(x *ast.SliceExpr) ast.Expr {
return x.Max
}

Some files were not shown because too many files have changed in this diff Show More