Add generated file
This PR adds generated files under pkg/client and vendor folder.
This commit is contained in:
627
vendor/golang.org/x/tools/go/ast/astutil/enclosing.go
generated
vendored
Normal file
627
vendor/golang.org/x/tools/go/ast/astutil/enclosing.go
generated
vendored
Normal file
@@ -0,0 +1,627 @@
|
||||
// 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}?
|
||||
|
||||
case *ast.BadDecl, *ast.BadExpr, *ast.BadStmt:
|
||||
// nop
|
||||
}
|
||||
|
||||
// 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:
|
||||
if len(n.Args) == 1 && !n.Ellipsis.IsValid() {
|
||||
return "function call (or conversion)"
|
||||
}
|
||||
return "function call"
|
||||
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))
|
||||
}
|
195
vendor/golang.org/x/tools/go/ast/astutil/enclosing_test.go
generated
vendored
Normal file
195
vendor/golang.org/x/tools/go/ast/astutil/enclosing_test.go
generated
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
// 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_test
|
||||
|
||||
// This file defines tests of PathEnclosingInterval.
|
||||
|
||||
// TODO(adonovan): exhaustive tests that run over the whole input
|
||||
// tree, not just handcrafted examples.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
// pathToString returns a string containing the concrete types of the
|
||||
// nodes in path.
|
||||
func pathToString(path []ast.Node) string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprint(&buf, "[")
|
||||
for i, n := range path {
|
||||
if i > 0 {
|
||||
fmt.Fprint(&buf, " ")
|
||||
}
|
||||
fmt.Fprint(&buf, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast."))
|
||||
}
|
||||
fmt.Fprint(&buf, "]")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// findInterval parses input and returns the [start, end) positions of
|
||||
// the first occurrence of substr in input. f==nil indicates failure;
|
||||
// an error has already been reported in that case.
|
||||
//
|
||||
func findInterval(t *testing.T, fset *token.FileSet, input, substr string) (f *ast.File, start, end token.Pos) {
|
||||
f, err := parser.ParseFile(fset, "<input>", input, 0)
|
||||
if err != nil {
|
||||
t.Errorf("parse error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
i := strings.Index(input, substr)
|
||||
if i < 0 {
|
||||
t.Errorf("%q is not a substring of input", substr)
|
||||
f = nil
|
||||
return
|
||||
}
|
||||
|
||||
filePos := fset.File(f.Package)
|
||||
return f, filePos.Pos(i), filePos.Pos(i + len(substr))
|
||||
}
|
||||
|
||||
// Common input for following tests.
|
||||
const input = `
|
||||
// Hello.
|
||||
package main
|
||||
import "fmt"
|
||||
func f() {}
|
||||
func main() {
|
||||
z := (x + y) // add them
|
||||
f() // NB: ExprStmt and its CallExpr have same Pos/End
|
||||
}
|
||||
`
|
||||
|
||||
func TestPathEnclosingInterval_Exact(t *testing.T) {
|
||||
// For the exact tests, we check that a substring is mapped to
|
||||
// the canonical string for the node it denotes.
|
||||
tests := []struct {
|
||||
substr string // first occurrence of this string indicates interval
|
||||
node string // complete text of expected containing node
|
||||
}{
|
||||
{"package",
|
||||
input[11 : len(input)-1]},
|
||||
{"\npack",
|
||||
input[11 : len(input)-1]},
|
||||
{"main",
|
||||
"main"},
|
||||
{"import",
|
||||
"import \"fmt\""},
|
||||
{"\"fmt\"",
|
||||
"\"fmt\""},
|
||||
{"\nfunc f() {}\n",
|
||||
"func f() {}"},
|
||||
{"x ",
|
||||
"x"},
|
||||
{" y",
|
||||
"y"},
|
||||
{"z",
|
||||
"z"},
|
||||
{" + ",
|
||||
"x + y"},
|
||||
{" :=",
|
||||
"z := (x + y)"},
|
||||
{"x + y",
|
||||
"x + y"},
|
||||
{"(x + y)",
|
||||
"(x + y)"},
|
||||
{" (x + y) ",
|
||||
"(x + y)"},
|
||||
{" (x + y) // add",
|
||||
"(x + y)"},
|
||||
{"func",
|
||||
"func f() {}"},
|
||||
{"func f() {}",
|
||||
"func f() {}"},
|
||||
{"\nfun",
|
||||
"func f() {}"},
|
||||
{" f",
|
||||
"f"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
path, exact := astutil.PathEnclosingInterval(f, start, end)
|
||||
if !exact {
|
||||
t.Errorf("PathEnclosingInterval(%q) not exact", test.substr)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(path) == 0 {
|
||||
if test.node != "" {
|
||||
t.Errorf("PathEnclosingInterval(%q).path: got [], want %q",
|
||||
test.substr, test.node)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if got := input[path[0].Pos():path[0].End()]; got != test.node {
|
||||
t.Errorf("PathEnclosingInterval(%q): got %q, want %q (path was %s)",
|
||||
test.substr, got, test.node, pathToString(path))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathEnclosingInterval_Paths(t *testing.T) {
|
||||
// For these tests, we check only the path of the enclosing
|
||||
// node, but not its complete text because it's often quite
|
||||
// large when !exact.
|
||||
tests := []struct {
|
||||
substr string // first occurrence of this string indicates interval
|
||||
path string // the pathToString(),exact of the expected path
|
||||
}{
|
||||
{"// add",
|
||||
"[BlockStmt FuncDecl File],false"},
|
||||
{"(x + y",
|
||||
"[ParenExpr AssignStmt BlockStmt FuncDecl File],false"},
|
||||
{"x +",
|
||||
"[BinaryExpr ParenExpr AssignStmt BlockStmt FuncDecl File],false"},
|
||||
{"z := (x",
|
||||
"[AssignStmt BlockStmt FuncDecl File],false"},
|
||||
{"func f",
|
||||
"[FuncDecl File],false"},
|
||||
{"func f()",
|
||||
"[FuncDecl File],false"},
|
||||
{" f()",
|
||||
"[FuncDecl File],false"},
|
||||
{"() {}",
|
||||
"[FuncDecl File],false"},
|
||||
{"// Hello",
|
||||
"[File],false"},
|
||||
{" f",
|
||||
"[Ident FuncDecl File],true"},
|
||||
{"func ",
|
||||
"[FuncDecl File],true"},
|
||||
{"mai",
|
||||
"[Ident File],true"},
|
||||
{"f() // NB",
|
||||
"[CallExpr ExprStmt BlockStmt FuncDecl File],true"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
f, start, end := findInterval(t, new(token.FileSet), input, test.substr)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
path, exact := astutil.PathEnclosingInterval(f, start, end)
|
||||
if got := fmt.Sprintf("%s,%v", pathToString(path), exact); got != test.path {
|
||||
t.Errorf("PathEnclosingInterval(%q): got %q, want %q",
|
||||
test.substr, got, test.path)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
471
vendor/golang.org/x/tools/go/ast/astutil/imports.go
generated
vendored
Normal file
471
vendor/golang.org/x/tools/go/ast/astutil/imports.go
generated
vendored
Normal file
@@ -0,0 +1,471 @@
|
||||
// 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
|
||||
|
||||
isThirdPartyPath = isThirdParty(ipath)
|
||||
)
|
||||
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 and find best
|
||||
// matched import spec.
|
||||
// 1. Always prefer import spec with longest shared prefix.
|
||||
// 2. While match length is 0,
|
||||
// - for stdlib package: prefer first import spec.
|
||||
// - for third party package: prefer first third party import spec.
|
||||
// We cannot use last import spec as best match for third party package
|
||||
// because grouped imports are usually placed last by goimports -local
|
||||
// flag.
|
||||
// See issue #19190.
|
||||
seenAnyThirdParty := false
|
||||
for j, spec := range gen.Specs {
|
||||
impspec := spec.(*ast.ImportSpec)
|
||||
p := importPath(impspec)
|
||||
n := matchLen(p, ipath)
|
||||
if n > bestMatch || (bestMatch == 0 && !seenAnyThirdParty && isThirdPartyPath) {
|
||||
bestMatch = n
|
||||
impDecl = gen
|
||||
impIndex = j
|
||||
}
|
||||
seenAnyThirdParty = seenAnyThirdParty || isThirdParty(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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, preceded by a blank line, 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
|
||||
}
|
||||
// +2 for a blank line
|
||||
impDecl.TokPos = c.End() + 2
|
||||
}
|
||||
}
|
||||
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 {
|
||||
// If there is a comment after an existing import, preserve the comment
|
||||
// position by adding the new import after the comment.
|
||||
if spec, ok := impDecl.Specs[insertAt-1].(*ast.ImportSpec); ok && spec.Comment != nil {
|
||||
pos = spec.Comment.End()
|
||||
} else {
|
||||
// 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)
|
||||
|
||||
if len(f.Decls) <= 1 {
|
||||
return true
|
||||
}
|
||||
|
||||
// Merge all the import declarations into the first one.
|
||||
var first *ast.GenDecl
|
||||
for i := 0; i < len(f.Decls); i++ {
|
||||
decl := f.Decls[i]
|
||||
gen, ok := decl.(*ast.GenDecl)
|
||||
if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") {
|
||||
continue
|
||||
}
|
||||
if first == nil {
|
||||
first = gen
|
||||
continue // Don't touch the first one.
|
||||
}
|
||||
// We now know there is more than one package in this import
|
||||
// declaration. Ensure that it ends up parenthesized.
|
||||
first.Lparen = first.Pos()
|
||||
// Move the imports of the other import declaration to the first one.
|
||||
for _, spec := range gen.Specs {
|
||||
spec.(*ast.ImportSpec).Path.ValuePos = first.Pos()
|
||||
first.Specs = append(first.Specs, spec)
|
||||
}
|
||||
f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
|
||||
i--
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func isThirdParty(importPath string) bool {
|
||||
// Third party package import path usually contains "." (".com", ".org", ...)
|
||||
// This logic is taken from golang.org/x/tools/imports package.
|
||||
return strings.Contains(importPath, ".")
|
||||
}
|
||||
|
||||
// DeleteImport deletes the import path from the file f, if present.
|
||||
func DeleteImport(fset *token.FileSet, f *ast.File, path string) (deleted bool) {
|
||||
return DeleteNamedImport(fset, f, "", path)
|
||||
}
|
||||
|
||||
// DeleteNamedImport deletes the import with the given name and path from the file f, if present.
|
||||
func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (deleted bool) {
|
||||
var delspecs []*ast.ImportSpec
|
||||
var delcomments []*ast.CommentGroup
|
||||
|
||||
// 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 impspec.Name == nil && name != "" {
|
||||
continue
|
||||
}
|
||||
if impspec.Name != nil && impspec.Name.Name != name {
|
||||
continue
|
||||
}
|
||||
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 {
|
||||
if impspec.Doc != nil {
|
||||
delcomments = append(delcomments, impspec.Doc)
|
||||
}
|
||||
if impspec.Comment != nil {
|
||||
delcomments = append(delcomments, impspec.Comment)
|
||||
}
|
||||
for _, cg := range f.Comments {
|
||||
// Found comment on the same line as the import spec.
|
||||
if cg.End() < impspec.Pos() && fset.Position(cg.End()).Line == fset.Position(impspec.Pos()).Line {
|
||||
delcomments = append(delcomments, cg)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
spec := gen.Specs[0].(*ast.ImportSpec)
|
||||
|
||||
// Move the documentation right after the import decl.
|
||||
if spec.Doc != nil {
|
||||
for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Doc.Pos()).Line {
|
||||
fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line)
|
||||
}
|
||||
}
|
||||
for _, cg := range f.Comments {
|
||||
if cg.End() < spec.Pos() && fset.Position(cg.End()).Line == fset.Position(spec.Pos()).Line {
|
||||
for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Pos()).Line {
|
||||
fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
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 if line != fset.File(gen.Rparen).LineCount() {
|
||||
// There was no blank line. Close the hole.
|
||||
fset.File(gen.Rparen).MergeLine(line)
|
||||
}
|
||||
}
|
||||
j--
|
||||
}
|
||||
}
|
||||
|
||||
// Delete imports 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete comments from f.Comments.
|
||||
for i := 0; i < len(f.Comments); i++ {
|
||||
cg := f.Comments[i]
|
||||
for j, del := range delcomments {
|
||||
if cg == del {
|
||||
copy(f.Comments[i:], f.Comments[i+1:])
|
||||
f.Comments = f.Comments[:len(f.Comments)-1]
|
||||
copy(delcomments[j:], delcomments[j+1:])
|
||||
delcomments = delcomments[:len(delcomments)-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
|
||||
}
|
1820
vendor/golang.org/x/tools/go/ast/astutil/imports_test.go
generated
vendored
Normal file
1820
vendor/golang.org/x/tools/go/ast/astutil/imports_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
477
vendor/golang.org/x/tools/go/ast/astutil/rewrite.go
generated
vendored
Normal file
477
vendor/golang.org/x/tools/go/ast/astutil/rewrite.go
generated
vendored
Normal file
@@ -0,0 +1,477 @@
|
||||
// Copyright 2017 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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"reflect"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// An ApplyFunc is invoked by Apply for each node n, even if n is nil,
|
||||
// before and/or after the node's children, using a Cursor describing
|
||||
// the current node and providing operations on it.
|
||||
//
|
||||
// The return value of ApplyFunc controls the syntax tree traversal.
|
||||
// See Apply for details.
|
||||
type ApplyFunc func(*Cursor) bool
|
||||
|
||||
// Apply traverses a syntax tree recursively, starting with root,
|
||||
// and calling pre and post for each node as described below.
|
||||
// Apply returns the syntax tree, possibly modified.
|
||||
//
|
||||
// If pre is not nil, it is called for each node before the node's
|
||||
// children are traversed (pre-order). If pre returns false, no
|
||||
// children are traversed, and post is not called for that node.
|
||||
//
|
||||
// If post is not nil, and a prior call of pre didn't return false,
|
||||
// post is called for each node after its children are traversed
|
||||
// (post-order). If post returns false, traversal is terminated and
|
||||
// Apply returns immediately.
|
||||
//
|
||||
// Only fields that refer to AST nodes are considered children;
|
||||
// i.e., token.Pos, Scopes, Objects, and fields of basic types
|
||||
// (strings, etc.) are ignored.
|
||||
//
|
||||
// Children are traversed in the order in which they appear in the
|
||||
// respective node's struct definition. A package's files are
|
||||
// traversed in the filenames' alphabetical order.
|
||||
//
|
||||
func Apply(root ast.Node, pre, post ApplyFunc) (result ast.Node) {
|
||||
parent := &struct{ ast.Node }{root}
|
||||
defer func() {
|
||||
if r := recover(); r != nil && r != abort {
|
||||
panic(r)
|
||||
}
|
||||
result = parent.Node
|
||||
}()
|
||||
a := &application{pre: pre, post: post}
|
||||
a.apply(parent, "Node", nil, root)
|
||||
return
|
||||
}
|
||||
|
||||
var abort = new(int) // singleton, to signal termination of Apply
|
||||
|
||||
// A Cursor describes a node encountered during Apply.
|
||||
// Information about the node and its parent is available
|
||||
// from the Node, Parent, Name, and Index methods.
|
||||
//
|
||||
// If p is a variable of type and value of the current parent node
|
||||
// c.Parent(), and f is the field identifier with name c.Name(),
|
||||
// the following invariants hold:
|
||||
//
|
||||
// p.f == c.Node() if c.Index() < 0
|
||||
// p.f[c.Index()] == c.Node() if c.Index() >= 0
|
||||
//
|
||||
// The methods Replace, Delete, InsertBefore, and InsertAfter
|
||||
// can be used to change the AST without disrupting Apply.
|
||||
type Cursor struct {
|
||||
parent ast.Node
|
||||
name string
|
||||
iter *iterator // valid if non-nil
|
||||
node ast.Node
|
||||
}
|
||||
|
||||
// Node returns the current Node.
|
||||
func (c *Cursor) Node() ast.Node { return c.node }
|
||||
|
||||
// Parent returns the parent of the current Node.
|
||||
func (c *Cursor) Parent() ast.Node { return c.parent }
|
||||
|
||||
// Name returns the name of the parent Node field that contains the current Node.
|
||||
// If the parent is a *ast.Package and the current Node is a *ast.File, Name returns
|
||||
// the filename for the current Node.
|
||||
func (c *Cursor) Name() string { return c.name }
|
||||
|
||||
// Index reports the index >= 0 of the current Node in the slice of Nodes that
|
||||
// contains it, or a value < 0 if the current Node is not part of a slice.
|
||||
// The index of the current node changes if InsertBefore is called while
|
||||
// processing the current node.
|
||||
func (c *Cursor) Index() int {
|
||||
if c.iter != nil {
|
||||
return c.iter.index
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// field returns the current node's parent field value.
|
||||
func (c *Cursor) field() reflect.Value {
|
||||
return reflect.Indirect(reflect.ValueOf(c.parent)).FieldByName(c.name)
|
||||
}
|
||||
|
||||
// Replace replaces the current Node with n.
|
||||
// The replacement node is not walked by Apply.
|
||||
func (c *Cursor) Replace(n ast.Node) {
|
||||
if _, ok := c.node.(*ast.File); ok {
|
||||
file, ok := n.(*ast.File)
|
||||
if !ok {
|
||||
panic("attempt to replace *ast.File with non-*ast.File")
|
||||
}
|
||||
c.parent.(*ast.Package).Files[c.name] = file
|
||||
return
|
||||
}
|
||||
|
||||
v := c.field()
|
||||
if i := c.Index(); i >= 0 {
|
||||
v = v.Index(i)
|
||||
}
|
||||
v.Set(reflect.ValueOf(n))
|
||||
}
|
||||
|
||||
// Delete deletes the current Node from its containing slice.
|
||||
// If the current Node is not part of a slice, Delete panics.
|
||||
// As a special case, if the current node is a package file,
|
||||
// Delete removes it from the package's Files map.
|
||||
func (c *Cursor) Delete() {
|
||||
if _, ok := c.node.(*ast.File); ok {
|
||||
delete(c.parent.(*ast.Package).Files, c.name)
|
||||
return
|
||||
}
|
||||
|
||||
i := c.Index()
|
||||
if i < 0 {
|
||||
panic("Delete node not contained in slice")
|
||||
}
|
||||
v := c.field()
|
||||
l := v.Len()
|
||||
reflect.Copy(v.Slice(i, l), v.Slice(i+1, l))
|
||||
v.Index(l - 1).Set(reflect.Zero(v.Type().Elem()))
|
||||
v.SetLen(l - 1)
|
||||
c.iter.step--
|
||||
}
|
||||
|
||||
// InsertAfter inserts n after the current Node in its containing slice.
|
||||
// If the current Node is not part of a slice, InsertAfter panics.
|
||||
// Apply does not walk n.
|
||||
func (c *Cursor) InsertAfter(n ast.Node) {
|
||||
i := c.Index()
|
||||
if i < 0 {
|
||||
panic("InsertAfter node not contained in slice")
|
||||
}
|
||||
v := c.field()
|
||||
v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem())))
|
||||
l := v.Len()
|
||||
reflect.Copy(v.Slice(i+2, l), v.Slice(i+1, l))
|
||||
v.Index(i + 1).Set(reflect.ValueOf(n))
|
||||
c.iter.step++
|
||||
}
|
||||
|
||||
// InsertBefore inserts n before the current Node in its containing slice.
|
||||
// If the current Node is not part of a slice, InsertBefore panics.
|
||||
// Apply will not walk n.
|
||||
func (c *Cursor) InsertBefore(n ast.Node) {
|
||||
i := c.Index()
|
||||
if i < 0 {
|
||||
panic("InsertBefore node not contained in slice")
|
||||
}
|
||||
v := c.field()
|
||||
v.Set(reflect.Append(v, reflect.Zero(v.Type().Elem())))
|
||||
l := v.Len()
|
||||
reflect.Copy(v.Slice(i+1, l), v.Slice(i, l))
|
||||
v.Index(i).Set(reflect.ValueOf(n))
|
||||
c.iter.index++
|
||||
}
|
||||
|
||||
// application carries all the shared data so we can pass it around cheaply.
|
||||
type application struct {
|
||||
pre, post ApplyFunc
|
||||
cursor Cursor
|
||||
iter iterator
|
||||
}
|
||||
|
||||
func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast.Node) {
|
||||
// convert typed nil into untyped nil
|
||||
if v := reflect.ValueOf(n); v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
n = nil
|
||||
}
|
||||
|
||||
// avoid heap-allocating a new cursor for each apply call; reuse a.cursor instead
|
||||
saved := a.cursor
|
||||
a.cursor.parent = parent
|
||||
a.cursor.name = name
|
||||
a.cursor.iter = iter
|
||||
a.cursor.node = n
|
||||
|
||||
if a.pre != nil && !a.pre(&a.cursor) {
|
||||
a.cursor = saved
|
||||
return
|
||||
}
|
||||
|
||||
// walk children
|
||||
// (the order of the cases matches the order of the corresponding node types in go/ast)
|
||||
switch n := n.(type) {
|
||||
case nil:
|
||||
// nothing to do
|
||||
|
||||
// Comments and fields
|
||||
case *ast.Comment:
|
||||
// nothing to do
|
||||
|
||||
case *ast.CommentGroup:
|
||||
if n != nil {
|
||||
a.applyList(n, "List")
|
||||
}
|
||||
|
||||
case *ast.Field:
|
||||
a.apply(n, "Doc", nil, n.Doc)
|
||||
a.applyList(n, "Names")
|
||||
a.apply(n, "Type", nil, n.Type)
|
||||
a.apply(n, "Tag", nil, n.Tag)
|
||||
a.apply(n, "Comment", nil, n.Comment)
|
||||
|
||||
case *ast.FieldList:
|
||||
a.applyList(n, "List")
|
||||
|
||||
// Expressions
|
||||
case *ast.BadExpr, *ast.Ident, *ast.BasicLit:
|
||||
// nothing to do
|
||||
|
||||
case *ast.Ellipsis:
|
||||
a.apply(n, "Elt", nil, n.Elt)
|
||||
|
||||
case *ast.FuncLit:
|
||||
a.apply(n, "Type", nil, n.Type)
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
|
||||
case *ast.CompositeLit:
|
||||
a.apply(n, "Type", nil, n.Type)
|
||||
a.applyList(n, "Elts")
|
||||
|
||||
case *ast.ParenExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
a.apply(n, "Sel", nil, n.Sel)
|
||||
|
||||
case *ast.IndexExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
a.apply(n, "Index", nil, n.Index)
|
||||
|
||||
case *ast.SliceExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
a.apply(n, "Low", nil, n.Low)
|
||||
a.apply(n, "High", nil, n.High)
|
||||
a.apply(n, "Max", nil, n.Max)
|
||||
|
||||
case *ast.TypeAssertExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
a.apply(n, "Type", nil, n.Type)
|
||||
|
||||
case *ast.CallExpr:
|
||||
a.apply(n, "Fun", nil, n.Fun)
|
||||
a.applyList(n, "Args")
|
||||
|
||||
case *ast.StarExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
|
||||
case *ast.BinaryExpr:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
a.apply(n, "Y", nil, n.Y)
|
||||
|
||||
case *ast.KeyValueExpr:
|
||||
a.apply(n, "Key", nil, n.Key)
|
||||
a.apply(n, "Value", nil, n.Value)
|
||||
|
||||
// Types
|
||||
case *ast.ArrayType:
|
||||
a.apply(n, "Len", nil, n.Len)
|
||||
a.apply(n, "Elt", nil, n.Elt)
|
||||
|
||||
case *ast.StructType:
|
||||
a.apply(n, "Fields", nil, n.Fields)
|
||||
|
||||
case *ast.FuncType:
|
||||
a.apply(n, "Params", nil, n.Params)
|
||||
a.apply(n, "Results", nil, n.Results)
|
||||
|
||||
case *ast.InterfaceType:
|
||||
a.apply(n, "Methods", nil, n.Methods)
|
||||
|
||||
case *ast.MapType:
|
||||
a.apply(n, "Key", nil, n.Key)
|
||||
a.apply(n, "Value", nil, n.Value)
|
||||
|
||||
case *ast.ChanType:
|
||||
a.apply(n, "Value", nil, n.Value)
|
||||
|
||||
// Statements
|
||||
case *ast.BadStmt:
|
||||
// nothing to do
|
||||
|
||||
case *ast.DeclStmt:
|
||||
a.apply(n, "Decl", nil, n.Decl)
|
||||
|
||||
case *ast.EmptyStmt:
|
||||
// nothing to do
|
||||
|
||||
case *ast.LabeledStmt:
|
||||
a.apply(n, "Label", nil, n.Label)
|
||||
a.apply(n, "Stmt", nil, n.Stmt)
|
||||
|
||||
case *ast.ExprStmt:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
|
||||
case *ast.SendStmt:
|
||||
a.apply(n, "Chan", nil, n.Chan)
|
||||
a.apply(n, "Value", nil, n.Value)
|
||||
|
||||
case *ast.IncDecStmt:
|
||||
a.apply(n, "X", nil, n.X)
|
||||
|
||||
case *ast.AssignStmt:
|
||||
a.applyList(n, "Lhs")
|
||||
a.applyList(n, "Rhs")
|
||||
|
||||
case *ast.GoStmt:
|
||||
a.apply(n, "Call", nil, n.Call)
|
||||
|
||||
case *ast.DeferStmt:
|
||||
a.apply(n, "Call", nil, n.Call)
|
||||
|
||||
case *ast.ReturnStmt:
|
||||
a.applyList(n, "Results")
|
||||
|
||||
case *ast.BranchStmt:
|
||||
a.apply(n, "Label", nil, n.Label)
|
||||
|
||||
case *ast.BlockStmt:
|
||||
a.applyList(n, "List")
|
||||
|
||||
case *ast.IfStmt:
|
||||
a.apply(n, "Init", nil, n.Init)
|
||||
a.apply(n, "Cond", nil, n.Cond)
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
a.apply(n, "Else", nil, n.Else)
|
||||
|
||||
case *ast.CaseClause:
|
||||
a.applyList(n, "List")
|
||||
a.applyList(n, "Body")
|
||||
|
||||
case *ast.SwitchStmt:
|
||||
a.apply(n, "Init", nil, n.Init)
|
||||
a.apply(n, "Tag", nil, n.Tag)
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
|
||||
case *ast.TypeSwitchStmt:
|
||||
a.apply(n, "Init", nil, n.Init)
|
||||
a.apply(n, "Assign", nil, n.Assign)
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
|
||||
case *ast.CommClause:
|
||||
a.apply(n, "Comm", nil, n.Comm)
|
||||
a.applyList(n, "Body")
|
||||
|
||||
case *ast.SelectStmt:
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
|
||||
case *ast.ForStmt:
|
||||
a.apply(n, "Init", nil, n.Init)
|
||||
a.apply(n, "Cond", nil, n.Cond)
|
||||
a.apply(n, "Post", nil, n.Post)
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
|
||||
case *ast.RangeStmt:
|
||||
a.apply(n, "Key", nil, n.Key)
|
||||
a.apply(n, "Value", nil, n.Value)
|
||||
a.apply(n, "X", nil, n.X)
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
|
||||
// Declarations
|
||||
case *ast.ImportSpec:
|
||||
a.apply(n, "Doc", nil, n.Doc)
|
||||
a.apply(n, "Name", nil, n.Name)
|
||||
a.apply(n, "Path", nil, n.Path)
|
||||
a.apply(n, "Comment", nil, n.Comment)
|
||||
|
||||
case *ast.ValueSpec:
|
||||
a.apply(n, "Doc", nil, n.Doc)
|
||||
a.applyList(n, "Names")
|
||||
a.apply(n, "Type", nil, n.Type)
|
||||
a.applyList(n, "Values")
|
||||
a.apply(n, "Comment", nil, n.Comment)
|
||||
|
||||
case *ast.TypeSpec:
|
||||
a.apply(n, "Doc", nil, n.Doc)
|
||||
a.apply(n, "Name", nil, n.Name)
|
||||
a.apply(n, "Type", nil, n.Type)
|
||||
a.apply(n, "Comment", nil, n.Comment)
|
||||
|
||||
case *ast.BadDecl:
|
||||
// nothing to do
|
||||
|
||||
case *ast.GenDecl:
|
||||
a.apply(n, "Doc", nil, n.Doc)
|
||||
a.applyList(n, "Specs")
|
||||
|
||||
case *ast.FuncDecl:
|
||||
a.apply(n, "Doc", nil, n.Doc)
|
||||
a.apply(n, "Recv", nil, n.Recv)
|
||||
a.apply(n, "Name", nil, n.Name)
|
||||
a.apply(n, "Type", nil, n.Type)
|
||||
a.apply(n, "Body", nil, n.Body)
|
||||
|
||||
// Files and packages
|
||||
case *ast.File:
|
||||
a.apply(n, "Doc", nil, n.Doc)
|
||||
a.apply(n, "Name", nil, n.Name)
|
||||
a.applyList(n, "Decls")
|
||||
// Don't walk n.Comments; they have either been walked already if
|
||||
// they are Doc comments, or they can be easily walked explicitly.
|
||||
|
||||
case *ast.Package:
|
||||
// collect and sort names for reproducible behavior
|
||||
var names []string
|
||||
for name := range n.Files {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
a.apply(n, name, nil, n.Files[name])
|
||||
}
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("Apply: unexpected node type %T", n))
|
||||
}
|
||||
|
||||
if a.post != nil && !a.post(&a.cursor) {
|
||||
panic(abort)
|
||||
}
|
||||
|
||||
a.cursor = saved
|
||||
}
|
||||
|
||||
// An iterator controls iteration over a slice of nodes.
|
||||
type iterator struct {
|
||||
index, step int
|
||||
}
|
||||
|
||||
func (a *application) applyList(parent ast.Node, name string) {
|
||||
// avoid heap-allocating a new iterator for each applyList call; reuse a.iter instead
|
||||
saved := a.iter
|
||||
a.iter.index = 0
|
||||
for {
|
||||
// must reload parent.name each time, since cursor modifications might change it
|
||||
v := reflect.Indirect(reflect.ValueOf(parent)).FieldByName(name)
|
||||
if a.iter.index >= v.Len() {
|
||||
break
|
||||
}
|
||||
|
||||
// element x may be nil in a bad AST - be cautious
|
||||
var x ast.Node
|
||||
if e := v.Index(a.iter.index); e.IsValid() {
|
||||
x = e.Interface().(ast.Node)
|
||||
}
|
||||
|
||||
a.iter.step = 1
|
||||
a.apply(parent, name, &a.iter, x)
|
||||
a.iter.index += a.iter.step
|
||||
}
|
||||
a.iter = saved
|
||||
}
|
248
vendor/golang.org/x/tools/go/ast/astutil/rewrite_test.go
generated
vendored
Normal file
248
vendor/golang.org/x/tools/go/ast/astutil/rewrite_test.go
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
// Copyright 2017 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_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/ast/astutil"
|
||||
)
|
||||
|
||||
var rewriteTests = [...]struct {
|
||||
name string
|
||||
orig, want string
|
||||
pre, post astutil.ApplyFunc
|
||||
}{
|
||||
{name: "nop", orig: "package p\n", want: "package p\n"},
|
||||
|
||||
{name: "replace",
|
||||
orig: `package p
|
||||
|
||||
var x int
|
||||
`,
|
||||
want: `package p
|
||||
|
||||
var t T
|
||||
`,
|
||||
post: func(c *astutil.Cursor) bool {
|
||||
if _, ok := c.Node().(*ast.ValueSpec); ok {
|
||||
c.Replace(valspec("t", "T"))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
},
|
||||
|
||||
{name: "set doc strings",
|
||||
orig: `package p
|
||||
|
||||
const z = 0
|
||||
|
||||
type T struct{}
|
||||
|
||||
var x int
|
||||
`,
|
||||
want: `package p
|
||||
// a foo is a foo
|
||||
const z = 0
|
||||
// a foo is a foo
|
||||
type T struct{}
|
||||
// a foo is a foo
|
||||
var x int
|
||||
`,
|
||||
post: func(c *astutil.Cursor) bool {
|
||||
if _, ok := c.Parent().(*ast.GenDecl); ok && c.Name() == "Doc" && c.Node() == nil {
|
||||
c.Replace(&ast.CommentGroup{List: []*ast.Comment{{Text: "// a foo is a foo"}}})
|
||||
}
|
||||
return true
|
||||
},
|
||||
},
|
||||
|
||||
{name: "insert names",
|
||||
orig: `package p
|
||||
|
||||
const a = 1
|
||||
`,
|
||||
want: `package p
|
||||
|
||||
const a, b, c = 1, 2, 3
|
||||
`,
|
||||
pre: func(c *astutil.Cursor) bool {
|
||||
if _, ok := c.Parent().(*ast.ValueSpec); ok {
|
||||
switch c.Name() {
|
||||
case "Names":
|
||||
c.InsertAfter(ast.NewIdent("c"))
|
||||
c.InsertAfter(ast.NewIdent("b"))
|
||||
case "Values":
|
||||
c.InsertAfter(&ast.BasicLit{Kind: token.INT, Value: "3"})
|
||||
c.InsertAfter(&ast.BasicLit{Kind: token.INT, Value: "2"})
|
||||
}
|
||||
}
|
||||
return true
|
||||
},
|
||||
},
|
||||
|
||||
{name: "insert",
|
||||
orig: `package p
|
||||
|
||||
var (
|
||||
x int
|
||||
y int
|
||||
)
|
||||
`,
|
||||
want: `package p
|
||||
|
||||
var before1 int
|
||||
var before2 int
|
||||
|
||||
var (
|
||||
x int
|
||||
y int
|
||||
)
|
||||
var after2 int
|
||||
var after1 int
|
||||
`,
|
||||
pre: func(c *astutil.Cursor) bool {
|
||||
if _, ok := c.Node().(*ast.GenDecl); ok {
|
||||
c.InsertBefore(vardecl("before1", "int"))
|
||||
c.InsertAfter(vardecl("after1", "int"))
|
||||
c.InsertAfter(vardecl("after2", "int"))
|
||||
c.InsertBefore(vardecl("before2", "int"))
|
||||
}
|
||||
return true
|
||||
},
|
||||
},
|
||||
|
||||
{name: "delete",
|
||||
orig: `package p
|
||||
|
||||
var x int
|
||||
var y int
|
||||
var z int
|
||||
`,
|
||||
want: `package p
|
||||
|
||||
var y int
|
||||
var z int
|
||||
`,
|
||||
pre: func(c *astutil.Cursor) bool {
|
||||
n := c.Node()
|
||||
if d, ok := n.(*ast.GenDecl); ok && d.Specs[0].(*ast.ValueSpec).Names[0].Name == "x" {
|
||||
c.Delete()
|
||||
}
|
||||
return true
|
||||
},
|
||||
},
|
||||
|
||||
{name: "insertafter-delete",
|
||||
orig: `package p
|
||||
|
||||
var x int
|
||||
var y int
|
||||
var z int
|
||||
`,
|
||||
want: `package p
|
||||
|
||||
var x1 int
|
||||
|
||||
var y int
|
||||
var z int
|
||||
`,
|
||||
pre: func(c *astutil.Cursor) bool {
|
||||
n := c.Node()
|
||||
if d, ok := n.(*ast.GenDecl); ok && d.Specs[0].(*ast.ValueSpec).Names[0].Name == "x" {
|
||||
c.InsertAfter(vardecl("x1", "int"))
|
||||
c.Delete()
|
||||
}
|
||||
return true
|
||||
},
|
||||
},
|
||||
|
||||
{name: "delete-insertafter",
|
||||
orig: `package p
|
||||
|
||||
var x int
|
||||
var y int
|
||||
var z int
|
||||
`,
|
||||
want: `package p
|
||||
|
||||
var y int
|
||||
var x1 int
|
||||
var z int
|
||||
`,
|
||||
pre: func(c *astutil.Cursor) bool {
|
||||
n := c.Node()
|
||||
if d, ok := n.(*ast.GenDecl); ok && d.Specs[0].(*ast.ValueSpec).Names[0].Name == "x" {
|
||||
c.Delete()
|
||||
// The cursor is now effectively atop the 'var y int' node.
|
||||
c.InsertAfter(vardecl("x1", "int"))
|
||||
}
|
||||
return true
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func valspec(name, typ string) *ast.ValueSpec {
|
||||
return &ast.ValueSpec{Names: []*ast.Ident{ast.NewIdent(name)},
|
||||
Type: ast.NewIdent(typ),
|
||||
}
|
||||
}
|
||||
|
||||
func vardecl(name, typ string) *ast.GenDecl {
|
||||
return &ast.GenDecl{
|
||||
Tok: token.VAR,
|
||||
Specs: []ast.Spec{valspec(name, typ)},
|
||||
}
|
||||
}
|
||||
|
||||
func TestRewrite(t *testing.T) {
|
||||
t.Run("*", func(t *testing.T) {
|
||||
for _, test := range rewriteTests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, test.name, test.orig, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
n := astutil.Apply(f, test.pre, test.post)
|
||||
var buf bytes.Buffer
|
||||
if err := format.Node(&buf, fset, n); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := buf.String()
|
||||
if got != test.want {
|
||||
t.Errorf("got:\n\n%s\nwant:\n\n%s\n", got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var sink ast.Node
|
||||
|
||||
func BenchmarkRewrite(b *testing.B) {
|
||||
for _, test := range rewriteTests {
|
||||
b.Run(test.name, func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
b.StopTimer()
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, test.name, test.orig, parser.ParseComments)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
b.StartTimer()
|
||||
sink = astutil.Apply(f, test.pre, test.post)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
14
vendor/golang.org/x/tools/go/ast/astutil/util.go
generated
vendored
Normal file
14
vendor/golang.org/x/tools/go/ast/astutil/util.go
generated
vendored
Normal 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
|
||||
}
|
||||
}
|
198
vendor/golang.org/x/tools/go/buildutil/allpackages.go
generated
vendored
Normal file
198
vendor/golang.org/x/tools/go/buildutil/allpackages.go
generated
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
// 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 package 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.
|
||||
// All package paths are canonical, and thus may contain "/vendor/".
|
||||
//
|
||||
// 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 package path of
|
||||
// each Go package it finds in any source directory of the specified
|
||||
// build context (e.g. $GOROOT or an element of $GOPATH).
|
||||
// All package paths are canonical, and thus may contain "/vendor/".
|
||||
//
|
||||
// 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)) {
|
||||
ch := make(chan item)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, root := range ctxt.SrcDirs() {
|
||||
root := root
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
allPackages(ctxt, 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)
|
||||
}
|
||||
|
||||
// We use a process-wide counting semaphore to limit
|
||||
// the number of parallel calls to ReadDir.
|
||||
var ioLimit = make(chan bool, 20)
|
||||
|
||||
func allPackages(ctxt *build.Context, 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
|
||||
}
|
||||
|
||||
ioLimit <- true
|
||||
files, err := ReadDir(ctxt, dir)
|
||||
<-ioLimit
|
||||
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()
|
||||
}
|
||||
|
||||
// ExpandPatterns returns the set of packages matched by patterns,
|
||||
// which may have the following forms:
|
||||
//
|
||||
// golang.org/x/tools/cmd/guru # a single package
|
||||
// golang.org/x/tools/... # all packages beneath dir
|
||||
// ... # the entire workspace.
|
||||
//
|
||||
// Order is significant: a pattern preceded by '-' removes matching
|
||||
// packages from the set. For example, these patterns match all encoding
|
||||
// packages except encoding/xml:
|
||||
//
|
||||
// encoding/... -encoding/xml
|
||||
//
|
||||
// A trailing slash in a pattern is ignored. (Path components of Go
|
||||
// package names are separated by slash, not the platform's path separator.)
|
||||
//
|
||||
func ExpandPatterns(ctxt *build.Context, patterns []string) map[string]bool {
|
||||
// TODO(adonovan): support other features of 'go list':
|
||||
// - "std"/"cmd"/"all" meta-packages
|
||||
// - "..." not at the end of a pattern
|
||||
// - relative patterns using "./" or "../" prefix
|
||||
|
||||
pkgs := make(map[string]bool)
|
||||
doPkg := func(pkg string, neg bool) {
|
||||
if neg {
|
||||
delete(pkgs, pkg)
|
||||
} else {
|
||||
pkgs[pkg] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Scan entire workspace if wildcards are present.
|
||||
// TODO(adonovan): opt: scan only the necessary subtrees of the workspace.
|
||||
var all []string
|
||||
for _, arg := range patterns {
|
||||
if strings.HasSuffix(arg, "...") {
|
||||
all = AllPackages(ctxt)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, arg := range patterns {
|
||||
if arg == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
neg := arg[0] == '-'
|
||||
if neg {
|
||||
arg = arg[1:]
|
||||
}
|
||||
|
||||
if arg == "..." {
|
||||
// ... matches all packages
|
||||
for _, pkg := range all {
|
||||
doPkg(pkg, neg)
|
||||
}
|
||||
} else if dir := strings.TrimSuffix(arg, "/..."); dir != arg {
|
||||
// dir/... matches all packages beneath dir
|
||||
for _, pkg := range all {
|
||||
if strings.HasPrefix(pkg, dir) &&
|
||||
(len(pkg) == len(dir) || pkg[len(dir)] == '/') {
|
||||
doPkg(pkg, neg)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// single package
|
||||
doPkg(strings.TrimSuffix(arg, "/"), neg)
|
||||
}
|
||||
}
|
||||
|
||||
return pkgs
|
||||
}
|
83
vendor/golang.org/x/tools/go/buildutil/allpackages_test.go
generated
vendored
Normal file
83
vendor/golang.org/x/tools/go/buildutil/allpackages_test.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
// 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.
|
||||
|
||||
// Incomplete source tree on Android.
|
||||
|
||||
// +build !android
|
||||
|
||||
package buildutil_test
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
)
|
||||
|
||||
func TestAllPackages(t *testing.T) {
|
||||
if runtime.Compiler == "gccgo" {
|
||||
t.Skip("gccgo has no standard packages")
|
||||
}
|
||||
|
||||
all := buildutil.AllPackages(&build.Default)
|
||||
|
||||
set := make(map[string]bool)
|
||||
for _, pkg := range all {
|
||||
set[pkg] = true
|
||||
}
|
||||
|
||||
const wantAtLeast = 250
|
||||
if len(all) < wantAtLeast {
|
||||
t.Errorf("Found only %d packages, want at least %d", len(all), wantAtLeast)
|
||||
}
|
||||
|
||||
for _, want := range []string{"fmt", "crypto/sha256", "golang.org/x/tools/go/buildutil"} {
|
||||
if !set[want] {
|
||||
t.Errorf("Package %q not found; got %s", want, all)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpandPatterns(t *testing.T) {
|
||||
tree := make(map[string]map[string]string)
|
||||
for _, pkg := range []string{
|
||||
"encoding",
|
||||
"encoding/xml",
|
||||
"encoding/hex",
|
||||
"encoding/json",
|
||||
"fmt",
|
||||
} {
|
||||
tree[pkg] = make(map[string]string)
|
||||
}
|
||||
ctxt := buildutil.FakeContext(tree)
|
||||
|
||||
for _, test := range []struct {
|
||||
patterns string
|
||||
want string
|
||||
}{
|
||||
{"", ""},
|
||||
{"fmt", "fmt"},
|
||||
{"nosuchpkg", "nosuchpkg"},
|
||||
{"nosuchdir/...", ""},
|
||||
{"...", "encoding encoding/hex encoding/json encoding/xml fmt"},
|
||||
{"encoding/... -encoding/xml", "encoding encoding/hex encoding/json"},
|
||||
{"... -encoding/...", "fmt"},
|
||||
{"encoding", "encoding"},
|
||||
{"encoding/", "encoding"},
|
||||
} {
|
||||
var pkgs []string
|
||||
for pkg := range buildutil.ExpandPatterns(ctxt, strings.Fields(test.patterns)) {
|
||||
pkgs = append(pkgs, pkg)
|
||||
}
|
||||
sort.Strings(pkgs)
|
||||
got := strings.Join(pkgs, " ")
|
||||
if got != test.want {
|
||||
t.Errorf("ExpandPatterns(%s) = %s, want %s",
|
||||
test.patterns, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
109
vendor/golang.org/x/tools/go/buildutil/fakecontext.go
generated
vendored
Normal file
109
vendor/golang.org/x/tools/go/buildutil/fakecontext.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
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.Compiler = "gc"
|
||||
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 }
|
103
vendor/golang.org/x/tools/go/buildutil/overlay.go
generated
vendored
Normal file
103
vendor/golang.org/x/tools/go/buildutil/overlay.go
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright 2016 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 (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// OverlayContext overlays a build.Context with additional files from
|
||||
// a map. Files in the map take precedence over other files.
|
||||
//
|
||||
// In addition to plain string comparison, two file names are
|
||||
// considered equal if their base names match and their directory
|
||||
// components point at the same directory on the file system. That is,
|
||||
// symbolic links are followed for directories, but not files.
|
||||
//
|
||||
// A common use case for OverlayContext is to allow editors to pass in
|
||||
// a set of unsaved, modified files.
|
||||
//
|
||||
// Currently, only the Context.OpenFile function will respect the
|
||||
// overlay. This may change in the future.
|
||||
func OverlayContext(orig *build.Context, overlay map[string][]byte) *build.Context {
|
||||
// TODO(dominikh): Implement IsDir, HasSubdir and ReadDir
|
||||
|
||||
rc := func(data []byte) (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(bytes.NewBuffer(data)), nil
|
||||
}
|
||||
|
||||
copy := *orig // make a copy
|
||||
ctxt := ©
|
||||
ctxt.OpenFile = func(path string) (io.ReadCloser, error) {
|
||||
// Fast path: names match exactly.
|
||||
if content, ok := overlay[path]; ok {
|
||||
return rc(content)
|
||||
}
|
||||
|
||||
// Slow path: check for same file under a different
|
||||
// alias, perhaps due to a symbolic link.
|
||||
for filename, content := range overlay {
|
||||
if sameFile(path, filename) {
|
||||
return rc(content)
|
||||
}
|
||||
}
|
||||
|
||||
return OpenFile(orig, path)
|
||||
}
|
||||
return ctxt
|
||||
}
|
||||
|
||||
// ParseOverlayArchive parses an archive containing Go files and their
|
||||
// contents. The result is intended to be used with OverlayContext.
|
||||
//
|
||||
//
|
||||
// Archive format
|
||||
//
|
||||
// The archive consists of a series of files. Each file consists of a
|
||||
// name, a decimal file size and the file contents, separated by
|
||||
// newlinews. No newline follows after the file contents.
|
||||
func ParseOverlayArchive(archive io.Reader) (map[string][]byte, error) {
|
||||
overlay := make(map[string][]byte)
|
||||
r := bufio.NewReader(archive)
|
||||
for {
|
||||
// Read file name.
|
||||
filename, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break // OK
|
||||
}
|
||||
return nil, fmt.Errorf("reading archive file name: %v", err)
|
||||
}
|
||||
filename = filepath.Clean(strings.TrimSpace(filename))
|
||||
|
||||
// Read file size.
|
||||
sz, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading size of archive file %s: %v", filename, err)
|
||||
}
|
||||
sz = strings.TrimSpace(sz)
|
||||
size, err := strconv.ParseUint(sz, 10, 32)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing size of archive file %s: %v", filename, err)
|
||||
}
|
||||
|
||||
// Read file content.
|
||||
content := make([]byte, size)
|
||||
if _, err := io.ReadFull(r, content); err != nil {
|
||||
return nil, fmt.Errorf("reading archive file %s: %v", filename, err)
|
||||
}
|
||||
overlay[filename] = content
|
||||
}
|
||||
|
||||
return overlay, nil
|
||||
}
|
70
vendor/golang.org/x/tools/go/buildutil/overlay_test.go
generated
vendored
Normal file
70
vendor/golang.org/x/tools/go/buildutil/overlay_test.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
package buildutil_test
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
)
|
||||
|
||||
func TestParseOverlayArchive(t *testing.T) {
|
||||
var tt = []struct {
|
||||
in string
|
||||
out map[string][]byte
|
||||
hasErr bool
|
||||
}{
|
||||
{
|
||||
"a.go\n5\n12345",
|
||||
map[string][]byte{"a.go": []byte("12345")},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"a.go\n5\n1234",
|
||||
nil,
|
||||
true,
|
||||
},
|
||||
{
|
||||
"a.go\n5\n12345b.go\n4\n1234",
|
||||
map[string][]byte{"a.go": []byte("12345"), "b.go": []byte("1234")},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tt {
|
||||
got, err := buildutil.ParseOverlayArchive(strings.NewReader(test.in))
|
||||
if err == nil && test.hasErr {
|
||||
t.Errorf("expected error for %q", test.in)
|
||||
}
|
||||
if err != nil && !test.hasErr {
|
||||
t.Errorf("unexpected error %v for %q", err, test.in)
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.out) {
|
||||
t.Errorf("got %#v, want %#v", got, test.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestOverlay(t *testing.T) {
|
||||
ctx := &build.Default
|
||||
ov := map[string][]byte{
|
||||
"/somewhere/a.go": []byte("file contents"),
|
||||
}
|
||||
names := []string{"/somewhere/a.go", "/somewhere//a.go"}
|
||||
ctx = buildutil.OverlayContext(ctx, ov)
|
||||
for _, name := range names {
|
||||
f, err := buildutil.OpenFile(ctx, name)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
b, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error %v", err)
|
||||
}
|
||||
if got, expected := string(b), string(ov["/somewhere/a.go"]); got != expected {
|
||||
t.Errorf("read %q, expected %q", got, expected)
|
||||
}
|
||||
}
|
||||
}
|
75
vendor/golang.org/x/tools/go/buildutil/tags.go
generated
vendored
Normal file
75
vendor/golang.org/x/tools/go/buildutil/tags.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
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 and flag.Getter interfaces 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.TagsFlagDoc)
|
||||
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 (v *TagsFlag) Get() interface{} { return *v }
|
||||
|
||||
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'
|
||||
}
|
28
vendor/golang.org/x/tools/go/buildutil/tags_test.go
generated
vendored
Normal file
28
vendor/golang.org/x/tools/go/buildutil/tags_test.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package buildutil_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"go/build"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
)
|
||||
|
||||
func TestTags(t *testing.T) {
|
||||
f := flag.NewFlagSet("TestTags", flag.PanicOnError)
|
||||
var ctxt build.Context
|
||||
f.Var((*buildutil.TagsFlag)(&ctxt.BuildTags), "tags", buildutil.TagsFlagDoc)
|
||||
f.Parse([]string{"-tags", ` 'one'"two" 'three "four"'`, "rest"})
|
||||
|
||||
// BuildTags
|
||||
want := []string{"one", "two", "three \"four\""}
|
||||
if !reflect.DeepEqual(ctxt.BuildTags, want) {
|
||||
t.Errorf("BuildTags = %q, want %q", ctxt.BuildTags, want)
|
||||
}
|
||||
|
||||
// Args()
|
||||
if want := []string{"rest"}; !reflect.DeepEqual(f.Args(), want) {
|
||||
t.Errorf("f.Args() = %q, want %q", f.Args(), want)
|
||||
}
|
||||
}
|
212
vendor/golang.org/x/tools/go/buildutil/util.go
generated
vendored
Normal file
212
vendor/golang.org/x/tools/go/buildutil/util.go
generated
vendored
Normal file
@@ -0,0 +1,212 @@
|
||||
// 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).
|
||||
//
|
||||
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 importPath, ok := HasSubdir(ctxt, srcdirSlash, dirSlash); ok {
|
||||
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.)
|
||||
|
||||
// hasSubdir calls ctxt.HasSubdir (if not nil) or else uses
|
||||
// the local file system to answer the question.
|
||||
func HasSubdir(ctxt *build.Context, root, dir string) (rel string, ok bool) {
|
||||
if f := ctxt.HasSubdir; f != nil {
|
||||
return f(root, dir)
|
||||
}
|
||||
|
||||
// Try using paths we received.
|
||||
if rel, ok = hasSubdir(root, dir); ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Try expanding symlinks and comparing
|
||||
// expanded against unexpanded and
|
||||
// expanded against expanded.
|
||||
rootSym, _ := filepath.EvalSymlinks(root)
|
||||
dirSym, _ := filepath.EvalSymlinks(dir)
|
||||
|
||||
if rel, ok = hasSubdir(rootSym, dir); ok {
|
||||
return
|
||||
}
|
||||
if rel, ok = hasSubdir(root, dirSym); ok {
|
||||
return
|
||||
}
|
||||
return hasSubdir(rootSym, dirSym)
|
||||
}
|
||||
|
||||
func hasSubdir(root, dir string) (rel string, ok bool) {
|
||||
const sep = string(filepath.Separator)
|
||||
root = filepath.Clean(root)
|
||||
if !strings.HasSuffix(root, sep) {
|
||||
root += sep
|
||||
}
|
||||
|
||||
dir = filepath.Clean(dir)
|
||||
if !strings.HasPrefix(dir, root) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return filepath.ToSlash(dir[len(root):]), true
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// sameFile returns true if x and y have the same basename and denote
|
||||
// the same file.
|
||||
//
|
||||
func sameFile(x, y string) bool {
|
||||
if path.Clean(x) == path.Clean(y) {
|
||||
return true
|
||||
}
|
||||
if filepath.Base(x) == filepath.Base(y) { // (optimisation)
|
||||
if xi, err := os.Stat(x); err == nil {
|
||||
if yi, err := os.Stat(y); err == nil {
|
||||
return os.SameFile(xi, yi)
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
85
vendor/golang.org/x/tools/go/buildutil/util_test.go
generated
vendored
Normal file
85
vendor/golang.org/x/tools/go/buildutil/util_test.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
// 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_test
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
)
|
||||
|
||||
func TestContainingPackage(t *testing.T) {
|
||||
if runtime.Compiler == "gccgo" {
|
||||
t.Skip("gccgo has no GOROOT")
|
||||
}
|
||||
|
||||
// unvirtualized:
|
||||
goroot := runtime.GOROOT()
|
||||
gopath := gopathContainingTools(t)
|
||||
|
||||
type Test struct {
|
||||
gopath, filename, wantPkg string
|
||||
}
|
||||
|
||||
tests := []Test{
|
||||
{gopath, goroot + "/src/fmt/print.go", "fmt"},
|
||||
{gopath, goroot + "/src/encoding/json/foo.go", "encoding/json"},
|
||||
{gopath, goroot + "/src/encoding/missing/foo.go", "(not found)"},
|
||||
{gopath, gopath + "/src/golang.org/x/tools/go/buildutil/util_test.go",
|
||||
"golang.org/x/tools/go/buildutil"},
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" && runtime.GOOS != "plan9" {
|
||||
// Make a symlink to gopath for test
|
||||
tmp, err := ioutil.TempDir(os.TempDir(), "go")
|
||||
if err != nil {
|
||||
t.Errorf("Unable to create a temporary directory in %s", os.TempDir())
|
||||
}
|
||||
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
// symlink between $GOPATH/src and /tmp/go/src
|
||||
// in order to test all possible symlink cases
|
||||
if err := os.Symlink(gopath+"/src", tmp+"/src"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tests = append(tests, []Test{
|
||||
{gopath, tmp + "/src/golang.org/x/tools/go/buildutil/util_test.go", "golang.org/x/tools/go/buildutil"},
|
||||
{tmp, gopath + "/src/golang.org/x/tools/go/buildutil/util_test.go", "golang.org/x/tools/go/buildutil"},
|
||||
{tmp, tmp + "/src/golang.org/x/tools/go/buildutil/util_test.go", "golang.org/x/tools/go/buildutil"},
|
||||
}...)
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
var got string
|
||||
var buildContext = build.Default
|
||||
buildContext.GOPATH = test.gopath
|
||||
bp, err := buildutil.ContainingPackage(&buildContext, ".", test.filename)
|
||||
if err != nil {
|
||||
got = "(not found)"
|
||||
} else {
|
||||
got = bp.ImportPath
|
||||
}
|
||||
if got != test.wantPkg {
|
||||
t.Errorf("ContainingPackage(%q) = %s, want %s", test.filename, got, test.wantPkg)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(adonovan): test on virtualized GOPATH too.
|
||||
}
|
||||
|
||||
// gopathContainingTools returns the path of the GOPATH workspace
|
||||
// with golang.org/x/tools, or fails the test if it can't locate it.
|
||||
func gopathContainingTools(t *testing.T) string {
|
||||
p, err := build.Import("golang.org/x/tools", "", build.FindOnly)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return p.Root
|
||||
}
|
48
vendor/golang.org/x/tools/go/buildutil/util_windows_test.go
generated
vendored
Normal file
48
vendor/golang.org/x/tools/go/buildutil/util_windows_test.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// 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 buildutil_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
)
|
||||
|
||||
func testContainingPackageCaseFold(file, want string) error {
|
||||
bp, err := buildutil.ContainingPackage(&build.Default, ".", file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if got := bp.ImportPath; got != want {
|
||||
return fmt.Errorf("ContainingPackage(%q) = %s, want %s", file, got, want)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestContainingPackageCaseFold(t *testing.T) {
|
||||
path := filepath.Join(runtime.GOROOT(), `src\fmt\print.go`)
|
||||
err := testContainingPackageCaseFold(path, "fmt")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
vol := filepath.VolumeName(path)
|
||||
if len(vol) != 2 || vol[1] != ':' {
|
||||
t.Fatalf("GOROOT path has unexpected volume name: %v", vol)
|
||||
}
|
||||
rest := path[len(vol):]
|
||||
err = testContainingPackageCaseFold(strings.ToUpper(vol)+rest, "fmt")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
err = testContainingPackageCaseFold(strings.ToLower(vol)+rest, "fmt")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
129
vendor/golang.org/x/tools/go/callgraph/callgraph.go
generated
vendored
Normal file
129
vendor/golang.org/x/tools/go/callgraph/callgraph.go
generated
vendored
Normal 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)
|
||||
}
|
139
vendor/golang.org/x/tools/go/callgraph/cha/cha.go
generated
vendored
Normal file
139
vendor/golang.org/x/tools/go/callgraph/cha/cha.go
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
// 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 cha computes the call graph of a Go program using the Class
|
||||
// Hierarchy Analysis (CHA) algorithm.
|
||||
//
|
||||
// CHA was first described in "Optimization of Object-Oriented Programs
|
||||
// Using Static Class Hierarchy Analysis", Jeffrey Dean, David Grove,
|
||||
// and Craig Chambers, ECOOP'95.
|
||||
//
|
||||
// CHA is related to RTA (see go/callgraph/rta); the difference is that
|
||||
// CHA conservatively computes the entire "implements" relation between
|
||||
// interfaces and concrete types ahead of time, whereas RTA uses dynamic
|
||||
// programming to construct it on the fly as it encounters new functions
|
||||
// reachable from main. CHA may thus include spurious call edges for
|
||||
// types that haven't been instantiated yet, or types that are never
|
||||
// instantiated.
|
||||
//
|
||||
// Since CHA conservatively assumes that all functions are address-taken
|
||||
// and all concrete types are put into interfaces, it is sound to run on
|
||||
// partial programs, such as libraries without a main or test function.
|
||||
//
|
||||
package cha // import "golang.org/x/tools/go/callgraph/cha"
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/callgraph"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
// CallGraph computes the call graph of the specified program using the
|
||||
// Class Hierarchy Analysis algorithm.
|
||||
//
|
||||
func CallGraph(prog *ssa.Program) *callgraph.Graph {
|
||||
cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph
|
||||
|
||||
allFuncs := ssautil.AllFunctions(prog)
|
||||
|
||||
// funcsBySig contains all functions, keyed by signature. It is
|
||||
// the effective set of address-taken functions used to resolve
|
||||
// a dynamic call of a particular signature.
|
||||
var funcsBySig typeutil.Map // value is []*ssa.Function
|
||||
|
||||
// methodsByName contains all methods,
|
||||
// grouped by name for efficient lookup.
|
||||
// (methodsById would be better but not every SSA method has a go/types ID.)
|
||||
methodsByName := make(map[string][]*ssa.Function)
|
||||
|
||||
// An imethod represents an interface method I.m.
|
||||
// (There's no go/types object for it;
|
||||
// a *types.Func may be shared by many interfaces due to interface embedding.)
|
||||
type imethod struct {
|
||||
I *types.Interface
|
||||
id string
|
||||
}
|
||||
// methodsMemo records, for every abstract method call I.m on
|
||||
// interface type I, the set of concrete methods C.m of all
|
||||
// types C that satisfy interface I.
|
||||
//
|
||||
// Abstract methods may be shared by several interfaces,
|
||||
// hence we must pass I explicitly, not guess from m.
|
||||
//
|
||||
// methodsMemo is just a cache, so it needn't be a typeutil.Map.
|
||||
methodsMemo := make(map[imethod][]*ssa.Function)
|
||||
lookupMethods := func(I *types.Interface, m *types.Func) []*ssa.Function {
|
||||
id := m.Id()
|
||||
methods, ok := methodsMemo[imethod{I, id}]
|
||||
if !ok {
|
||||
for _, f := range methodsByName[m.Name()] {
|
||||
C := f.Signature.Recv().Type() // named or *named
|
||||
if types.Implements(C, I) {
|
||||
methods = append(methods, f)
|
||||
}
|
||||
}
|
||||
methodsMemo[imethod{I, id}] = methods
|
||||
}
|
||||
return methods
|
||||
}
|
||||
|
||||
for f := range allFuncs {
|
||||
if f.Signature.Recv() == nil {
|
||||
// Package initializers can never be address-taken.
|
||||
if f.Name() == "init" && f.Synthetic == "package initializer" {
|
||||
continue
|
||||
}
|
||||
funcs, _ := funcsBySig.At(f.Signature).([]*ssa.Function)
|
||||
funcs = append(funcs, f)
|
||||
funcsBySig.Set(f.Signature, funcs)
|
||||
} else {
|
||||
methodsByName[f.Name()] = append(methodsByName[f.Name()], f)
|
||||
}
|
||||
}
|
||||
|
||||
addEdge := func(fnode *callgraph.Node, site ssa.CallInstruction, g *ssa.Function) {
|
||||
gnode := cg.CreateNode(g)
|
||||
callgraph.AddEdge(fnode, site, gnode)
|
||||
}
|
||||
|
||||
addEdges := func(fnode *callgraph.Node, site ssa.CallInstruction, callees []*ssa.Function) {
|
||||
// Because every call to a highly polymorphic and
|
||||
// frequently used abstract method such as
|
||||
// (io.Writer).Write is assumed to call every concrete
|
||||
// Write method in the program, the call graph can
|
||||
// contain a lot of duplication.
|
||||
//
|
||||
// TODO(adonovan): opt: consider factoring the callgraph
|
||||
// API so that the Callers component of each edge is a
|
||||
// slice of nodes, not a singleton.
|
||||
for _, g := range callees {
|
||||
addEdge(fnode, site, g)
|
||||
}
|
||||
}
|
||||
|
||||
for f := range allFuncs {
|
||||
fnode := cg.CreateNode(f)
|
||||
for _, b := range f.Blocks {
|
||||
for _, instr := range b.Instrs {
|
||||
if site, ok := instr.(ssa.CallInstruction); ok {
|
||||
call := site.Common()
|
||||
if call.IsInvoke() {
|
||||
tiface := call.Value.Type().Underlying().(*types.Interface)
|
||||
addEdges(fnode, site, lookupMethods(tiface, call.Method))
|
||||
} else if g := call.StaticCallee(); g != nil {
|
||||
addEdge(fnode, site, g)
|
||||
} else if _, ok := call.Value.(*ssa.Builtin); !ok {
|
||||
callees, _ := funcsBySig.At(call.Signature()).([]*ssa.Function)
|
||||
addEdges(fnode, site, callees)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cg
|
||||
}
|
111
vendor/golang.org/x/tools/go/callgraph/cha/cha_test.go
generated
vendored
Normal file
111
vendor/golang.org/x/tools/go/callgraph/cha/cha_test.go
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
// 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.
|
||||
|
||||
// No testdata on Android.
|
||||
|
||||
// +build !android
|
||||
|
||||
package cha_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/callgraph"
|
||||
"golang.org/x/tools/go/callgraph/cha"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
)
|
||||
|
||||
var inputs = []string{
|
||||
"testdata/func.go",
|
||||
"testdata/iface.go",
|
||||
"testdata/recv.go",
|
||||
"testdata/issue23925.go",
|
||||
}
|
||||
|
||||
func expectation(f *ast.File) (string, token.Pos) {
|
||||
for _, c := range f.Comments {
|
||||
text := strings.TrimSpace(c.Text())
|
||||
if t := strings.TrimPrefix(text, "WANT:\n"); t != text {
|
||||
return t, c.Pos()
|
||||
}
|
||||
}
|
||||
return "", token.NoPos
|
||||
}
|
||||
|
||||
// TestCHA runs CHA on each file in inputs, prints the dynamic edges of
|
||||
// the call graph, and compares it with the golden results embedded in
|
||||
// the WANT comment at the end of the file.
|
||||
//
|
||||
func TestCHA(t *testing.T) {
|
||||
for _, filename := range inputs {
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Errorf("couldn't read file '%s': %s", filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
conf := loader.Config{
|
||||
ParserMode: parser.ParseComments,
|
||||
}
|
||||
f, err := conf.ParseFile(filename, content)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
want, pos := expectation(f)
|
||||
if pos == token.NoPos {
|
||||
t.Errorf("No WANT: comment in %s", filename)
|
||||
continue
|
||||
}
|
||||
|
||||
conf.CreateFromFiles("main", f)
|
||||
iprog, err := conf.Load()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
prog := ssautil.CreateProgram(iprog, 0)
|
||||
mainPkg := prog.Package(iprog.Created[0].Pkg)
|
||||
prog.Build()
|
||||
|
||||
cg := cha.CallGraph(prog)
|
||||
|
||||
if got := printGraph(cg, mainPkg.Pkg); got != want {
|
||||
t.Errorf("%s: got:\n%s\nwant:\n%s",
|
||||
prog.Fset.Position(pos), got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printGraph(cg *callgraph.Graph, from *types.Package) string {
|
||||
var edges []string
|
||||
callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error {
|
||||
if strings.Contains(e.Description(), "dynamic") {
|
||||
edges = append(edges, fmt.Sprintf("%s --> %s",
|
||||
e.Caller.Func.RelString(from),
|
||||
e.Callee.Func.RelString(from)))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
sort.Strings(edges)
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("Dynamic calls\n")
|
||||
for _, edge := range edges {
|
||||
fmt.Fprintf(&buf, " %s\n", edge)
|
||||
}
|
||||
return strings.TrimSpace(buf.String())
|
||||
}
|
23
vendor/golang.org/x/tools/go/callgraph/cha/testdata/func.go
generated
vendored
Normal file
23
vendor/golang.org/x/tools/go/callgraph/cha/testdata/func.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
// Test of dynamic function calls; no interfaces.
|
||||
|
||||
func A(int) {}
|
||||
|
||||
var (
|
||||
B = func(int) {}
|
||||
C = func(int) {}
|
||||
)
|
||||
|
||||
func f() {
|
||||
pfn := B
|
||||
pfn(0) // calls A, B, C, even though A is not even address-taken
|
||||
}
|
||||
|
||||
// WANT:
|
||||
// Dynamic calls
|
||||
// f --> A
|
||||
// f --> init$1
|
||||
// f --> init$2
|
65
vendor/golang.org/x/tools/go/callgraph/cha/testdata/iface.go
generated
vendored
Normal file
65
vendor/golang.org/x/tools/go/callgraph/cha/testdata/iface.go
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
// Test of interface calls. None of the concrete types are ever
|
||||
// instantiated or converted to interfaces.
|
||||
|
||||
type I interface {
|
||||
f()
|
||||
}
|
||||
|
||||
type J interface {
|
||||
f()
|
||||
g()
|
||||
}
|
||||
|
||||
type C int // implements I
|
||||
|
||||
func (*C) f()
|
||||
|
||||
type D int // implements I and J
|
||||
|
||||
func (*D) f()
|
||||
func (*D) g()
|
||||
|
||||
func one(i I, j J) {
|
||||
i.f() // calls *C and *D
|
||||
}
|
||||
|
||||
func two(i I, j J) {
|
||||
j.f() // calls *D (but not *C, even though it defines method f)
|
||||
}
|
||||
|
||||
func three(i I, j J) {
|
||||
j.g() // calls *D
|
||||
}
|
||||
|
||||
func four(i I, j J) {
|
||||
Jf := J.f
|
||||
if unknown {
|
||||
Jf = nil // suppress SSA constant propagation
|
||||
}
|
||||
Jf(nil) // calls *D
|
||||
}
|
||||
|
||||
func five(i I, j J) {
|
||||
jf := j.f
|
||||
if unknown {
|
||||
jf = nil // suppress SSA constant propagation
|
||||
}
|
||||
jf() // calls *D
|
||||
}
|
||||
|
||||
var unknown bool
|
||||
|
||||
// WANT:
|
||||
// Dynamic calls
|
||||
// (J).f$bound --> (*D).f
|
||||
// (J).f$thunk --> (*D).f
|
||||
// five --> (J).f$bound
|
||||
// four --> (J).f$thunk
|
||||
// one --> (*C).f
|
||||
// one --> (*D).f
|
||||
// three --> (*D).g
|
||||
// two --> (*D).f
|
38
vendor/golang.org/x/tools/go/callgraph/cha/testdata/issue23925.go
generated
vendored
Normal file
38
vendor/golang.org/x/tools/go/callgraph/cha/testdata/issue23925.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
// Regression test for https://github.com/golang/go/issues/23925
|
||||
|
||||
type stringFlagImpl string
|
||||
|
||||
func (*stringFlagImpl) Set(s string) error { return nil }
|
||||
|
||||
type boolFlagImpl bool
|
||||
|
||||
func (*boolFlagImpl) Set(s string) error { return nil }
|
||||
func (*boolFlagImpl) extra() {}
|
||||
|
||||
// A copy of flag.boolFlag interface, without a dependency.
|
||||
// Must appear first, so that it becomes the owner of the Set methods.
|
||||
type boolFlag interface {
|
||||
flagValue
|
||||
extra()
|
||||
}
|
||||
|
||||
// A copy of flag.Value, without adding a dependency.
|
||||
type flagValue interface {
|
||||
Set(string) error
|
||||
}
|
||||
|
||||
func main() {
|
||||
var x flagValue = new(stringFlagImpl)
|
||||
x.Set("")
|
||||
|
||||
var y boolFlag = new(boolFlagImpl)
|
||||
y.Set("")
|
||||
}
|
||||
|
||||
// WANT:
|
||||
// Dynamic calls
|
||||
// main --> (*boolFlagImpl).Set
|
||||
// main --> (*boolFlagImpl).Set
|
||||
// main --> (*stringFlagImpl).Set
|
37
vendor/golang.org/x/tools/go/callgraph/cha/testdata/recv.go
generated
vendored
Normal file
37
vendor/golang.org/x/tools/go/callgraph/cha/testdata/recv.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
type I interface {
|
||||
f()
|
||||
}
|
||||
|
||||
type J interface {
|
||||
g()
|
||||
}
|
||||
|
||||
type C int // C and *C implement I; *C implements J
|
||||
|
||||
func (C) f()
|
||||
func (*C) g()
|
||||
|
||||
type D int // *D implements I and J
|
||||
|
||||
func (*D) f()
|
||||
func (*D) g()
|
||||
|
||||
func f(i I) {
|
||||
i.f() // calls C, *C, *D
|
||||
}
|
||||
|
||||
func g(j J) {
|
||||
j.g() // calls *C, *D
|
||||
}
|
||||
|
||||
// WANT:
|
||||
// Dynamic calls
|
||||
// f --> (*C).f
|
||||
// f --> (*D).f
|
||||
// f --> (C).f
|
||||
// g --> (*C).g
|
||||
// g --> (*D).g
|
459
vendor/golang.org/x/tools/go/callgraph/rta/rta.go
generated
vendored
Normal file
459
vendor/golang.org/x/tools/go/callgraph/rta/rta.go
generated
vendored
Normal file
@@ -0,0 +1,459 @@
|
||||
// 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 package provides Rapid Type Analysis (RTA) for Go, a fast
|
||||
// algorithm for call graph construction and discovery of reachable code
|
||||
// (and hence dead code) and runtime types. The algorithm was first
|
||||
// described in:
|
||||
//
|
||||
// David F. Bacon and Peter F. Sweeney. 1996.
|
||||
// Fast static analysis of C++ virtual function calls. (OOPSLA '96)
|
||||
// http://doi.acm.org/10.1145/236337.236371
|
||||
//
|
||||
// The algorithm uses dynamic programming to tabulate the cross-product
|
||||
// of the set of known "address taken" functions with the set of known
|
||||
// dynamic calls of the same type. As each new address-taken function
|
||||
// is discovered, call graph edges are added from each known callsite,
|
||||
// and as each new call site is discovered, call graph edges are added
|
||||
// from it to each known address-taken function.
|
||||
//
|
||||
// A similar approach is used for dynamic calls via interfaces: it
|
||||
// tabulates the cross-product of the set of known "runtime types",
|
||||
// i.e. types that may appear in an interface value, or be derived from
|
||||
// one via reflection, with the set of known "invoke"-mode dynamic
|
||||
// calls. As each new "runtime type" is discovered, call edges are
|
||||
// added from the known call sites, and as each new call site is
|
||||
// discovered, call graph edges are added to each compatible
|
||||
// method.
|
||||
//
|
||||
// In addition, we must consider all exported methods of any runtime type
|
||||
// as reachable, since they may be called via reflection.
|
||||
//
|
||||
// Each time a newly added call edge causes a new function to become
|
||||
// reachable, the code of that function is analyzed for more call sites,
|
||||
// address-taken functions, and runtime types. The process continues
|
||||
// until a fixed point is achieved.
|
||||
//
|
||||
// The resulting call graph is less precise than one produced by pointer
|
||||
// analysis, but the algorithm is much faster. For example, running the
|
||||
// cmd/callgraph tool on its own source takes ~2.1s for RTA and ~5.4s
|
||||
// for points-to analysis.
|
||||
//
|
||||
package rta // import "golang.org/x/tools/go/callgraph/rta"
|
||||
|
||||
// TODO(adonovan): test it by connecting it to the interpreter and
|
||||
// replacing all "unreachable" functions by a special intrinsic, and
|
||||
// ensure that that intrinsic is never called.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/types"
|
||||
|
||||
"golang.org/x/tools/go/callgraph"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
// A Result holds the results of Rapid Type Analysis, which includes the
|
||||
// set of reachable functions/methods, runtime types, and the call graph.
|
||||
//
|
||||
type Result struct {
|
||||
// CallGraph is the discovered callgraph.
|
||||
// It does not include edges for calls made via reflection.
|
||||
CallGraph *callgraph.Graph
|
||||
|
||||
// Reachable contains the set of reachable functions and methods.
|
||||
// This includes exported methods of runtime types, since
|
||||
// they may be accessed via reflection.
|
||||
// The value indicates whether the function is address-taken.
|
||||
//
|
||||
// (We wrap the bool in a struct to avoid inadvertent use of
|
||||
// "if Reachable[f] {" to test for set membership.)
|
||||
Reachable map[*ssa.Function]struct{ AddrTaken bool }
|
||||
|
||||
// RuntimeTypes contains the set of types that are needed at
|
||||
// runtime, for interfaces or reflection.
|
||||
//
|
||||
// The value indicates whether the type is inaccessible to reflection.
|
||||
// Consider:
|
||||
// type A struct{B}
|
||||
// fmt.Println(new(A))
|
||||
// Types *A, A and B are accessible to reflection, but the unnamed
|
||||
// type struct{B} is not.
|
||||
RuntimeTypes typeutil.Map
|
||||
}
|
||||
|
||||
// Working state of the RTA algorithm.
|
||||
type rta struct {
|
||||
result *Result
|
||||
|
||||
prog *ssa.Program
|
||||
|
||||
worklist []*ssa.Function // list of functions to visit
|
||||
|
||||
// addrTakenFuncsBySig contains all address-taken *Functions, grouped by signature.
|
||||
// Keys are *types.Signature, values are map[*ssa.Function]bool sets.
|
||||
addrTakenFuncsBySig typeutil.Map
|
||||
|
||||
// dynCallSites contains all dynamic "call"-mode call sites, grouped by signature.
|
||||
// Keys are *types.Signature, values are unordered []ssa.CallInstruction.
|
||||
dynCallSites typeutil.Map
|
||||
|
||||
// invokeSites contains all "invoke"-mode call sites, grouped by interface.
|
||||
// Keys are *types.Interface (never *types.Named),
|
||||
// Values are unordered []ssa.CallInstruction sets.
|
||||
invokeSites typeutil.Map
|
||||
|
||||
// The following two maps together define the subset of the
|
||||
// m:n "implements" relation needed by the algorithm.
|
||||
|
||||
// concreteTypes maps each concrete type to the set of interfaces that it implements.
|
||||
// Keys are types.Type, values are unordered []*types.Interface.
|
||||
// Only concrete types used as MakeInterface operands are included.
|
||||
concreteTypes typeutil.Map
|
||||
|
||||
// interfaceTypes maps each interface type to
|
||||
// the set of concrete types that implement it.
|
||||
// Keys are *types.Interface, values are unordered []types.Type.
|
||||
// Only interfaces used in "invoke"-mode CallInstructions are included.
|
||||
interfaceTypes typeutil.Map
|
||||
}
|
||||
|
||||
// addReachable marks a function as potentially callable at run-time,
|
||||
// and ensures that it gets processed.
|
||||
func (r *rta) addReachable(f *ssa.Function, addrTaken bool) {
|
||||
reachable := r.result.Reachable
|
||||
n := len(reachable)
|
||||
v := reachable[f]
|
||||
if addrTaken {
|
||||
v.AddrTaken = true
|
||||
}
|
||||
reachable[f] = v
|
||||
if len(reachable) > n {
|
||||
// First time seeing f. Add it to the worklist.
|
||||
r.worklist = append(r.worklist, f)
|
||||
}
|
||||
}
|
||||
|
||||
// addEdge adds the specified call graph edge, and marks it reachable.
|
||||
// addrTaken indicates whether to mark the callee as "address-taken".
|
||||
func (r *rta) addEdge(site ssa.CallInstruction, callee *ssa.Function, addrTaken bool) {
|
||||
r.addReachable(callee, addrTaken)
|
||||
|
||||
if g := r.result.CallGraph; g != nil {
|
||||
if site.Parent() == nil {
|
||||
panic(site)
|
||||
}
|
||||
from := g.CreateNode(site.Parent())
|
||||
to := g.CreateNode(callee)
|
||||
callgraph.AddEdge(from, site, to)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- addrTakenFuncs × dynCallSites ----------
|
||||
|
||||
// visitAddrTakenFunc is called each time we encounter an address-taken function f.
|
||||
func (r *rta) visitAddrTakenFunc(f *ssa.Function) {
|
||||
// Create two-level map (Signature -> Function -> bool).
|
||||
S := f.Signature
|
||||
funcs, _ := r.addrTakenFuncsBySig.At(S).(map[*ssa.Function]bool)
|
||||
if funcs == nil {
|
||||
funcs = make(map[*ssa.Function]bool)
|
||||
r.addrTakenFuncsBySig.Set(S, funcs)
|
||||
}
|
||||
if !funcs[f] {
|
||||
// First time seeing f.
|
||||
funcs[f] = true
|
||||
|
||||
// If we've seen any dyncalls of this type, mark it reachable,
|
||||
// and add call graph edges.
|
||||
sites, _ := r.dynCallSites.At(S).([]ssa.CallInstruction)
|
||||
for _, site := range sites {
|
||||
r.addEdge(site, f, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// visitDynCall is called each time we encounter a dynamic "call"-mode call.
|
||||
func (r *rta) visitDynCall(site ssa.CallInstruction) {
|
||||
S := site.Common().Signature()
|
||||
|
||||
// Record the call site.
|
||||
sites, _ := r.dynCallSites.At(S).([]ssa.CallInstruction)
|
||||
r.dynCallSites.Set(S, append(sites, site))
|
||||
|
||||
// For each function of signature S that we know is address-taken,
|
||||
// mark it reachable. We'll add the callgraph edges later.
|
||||
funcs, _ := r.addrTakenFuncsBySig.At(S).(map[*ssa.Function]bool)
|
||||
for g := range funcs {
|
||||
r.addEdge(site, g, true)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- concrete types × invoke sites ----------
|
||||
|
||||
// addInvokeEdge is called for each new pair (site, C) in the matrix.
|
||||
func (r *rta) addInvokeEdge(site ssa.CallInstruction, C types.Type) {
|
||||
// Ascertain the concrete method of C to be called.
|
||||
imethod := site.Common().Method
|
||||
cmethod := r.prog.MethodValue(r.prog.MethodSets.MethodSet(C).Lookup(imethod.Pkg(), imethod.Name()))
|
||||
r.addEdge(site, cmethod, true)
|
||||
}
|
||||
|
||||
// visitInvoke is called each time the algorithm encounters an "invoke"-mode call.
|
||||
func (r *rta) visitInvoke(site ssa.CallInstruction) {
|
||||
I := site.Common().Value.Type().Underlying().(*types.Interface)
|
||||
|
||||
// Record the invoke site.
|
||||
sites, _ := r.invokeSites.At(I).([]ssa.CallInstruction)
|
||||
r.invokeSites.Set(I, append(sites, site))
|
||||
|
||||
// Add callgraph edge for each existing
|
||||
// address-taken concrete type implementing I.
|
||||
for _, C := range r.implementations(I) {
|
||||
r.addInvokeEdge(site, C)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- main algorithm ----------
|
||||
|
||||
// visitFunc processes function f.
|
||||
func (r *rta) visitFunc(f *ssa.Function) {
|
||||
var space [32]*ssa.Value // preallocate space for common case
|
||||
|
||||
for _, b := range f.Blocks {
|
||||
for _, instr := range b.Instrs {
|
||||
rands := instr.Operands(space[:0])
|
||||
|
||||
switch instr := instr.(type) {
|
||||
case ssa.CallInstruction:
|
||||
call := instr.Common()
|
||||
if call.IsInvoke() {
|
||||
r.visitInvoke(instr)
|
||||
} else if g := call.StaticCallee(); g != nil {
|
||||
r.addEdge(instr, g, false)
|
||||
} else if _, ok := call.Value.(*ssa.Builtin); !ok {
|
||||
r.visitDynCall(instr)
|
||||
}
|
||||
|
||||
// Ignore the call-position operand when
|
||||
// looking for address-taken Functions.
|
||||
// Hack: assume this is rands[0].
|
||||
rands = rands[1:]
|
||||
|
||||
case *ssa.MakeInterface:
|
||||
r.addRuntimeType(instr.X.Type(), false)
|
||||
}
|
||||
|
||||
// Process all address-taken functions.
|
||||
for _, op := range rands {
|
||||
if g, ok := (*op).(*ssa.Function); ok {
|
||||
r.visitAddrTakenFunc(g)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Analyze performs Rapid Type Analysis, starting at the specified root
|
||||
// functions. It returns nil if no roots were specified.
|
||||
//
|
||||
// If buildCallGraph is true, Result.CallGraph will contain a call
|
||||
// graph; otherwise, only the other fields (reachable functions) are
|
||||
// populated.
|
||||
//
|
||||
func Analyze(roots []*ssa.Function, buildCallGraph bool) *Result {
|
||||
if len(roots) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
r := &rta{
|
||||
result: &Result{Reachable: make(map[*ssa.Function]struct{ AddrTaken bool })},
|
||||
prog: roots[0].Prog,
|
||||
}
|
||||
|
||||
if buildCallGraph {
|
||||
// TODO(adonovan): change callgraph API to eliminate the
|
||||
// notion of a distinguished root node. Some callgraphs
|
||||
// have many roots, or none.
|
||||
r.result.CallGraph = callgraph.New(roots[0])
|
||||
}
|
||||
|
||||
hasher := typeutil.MakeHasher()
|
||||
r.result.RuntimeTypes.SetHasher(hasher)
|
||||
r.addrTakenFuncsBySig.SetHasher(hasher)
|
||||
r.dynCallSites.SetHasher(hasher)
|
||||
r.invokeSites.SetHasher(hasher)
|
||||
r.concreteTypes.SetHasher(hasher)
|
||||
r.interfaceTypes.SetHasher(hasher)
|
||||
|
||||
// Visit functions, processing their instructions, and adding
|
||||
// new functions to the worklist, until a fixed point is
|
||||
// reached.
|
||||
var shadow []*ssa.Function // for efficiency, we double-buffer the worklist
|
||||
r.worklist = append(r.worklist, roots...)
|
||||
for len(r.worklist) > 0 {
|
||||
shadow, r.worklist = r.worklist, shadow[:0]
|
||||
for _, f := range shadow {
|
||||
r.visitFunc(f)
|
||||
}
|
||||
}
|
||||
return r.result
|
||||
}
|
||||
|
||||
// interfaces(C) returns all currently known interfaces implemented by C.
|
||||
func (r *rta) interfaces(C types.Type) []*types.Interface {
|
||||
// Ascertain set of interfaces C implements
|
||||
// and update 'implements' relation.
|
||||
var ifaces []*types.Interface
|
||||
r.interfaceTypes.Iterate(func(I types.Type, concs interface{}) {
|
||||
if I := I.(*types.Interface); types.Implements(C, I) {
|
||||
concs, _ := concs.([]types.Type)
|
||||
r.interfaceTypes.Set(I, append(concs, C))
|
||||
ifaces = append(ifaces, I)
|
||||
}
|
||||
})
|
||||
r.concreteTypes.Set(C, ifaces)
|
||||
return ifaces
|
||||
}
|
||||
|
||||
// implementations(I) returns all currently known concrete types that implement I.
|
||||
func (r *rta) implementations(I *types.Interface) []types.Type {
|
||||
var concs []types.Type
|
||||
if v := r.interfaceTypes.At(I); v != nil {
|
||||
concs = v.([]types.Type)
|
||||
} else {
|
||||
// First time seeing this interface.
|
||||
// Update the 'implements' relation.
|
||||
r.concreteTypes.Iterate(func(C types.Type, ifaces interface{}) {
|
||||
if types.Implements(C, I) {
|
||||
ifaces, _ := ifaces.([]*types.Interface)
|
||||
r.concreteTypes.Set(C, append(ifaces, I))
|
||||
concs = append(concs, C)
|
||||
}
|
||||
})
|
||||
r.interfaceTypes.Set(I, concs)
|
||||
}
|
||||
return concs
|
||||
}
|
||||
|
||||
// addRuntimeType is called for each concrete type that can be the
|
||||
// dynamic type of some interface or reflect.Value.
|
||||
// Adapted from needMethods in go/ssa/builder.go
|
||||
//
|
||||
func (r *rta) addRuntimeType(T types.Type, skip bool) {
|
||||
if prev, ok := r.result.RuntimeTypes.At(T).(bool); ok {
|
||||
if skip && !prev {
|
||||
r.result.RuntimeTypes.Set(T, skip)
|
||||
}
|
||||
return
|
||||
}
|
||||
r.result.RuntimeTypes.Set(T, skip)
|
||||
|
||||
mset := r.prog.MethodSets.MethodSet(T)
|
||||
|
||||
if _, ok := T.Underlying().(*types.Interface); !ok {
|
||||
// T is a new concrete type.
|
||||
for i, n := 0, mset.Len(); i < n; i++ {
|
||||
sel := mset.At(i)
|
||||
m := sel.Obj()
|
||||
|
||||
if m.Exported() {
|
||||
// Exported methods are always potentially callable via reflection.
|
||||
r.addReachable(r.prog.MethodValue(sel), true)
|
||||
}
|
||||
}
|
||||
|
||||
// Add callgraph edge for each existing dynamic
|
||||
// "invoke"-mode call via that interface.
|
||||
for _, I := range r.interfaces(T) {
|
||||
sites, _ := r.invokeSites.At(I).([]ssa.CallInstruction)
|
||||
for _, site := range sites {
|
||||
r.addInvokeEdge(site, T)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Precondition: T is not a method signature (*Signature with Recv()!=nil).
|
||||
// Recursive case: skip => don't call makeMethods(T).
|
||||
// Each package maintains its own set of types it has visited.
|
||||
|
||||
var n *types.Named
|
||||
switch T := T.(type) {
|
||||
case *types.Named:
|
||||
n = T
|
||||
case *types.Pointer:
|
||||
n, _ = T.Elem().(*types.Named)
|
||||
}
|
||||
if n != nil {
|
||||
owner := n.Obj().Pkg()
|
||||
if owner == nil {
|
||||
return // built-in error type
|
||||
}
|
||||
}
|
||||
|
||||
// Recursion over signatures of each exported method.
|
||||
for i := 0; i < mset.Len(); i++ {
|
||||
if mset.At(i).Obj().Exported() {
|
||||
sig := mset.At(i).Type().(*types.Signature)
|
||||
r.addRuntimeType(sig.Params(), true) // skip the Tuple itself
|
||||
r.addRuntimeType(sig.Results(), true) // skip the Tuple itself
|
||||
}
|
||||
}
|
||||
|
||||
switch t := T.(type) {
|
||||
case *types.Basic:
|
||||
// nop
|
||||
|
||||
case *types.Interface:
|
||||
// nop---handled by recursion over method set.
|
||||
|
||||
case *types.Pointer:
|
||||
r.addRuntimeType(t.Elem(), false)
|
||||
|
||||
case *types.Slice:
|
||||
r.addRuntimeType(t.Elem(), false)
|
||||
|
||||
case *types.Chan:
|
||||
r.addRuntimeType(t.Elem(), false)
|
||||
|
||||
case *types.Map:
|
||||
r.addRuntimeType(t.Key(), false)
|
||||
r.addRuntimeType(t.Elem(), false)
|
||||
|
||||
case *types.Signature:
|
||||
if t.Recv() != nil {
|
||||
panic(fmt.Sprintf("Signature %s has Recv %s", t, t.Recv()))
|
||||
}
|
||||
r.addRuntimeType(t.Params(), true) // skip the Tuple itself
|
||||
r.addRuntimeType(t.Results(), true) // skip the Tuple itself
|
||||
|
||||
case *types.Named:
|
||||
// A pointer-to-named type can be derived from a named
|
||||
// type via reflection. It may have methods too.
|
||||
r.addRuntimeType(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.
|
||||
r.addRuntimeType(t.Underlying(), true)
|
||||
|
||||
case *types.Array:
|
||||
r.addRuntimeType(t.Elem(), false)
|
||||
|
||||
case *types.Struct:
|
||||
for i, n := 0, t.NumFields(); i < n; i++ {
|
||||
r.addRuntimeType(t.Field(i).Type(), false)
|
||||
}
|
||||
|
||||
case *types.Tuple:
|
||||
for i, n := 0, t.Len(); i < n; i++ {
|
||||
r.addRuntimeType(t.At(i).Type(), false)
|
||||
}
|
||||
|
||||
default:
|
||||
panic(T)
|
||||
}
|
||||
}
|
139
vendor/golang.org/x/tools/go/callgraph/rta/rta_test.go
generated
vendored
Normal file
139
vendor/golang.org/x/tools/go/callgraph/rta/rta_test.go
generated
vendored
Normal file
@@ -0,0 +1,139 @@
|
||||
// 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.
|
||||
|
||||
// No testdata on Android.
|
||||
|
||||
// +build !android
|
||||
|
||||
package rta_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/callgraph"
|
||||
"golang.org/x/tools/go/callgraph/rta"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
)
|
||||
|
||||
var inputs = []string{
|
||||
"testdata/func.go",
|
||||
"testdata/rtype.go",
|
||||
"testdata/iface.go",
|
||||
}
|
||||
|
||||
func expectation(f *ast.File) (string, token.Pos) {
|
||||
for _, c := range f.Comments {
|
||||
text := strings.TrimSpace(c.Text())
|
||||
if t := strings.TrimPrefix(text, "WANT:\n"); t != text {
|
||||
return t, c.Pos()
|
||||
}
|
||||
}
|
||||
return "", token.NoPos
|
||||
}
|
||||
|
||||
// TestRTA runs RTA on each file in inputs, prints the results, and
|
||||
// compares it with the golden results embedded in the WANT comment at
|
||||
// the end of the file.
|
||||
//
|
||||
// The results string consists of two parts: the set of dynamic call
|
||||
// edges, "f --> g", one per line, and the set of reachable functions,
|
||||
// one per line. Each set is sorted.
|
||||
//
|
||||
func TestRTA(t *testing.T) {
|
||||
for _, filename := range inputs {
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
t.Errorf("couldn't read file '%s': %s", filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
conf := loader.Config{
|
||||
ParserMode: parser.ParseComments,
|
||||
}
|
||||
f, err := conf.ParseFile(filename, content)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
want, pos := expectation(f)
|
||||
if pos == token.NoPos {
|
||||
t.Errorf("No WANT: comment in %s", filename)
|
||||
continue
|
||||
}
|
||||
|
||||
conf.CreateFromFiles("main", f)
|
||||
iprog, err := conf.Load()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
prog := ssautil.CreateProgram(iprog, 0)
|
||||
mainPkg := prog.Package(iprog.Created[0].Pkg)
|
||||
prog.Build()
|
||||
|
||||
res := rta.Analyze([]*ssa.Function{
|
||||
mainPkg.Func("main"),
|
||||
mainPkg.Func("init"),
|
||||
}, true)
|
||||
|
||||
if got := printResult(res, mainPkg.Pkg); got != want {
|
||||
t.Errorf("%s: got:\n%s\nwant:\n%s",
|
||||
prog.Fset.Position(pos), got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printResult(res *rta.Result, from *types.Package) string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
writeSorted := func(ss []string) {
|
||||
sort.Strings(ss)
|
||||
for _, s := range ss {
|
||||
fmt.Fprintf(&buf, " %s\n", s)
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString("Dynamic calls\n")
|
||||
var edges []string
|
||||
callgraph.GraphVisitEdges(res.CallGraph, func(e *callgraph.Edge) error {
|
||||
if strings.Contains(e.Description(), "dynamic") {
|
||||
edges = append(edges, fmt.Sprintf("%s --> %s",
|
||||
e.Caller.Func.RelString(from),
|
||||
e.Callee.Func.RelString(from)))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
writeSorted(edges)
|
||||
|
||||
buf.WriteString("Reachable functions\n")
|
||||
var reachable []string
|
||||
for f := range res.Reachable {
|
||||
reachable = append(reachable, f.RelString(from))
|
||||
}
|
||||
writeSorted(reachable)
|
||||
|
||||
buf.WriteString("Reflect types\n")
|
||||
var rtypes []string
|
||||
res.RuntimeTypes.Iterate(func(key types.Type, value interface{}) {
|
||||
if value == false { // accessible to reflection
|
||||
rtypes = append(rtypes, types.TypeString(key, types.RelativeTo(from)))
|
||||
}
|
||||
})
|
||||
writeSorted(rtypes)
|
||||
|
||||
return strings.TrimSpace(buf.String())
|
||||
}
|
37
vendor/golang.org/x/tools/go/callgraph/rta/testdata/func.go
generated
vendored
Normal file
37
vendor/golang.org/x/tools/go/callgraph/rta/testdata/func.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
// Test of dynamic function calls.
|
||||
// No interfaces, so no runtime/reflect types.
|
||||
|
||||
func A1() {
|
||||
A2(0)
|
||||
}
|
||||
|
||||
func A2(int) {} // not address-taken
|
||||
|
||||
func B() {} // unreachable
|
||||
|
||||
var (
|
||||
C = func(int) {}
|
||||
D = func(int) {}
|
||||
)
|
||||
|
||||
func main() {
|
||||
A1()
|
||||
|
||||
pfn := C
|
||||
pfn(0) // calls C and D but not A2 (same sig but not address-taken)
|
||||
}
|
||||
|
||||
// WANT:
|
||||
// Dynamic calls
|
||||
// main --> init$1
|
||||
// main --> init$2
|
||||
// Reachable functions
|
||||
// A1
|
||||
// A2
|
||||
// init$1
|
||||
// init$2
|
||||
// Reflect types
|
79
vendor/golang.org/x/tools/go/callgraph/rta/testdata/iface.go
generated
vendored
Normal file
79
vendor/golang.org/x/tools/go/callgraph/rta/testdata/iface.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
// Test of interface calls.
|
||||
|
||||
func use(interface{})
|
||||
|
||||
type A byte // instantiated but not a reflect type
|
||||
|
||||
func (A) f() {} // called directly
|
||||
func (A) F() {} // unreachable
|
||||
|
||||
type B int // a reflect type
|
||||
|
||||
func (*B) f() {} // reachable via interface invoke
|
||||
func (*B) F() {} // reachable: exported method of reflect type
|
||||
|
||||
type B2 int // a reflect type, and *B2 also
|
||||
|
||||
func (B2) f() {} // reachable via interface invoke
|
||||
func (B2) g() {} // reachable: exported method of reflect type
|
||||
|
||||
type C string // not instantiated
|
||||
|
||||
func (C) f() {} // unreachable
|
||||
func (C) F() {} // unreachable
|
||||
|
||||
type D uint // instantiated only in dead code
|
||||
|
||||
func (D) f() {} // unreachable
|
||||
func (D) F() {} // unreachable
|
||||
|
||||
func main() {
|
||||
A(0).f()
|
||||
|
||||
use(new(B))
|
||||
use(B2(0))
|
||||
|
||||
var i interface {
|
||||
f()
|
||||
}
|
||||
i.f() // calls (*B).f, (*B2).f and (B2.f)
|
||||
|
||||
live()
|
||||
}
|
||||
|
||||
func live() {
|
||||
var j interface {
|
||||
f()
|
||||
g()
|
||||
}
|
||||
j.f() // calls (B2).f and (*B2).f but not (*B).f (no g method).
|
||||
}
|
||||
|
||||
func dead() {
|
||||
use(D(0))
|
||||
}
|
||||
|
||||
// WANT:
|
||||
// Dynamic calls
|
||||
// live --> (*B2).f
|
||||
// live --> (B2).f
|
||||
// main --> (*B).f
|
||||
// main --> (*B2).f
|
||||
// main --> (B2).f
|
||||
// Reachable functions
|
||||
// (*B).F
|
||||
// (*B).f
|
||||
// (*B2).f
|
||||
// (A).f
|
||||
// (B2).f
|
||||
// live
|
||||
// use
|
||||
// Reflect types
|
||||
// *B
|
||||
// *B2
|
||||
// B
|
||||
// B2
|
35
vendor/golang.org/x/tools/go/callgraph/rta/testdata/rtype.go
generated
vendored
Normal file
35
vendor/golang.org/x/tools/go/callgraph/rta/testdata/rtype.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
// Test of runtime types (types for which descriptors are needed).
|
||||
|
||||
func use(interface{})
|
||||
|
||||
type A byte // neither A nor byte are runtime types
|
||||
|
||||
type B struct{ x uint } // B and uint are runtime types, but not the struct
|
||||
|
||||
func main() {
|
||||
var x int // not a runtime type
|
||||
print(x)
|
||||
|
||||
var y string // runtime type due to interface conversion
|
||||
use(y)
|
||||
|
||||
use(struct{ uint64 }{}) // struct is a runtime type
|
||||
|
||||
use(new(B)) // *B is a runtime type
|
||||
}
|
||||
|
||||
// WANT:
|
||||
// Dynamic calls
|
||||
// Reachable functions
|
||||
// use
|
||||
// Reflect types
|
||||
// *B
|
||||
// B
|
||||
// string
|
||||
// struct{uint64}
|
||||
// uint
|
||||
// uint64
|
35
vendor/golang.org/x/tools/go/callgraph/static/static.go
generated
vendored
Normal file
35
vendor/golang.org/x/tools/go/callgraph/static/static.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Package static computes the call graph of a Go program containing
|
||||
// only static call edges.
|
||||
package static // import "golang.org/x/tools/go/callgraph/static"
|
||||
|
||||
import (
|
||||
"golang.org/x/tools/go/callgraph"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
)
|
||||
|
||||
// CallGraph computes the call graph of the specified program
|
||||
// considering only static calls.
|
||||
//
|
||||
func CallGraph(prog *ssa.Program) *callgraph.Graph {
|
||||
cg := callgraph.New(nil) // TODO(adonovan) eliminate concept of rooted callgraph
|
||||
|
||||
// TODO(adonovan): opt: use only a single pass over the ssa.Program.
|
||||
// TODO(adonovan): opt: this is slower than RTA (perhaps because
|
||||
// the lower precision means so many edges are allocated)!
|
||||
for f := range ssautil.AllFunctions(prog) {
|
||||
fnode := cg.CreateNode(f)
|
||||
for _, b := range f.Blocks {
|
||||
for _, instr := range b.Instrs {
|
||||
if site, ok := instr.(ssa.CallInstruction); ok {
|
||||
if g := site.Common().StaticCallee(); g != nil {
|
||||
gnode := cg.CreateNode(g)
|
||||
callgraph.AddEdge(fnode, site, gnode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cg
|
||||
}
|
88
vendor/golang.org/x/tools/go/callgraph/static/static_test.go
generated
vendored
Normal file
88
vendor/golang.org/x/tools/go/callgraph/static/static_test.go
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
// 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 static_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/callgraph"
|
||||
"golang.org/x/tools/go/callgraph/static"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
)
|
||||
|
||||
const input = `package P
|
||||
|
||||
type C int
|
||||
func (C) f()
|
||||
|
||||
type I interface{f()}
|
||||
|
||||
func f() {
|
||||
p := func() {}
|
||||
g()
|
||||
p() // SSA constant propagation => static
|
||||
|
||||
if unknown {
|
||||
p = h
|
||||
}
|
||||
p() // dynamic
|
||||
|
||||
C(0).f()
|
||||
}
|
||||
|
||||
func g() {
|
||||
var i I = C(0)
|
||||
i.f()
|
||||
}
|
||||
|
||||
func h()
|
||||
|
||||
var unknown bool
|
||||
`
|
||||
|
||||
func TestStatic(t *testing.T) {
|
||||
conf := loader.Config{ParserMode: parser.ParseComments}
|
||||
f, err := conf.ParseFile("P.go", input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
conf.CreateFromFiles("P", f)
|
||||
iprog, err := conf.Load()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
P := iprog.Created[0].Pkg
|
||||
|
||||
prog := ssautil.CreateProgram(iprog, 0)
|
||||
prog.Build()
|
||||
|
||||
cg := static.CallGraph(prog)
|
||||
|
||||
var edges []string
|
||||
callgraph.GraphVisitEdges(cg, func(e *callgraph.Edge) error {
|
||||
edges = append(edges, fmt.Sprintf("%s -> %s",
|
||||
e.Caller.Func.RelString(P),
|
||||
e.Callee.Func.RelString(P)))
|
||||
return nil
|
||||
})
|
||||
sort.Strings(edges)
|
||||
|
||||
want := []string{
|
||||
"(*C).f -> (C).f",
|
||||
"f -> (C).f",
|
||||
"f -> f$1",
|
||||
"f -> g",
|
||||
}
|
||||
if !reflect.DeepEqual(edges, want) {
|
||||
t.Errorf("Got edges %v, want %v", edges, want)
|
||||
}
|
||||
}
|
181
vendor/golang.org/x/tools/go/callgraph/util.go
generated
vendored
Normal file
181
vendor/golang.org/x/tools/go/callgraph/util.go
generated
vendored
Normal 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())
|
||||
}
|
129
vendor/golang.org/x/tools/go/gccgoexportdata/gccgoexportdata.go
generated
vendored
Normal file
129
vendor/golang.org/x/tools/go/gccgoexportdata/gccgoexportdata.go
generated
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright 2016 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 gccgoexportdata provides functions for reading export data
|
||||
// files containing type information produced by the gccgo compiler.
|
||||
//
|
||||
// This package is a stop-gap until such time as gccgo uses the same
|
||||
// export data format as gc; see Go issue 17573. Once that occurs, this
|
||||
// package will be deprecated and eventually deleted.
|
||||
package gccgoexportdata
|
||||
|
||||
// TODO(adonovan): add Find, Write, Importer to the API,
|
||||
// for symmetry with gcexportdata.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/internal/gccgoimporter"
|
||||
)
|
||||
|
||||
// CompilerInfo executes the specified gccgo compiler and returns
|
||||
// information about it: its version (e.g. "4.8.0"), its target triple
|
||||
// (e.g. "x86_64-unknown-linux-gnu"), and the list of directories it
|
||||
// searches to find standard packages. The given arguments are passed
|
||||
// directly to calls to the specified gccgo compiler.
|
||||
func CompilerInfo(gccgo string, args ...string) (version, triple string, dirs []string, err error) {
|
||||
var inst gccgoimporter.GccgoInstallation
|
||||
err = inst.InitFromDriver(gccgo, args...)
|
||||
if err == nil {
|
||||
version = inst.GccVersion
|
||||
triple = inst.TargetTriple
|
||||
dirs = inst.SearchPaths()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewReader returns a reader for the export data section of an object
|
||||
// (.o) or archive (.a) file read from r.
|
||||
func NewReader(r io.Reader) (io.Reader, error) {
|
||||
data, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the file is an archive, extract the first section.
|
||||
const archiveMagic = "!<arch>\n"
|
||||
if bytes.HasPrefix(data, []byte(archiveMagic)) {
|
||||
section, err := firstSection(data[len(archiveMagic):])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data = section
|
||||
}
|
||||
|
||||
// Data contains an ELF file with a .go_export section.
|
||||
// ELF magic number is "\x7fELF".
|
||||
ef, err := elf.NewFile(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sec := ef.Section(".go_export")
|
||||
if sec == nil {
|
||||
return nil, fmt.Errorf("no .go_export section")
|
||||
}
|
||||
return sec.Open(), nil
|
||||
}
|
||||
|
||||
// firstSection returns the contents of the first regular file in an ELF
|
||||
// archive (http://www.sco.com/developers/devspecs/gabi41.pdf, §7.2).
|
||||
func firstSection(a []byte) ([]byte, error) {
|
||||
for len(a) >= 60 {
|
||||
var hdr []byte
|
||||
hdr, a = a[:60], a[60:]
|
||||
|
||||
name := strings.TrimSpace(string(hdr[:16]))
|
||||
|
||||
sizeStr := string(hdr[48:58])
|
||||
size, err := strconv.Atoi(strings.TrimSpace(sizeStr))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid size: %q", sizeStr)
|
||||
}
|
||||
|
||||
if len(a) < size {
|
||||
return nil, fmt.Errorf("invalid section size: %d", size)
|
||||
}
|
||||
|
||||
// The payload is padded to an even number of bytes.
|
||||
var payload []byte
|
||||
payload, a = a[:size], a[size+size&1:]
|
||||
|
||||
// Skip special files:
|
||||
// "/" archive symbol table
|
||||
// "/SYM64/" archive symbol table on e.g. s390x
|
||||
// "//" archive string table (if any filename is >15 bytes)
|
||||
if name == "/" || name == "/SYM64/" || name == "//" {
|
||||
continue
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
return nil, fmt.Errorf("archive has no regular sections")
|
||||
}
|
||||
|
||||
// Read reads export data from in, decodes it, and returns type
|
||||
// information for the package.
|
||||
// The package name is specified by path.
|
||||
//
|
||||
// The FileSet parameter is currently unused but exists for symmetry
|
||||
// with gcexportdata.
|
||||
//
|
||||
// Read may inspect and add to the imports map to ensure that references
|
||||
// within the export data to other packages are consistent. The caller
|
||||
// must ensure that imports[path] does not exist, or exists but is
|
||||
// incomplete (see types.Package.Complete), and Read inserts the
|
||||
// resulting package into this map entry.
|
||||
//
|
||||
// On return, the state of the reader is undefined.
|
||||
func Read(in io.Reader, _ *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) {
|
||||
return gccgoimporter.Parse(in, imports, path)
|
||||
}
|
67
vendor/golang.org/x/tools/go/gccgoexportdata/gccgoexportdata_test.go
generated
vendored
Normal file
67
vendor/golang.org/x/tools/go/gccgoexportdata/gccgoexportdata_test.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
package gccgoexportdata_test
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/gccgoexportdata"
|
||||
)
|
||||
|
||||
// Test ensures this package can read gccgo export data from the
|
||||
// .go_export from a standalone ELF file or such a file in an archive
|
||||
// library.
|
||||
//
|
||||
// The testdata/{short,long}.a ELF archive files were produced by:
|
||||
//
|
||||
// $ echo 'package foo; func F()' > foo.go
|
||||
// $ gccgo -c -fgo-pkgpath blah foo.go
|
||||
// $ objcopy -j .go_export foo.o foo.gox
|
||||
// $ ar q short.a foo.gox
|
||||
// $ objcopy -j .go_export foo.o name-longer-than-16-bytes.gox
|
||||
// $ ar q long.a name-longer-than-16-bytes.gox
|
||||
//
|
||||
// The file long.a contains an archive string table.
|
||||
//
|
||||
// The errors.gox file (an ELF object file) comes from the toolchain's
|
||||
// standard library.
|
||||
func Test(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
filename, path, member, wantType string
|
||||
}{
|
||||
{"testdata/errors.gox", "errors", "New", "func(text string) error"},
|
||||
{"testdata/short.a", "short", "F", "func()"},
|
||||
{"testdata/long.a", "long", "F", "func()"},
|
||||
} {
|
||||
t.Logf("filename = %s", test.filename)
|
||||
f, err := os.Open(test.filename)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
defer f.Close()
|
||||
r, err := gccgoexportdata.NewReader(f)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
imports := make(map[string]*types.Package)
|
||||
pkg, err := gccgoexportdata.Read(r, nil, imports, test.path)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check type of designated package member.
|
||||
obj := pkg.Scope().Lookup(test.member)
|
||||
if obj == nil {
|
||||
t.Errorf("%s.%s not found", test.path, test.member)
|
||||
continue
|
||||
}
|
||||
if obj.Type().String() != test.wantType {
|
||||
t.Errorf("%s.%s.Type = %s, want %s",
|
||||
test.path, test.member, obj.Type(), test.wantType)
|
||||
}
|
||||
}
|
||||
}
|
BIN
vendor/golang.org/x/tools/go/gccgoexportdata/testdata/errors.gox
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/gccgoexportdata/testdata/errors.gox
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/golang.org/x/tools/go/gccgoexportdata/testdata/long.a
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/gccgoexportdata/testdata/long.a
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/golang.org/x/tools/go/gccgoexportdata/testdata/short.a
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/gccgoexportdata/testdata/short.a
generated
vendored
Normal file
Binary file not shown.
122
vendor/golang.org/x/tools/go/gcexportdata/example_test.go
generated
vendored
Normal file
122
vendor/golang.org/x/tools/go/gcexportdata/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright 2016 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.7
|
||||
// +build gc
|
||||
|
||||
package gcexportdata_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/tools/go/gcexportdata"
|
||||
)
|
||||
|
||||
// ExampleRead uses gcexportdata.Read to load type information for the
|
||||
// "fmt" package from the fmt.a file produced by the gc compiler.
|
||||
func ExampleRead() {
|
||||
// Find the export data file.
|
||||
filename, path := gcexportdata.Find("fmt", "")
|
||||
if filename == "" {
|
||||
log.Fatalf("can't find export data for fmt")
|
||||
}
|
||||
fmt.Printf("Package path: %s\n", path)
|
||||
fmt.Printf("Export data: %s\n", filepath.Base(filename))
|
||||
|
||||
// Open and read the file.
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
r, err := gcexportdata.NewReader(f)
|
||||
if err != nil {
|
||||
log.Fatalf("reading export data %s: %v", filename, err)
|
||||
}
|
||||
|
||||
// Decode the export data.
|
||||
fset := token.NewFileSet()
|
||||
imports := make(map[string]*types.Package)
|
||||
pkg, err := gcexportdata.Read(r, fset, imports, path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Print package information.
|
||||
fmt.Printf("Package members: %s...\n", pkg.Scope().Names()[:5])
|
||||
println := pkg.Scope().Lookup("Println")
|
||||
posn := fset.Position(println.Pos())
|
||||
posn.Line = 123 // make example deterministic
|
||||
fmt.Printf("Println type: %s\n", println.Type())
|
||||
fmt.Printf("Println location: %s\n", slashify(posn))
|
||||
|
||||
// Output:
|
||||
//
|
||||
// Package path: fmt
|
||||
// Export data: fmt.a
|
||||
// Package members: [Errorf Formatter Fprint Fprintf Fprintln]...
|
||||
// Println type: func(a ...interface{}) (n int, err error)
|
||||
// Println location: $GOROOT/src/fmt/print.go:123:1
|
||||
}
|
||||
|
||||
// ExampleNewImporter demonstrates usage of NewImporter to provide type
|
||||
// information for dependencies when type-checking Go source code.
|
||||
func ExampleNewImporter() {
|
||||
const src = `package myscanner
|
||||
|
||||
// choosing a package that is unlikely to change across releases
|
||||
import "text/scanner"
|
||||
|
||||
const eof = scanner.EOF
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, "myscanner.go", src, 0)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
packages := make(map[string]*types.Package)
|
||||
imp := gcexportdata.NewImporter(fset, packages)
|
||||
conf := types.Config{Importer: imp}
|
||||
pkg, err := conf.Check("myscanner", fset, []*ast.File{f}, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// object from imported package
|
||||
pi := packages["text/scanner"].Scope().Lookup("EOF")
|
||||
fmt.Printf("const %s.%s %s = %s // %s\n",
|
||||
pi.Pkg().Path(),
|
||||
pi.Name(),
|
||||
pi.Type(),
|
||||
pi.(*types.Const).Val(),
|
||||
slashify(fset.Position(pi.Pos())),
|
||||
)
|
||||
|
||||
// object in source package
|
||||
twopi := pkg.Scope().Lookup("eof")
|
||||
fmt.Printf("const %s %s = %s // %s\n",
|
||||
twopi.Name(),
|
||||
twopi.Type(),
|
||||
twopi.(*types.Const).Val(),
|
||||
slashify(fset.Position(twopi.Pos())),
|
||||
)
|
||||
|
||||
// Output:
|
||||
//
|
||||
// const text/scanner.EOF untyped int = -1 // $GOROOT/src/text/scanner/scanner.go:75:1
|
||||
// const eof untyped int = -1 // myscanner.go:6:7
|
||||
}
|
||||
|
||||
func slashify(posn token.Position) token.Position {
|
||||
posn.Filename = filepath.ToSlash(posn.Filename) // for MS Windows portability
|
||||
return posn
|
||||
}
|
109
vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go
generated
vendored
Normal file
109
vendor/golang.org/x/tools/go/gcexportdata/gcexportdata.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright 2016 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 gcexportdata provides functions for locating, reading, and
|
||||
// writing export data files containing type information produced by the
|
||||
// gc compiler. This package supports go1.7 export data format and all
|
||||
// later versions.
|
||||
//
|
||||
// Although it might seem convenient for this package to live alongside
|
||||
// go/types in the standard library, this would cause version skew
|
||||
// problems for developer tools that use it, since they must be able to
|
||||
// consume the outputs of the gc compiler both before and after a Go
|
||||
// update such as from Go 1.7 to Go 1.8. Because this package lives in
|
||||
// golang.org/x/tools, sites can update their version of this repo some
|
||||
// time before the Go 1.8 release and rebuild and redeploy their
|
||||
// developer tools, which will then be able to consume both Go 1.7 and
|
||||
// Go 1.8 export data files, so they will work before and after the
|
||||
// Go update. (See discussion at https://github.com/golang/go/issues/15651.)
|
||||
//
|
||||
package gcexportdata // import "golang.org/x/tools/go/gcexportdata"
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"golang.org/x/tools/go/internal/gcimporter"
|
||||
)
|
||||
|
||||
// Find returns the name of an object (.o) or archive (.a) file
|
||||
// containing type information for the specified import path,
|
||||
// using the workspace layout conventions of go/build.
|
||||
// If no file was found, an empty filename is returned.
|
||||
//
|
||||
// A relative srcDir is interpreted relative to the current working directory.
|
||||
//
|
||||
// Find also returns the package's resolved (canonical) import path,
|
||||
// reflecting the effects of srcDir and vendoring on importPath.
|
||||
func Find(importPath, srcDir string) (filename, path string) {
|
||||
return gcimporter.FindPkg(importPath, srcDir)
|
||||
}
|
||||
|
||||
// NewReader returns a reader for the export data section of an object
|
||||
// (.o) or archive (.a) file read from r. The new reader may provide
|
||||
// additional trailing data beyond the end of the export data.
|
||||
func NewReader(r io.Reader) (io.Reader, error) {
|
||||
buf := bufio.NewReader(r)
|
||||
_, err := gcimporter.FindExportData(buf)
|
||||
// If we ever switch to a zip-like archive format with the ToC
|
||||
// at the end, we can return the correct portion of export data,
|
||||
// but for now we must return the entire rest of the file.
|
||||
return buf, err
|
||||
}
|
||||
|
||||
// Read reads export data from in, decodes it, and returns type
|
||||
// information for the package.
|
||||
// The package name is specified by path.
|
||||
// File position information is added to fset.
|
||||
//
|
||||
// Read may inspect and add to the imports map to ensure that references
|
||||
// within the export data to other packages are consistent. The caller
|
||||
// must ensure that imports[path] does not exist, or exists but is
|
||||
// incomplete (see types.Package.Complete), and Read inserts the
|
||||
// resulting package into this map entry.
|
||||
//
|
||||
// On return, the state of the reader is undefined.
|
||||
func Read(in io.Reader, fset *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) {
|
||||
data, err := ioutil.ReadAll(in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading export data for %q: %v", path, err)
|
||||
}
|
||||
|
||||
if bytes.HasPrefix(data, []byte("!<arch>")) {
|
||||
return nil, fmt.Errorf("can't read export data for %q directly from an archive file (call gcexportdata.NewReader first to extract export data)", path)
|
||||
}
|
||||
|
||||
// The App Engine Go runtime v1.6 uses the old export data format.
|
||||
// TODO(adonovan): delete once v1.7 has been around for a while.
|
||||
if bytes.HasPrefix(data, []byte("package ")) {
|
||||
return gcimporter.ImportData(imports, path, path, bytes.NewReader(data))
|
||||
}
|
||||
|
||||
// The indexed export format starts with an 'i'; the older
|
||||
// binary export format starts with a 'c', 'd', or 'v'
|
||||
// (from "version"). Select appropriate importer.
|
||||
if len(data) > 0 && data[0] == 'i' {
|
||||
_, pkg, err := gcimporter.IImportData(fset, imports, data[1:], path)
|
||||
return pkg, err
|
||||
}
|
||||
|
||||
_, pkg, err := gcimporter.BImportData(fset, imports, data, path)
|
||||
return pkg, err
|
||||
}
|
||||
|
||||
// Write writes encoded type information for the specified package to out.
|
||||
// The FileSet provides file position information for named objects.
|
||||
func Write(out io.Writer, fset *token.FileSet, pkg *types.Package) error {
|
||||
b, err := gcimporter.BExportData(fset, pkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = out.Write(b)
|
||||
return err
|
||||
}
|
41
vendor/golang.org/x/tools/go/gcexportdata/gcexportdata_test.go
generated
vendored
Normal file
41
vendor/golang.org/x/tools/go/gcexportdata/gcexportdata_test.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
package gcexportdata_test
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/gcexportdata"
|
||||
)
|
||||
|
||||
// Test to ensure that gcexportdata can read files produced by App
|
||||
// Engine Go runtime v1.6.
|
||||
func TestAppEngine16(t *testing.T) {
|
||||
// Open and read the file.
|
||||
f, err := os.Open("testdata/errors-ae16.a")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
r, err := gcexportdata.NewReader(f)
|
||||
if err != nil {
|
||||
log.Fatalf("reading export data: %v", err)
|
||||
}
|
||||
|
||||
// Decode the export data.
|
||||
fset := token.NewFileSet()
|
||||
imports := make(map[string]*types.Package)
|
||||
pkg, err := gcexportdata.Read(r, fset, imports, "errors")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Print package information.
|
||||
got := pkg.Scope().Lookup("New").Type().String()
|
||||
want := "func(text string) error"
|
||||
if got != want {
|
||||
t.Errorf("New.Type = %s, want %s", got, want)
|
||||
}
|
||||
}
|
73
vendor/golang.org/x/tools/go/gcexportdata/importer.go
generated
vendored
Normal file
73
vendor/golang.org/x/tools/go/gcexportdata/importer.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2016 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 gcexportdata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
)
|
||||
|
||||
// NewImporter returns a new instance of the types.Importer interface
|
||||
// that reads type information from export data files written by gc.
|
||||
// The Importer also satisfies types.ImporterFrom.
|
||||
//
|
||||
// Export data files are located using "go build" workspace conventions
|
||||
// and the build.Default context.
|
||||
//
|
||||
// Use this importer instead of go/importer.For("gc", ...) to avoid the
|
||||
// version-skew problems described in the documentation of this package,
|
||||
// or to control the FileSet or access the imports map populated during
|
||||
// package loading.
|
||||
//
|
||||
func NewImporter(fset *token.FileSet, imports map[string]*types.Package) types.ImporterFrom {
|
||||
return importer{fset, imports}
|
||||
}
|
||||
|
||||
type importer struct {
|
||||
fset *token.FileSet
|
||||
imports map[string]*types.Package
|
||||
}
|
||||
|
||||
func (imp importer) Import(importPath string) (*types.Package, error) {
|
||||
return imp.ImportFrom(importPath, "", 0)
|
||||
}
|
||||
|
||||
func (imp importer) ImportFrom(importPath, srcDir string, mode types.ImportMode) (_ *types.Package, err error) {
|
||||
filename, path := Find(importPath, srcDir)
|
||||
if filename == "" {
|
||||
if importPath == "unsafe" {
|
||||
// Even for unsafe, call Find first in case
|
||||
// the package was vendored.
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
return nil, fmt.Errorf("can't find import: %s", importPath)
|
||||
}
|
||||
|
||||
if pkg, ok := imp.imports[path]; ok && pkg.Complete() {
|
||||
return pkg, nil // cache hit
|
||||
}
|
||||
|
||||
// open file
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
f.Close()
|
||||
if err != nil {
|
||||
// add file name to error
|
||||
err = fmt.Errorf("reading export data: %s: %v", filename, err)
|
||||
}
|
||||
}()
|
||||
|
||||
r, err := NewReader(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return Read(r, imp.fset, imp.imports, path)
|
||||
}
|
99
vendor/golang.org/x/tools/go/gcexportdata/main.go
generated
vendored
Normal file
99
vendor/golang.org/x/tools/go/gcexportdata/main.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright 2017 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 ignore
|
||||
|
||||
// The gcexportdata command is a diagnostic tool that displays the
|
||||
// contents of gc export data files.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"golang.org/x/tools/go/gcexportdata"
|
||||
"golang.org/x/tools/go/types/typeutil"
|
||||
)
|
||||
|
||||
var packageFlag = flag.String("package", "", "alternative package to print")
|
||||
|
||||
func main() {
|
||||
log.SetPrefix("gcexportdata: ")
|
||||
log.SetFlags(0)
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintln(os.Stderr, "usage: gcexportdata [-package path] file.a")
|
||||
}
|
||||
flag.Parse()
|
||||
if flag.NArg() != 1 {
|
||||
flag.Usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
filename := flag.Args()[0]
|
||||
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
r, err := gcexportdata.NewReader(f)
|
||||
if err != nil {
|
||||
log.Fatalf("%s: %s", filename, err)
|
||||
}
|
||||
|
||||
// Decode the package.
|
||||
const primary = "<primary>"
|
||||
imports := make(map[string]*types.Package)
|
||||
fset := token.NewFileSet()
|
||||
pkg, err := gcexportdata.Read(r, fset, imports, primary)
|
||||
if err != nil {
|
||||
log.Fatalf("%s: %s", filename, err)
|
||||
}
|
||||
|
||||
// Optionally select an indirectly mentioned package.
|
||||
if *packageFlag != "" {
|
||||
pkg = imports[*packageFlag]
|
||||
if pkg == nil {
|
||||
fmt.Fprintf(os.Stderr, "export data file %s does not mention %s; has:\n",
|
||||
filename, *packageFlag)
|
||||
for p := range imports {
|
||||
if p != primary {
|
||||
fmt.Fprintf(os.Stderr, "\t%s\n", p)
|
||||
}
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Print all package-level declarations, including non-exported ones.
|
||||
fmt.Printf("package %s\n", pkg.Name())
|
||||
for _, imp := range pkg.Imports() {
|
||||
fmt.Printf("import %q\n", imp.Path())
|
||||
}
|
||||
qual := func(p *types.Package) string {
|
||||
if pkg == p {
|
||||
return ""
|
||||
}
|
||||
return p.Name()
|
||||
}
|
||||
scope := pkg.Scope()
|
||||
for _, name := range scope.Names() {
|
||||
obj := scope.Lookup(name)
|
||||
fmt.Printf("%s: %s\n",
|
||||
fset.Position(obj.Pos()),
|
||||
types.ObjectString(obj, qual))
|
||||
|
||||
// For types, print each method.
|
||||
if _, ok := obj.(*types.TypeName); ok {
|
||||
for _, method := range typeutil.IntuitiveMethodSet(obj.Type(), nil) {
|
||||
fmt.Printf("%s: %s\n",
|
||||
fset.Position(method.Obj().Pos()),
|
||||
types.SelectionString(method, qual))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
vendor/golang.org/x/tools/go/gcexportdata/testdata/errors-ae16.a
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/gcexportdata/testdata/errors-ae16.a
generated
vendored
Normal file
Binary file not shown.
28
vendor/golang.org/x/tools/go/internal/gccgoimporter/backdoor.go
generated
vendored
Normal file
28
vendor/golang.org/x/tools/go/internal/gccgoimporter/backdoor.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
package gccgoimporter
|
||||
|
||||
// This file opens a back door to the parser for golang.org/x/tools/go/gccgoexportdata.
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Parse reads and parses gccgo export data from in and constructs a
|
||||
// Package, inserting it into the imports map.
|
||||
func Parse(in io.Reader, imports map[string]*types.Package, path string) (_ *types.Package, err error) {
|
||||
var p parser
|
||||
p.init(path, in, imports)
|
||||
defer func() {
|
||||
switch x := recover().(type) {
|
||||
case nil:
|
||||
// success
|
||||
case importError:
|
||||
err = x
|
||||
default:
|
||||
panic(x) // resume unexpected panic
|
||||
}
|
||||
}()
|
||||
pkg := p.parsePackage()
|
||||
imports[path] = pkg
|
||||
return pkg, err
|
||||
}
|
99
vendor/golang.org/x/tools/go/internal/gccgoimporter/gccgoinstallation.go
generated
vendored
Normal file
99
vendor/golang.org/x/tools/go/internal/gccgoimporter/gccgoinstallation.go
generated
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
// 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 gccgoimporter
|
||||
|
||||
// This is a verbatim copy of $GOROOT/src/go/internal/gccgoimporter/gccgoinstallation.go.
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"go/types"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Information about a specific installation of gccgo.
|
||||
type GccgoInstallation struct {
|
||||
// Version of gcc (e.g. 4.8.0).
|
||||
GccVersion string
|
||||
|
||||
// Target triple (e.g. x86_64-unknown-linux-gnu).
|
||||
TargetTriple string
|
||||
|
||||
// Built-in library paths used by this installation.
|
||||
LibPaths []string
|
||||
}
|
||||
|
||||
// Ask the driver at the given path for information for this GccgoInstallation.
|
||||
// The given arguments are passed directly to the call to the driver.
|
||||
func (inst *GccgoInstallation) InitFromDriver(gccgoPath string, args ...string) (err error) {
|
||||
argv := append([]string{"-###", "-S", "-x", "go", "-"}, args...)
|
||||
cmd := exec.Command(gccgoPath, argv...)
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(stderr)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
switch {
|
||||
case strings.HasPrefix(line, "Target: "):
|
||||
inst.TargetTriple = line[8:]
|
||||
|
||||
case line[0] == ' ':
|
||||
args := strings.Fields(line)
|
||||
for _, arg := range args[1:] {
|
||||
if strings.HasPrefix(arg, "-L") {
|
||||
inst.LibPaths = append(inst.LibPaths, arg[2:])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
argv = append([]string{"-dumpversion"}, args...)
|
||||
stdout, err := exec.Command(gccgoPath, argv...).Output()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
inst.GccVersion = strings.TrimSpace(string(stdout))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Return the list of export search paths for this GccgoInstallation.
|
||||
func (inst *GccgoInstallation) SearchPaths() (paths []string) {
|
||||
for _, lpath := range inst.LibPaths {
|
||||
spath := filepath.Join(lpath, "go", inst.GccVersion)
|
||||
fi, err := os.Stat(spath)
|
||||
if err != nil || !fi.IsDir() {
|
||||
continue
|
||||
}
|
||||
paths = append(paths, spath)
|
||||
|
||||
spath = filepath.Join(spath, inst.TargetTriple)
|
||||
fi, err = os.Stat(spath)
|
||||
if err != nil || !fi.IsDir() {
|
||||
continue
|
||||
}
|
||||
paths = append(paths, spath)
|
||||
}
|
||||
|
||||
paths = append(paths, inst.LibPaths...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Return an importer that searches incpaths followed by the gcc installation's
|
||||
// built-in search paths and the current directory.
|
||||
func (inst *GccgoInstallation) GetImporter(incpaths []string, initmap map[*types.Package]InitData) Importer {
|
||||
return GetImporter(append(append(incpaths, inst.SearchPaths()...), "."), initmap)
|
||||
}
|
191
vendor/golang.org/x/tools/go/internal/gccgoimporter/gccgoinstallation_test.go
generated
vendored
Normal file
191
vendor/golang.org/x/tools/go/internal/gccgoimporter/gccgoinstallation_test.go
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
// 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 gccgoimporter
|
||||
|
||||
// This is a verbatim copy of $GOROOT/src/go/internal/gccgoimporter/gccgoinstallation_test.go.
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var importablePackages = [...]string{
|
||||
"archive/tar",
|
||||
"archive/zip",
|
||||
"bufio",
|
||||
"bytes",
|
||||
"compress/bzip2",
|
||||
"compress/flate",
|
||||
"compress/gzip",
|
||||
"compress/lzw",
|
||||
"compress/zlib",
|
||||
"container/heap",
|
||||
"container/list",
|
||||
"container/ring",
|
||||
"crypto/aes",
|
||||
"crypto/cipher",
|
||||
"crypto/des",
|
||||
"crypto/dsa",
|
||||
"crypto/ecdsa",
|
||||
"crypto/elliptic",
|
||||
"crypto",
|
||||
"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/driver",
|
||||
"database/sql",
|
||||
"debug/dwarf",
|
||||
"debug/elf",
|
||||
"debug/gosym",
|
||||
"debug/macho",
|
||||
"debug/pe",
|
||||
"encoding/ascii85",
|
||||
"encoding/asn1",
|
||||
"encoding/base32",
|
||||
"encoding/base64",
|
||||
"encoding/binary",
|
||||
"encoding/csv",
|
||||
"encoding/gob",
|
||||
"encoding",
|
||||
"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/adler32",
|
||||
"hash/crc32",
|
||||
"hash/crc64",
|
||||
"hash/fnv",
|
||||
"hash",
|
||||
"html",
|
||||
"html/template",
|
||||
"image/color",
|
||||
"image/color/palette",
|
||||
"image/draw",
|
||||
"image/gif",
|
||||
"image",
|
||||
"image/jpeg",
|
||||
"image/png",
|
||||
"index/suffixarray",
|
||||
"io",
|
||||
"io/ioutil",
|
||||
"log",
|
||||
"log/syslog",
|
||||
"math/big",
|
||||
"math/cmplx",
|
||||
"math",
|
||||
"math/rand",
|
||||
"mime",
|
||||
"mime/multipart",
|
||||
"net",
|
||||
"net/http/cgi",
|
||||
"net/http/cookiejar",
|
||||
"net/http/fcgi",
|
||||
"net/http",
|
||||
"net/http/httptest",
|
||||
"net/http/httputil",
|
||||
"net/http/pprof",
|
||||
"net/mail",
|
||||
"net/rpc",
|
||||
"net/rpc/jsonrpc",
|
||||
"net/smtp",
|
||||
"net/textproto",
|
||||
"net/url",
|
||||
"os/exec",
|
||||
"os",
|
||||
"os/signal",
|
||||
"os/user",
|
||||
"path/filepath",
|
||||
"path",
|
||||
"reflect",
|
||||
"regexp",
|
||||
"regexp/syntax",
|
||||
"runtime/debug",
|
||||
"runtime",
|
||||
"runtime/pprof",
|
||||
"sort",
|
||||
"strconv",
|
||||
"strings",
|
||||
"sync/atomic",
|
||||
"sync",
|
||||
"syscall",
|
||||
"testing",
|
||||
"testing/iotest",
|
||||
"testing/quick",
|
||||
"text/scanner",
|
||||
"text/tabwriter",
|
||||
"text/template",
|
||||
"text/template/parse",
|
||||
"time",
|
||||
"unicode",
|
||||
"unicode/utf16",
|
||||
"unicode/utf8",
|
||||
}
|
||||
|
||||
func TestInstallationImporter(t *testing.T) {
|
||||
// This test relies on gccgo being around, which it most likely will be if we
|
||||
// were compiled with gccgo.
|
||||
if runtime.Compiler != "gccgo" {
|
||||
t.Skip("This test needs gccgo")
|
||||
return
|
||||
}
|
||||
|
||||
var inst GccgoInstallation
|
||||
err := inst.InitFromDriver("gccgo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
imp := inst.GetImporter(nil, nil)
|
||||
|
||||
// Ensure we don't regress the number of packages we can parse. First import
|
||||
// all packages into the same map and then each individually.
|
||||
pkgMap := make(map[string]*types.Package)
|
||||
for _, pkg := range importablePackages {
|
||||
_, err = imp(pkgMap, pkg)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, pkg := range importablePackages {
|
||||
_, err = imp(make(map[string]*types.Package), pkg)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test for certain specific entities in the imported data.
|
||||
for _, test := range [...]importerTest{
|
||||
{pkgpath: "io", name: "Reader", want: "type Reader interface{Read(p []byte) (n int, err error)}"},
|
||||
{pkgpath: "io", name: "ReadWriter", want: "type ReadWriter interface{Reader; Writer}"},
|
||||
{pkgpath: "math", name: "Pi", want: "const Pi untyped float"},
|
||||
{pkgpath: "math", name: "Sin", want: "func Sin(x float64) float64"},
|
||||
{pkgpath: "sort", name: "Ints", want: "func Ints(a []int)"},
|
||||
{pkgpath: "unsafe", name: "Pointer", want: "type Pointer"},
|
||||
} {
|
||||
runImporterTest(t, imp, nil, &test)
|
||||
}
|
||||
}
|
209
vendor/golang.org/x/tools/go/internal/gccgoimporter/importer.go
generated
vendored
Normal file
209
vendor/golang.org/x/tools/go/internal/gccgoimporter/importer.go
generated
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
// 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 gccgoimporter implements Import for gccgo-generated object files.
|
||||
package gccgoimporter // import "golang.org/x/tools/go/internal/gccgoimporter"
|
||||
|
||||
// This is a verbatim copy of $GOROOT/src/go/internal/gccgoimporter/importer.go.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A PackageInit describes an imported package that needs initialization.
|
||||
type PackageInit struct {
|
||||
Name string // short package name
|
||||
InitFunc string // name of init function
|
||||
Priority int // priority of init function, see InitData.Priority
|
||||
}
|
||||
|
||||
// The gccgo-specific init data for a package.
|
||||
type InitData struct {
|
||||
// Initialization priority of this package relative to other packages.
|
||||
// This is based on the maximum depth of the package's dependency graph;
|
||||
// it is guaranteed to be greater than that of its dependencies.
|
||||
Priority int
|
||||
|
||||
// The list of packages which this package depends on to be initialized,
|
||||
// including itself if needed. This is the subset of the transitive closure of
|
||||
// the package's dependencies that need initialization.
|
||||
Inits []PackageInit
|
||||
}
|
||||
|
||||
// Locate the file from which to read export data.
|
||||
// This is intended to replicate the logic in gofrontend.
|
||||
func findExportFile(searchpaths []string, pkgpath string) (string, error) {
|
||||
for _, spath := range searchpaths {
|
||||
pkgfullpath := filepath.Join(spath, pkgpath)
|
||||
pkgdir, name := filepath.Split(pkgfullpath)
|
||||
|
||||
for _, filepath := range [...]string{
|
||||
pkgfullpath,
|
||||
pkgfullpath + ".gox",
|
||||
pkgdir + "lib" + name + ".so",
|
||||
pkgdir + "lib" + name + ".a",
|
||||
pkgfullpath + ".o",
|
||||
} {
|
||||
fi, err := os.Stat(filepath)
|
||||
if err == nil && !fi.IsDir() {
|
||||
return filepath, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, strings.Join(searchpaths, ":"))
|
||||
}
|
||||
|
||||
const (
|
||||
gccgov1Magic = "v1;\n"
|
||||
gccgov2Magic = "v2;\n"
|
||||
goimporterMagic = "\n$$ "
|
||||
archiveMagic = "!<ar"
|
||||
)
|
||||
|
||||
// Opens the export data file at the given path. If this is an ELF file,
|
||||
// searches for and opens the .go_export section. If this is an archive,
|
||||
// reads the export data from the first member, which is assumed to be an ELF file.
|
||||
// This is intended to replicate the logic in gofrontend.
|
||||
func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) {
|
||||
f, err := os.Open(fpath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
closer = f
|
||||
defer func() {
|
||||
if err != nil && closer != nil {
|
||||
f.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
var magic [4]byte
|
||||
_, err = f.ReadAt(magic[:], 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var elfreader io.ReaderAt
|
||||
switch string(magic[:]) {
|
||||
case gccgov1Magic, gccgov2Magic, goimporterMagic:
|
||||
// Raw export data.
|
||||
reader = f
|
||||
return
|
||||
|
||||
case archiveMagic:
|
||||
// TODO(pcc): Read the archive directly instead of using "ar".
|
||||
f.Close()
|
||||
closer = nil
|
||||
|
||||
cmd := exec.Command("ar", "p", fpath)
|
||||
var out []byte
|
||||
out, err = cmd.Output()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
elfreader = bytes.NewReader(out)
|
||||
|
||||
default:
|
||||
elfreader = f
|
||||
}
|
||||
|
||||
ef, err := elf.NewFile(elfreader)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
sec := ef.Section(".go_export")
|
||||
if sec == nil {
|
||||
err = fmt.Errorf("%s: .go_export section not found", fpath)
|
||||
return
|
||||
}
|
||||
|
||||
reader = sec.Open()
|
||||
return
|
||||
}
|
||||
|
||||
// An Importer resolves import paths to Packages. The imports map records
|
||||
// packages already known, indexed by package path.
|
||||
// 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.
|
||||
type Importer func(imports map[string]*types.Package, path string) (*types.Package, error)
|
||||
|
||||
func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Importer {
|
||||
return func(imports map[string]*types.Package, pkgpath string) (pkg *types.Package, err error) {
|
||||
if pkgpath == "unsafe" {
|
||||
return types.Unsafe, nil
|
||||
}
|
||||
|
||||
fpath, err := findExportFile(searchpaths, pkgpath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
reader, closer, err := openExportFile(fpath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if closer != nil {
|
||||
defer closer.Close()
|
||||
}
|
||||
|
||||
var magic [4]byte
|
||||
_, err = reader.Read(magic[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = reader.Seek(0, 0) // 0 bytes after start (use 0, io.SeekStart when we drop Go 1.6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch string(magic[:]) {
|
||||
case gccgov1Magic, gccgov2Magic:
|
||||
var p parser
|
||||
p.init(fpath, reader, imports)
|
||||
pkg = p.parsePackage()
|
||||
if initmap != nil {
|
||||
initmap[pkg] = p.initdata
|
||||
}
|
||||
|
||||
// Excluded for now: Standard gccgo doesn't support this import format currently.
|
||||
// case goimporterMagic:
|
||||
// var data []byte
|
||||
// data, err = ioutil.ReadAll(reader)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// var n int
|
||||
// n, pkg, err = importer.ImportData(imports, data)
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
|
||||
// if initmap != nil {
|
||||
// suffixreader := bytes.NewReader(data[n:])
|
||||
// var p parser
|
||||
// p.init(fpath, suffixreader, nil)
|
||||
// p.parseInitData()
|
||||
// initmap[pkg] = p.initdata
|
||||
// }
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("unrecognized magic string: %q", string(magic[:]))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
15
vendor/golang.org/x/tools/go/internal/gccgoimporter/importer19_test.go
generated
vendored
Normal file
15
vendor/golang.org/x/tools/go/internal/gccgoimporter/importer19_test.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2017 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.9
|
||||
|
||||
package gccgoimporter
|
||||
|
||||
var aliasTests = []importerTest{
|
||||
{pkgpath: "alias", name: "IntAlias2", want: "type IntAlias2 = Int"},
|
||||
}
|
||||
|
||||
func init() {
|
||||
importerTests = append(importerTests, aliasTests...)
|
||||
}
|
190
vendor/golang.org/x/tools/go/internal/gccgoimporter/importer_test.go
generated
vendored
Normal file
190
vendor/golang.org/x/tools/go/internal/gccgoimporter/importer_test.go
generated
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
// 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 gccgoimporter
|
||||
|
||||
// This is a verbatim copy of $GOROOT/src/go/internal/gccgoimporter/importer_test.go
|
||||
// except for the importerTests variable which does not contain Go1.9-specific tests.
|
||||
// Those are added via importer19_test.go.
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type importerTest struct {
|
||||
pkgpath, name, want, wantval string
|
||||
wantinits []string
|
||||
}
|
||||
|
||||
func runImporterTest(t *testing.T, imp Importer, initmap map[*types.Package]InitData, test *importerTest) {
|
||||
pkg, err := imp(make(map[string]*types.Package), test.pkgpath)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if test.name != "" {
|
||||
obj := pkg.Scope().Lookup(test.name)
|
||||
if obj == nil {
|
||||
t.Errorf("%s: object not found", test.name)
|
||||
return
|
||||
}
|
||||
|
||||
got := types.ObjectString(obj, types.RelativeTo(pkg))
|
||||
if got != test.want {
|
||||
t.Errorf("%s: got %q; want %q", test.name, got, test.want)
|
||||
}
|
||||
|
||||
if test.wantval != "" {
|
||||
gotval := obj.(*types.Const).Val().String()
|
||||
if gotval != test.wantval {
|
||||
t.Errorf("%s: got val %q; want val %q", test.name, gotval, test.wantval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(test.wantinits) > 0 {
|
||||
initdata := initmap[pkg]
|
||||
found := false
|
||||
// Check that the package's own init function has the package's priority
|
||||
for _, pkginit := range initdata.Inits {
|
||||
if pkginit.InitFunc == test.wantinits[0] {
|
||||
if initdata.Priority != pkginit.Priority {
|
||||
t.Errorf("%s: got self priority %d; want %d", test.pkgpath, pkginit.Priority, initdata.Priority)
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Errorf("%s: could not find expected function %q", test.pkgpath, test.wantinits[0])
|
||||
}
|
||||
|
||||
// Each init function in the list other than the first one is a
|
||||
// dependency of the function immediately before it. Check that
|
||||
// the init functions appear in descending priority order.
|
||||
priority := initdata.Priority
|
||||
for _, wantdepinit := range test.wantinits[1:] {
|
||||
found = false
|
||||
for _, pkginit := range initdata.Inits {
|
||||
if pkginit.InitFunc == wantdepinit {
|
||||
if priority <= pkginit.Priority {
|
||||
t.Errorf("%s: got dep priority %d; want less than %d", test.pkgpath, pkginit.Priority, priority)
|
||||
}
|
||||
found = true
|
||||
priority = pkginit.Priority
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Errorf("%s: could not find expected function %q", test.pkgpath, wantdepinit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var importerTests = []importerTest{
|
||||
{pkgpath: "pointer", name: "Int8Ptr", want: "type Int8Ptr *int8"},
|
||||
{pkgpath: "complexnums", name: "NN", want: "const NN untyped complex", wantval: "(-1 + -1i)"},
|
||||
{pkgpath: "complexnums", name: "NP", want: "const NP untyped complex", wantval: "(-1 + 1i)"},
|
||||
{pkgpath: "complexnums", name: "PN", want: "const PN untyped complex", wantval: "(1 + -1i)"},
|
||||
{pkgpath: "complexnums", name: "PP", want: "const PP untyped complex", wantval: "(1 + 1i)"},
|
||||
{pkgpath: "conversions", name: "Bits", want: "const Bits Units", wantval: `"bits"`},
|
||||
{pkgpath: "time", name: "Duration", want: "type Duration int64"},
|
||||
{pkgpath: "time", name: "Nanosecond", want: "const Nanosecond Duration", wantval: "1"},
|
||||
{pkgpath: "unicode", name: "IsUpper", want: "func IsUpper(r rune) bool"},
|
||||
{pkgpath: "unicode", name: "MaxRune", want: "const MaxRune untyped rune", wantval: "1114111"},
|
||||
{pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}},
|
||||
{pkgpath: "escapeinfo", name: "NewT", want: "func NewT(data []byte) *T"},
|
||||
}
|
||||
|
||||
func TestGoxImporter(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
initmap := make(map[*types.Package]InitData)
|
||||
imp := GetImporter([]string{"testdata"}, initmap)
|
||||
|
||||
for _, test := range importerTests {
|
||||
runImporterTest(t, imp, initmap, &test)
|
||||
}
|
||||
}
|
||||
|
||||
func TestObjImporter(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
|
||||
// This test relies on gccgo being around, which it most likely will be if we
|
||||
// were compiled with gccgo.
|
||||
if runtime.Compiler != "gccgo" {
|
||||
t.Skip("This test needs gccgo")
|
||||
return
|
||||
}
|
||||
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
initmap := make(map[*types.Package]InitData)
|
||||
imp := GetImporter([]string{tmpdir}, initmap)
|
||||
|
||||
artmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
arinitmap := make(map[*types.Package]InitData)
|
||||
arimp := GetImporter([]string{artmpdir}, arinitmap)
|
||||
|
||||
for _, test := range importerTests {
|
||||
gofile := filepath.Join("testdata", test.pkgpath+".go")
|
||||
|
||||
if _, err := os.Stat(gofile); err != nil {
|
||||
// There is a .gox file but no .go file,
|
||||
// so there is nothing to compile.
|
||||
continue
|
||||
}
|
||||
|
||||
ofile := filepath.Join(tmpdir, test.pkgpath+".o")
|
||||
afile := filepath.Join(artmpdir, "lib"+test.pkgpath+".a")
|
||||
|
||||
cmd := exec.Command("gccgo", "-fgo-pkgpath="+test.pkgpath, "-c", "-o", ofile, gofile)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatalf("gccgo %s failed: %s", gofile, err)
|
||||
}
|
||||
|
||||
// The expected initializations are version dependent,
|
||||
// so don't check for them.
|
||||
test.wantinits = nil
|
||||
|
||||
runImporterTest(t, imp, initmap, &test)
|
||||
|
||||
cmd = exec.Command("ar", "cr", afile, ofile)
|
||||
out, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatalf("ar cr %s %s failed: %s", afile, ofile, err)
|
||||
}
|
||||
|
||||
runImporterTest(t, arimp, arinitmap, &test)
|
||||
|
||||
if err = os.Remove(ofile); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = os.Remove(afile); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = os.Remove(tmpdir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
21
vendor/golang.org/x/tools/go/internal/gccgoimporter/newInterface10.go
generated
vendored
Normal file
21
vendor/golang.org/x/tools/go/internal/gccgoimporter/newInterface10.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2018 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.11
|
||||
|
||||
package gccgoimporter
|
||||
|
||||
import "go/types"
|
||||
|
||||
func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface {
|
||||
named := make([]*types.Named, len(embeddeds))
|
||||
for i, e := range embeddeds {
|
||||
var ok bool
|
||||
named[i], ok = e.(*types.Named)
|
||||
if !ok {
|
||||
panic("embedding of non-defined interfaces in interfaces is not supported before Go 1.11")
|
||||
}
|
||||
}
|
||||
return types.NewInterface(methods, named)
|
||||
}
|
13
vendor/golang.org/x/tools/go/internal/gccgoimporter/newInterface11.go
generated
vendored
Normal file
13
vendor/golang.org/x/tools/go/internal/gccgoimporter/newInterface11.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2018 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.11
|
||||
|
||||
package gccgoimporter
|
||||
|
||||
import "go/types"
|
||||
|
||||
func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface {
|
||||
return types.NewInterfaceType(methods, embeddeds)
|
||||
}
|
910
vendor/golang.org/x/tools/go/internal/gccgoimporter/parser.go
generated
vendored
Normal file
910
vendor/golang.org/x/tools/go/internal/gccgoimporter/parser.go
generated
vendored
Normal file
@@ -0,0 +1,910 @@
|
||||
// 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 gccgoimporter
|
||||
|
||||
// This is a verbatim copy of $GOROOT/src/go/internal/gccgoimporter/parser.go.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/scanner"
|
||||
)
|
||||
|
||||
type parser struct {
|
||||
scanner scanner.Scanner
|
||||
version string // format version
|
||||
tok rune // current token
|
||||
lit string // literal string; only valid for Ident, Int, String tokens
|
||||
pkgpath string // package path of imported package
|
||||
pkgname string // name of imported package
|
||||
pkg *types.Package // reference to imported package
|
||||
imports map[string]*types.Package // package path -> package object
|
||||
typeMap map[int]types.Type // type number -> type
|
||||
initdata InitData // package init priority data
|
||||
}
|
||||
|
||||
func (p *parser) init(filename string, src io.Reader, imports 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.ScanFloats | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
|
||||
p.scanner.Whitespace = 1<<'\t' | 1<<'\n' | 1<<' '
|
||||
p.scanner.Filename = filename // for good error messages
|
||||
p.next()
|
||||
p.imports = imports
|
||||
p.typeMap = make(map[int]types.Type)
|
||||
}
|
||||
|
||||
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.Errorf(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) expectKeyword(keyword string) {
|
||||
lit := p.expect(scanner.Ident)
|
||||
if lit != keyword {
|
||||
p.errorf("expected keyword %s, got %q", keyword, lit)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) parseString() string {
|
||||
str, err := strconv.Unquote(p.expect(scanner.String))
|
||||
if err != nil {
|
||||
p.error(err)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// unquotedString = { unquotedStringChar } .
|
||||
// unquotedStringChar = <neither a whitespace nor a ';' char> .
|
||||
func (p *parser) parseUnquotedString() string {
|
||||
if p.tok == scanner.EOF {
|
||||
p.error("unexpected EOF")
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(p.scanner.TokenText())
|
||||
// This loop needs to examine each character before deciding whether to consume it. If we see a semicolon,
|
||||
// we need to let it be consumed by p.next().
|
||||
for ch := p.scanner.Peek(); ch != ';' && ch != scanner.EOF && p.scanner.Whitespace&(1<<uint(ch)) == 0; ch = p.scanner.Peek() {
|
||||
buf.WriteRune(ch)
|
||||
p.scanner.Next()
|
||||
}
|
||||
p.next()
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (p *parser) next() {
|
||||
p.tok = p.scanner.Scan()
|
||||
switch p.tok {
|
||||
case scanner.Ident, scanner.Int, scanner.Float, scanner.String, '·':
|
||||
p.lit = p.scanner.TokenText()
|
||||
default:
|
||||
p.lit = ""
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) parseQualifiedName() (path, name string) {
|
||||
return p.parseQualifiedNameStr(p.parseString())
|
||||
}
|
||||
|
||||
func (p *parser) parseUnquotedQualifiedName() (path, name string) {
|
||||
return p.parseQualifiedNameStr(p.parseUnquotedString())
|
||||
}
|
||||
|
||||
// qualifiedName = [ ["."] unquotedString "." ] unquotedString .
|
||||
//
|
||||
// The above production uses greedy matching.
|
||||
func (p *parser) parseQualifiedNameStr(unquotedName string) (pkgpath, name string) {
|
||||
parts := strings.Split(unquotedName, ".")
|
||||
if parts[0] == "" {
|
||||
parts = parts[1:]
|
||||
}
|
||||
|
||||
switch len(parts) {
|
||||
case 0:
|
||||
p.errorf("malformed qualified name: %q", unquotedName)
|
||||
case 1:
|
||||
// unqualified name
|
||||
pkgpath = p.pkgpath
|
||||
name = parts[0]
|
||||
default:
|
||||
// qualified name, which may contain periods
|
||||
pkgpath = strings.Join(parts[0:len(parts)-1], ".")
|
||||
name = parts[len(parts)-1]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// getPkg returns the package for a given path. If the package is
|
||||
// not found but we have a package name, create the package and
|
||||
// add it to the p.imports map.
|
||||
//
|
||||
func (p *parser) getPkg(pkgpath, name string) *types.Package {
|
||||
// package unsafe is not in the imports map - handle explicitly
|
||||
if pkgpath == "unsafe" {
|
||||
return types.Unsafe
|
||||
}
|
||||
pkg := p.imports[pkgpath]
|
||||
if pkg == nil && name != "" {
|
||||
pkg = types.NewPackage(pkgpath, name)
|
||||
p.imports[pkgpath] = pkg
|
||||
}
|
||||
return pkg
|
||||
}
|
||||
|
||||
// parseExportedName is like parseQualifiedName, but
|
||||
// the package path is resolved to an imported *types.Package.
|
||||
//
|
||||
// ExportedName = string [string] .
|
||||
func (p *parser) parseExportedName() (pkg *types.Package, name string) {
|
||||
path, name := p.parseQualifiedName()
|
||||
var pkgname string
|
||||
if p.tok == scanner.String {
|
||||
pkgname = p.parseString()
|
||||
}
|
||||
pkg = p.getPkg(path, pkgname)
|
||||
if pkg == nil {
|
||||
p.errorf("package %s (path = %q) not found", name, path)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Name = QualifiedName | "?" .
|
||||
func (p *parser) parseName() string {
|
||||
if p.tok == '?' {
|
||||
// Anonymous.
|
||||
p.next()
|
||||
return ""
|
||||
}
|
||||
// The package path is redundant for us. Don't try to parse it.
|
||||
_, name := p.parseUnquotedQualifiedName()
|
||||
return name
|
||||
}
|
||||
|
||||
func deref(typ types.Type) types.Type {
|
||||
if p, _ := typ.(*types.Pointer); p != nil {
|
||||
typ = p.Elem()
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
// Field = Name Type [string] .
|
||||
func (p *parser) parseField(pkg *types.Package) (field *types.Var, tag string) {
|
||||
name := p.parseName()
|
||||
typ := p.parseType(pkg)
|
||||
anon := false
|
||||
if name == "" {
|
||||
anon = true
|
||||
switch typ := deref(typ).(type) {
|
||||
case *types.Basic:
|
||||
name = typ.Name()
|
||||
case *types.Named:
|
||||
name = typ.Obj().Name()
|
||||
default:
|
||||
p.error("anonymous field expected")
|
||||
}
|
||||
}
|
||||
field = types.NewField(token.NoPos, pkg, name, typ, anon)
|
||||
if p.tok == scanner.String {
|
||||
tag = p.parseString()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Param = Name ["..."] Type .
|
||||
func (p *parser) parseParam(pkg *types.Package) (param *types.Var, isVariadic bool) {
|
||||
name := p.parseName()
|
||||
if p.tok == '<' && p.scanner.Peek() == 'e' {
|
||||
// EscInfo = "<esc:" int ">" . (optional and ignored)
|
||||
p.next()
|
||||
p.expectKeyword("esc")
|
||||
p.expect(':')
|
||||
p.expect(scanner.Int)
|
||||
p.expect('>')
|
||||
}
|
||||
if p.tok == '.' {
|
||||
p.next()
|
||||
p.expect('.')
|
||||
p.expect('.')
|
||||
isVariadic = true
|
||||
}
|
||||
typ := p.parseType(pkg)
|
||||
if isVariadic {
|
||||
typ = types.NewSlice(typ)
|
||||
}
|
||||
param = types.NewParam(token.NoPos, pkg, name, typ)
|
||||
return
|
||||
}
|
||||
|
||||
// Var = Name Type .
|
||||
func (p *parser) parseVar(pkg *types.Package) *types.Var {
|
||||
name := p.parseName()
|
||||
return types.NewVar(token.NoPos, pkg, name, p.parseType(pkg))
|
||||
}
|
||||
|
||||
// Conversion = "convert" "(" Type "," ConstValue ")" .
|
||||
func (p *parser) parseConversion(pkg *types.Package) (val constant.Value, typ types.Type) {
|
||||
p.expectKeyword("convert")
|
||||
p.expect('(')
|
||||
typ = p.parseType(pkg)
|
||||
p.expect(',')
|
||||
val, _ = p.parseConstValue(pkg)
|
||||
p.expect(')')
|
||||
return
|
||||
}
|
||||
|
||||
// ConstValue = string | "false" | "true" | ["-"] (int ["'"] | FloatOrComplex) | Conversion .
|
||||
// FloatOrComplex = float ["i" | ("+"|"-") float "i"] .
|
||||
func (p *parser) parseConstValue(pkg *types.Package) (val constant.Value, typ types.Type) {
|
||||
switch p.tok {
|
||||
case scanner.String:
|
||||
str := p.parseString()
|
||||
val = constant.MakeString(str)
|
||||
typ = types.Typ[types.UntypedString]
|
||||
return
|
||||
|
||||
case scanner.Ident:
|
||||
b := false
|
||||
switch p.lit {
|
||||
case "false":
|
||||
case "true":
|
||||
b = true
|
||||
|
||||
case "convert":
|
||||
return p.parseConversion(pkg)
|
||||
|
||||
default:
|
||||
p.errorf("expected const value, got %s (%q)", scanner.TokenString(p.tok), p.lit)
|
||||
}
|
||||
|
||||
p.next()
|
||||
val = constant.MakeBool(b)
|
||||
typ = types.Typ[types.UntypedBool]
|
||||
return
|
||||
}
|
||||
|
||||
sign := ""
|
||||
if p.tok == '-' {
|
||||
p.next()
|
||||
sign = "-"
|
||||
}
|
||||
|
||||
switch p.tok {
|
||||
case scanner.Int:
|
||||
val = constant.MakeFromLiteral(sign+p.lit, token.INT, 0)
|
||||
if val == nil {
|
||||
p.error("could not parse integer literal")
|
||||
}
|
||||
|
||||
p.next()
|
||||
if p.tok == '\'' {
|
||||
p.next()
|
||||
typ = types.Typ[types.UntypedRune]
|
||||
} else {
|
||||
typ = types.Typ[types.UntypedInt]
|
||||
}
|
||||
|
||||
case scanner.Float:
|
||||
re := sign + p.lit
|
||||
p.next()
|
||||
|
||||
var im string
|
||||
switch p.tok {
|
||||
case '+':
|
||||
p.next()
|
||||
im = p.expect(scanner.Float)
|
||||
|
||||
case '-':
|
||||
p.next()
|
||||
im = "-" + p.expect(scanner.Float)
|
||||
|
||||
case scanner.Ident:
|
||||
// re is in fact the imaginary component. Expect "i" below.
|
||||
im = re
|
||||
re = "0"
|
||||
|
||||
default:
|
||||
val = constant.MakeFromLiteral(re, token.FLOAT, 0)
|
||||
if val == nil {
|
||||
p.error("could not parse float literal")
|
||||
}
|
||||
typ = types.Typ[types.UntypedFloat]
|
||||
return
|
||||
}
|
||||
|
||||
p.expectKeyword("i")
|
||||
reval := constant.MakeFromLiteral(re, token.FLOAT, 0)
|
||||
if reval == nil {
|
||||
p.error("could not parse real component of complex literal")
|
||||
}
|
||||
imval := constant.MakeFromLiteral(im+"i", token.IMAG, 0)
|
||||
if imval == nil {
|
||||
p.error("could not parse imag component of complex literal")
|
||||
}
|
||||
val = constant.BinaryOp(reval, token.ADD, imval)
|
||||
typ = types.Typ[types.UntypedComplex]
|
||||
|
||||
default:
|
||||
p.errorf("expected const value, got %s (%q)", scanner.TokenString(p.tok), p.lit)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Const = Name [Type] "=" ConstValue .
|
||||
func (p *parser) parseConst(pkg *types.Package) *types.Const {
|
||||
name := p.parseName()
|
||||
var typ types.Type
|
||||
if p.tok == '<' {
|
||||
typ = p.parseType(pkg)
|
||||
}
|
||||
p.expect('=')
|
||||
val, vtyp := p.parseConstValue(pkg)
|
||||
if typ == nil {
|
||||
typ = vtyp
|
||||
}
|
||||
return types.NewConst(token.NoPos, pkg, name, typ, val)
|
||||
}
|
||||
|
||||
// NamedType = TypeName [ "=" ] Type { Method } .
|
||||
// TypeName = ExportedName .
|
||||
// Method = "func" "(" Param ")" Name ParamList ResultList ";" .
|
||||
func (p *parser) parseNamedType(n int) types.Type {
|
||||
pkg, name := p.parseExportedName()
|
||||
scope := pkg.Scope()
|
||||
|
||||
if p.tok == '=' {
|
||||
// type alias
|
||||
p.next()
|
||||
typ := p.parseType(pkg)
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
typ = obj.Type() // use previously imported type
|
||||
if typ == nil {
|
||||
p.errorf("%v (type alias) used in cycle", obj)
|
||||
}
|
||||
} else {
|
||||
obj = types.NewTypeName(token.NoPos, pkg, name, typ)
|
||||
scope.Insert(obj)
|
||||
}
|
||||
p.typeMap[n] = typ
|
||||
return typ
|
||||
}
|
||||
|
||||
// named type
|
||||
obj := scope.Lookup(name)
|
||||
if obj == nil {
|
||||
// a named type may be referred to before the underlying type
|
||||
// is known - set it up
|
||||
tname := types.NewTypeName(token.NoPos, pkg, name, nil)
|
||||
types.NewNamed(tname, nil, nil)
|
||||
scope.Insert(tname)
|
||||
obj = tname
|
||||
}
|
||||
|
||||
typ := obj.Type()
|
||||
p.typeMap[n] = typ
|
||||
|
||||
nt, ok := typ.(*types.Named)
|
||||
if !ok {
|
||||
// This can happen for unsafe.Pointer, which is a TypeName holding a Basic type.
|
||||
pt := p.parseType(pkg)
|
||||
if pt != typ {
|
||||
p.error("unexpected underlying type for non-named TypeName")
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
underlying := p.parseType(pkg)
|
||||
if nt.Underlying() == nil {
|
||||
nt.SetUnderlying(underlying.Underlying())
|
||||
}
|
||||
|
||||
// collect associated methods
|
||||
for p.tok == scanner.Ident {
|
||||
p.expectKeyword("func")
|
||||
p.expect('(')
|
||||
receiver, _ := p.parseParam(pkg)
|
||||
p.expect(')')
|
||||
name := p.parseName()
|
||||
params, isVariadic := p.parseParamList(pkg)
|
||||
results := p.parseResultList(pkg)
|
||||
p.expect(';')
|
||||
|
||||
sig := types.NewSignature(receiver, params, results, isVariadic)
|
||||
nt.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
|
||||
}
|
||||
|
||||
return nt
|
||||
}
|
||||
|
||||
func (p *parser) parseInt() int64 {
|
||||
lit := p.expect(scanner.Int)
|
||||
n, err := strconv.ParseInt(lit, 10, 0)
|
||||
if err != nil {
|
||||
p.error(err)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// ArrayOrSliceType = "[" [ int ] "]" Type .
|
||||
func (p *parser) parseArrayOrSliceType(pkg *types.Package) types.Type {
|
||||
p.expect('[')
|
||||
if p.tok == ']' {
|
||||
p.next()
|
||||
return types.NewSlice(p.parseType(pkg))
|
||||
}
|
||||
|
||||
n := p.parseInt()
|
||||
p.expect(']')
|
||||
return types.NewArray(p.parseType(pkg), n)
|
||||
}
|
||||
|
||||
// MapType = "map" "[" Type "]" Type .
|
||||
func (p *parser) parseMapType(pkg *types.Package) types.Type {
|
||||
p.expectKeyword("map")
|
||||
p.expect('[')
|
||||
key := p.parseType(pkg)
|
||||
p.expect(']')
|
||||
elem := p.parseType(pkg)
|
||||
return types.NewMap(key, elem)
|
||||
}
|
||||
|
||||
// ChanType = "chan" ["<-" | "-<"] Type .
|
||||
func (p *parser) parseChanType(pkg *types.Package) types.Type {
|
||||
p.expectKeyword("chan")
|
||||
dir := types.SendRecv
|
||||
switch p.tok {
|
||||
case '-':
|
||||
p.next()
|
||||
p.expect('<')
|
||||
dir = types.SendOnly
|
||||
|
||||
case '<':
|
||||
// don't consume '<' if it belongs to Type
|
||||
if p.scanner.Peek() == '-' {
|
||||
p.next()
|
||||
p.expect('-')
|
||||
dir = types.RecvOnly
|
||||
}
|
||||
}
|
||||
|
||||
return types.NewChan(dir, p.parseType(pkg))
|
||||
}
|
||||
|
||||
// StructType = "struct" "{" { Field } "}" .
|
||||
func (p *parser) parseStructType(pkg *types.Package) types.Type {
|
||||
p.expectKeyword("struct")
|
||||
|
||||
var fields []*types.Var
|
||||
var tags []string
|
||||
|
||||
p.expect('{')
|
||||
for p.tok != '}' && p.tok != scanner.EOF {
|
||||
field, tag := p.parseField(pkg)
|
||||
p.expect(';')
|
||||
fields = append(fields, field)
|
||||
tags = append(tags, tag)
|
||||
}
|
||||
p.expect('}')
|
||||
|
||||
return types.NewStruct(fields, tags)
|
||||
}
|
||||
|
||||
// ParamList = "(" [ { Parameter "," } Parameter ] ")" .
|
||||
func (p *parser) parseParamList(pkg *types.Package) (*types.Tuple, bool) {
|
||||
var list []*types.Var
|
||||
isVariadic := false
|
||||
|
||||
p.expect('(')
|
||||
for p.tok != ')' && p.tok != scanner.EOF {
|
||||
if len(list) > 0 {
|
||||
p.expect(',')
|
||||
}
|
||||
par, variadic := p.parseParam(pkg)
|
||||
list = append(list, par)
|
||||
if variadic {
|
||||
if isVariadic {
|
||||
p.error("... not on final argument")
|
||||
}
|
||||
isVariadic = true
|
||||
}
|
||||
}
|
||||
p.expect(')')
|
||||
|
||||
return types.NewTuple(list...), isVariadic
|
||||
}
|
||||
|
||||
// ResultList = Type | ParamList .
|
||||
func (p *parser) parseResultList(pkg *types.Package) *types.Tuple {
|
||||
switch p.tok {
|
||||
case '<':
|
||||
return types.NewTuple(types.NewParam(token.NoPos, pkg, "", p.parseType(pkg)))
|
||||
|
||||
case '(':
|
||||
params, _ := p.parseParamList(pkg)
|
||||
return params
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// FunctionType = ParamList ResultList .
|
||||
func (p *parser) parseFunctionType(pkg *types.Package) *types.Signature {
|
||||
params, isVariadic := p.parseParamList(pkg)
|
||||
results := p.parseResultList(pkg)
|
||||
return types.NewSignature(nil, params, results, isVariadic)
|
||||
}
|
||||
|
||||
// Func = Name FunctionType .
|
||||
func (p *parser) parseFunc(pkg *types.Package) *types.Func {
|
||||
name := p.parseName()
|
||||
if strings.ContainsRune(name, '$') {
|
||||
// This is a Type$equal or Type$hash function, which we don't want to parse,
|
||||
// except for the types.
|
||||
p.discardDirectiveWhileParsingTypes(pkg)
|
||||
return nil
|
||||
}
|
||||
return types.NewFunc(token.NoPos, pkg, name, p.parseFunctionType(pkg))
|
||||
}
|
||||
|
||||
// InterfaceType = "interface" "{" { ("?" Type | Func) ";" } "}" .
|
||||
func (p *parser) parseInterfaceType(pkg *types.Package) types.Type {
|
||||
p.expectKeyword("interface")
|
||||
|
||||
var methods []*types.Func
|
||||
var embeddeds []types.Type
|
||||
|
||||
p.expect('{')
|
||||
for p.tok != '}' && p.tok != scanner.EOF {
|
||||
if p.tok == '?' {
|
||||
p.next()
|
||||
embeddeds = append(embeddeds, p.parseType(pkg))
|
||||
} else {
|
||||
method := p.parseFunc(pkg)
|
||||
methods = append(methods, method)
|
||||
}
|
||||
p.expect(';')
|
||||
}
|
||||
p.expect('}')
|
||||
|
||||
return newInterface(methods, embeddeds)
|
||||
}
|
||||
|
||||
// PointerType = "*" ("any" | Type) .
|
||||
func (p *parser) parsePointerType(pkg *types.Package) types.Type {
|
||||
p.expect('*')
|
||||
if p.tok == scanner.Ident {
|
||||
p.expectKeyword("any")
|
||||
return types.Typ[types.UnsafePointer]
|
||||
}
|
||||
return types.NewPointer(p.parseType(pkg))
|
||||
}
|
||||
|
||||
// TypeDefinition = NamedType | MapType | ChanType | StructType | InterfaceType | PointerType | ArrayOrSliceType | FunctionType .
|
||||
func (p *parser) parseTypeDefinition(pkg *types.Package, n int) types.Type {
|
||||
var t types.Type
|
||||
switch p.tok {
|
||||
case scanner.String:
|
||||
t = p.parseNamedType(n)
|
||||
|
||||
case scanner.Ident:
|
||||
switch p.lit {
|
||||
case "map":
|
||||
t = p.parseMapType(pkg)
|
||||
|
||||
case "chan":
|
||||
t = p.parseChanType(pkg)
|
||||
|
||||
case "struct":
|
||||
t = p.parseStructType(pkg)
|
||||
|
||||
case "interface":
|
||||
t = p.parseInterfaceType(pkg)
|
||||
}
|
||||
|
||||
case '*':
|
||||
t = p.parsePointerType(pkg)
|
||||
|
||||
case '[':
|
||||
t = p.parseArrayOrSliceType(pkg)
|
||||
|
||||
case '(':
|
||||
t = p.parseFunctionType(pkg)
|
||||
}
|
||||
|
||||
p.typeMap[n] = t
|
||||
return t
|
||||
}
|
||||
|
||||
const (
|
||||
// From gofrontend/go/export.h
|
||||
// Note that these values are negative in the gofrontend and have been made positive
|
||||
// in the gccgoimporter.
|
||||
gccgoBuiltinINT8 = 1
|
||||
gccgoBuiltinINT16 = 2
|
||||
gccgoBuiltinINT32 = 3
|
||||
gccgoBuiltinINT64 = 4
|
||||
gccgoBuiltinUINT8 = 5
|
||||
gccgoBuiltinUINT16 = 6
|
||||
gccgoBuiltinUINT32 = 7
|
||||
gccgoBuiltinUINT64 = 8
|
||||
gccgoBuiltinFLOAT32 = 9
|
||||
gccgoBuiltinFLOAT64 = 10
|
||||
gccgoBuiltinINT = 11
|
||||
gccgoBuiltinUINT = 12
|
||||
gccgoBuiltinUINTPTR = 13
|
||||
gccgoBuiltinBOOL = 15
|
||||
gccgoBuiltinSTRING = 16
|
||||
gccgoBuiltinCOMPLEX64 = 17
|
||||
gccgoBuiltinCOMPLEX128 = 18
|
||||
gccgoBuiltinERROR = 19
|
||||
gccgoBuiltinBYTE = 20
|
||||
gccgoBuiltinRUNE = 21
|
||||
)
|
||||
|
||||
func lookupBuiltinType(typ int) types.Type {
|
||||
return [...]types.Type{
|
||||
gccgoBuiltinINT8: types.Typ[types.Int8],
|
||||
gccgoBuiltinINT16: types.Typ[types.Int16],
|
||||
gccgoBuiltinINT32: types.Typ[types.Int32],
|
||||
gccgoBuiltinINT64: types.Typ[types.Int64],
|
||||
gccgoBuiltinUINT8: types.Typ[types.Uint8],
|
||||
gccgoBuiltinUINT16: types.Typ[types.Uint16],
|
||||
gccgoBuiltinUINT32: types.Typ[types.Uint32],
|
||||
gccgoBuiltinUINT64: types.Typ[types.Uint64],
|
||||
gccgoBuiltinFLOAT32: types.Typ[types.Float32],
|
||||
gccgoBuiltinFLOAT64: types.Typ[types.Float64],
|
||||
gccgoBuiltinINT: types.Typ[types.Int],
|
||||
gccgoBuiltinUINT: types.Typ[types.Uint],
|
||||
gccgoBuiltinUINTPTR: types.Typ[types.Uintptr],
|
||||
gccgoBuiltinBOOL: types.Typ[types.Bool],
|
||||
gccgoBuiltinSTRING: types.Typ[types.String],
|
||||
gccgoBuiltinCOMPLEX64: types.Typ[types.Complex64],
|
||||
gccgoBuiltinCOMPLEX128: types.Typ[types.Complex128],
|
||||
gccgoBuiltinERROR: types.Universe.Lookup("error").Type(),
|
||||
gccgoBuiltinBYTE: types.Universe.Lookup("byte").Type(),
|
||||
gccgoBuiltinRUNE: types.Universe.Lookup("rune").Type(),
|
||||
}[typ]
|
||||
}
|
||||
|
||||
// Type = "<" "type" ( "-" int | int [ TypeDefinition ] ) ">" .
|
||||
func (p *parser) parseType(pkg *types.Package) (t types.Type) {
|
||||
p.expect('<')
|
||||
p.expectKeyword("type")
|
||||
|
||||
switch p.tok {
|
||||
case scanner.Int:
|
||||
n := p.parseInt()
|
||||
|
||||
if p.tok == '>' {
|
||||
t = p.typeMap[int(n)]
|
||||
} else {
|
||||
t = p.parseTypeDefinition(pkg, int(n))
|
||||
}
|
||||
|
||||
case '-':
|
||||
p.next()
|
||||
n := p.parseInt()
|
||||
t = lookupBuiltinType(int(n))
|
||||
|
||||
default:
|
||||
p.errorf("expected type number, got %s (%q)", scanner.TokenString(p.tok), p.lit)
|
||||
return nil
|
||||
}
|
||||
|
||||
p.expect('>')
|
||||
return
|
||||
}
|
||||
|
||||
// PackageInit = unquotedString unquotedString int .
|
||||
func (p *parser) parsePackageInit() PackageInit {
|
||||
name := p.parseUnquotedString()
|
||||
initfunc := p.parseUnquotedString()
|
||||
priority := -1
|
||||
if p.version == "v1" {
|
||||
priority = int(p.parseInt())
|
||||
}
|
||||
return PackageInit{Name: name, InitFunc: initfunc, Priority: priority}
|
||||
}
|
||||
|
||||
// Throw away tokens until we see a ';'. If we see a '<', attempt to parse as a type.
|
||||
func (p *parser) discardDirectiveWhileParsingTypes(pkg *types.Package) {
|
||||
for {
|
||||
switch p.tok {
|
||||
case ';':
|
||||
return
|
||||
case '<':
|
||||
p.parseType(pkg)
|
||||
case scanner.EOF:
|
||||
p.error("unexpected EOF")
|
||||
default:
|
||||
p.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the package if we have parsed both the package path and package name.
|
||||
func (p *parser) maybeCreatePackage() {
|
||||
if p.pkgname != "" && p.pkgpath != "" {
|
||||
p.pkg = p.getPkg(p.pkgpath, p.pkgname)
|
||||
}
|
||||
}
|
||||
|
||||
// InitDataDirective = ( "v1" | "v2" ) ";" |
|
||||
// "priority" int ";" |
|
||||
// "init" { PackageInit } ";" |
|
||||
// "checksum" unquotedString ";" .
|
||||
func (p *parser) parseInitDataDirective() {
|
||||
if p.tok != scanner.Ident {
|
||||
// unexpected token kind; panic
|
||||
p.expect(scanner.Ident)
|
||||
}
|
||||
|
||||
switch p.lit {
|
||||
case "v1", "v2":
|
||||
p.version = p.lit
|
||||
p.next()
|
||||
p.expect(';')
|
||||
|
||||
case "priority":
|
||||
p.next()
|
||||
p.initdata.Priority = int(p.parseInt())
|
||||
p.expect(';')
|
||||
|
||||
case "init":
|
||||
p.next()
|
||||
for p.tok != ';' && p.tok != scanner.EOF {
|
||||
p.initdata.Inits = append(p.initdata.Inits, p.parsePackageInit())
|
||||
}
|
||||
p.expect(';')
|
||||
|
||||
case "init_graph":
|
||||
p.next()
|
||||
// The graph data is thrown away for now.
|
||||
for p.tok != ';' && p.tok != scanner.EOF {
|
||||
p.parseInt()
|
||||
p.parseInt()
|
||||
}
|
||||
p.expect(';')
|
||||
|
||||
case "checksum":
|
||||
// Don't let the scanner try to parse the checksum as a number.
|
||||
defer func(mode uint) {
|
||||
p.scanner.Mode = mode
|
||||
}(p.scanner.Mode)
|
||||
p.scanner.Mode &^= scanner.ScanInts | scanner.ScanFloats
|
||||
p.next()
|
||||
p.parseUnquotedString()
|
||||
p.expect(';')
|
||||
|
||||
default:
|
||||
p.errorf("unexpected identifier: %q", p.lit)
|
||||
}
|
||||
}
|
||||
|
||||
// Directive = InitDataDirective |
|
||||
// "package" unquotedString [ unquotedString ] [ unquotedString ] ";" |
|
||||
// "pkgpath" unquotedString ";" |
|
||||
// "prefix" unquotedString ";" |
|
||||
// "import" unquotedString unquotedString string ";" |
|
||||
// "func" Func ";" |
|
||||
// "type" Type ";" |
|
||||
// "var" Var ";" |
|
||||
// "const" Const ";" .
|
||||
func (p *parser) parseDirective() {
|
||||
if p.tok != scanner.Ident {
|
||||
// unexpected token kind; panic
|
||||
p.expect(scanner.Ident)
|
||||
}
|
||||
|
||||
switch p.lit {
|
||||
case "v1", "v2", "priority", "init", "init_graph", "checksum":
|
||||
p.parseInitDataDirective()
|
||||
|
||||
case "package":
|
||||
p.next()
|
||||
p.pkgname = p.parseUnquotedString()
|
||||
p.maybeCreatePackage()
|
||||
if p.version == "v2" && p.tok != ';' {
|
||||
p.parseUnquotedString()
|
||||
p.parseUnquotedString()
|
||||
}
|
||||
p.expect(';')
|
||||
|
||||
case "pkgpath":
|
||||
p.next()
|
||||
p.pkgpath = p.parseUnquotedString()
|
||||
p.maybeCreatePackage()
|
||||
p.expect(';')
|
||||
|
||||
case "prefix":
|
||||
p.next()
|
||||
p.pkgpath = p.parseUnquotedString()
|
||||
p.expect(';')
|
||||
|
||||
case "import":
|
||||
p.next()
|
||||
pkgname := p.parseUnquotedString()
|
||||
pkgpath := p.parseUnquotedString()
|
||||
p.getPkg(pkgpath, pkgname)
|
||||
p.parseString()
|
||||
p.expect(';')
|
||||
|
||||
case "func":
|
||||
p.next()
|
||||
fun := p.parseFunc(p.pkg)
|
||||
if fun != nil {
|
||||
p.pkg.Scope().Insert(fun)
|
||||
}
|
||||
p.expect(';')
|
||||
|
||||
case "type":
|
||||
p.next()
|
||||
p.parseType(p.pkg)
|
||||
p.expect(';')
|
||||
|
||||
case "var":
|
||||
p.next()
|
||||
v := p.parseVar(p.pkg)
|
||||
p.pkg.Scope().Insert(v)
|
||||
p.expect(';')
|
||||
|
||||
case "const":
|
||||
p.next()
|
||||
c := p.parseConst(p.pkg)
|
||||
p.pkg.Scope().Insert(c)
|
||||
p.expect(';')
|
||||
|
||||
default:
|
||||
p.errorf("unexpected identifier: %q", p.lit)
|
||||
}
|
||||
}
|
||||
|
||||
// Package = { Directive } .
|
||||
func (p *parser) parsePackage() *types.Package {
|
||||
for p.tok != scanner.EOF {
|
||||
p.parseDirective()
|
||||
}
|
||||
for _, typ := range p.typeMap {
|
||||
if it, ok := typ.(*types.Interface); ok {
|
||||
it.Complete()
|
||||
}
|
||||
}
|
||||
p.pkg.MarkComplete()
|
||||
return p.pkg
|
||||
}
|
79
vendor/golang.org/x/tools/go/internal/gccgoimporter/parser_test.go
generated
vendored
Normal file
79
vendor/golang.org/x/tools/go/internal/gccgoimporter/parser_test.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
// 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 gccgoimporter
|
||||
|
||||
// This is a verbatim copy of $GOROOT/src/go/internal/gccgoimporter/parser_test.go.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/types"
|
||||
"strings"
|
||||
"testing"
|
||||
"text/scanner"
|
||||
)
|
||||
|
||||
var typeParserTests = []struct {
|
||||
id, typ, want, underlying, methods string
|
||||
}{
|
||||
{id: "foo", typ: "<type -1>", want: "int8"},
|
||||
{id: "foo", typ: "<type 1 *<type -19>>", want: "*error"},
|
||||
{id: "foo", typ: "<type 1 *any>", want: "unsafe.Pointer"},
|
||||
{id: "foo", typ: "<type 1 \"Bar\" <type 2 *<type 1>>>", want: "foo.Bar", underlying: "*foo.Bar"},
|
||||
{id: "foo", typ: "<type 1 \"bar.Foo\" \"bar\" <type -1> func (? <type 1>) M (); >", want: "bar.Foo", underlying: "int8", methods: "func (bar.Foo).M()"},
|
||||
{id: "foo", typ: "<type 1 \".bar.foo\" \"bar\" <type -1>>", want: "bar.foo", underlying: "int8"},
|
||||
{id: "foo", typ: "<type 1 []<type -1>>", want: "[]int8"},
|
||||
{id: "foo", typ: "<type 1 [42]<type -1>>", want: "[42]int8"},
|
||||
{id: "foo", typ: "<type 1 map [<type -1>] <type -2>>", want: "map[int8]int16"},
|
||||
{id: "foo", typ: "<type 1 chan <type -1>>", want: "chan int8"},
|
||||
{id: "foo", typ: "<type 1 chan <- <type -1>>", want: "<-chan int8"},
|
||||
{id: "foo", typ: "<type 1 chan -< <type -1>>", want: "chan<- int8"},
|
||||
{id: "foo", typ: "<type 1 struct { I8 <type -1>; I16 <type -2> \"i16\"; }>", want: "struct{I8 int8; I16 int16 \"i16\"}"},
|
||||
{id: "foo", typ: "<type 1 interface { Foo (a <type -1>, b <type -2>) <type -1>; Bar (? <type -2>, ? ...<type -1>) (? <type -2>, ? <type -1>); Baz (); }>", want: "interface{Bar(int16, ...int8) (int16, int8); Baz(); Foo(a int8, b int16) int8}"},
|
||||
{id: "foo", typ: "<type 1 (? <type -1>) <type -2>>", want: "func(int8) int16"},
|
||||
}
|
||||
|
||||
func TestTypeParser(t *testing.T) {
|
||||
for _, test := range typeParserTests {
|
||||
var p parser
|
||||
p.init("test.gox", strings.NewReader(test.typ), make(map[string]*types.Package))
|
||||
p.pkgname = test.id
|
||||
p.pkgpath = test.id
|
||||
p.maybeCreatePackage()
|
||||
typ := p.parseType(p.pkg)
|
||||
|
||||
if p.tok != scanner.EOF {
|
||||
t.Errorf("expected full parse, stopped at %q", p.lit)
|
||||
}
|
||||
|
||||
// interfaces must be explicitly completed
|
||||
if ityp, _ := typ.(*types.Interface); ityp != nil {
|
||||
ityp.Complete()
|
||||
}
|
||||
|
||||
got := typ.String()
|
||||
if got != test.want {
|
||||
t.Errorf("got type %q, expected %q", got, test.want)
|
||||
}
|
||||
|
||||
if test.underlying != "" {
|
||||
underlying := typ.Underlying().String()
|
||||
if underlying != test.underlying {
|
||||
t.Errorf("got underlying type %q, expected %q", underlying, test.underlying)
|
||||
}
|
||||
}
|
||||
|
||||
if test.methods != "" {
|
||||
nt := typ.(*types.Named)
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i != nt.NumMethods(); i++ {
|
||||
buf.WriteString(nt.Method(i).String())
|
||||
}
|
||||
methods := buf.String()
|
||||
if methods != test.methods {
|
||||
t.Errorf("got methods %q, expected %q", methods, test.methods)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
4
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/alias.gox
generated
vendored
Normal file
4
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/alias.gox
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
v1;
|
||||
package alias;
|
||||
pkgpath alias;
|
||||
type <type 115 "I1" <type 116 interface { M1 (? <type 117 "IntAlias2" = <type 118 "IntAlias" = <type 119 "Int" <type -11>>>>) < type 114>; M2 () <type 1>; }>>;
|
6
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/complexnums.go
generated
vendored
Normal file
6
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/complexnums.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
package complexnums
|
||||
|
||||
const NN = -1 - 1i
|
||||
const NP = -1 + 1i
|
||||
const PN = 1 - 1i
|
||||
const PP = 1 + 1i
|
8
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/complexnums.gox
generated
vendored
Normal file
8
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/complexnums.gox
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
v1;
|
||||
package complexnums;
|
||||
pkgpath complexnums;
|
||||
priority 1;
|
||||
const NN = -0.1E1-0.1E1i ;
|
||||
const NP = -0.1E1+0.1E1i ;
|
||||
const PN = 0.1E1-0.1E1i ;
|
||||
const PP = 0.1E1+0.1E1i ;
|
5
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/conversions.go
generated
vendored
Normal file
5
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/conversions.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package conversions
|
||||
|
||||
type Units string
|
||||
|
||||
const Bits = Units("bits")
|
6
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/conversions.gox
generated
vendored
Normal file
6
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/conversions.gox
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
v2;
|
||||
package conversions;
|
||||
prefix go;
|
||||
package conversions go.conversions go.conversions;
|
||||
const Bits <type 1 "Units" <type -16>> = convert(<type 1>, "bits");
|
||||
type <type 1>;
|
13
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/escapeinfo.go
generated
vendored
Normal file
13
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/escapeinfo.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Test case for escape info in export data. To compile and extract .gox file:
|
||||
// gccgo -fgo-optimize-allocs -c escapeinfo.go
|
||||
// objcopy -j .go_export escapeinfo.o escapeinfo.gox
|
||||
|
||||
package escapeinfo
|
||||
|
||||
type T struct{ data []byte }
|
||||
|
||||
func NewT(data []byte) *T {
|
||||
return &T{data}
|
||||
}
|
||||
|
||||
func (*T) Read(p []byte) {}
|
BIN
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/escapeinfo.gox
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/escapeinfo.gox
generated
vendored
Normal file
Binary file not shown.
5
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/imports.go
generated
vendored
Normal file
5
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/imports.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package imports
|
||||
|
||||
import "fmt"
|
||||
|
||||
var Hello = fmt.Sprintf("Hello, world")
|
7
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/imports.gox
generated
vendored
Normal file
7
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/imports.gox
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
v1;
|
||||
package imports;
|
||||
pkgpath imports;
|
||||
priority 7;
|
||||
import fmt fmt "fmt";
|
||||
init imports imports..import 7 math math..import 1 runtime runtime..import 1 strconv strconv..import 2 io io..import 3 reflect reflect..import 3 syscall syscall..import 3 time time..import 4 os os..import 5 fmt fmt..import 6;
|
||||
var Hello <type -16>;
|
3
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/pointer.go
generated
vendored
Normal file
3
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/pointer.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
package pointer
|
||||
|
||||
type Int8Ptr *int8
|
4
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/pointer.gox
generated
vendored
Normal file
4
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/pointer.gox
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
v1;
|
||||
package pointer;
|
||||
pkgpath pointer;
|
||||
type <type 1 "Int8Ptr" <type 2 *<type -1>>>;
|
BIN
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/time.gox
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/time.gox
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/unicode.gox
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/internal/gccgoimporter/testdata/unicode.gox
generated
vendored
Normal file
Binary file not shown.
40
vendor/golang.org/x/tools/go/internal/gccgoimporter/testenv_test.go
generated
vendored
Normal file
40
vendor/golang.org/x/tools/go/internal/gccgoimporter/testenv_test.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package gccgoimporter
|
||||
|
||||
// This file contains testing utilities copied from $GOROOT/src/internal/testenv/testenv.go.
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// HasGoBuild reports whether the current system can build programs with ``go build''
|
||||
// and then run them with os.StartProcess or exec.Command.
|
||||
func HasGoBuild() bool {
|
||||
switch runtime.GOOS {
|
||||
case "android", "nacl":
|
||||
return false
|
||||
case "darwin":
|
||||
if strings.HasPrefix(runtime.GOARCH, "arm") {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MustHaveGoBuild checks that the current system can build programs with ``go build''
|
||||
// and then run them with os.StartProcess or exec.Command.
|
||||
// If not, MustHaveGoBuild calls t.Skip with an explanation.
|
||||
func MustHaveGoBuild(t *testing.T) {
|
||||
if !HasGoBuild() {
|
||||
t.Skipf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
}
|
||||
|
||||
var testenv = struct {
|
||||
HasGoBuild func() bool
|
||||
MustHaveGoBuild func(*testing.T)
|
||||
}{
|
||||
HasGoBuild: HasGoBuild,
|
||||
MustHaveGoBuild: MustHaveGoBuild,
|
||||
}
|
852
vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go
generated
vendored
Normal file
852
vendor/golang.org/x/tools/go/internal/gcimporter/bexport.go
generated
vendored
Normal file
@@ -0,0 +1,852 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// Binary package export.
|
||||
// This file was derived from $GOROOT/src/cmd/compile/internal/gc/bexport.go;
|
||||
// see that file for specification of the format.
|
||||
|
||||
package gcimporter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// If debugFormat is set, each integer and string value is preceded by a marker
|
||||
// and position information in the encoding. This mechanism permits an importer
|
||||
// to recognize immediately when it is out of sync. The importer recognizes this
|
||||
// mode automatically (i.e., it can import export data produced with debugging
|
||||
// support even if debugFormat is not set at the time of import). This mode will
|
||||
// lead to massively larger export data (by a factor of 2 to 3) and should only
|
||||
// be enabled during development and debugging.
|
||||
//
|
||||
// NOTE: This flag is the first flag to enable if importing dies because of
|
||||
// (suspected) format errors, and whenever a change is made to the format.
|
||||
const debugFormat = false // default: false
|
||||
|
||||
// If trace is set, debugging output is printed to std out.
|
||||
const trace = false // default: false
|
||||
|
||||
// Current export format version. Increase with each format change.
|
||||
// Note: The latest binary (non-indexed) export format is at version 6.
|
||||
// This exporter is still at level 4, but it doesn't matter since
|
||||
// the binary importer can handle older versions just fine.
|
||||
// 6: package height (CL 105038) -- NOT IMPLEMENTED HERE
|
||||
// 5: improved position encoding efficiency (issue 20080, CL 41619) -- NOT IMPLEMEMTED HERE
|
||||
// 4: type name objects support type aliases, uses aliasTag
|
||||
// 3: Go1.8 encoding (same as version 2, aliasTag defined but never used)
|
||||
// 2: removed unused bool in ODCL export (compiler only)
|
||||
// 1: header format change (more regular), export package for _ struct fields
|
||||
// 0: Go1.7 encoding
|
||||
const exportVersion = 4
|
||||
|
||||
// trackAllTypes enables cycle tracking for all types, not just named
|
||||
// types. The existing compiler invariants assume that unnamed types
|
||||
// that are not completely set up are not used, or else there are spurious
|
||||
// errors.
|
||||
// If disabled, only named types are tracked, possibly leading to slightly
|
||||
// less efficient encoding in rare cases. It also prevents the export of
|
||||
// some corner-case type declarations (but those are not handled correctly
|
||||
// with with the textual export format either).
|
||||
// TODO(gri) enable and remove once issues caused by it are fixed
|
||||
const trackAllTypes = false
|
||||
|
||||
type exporter struct {
|
||||
fset *token.FileSet
|
||||
out bytes.Buffer
|
||||
|
||||
// object -> index maps, indexed in order of serialization
|
||||
strIndex map[string]int
|
||||
pkgIndex map[*types.Package]int
|
||||
typIndex map[types.Type]int
|
||||
|
||||
// position encoding
|
||||
posInfoFormat bool
|
||||
prevFile string
|
||||
prevLine int
|
||||
|
||||
// debugging support
|
||||
written int // bytes written
|
||||
indent int // for trace
|
||||
}
|
||||
|
||||
// internalError represents an error generated inside this package.
|
||||
type internalError string
|
||||
|
||||
func (e internalError) Error() string { return "gcimporter: " + string(e) }
|
||||
|
||||
func internalErrorf(format string, args ...interface{}) error {
|
||||
return internalError(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// BExportData returns binary export data for pkg.
|
||||
// If no file set is provided, position info will be missing.
|
||||
func BExportData(fset *token.FileSet, pkg *types.Package) (b []byte, err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
if ierr, ok := e.(internalError); ok {
|
||||
err = ierr
|
||||
return
|
||||
}
|
||||
// Not an internal error; panic again.
|
||||
panic(e)
|
||||
}
|
||||
}()
|
||||
|
||||
p := exporter{
|
||||
fset: fset,
|
||||
strIndex: map[string]int{"": 0}, // empty string is mapped to 0
|
||||
pkgIndex: make(map[*types.Package]int),
|
||||
typIndex: make(map[types.Type]int),
|
||||
posInfoFormat: true, // TODO(gri) might become a flag, eventually
|
||||
}
|
||||
|
||||
// write version info
|
||||
// The version string must start with "version %d" where %d is the version
|
||||
// number. Additional debugging information may follow after a blank; that
|
||||
// text is ignored by the importer.
|
||||
p.rawStringln(fmt.Sprintf("version %d", exportVersion))
|
||||
var debug string
|
||||
if debugFormat {
|
||||
debug = "debug"
|
||||
}
|
||||
p.rawStringln(debug) // cannot use p.bool since it's affected by debugFormat; also want to see this clearly
|
||||
p.bool(trackAllTypes)
|
||||
p.bool(p.posInfoFormat)
|
||||
|
||||
// --- generic export data ---
|
||||
|
||||
// populate type map with predeclared "known" types
|
||||
for index, typ := range predeclared {
|
||||
p.typIndex[typ] = index
|
||||
}
|
||||
if len(p.typIndex) != len(predeclared) {
|
||||
return nil, internalError("duplicate entries in type map?")
|
||||
}
|
||||
|
||||
// write package data
|
||||
p.pkg(pkg, true)
|
||||
if trace {
|
||||
p.tracef("\n")
|
||||
}
|
||||
|
||||
// write objects
|
||||
objcount := 0
|
||||
scope := pkg.Scope()
|
||||
for _, name := range scope.Names() {
|
||||
if !ast.IsExported(name) {
|
||||
continue
|
||||
}
|
||||
if trace {
|
||||
p.tracef("\n")
|
||||
}
|
||||
p.obj(scope.Lookup(name))
|
||||
objcount++
|
||||
}
|
||||
|
||||
// indicate end of list
|
||||
if trace {
|
||||
p.tracef("\n")
|
||||
}
|
||||
p.tag(endTag)
|
||||
|
||||
// for self-verification only (redundant)
|
||||
p.int(objcount)
|
||||
|
||||
if trace {
|
||||
p.tracef("\n")
|
||||
}
|
||||
|
||||
// --- end of export data ---
|
||||
|
||||
return p.out.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *exporter) pkg(pkg *types.Package, emptypath bool) {
|
||||
if pkg == nil {
|
||||
panic(internalError("unexpected nil pkg"))
|
||||
}
|
||||
|
||||
// if we saw the package before, write its index (>= 0)
|
||||
if i, ok := p.pkgIndex[pkg]; ok {
|
||||
p.index('P', i)
|
||||
return
|
||||
}
|
||||
|
||||
// otherwise, remember the package, write the package tag (< 0) and package data
|
||||
if trace {
|
||||
p.tracef("P%d = { ", len(p.pkgIndex))
|
||||
defer p.tracef("} ")
|
||||
}
|
||||
p.pkgIndex[pkg] = len(p.pkgIndex)
|
||||
|
||||
p.tag(packageTag)
|
||||
p.string(pkg.Name())
|
||||
if emptypath {
|
||||
p.string("")
|
||||
} else {
|
||||
p.string(pkg.Path())
|
||||
}
|
||||
}
|
||||
|
||||
func (p *exporter) obj(obj types.Object) {
|
||||
switch obj := obj.(type) {
|
||||
case *types.Const:
|
||||
p.tag(constTag)
|
||||
p.pos(obj)
|
||||
p.qualifiedName(obj)
|
||||
p.typ(obj.Type())
|
||||
p.value(obj.Val())
|
||||
|
||||
case *types.TypeName:
|
||||
if isAlias(obj) {
|
||||
p.tag(aliasTag)
|
||||
p.pos(obj)
|
||||
p.qualifiedName(obj)
|
||||
} else {
|
||||
p.tag(typeTag)
|
||||
}
|
||||
p.typ(obj.Type())
|
||||
|
||||
case *types.Var:
|
||||
p.tag(varTag)
|
||||
p.pos(obj)
|
||||
p.qualifiedName(obj)
|
||||
p.typ(obj.Type())
|
||||
|
||||
case *types.Func:
|
||||
p.tag(funcTag)
|
||||
p.pos(obj)
|
||||
p.qualifiedName(obj)
|
||||
sig := obj.Type().(*types.Signature)
|
||||
p.paramList(sig.Params(), sig.Variadic())
|
||||
p.paramList(sig.Results(), false)
|
||||
|
||||
default:
|
||||
panic(internalErrorf("unexpected object %v (%T)", obj, obj))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *exporter) pos(obj types.Object) {
|
||||
if !p.posInfoFormat {
|
||||
return
|
||||
}
|
||||
|
||||
file, line := p.fileLine(obj)
|
||||
if file == p.prevFile {
|
||||
// common case: write line delta
|
||||
// delta == 0 means different file or no line change
|
||||
delta := line - p.prevLine
|
||||
p.int(delta)
|
||||
if delta == 0 {
|
||||
p.int(-1) // -1 means no file change
|
||||
}
|
||||
} else {
|
||||
// different file
|
||||
p.int(0)
|
||||
// Encode filename as length of common prefix with previous
|
||||
// filename, followed by (possibly empty) suffix. Filenames
|
||||
// frequently share path prefixes, so this can save a lot
|
||||
// of space and make export data size less dependent on file
|
||||
// path length. The suffix is unlikely to be empty because
|
||||
// file names tend to end in ".go".
|
||||
n := commonPrefixLen(p.prevFile, file)
|
||||
p.int(n) // n >= 0
|
||||
p.string(file[n:]) // write suffix only
|
||||
p.prevFile = file
|
||||
p.int(line)
|
||||
}
|
||||
p.prevLine = line
|
||||
}
|
||||
|
||||
func (p *exporter) fileLine(obj types.Object) (file string, line int) {
|
||||
if p.fset != nil {
|
||||
pos := p.fset.Position(obj.Pos())
|
||||
file = pos.Filename
|
||||
line = pos.Line
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func commonPrefixLen(a, b string) int {
|
||||
if len(a) > len(b) {
|
||||
a, b = b, a
|
||||
}
|
||||
// len(a) <= len(b)
|
||||
i := 0
|
||||
for i < len(a) && a[i] == b[i] {
|
||||
i++
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func (p *exporter) qualifiedName(obj types.Object) {
|
||||
p.string(obj.Name())
|
||||
p.pkg(obj.Pkg(), false)
|
||||
}
|
||||
|
||||
func (p *exporter) typ(t types.Type) {
|
||||
if t == nil {
|
||||
panic(internalError("nil type"))
|
||||
}
|
||||
|
||||
// Possible optimization: Anonymous pointer types *T where
|
||||
// T is a named type are common. We could canonicalize all
|
||||
// such types *T to a single type PT = *T. This would lead
|
||||
// to at most one *T entry in typIndex, and all future *T's
|
||||
// would be encoded as the respective index directly. Would
|
||||
// save 1 byte (pointerTag) per *T and reduce the typIndex
|
||||
// size (at the cost of a canonicalization map). We can do
|
||||
// this later, without encoding format change.
|
||||
|
||||
// if we saw the type before, write its index (>= 0)
|
||||
if i, ok := p.typIndex[t]; ok {
|
||||
p.index('T', i)
|
||||
return
|
||||
}
|
||||
|
||||
// otherwise, remember the type, write the type tag (< 0) and type data
|
||||
if trackAllTypes {
|
||||
if trace {
|
||||
p.tracef("T%d = {>\n", len(p.typIndex))
|
||||
defer p.tracef("<\n} ")
|
||||
}
|
||||
p.typIndex[t] = len(p.typIndex)
|
||||
}
|
||||
|
||||
switch t := t.(type) {
|
||||
case *types.Named:
|
||||
if !trackAllTypes {
|
||||
// if we don't track all types, track named types now
|
||||
p.typIndex[t] = len(p.typIndex)
|
||||
}
|
||||
|
||||
p.tag(namedTag)
|
||||
p.pos(t.Obj())
|
||||
p.qualifiedName(t.Obj())
|
||||
p.typ(t.Underlying())
|
||||
if !types.IsInterface(t) {
|
||||
p.assocMethods(t)
|
||||
}
|
||||
|
||||
case *types.Array:
|
||||
p.tag(arrayTag)
|
||||
p.int64(t.Len())
|
||||
p.typ(t.Elem())
|
||||
|
||||
case *types.Slice:
|
||||
p.tag(sliceTag)
|
||||
p.typ(t.Elem())
|
||||
|
||||
case *dddSlice:
|
||||
p.tag(dddTag)
|
||||
p.typ(t.elem)
|
||||
|
||||
case *types.Struct:
|
||||
p.tag(structTag)
|
||||
p.fieldList(t)
|
||||
|
||||
case *types.Pointer:
|
||||
p.tag(pointerTag)
|
||||
p.typ(t.Elem())
|
||||
|
||||
case *types.Signature:
|
||||
p.tag(signatureTag)
|
||||
p.paramList(t.Params(), t.Variadic())
|
||||
p.paramList(t.Results(), false)
|
||||
|
||||
case *types.Interface:
|
||||
p.tag(interfaceTag)
|
||||
p.iface(t)
|
||||
|
||||
case *types.Map:
|
||||
p.tag(mapTag)
|
||||
p.typ(t.Key())
|
||||
p.typ(t.Elem())
|
||||
|
||||
case *types.Chan:
|
||||
p.tag(chanTag)
|
||||
p.int(int(3 - t.Dir())) // hack
|
||||
p.typ(t.Elem())
|
||||
|
||||
default:
|
||||
panic(internalErrorf("unexpected type %T: %s", t, t))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *exporter) assocMethods(named *types.Named) {
|
||||
// Sort methods (for determinism).
|
||||
var methods []*types.Func
|
||||
for i := 0; i < named.NumMethods(); i++ {
|
||||
methods = append(methods, named.Method(i))
|
||||
}
|
||||
sort.Sort(methodsByName(methods))
|
||||
|
||||
p.int(len(methods))
|
||||
|
||||
if trace && methods != nil {
|
||||
p.tracef("associated methods {>\n")
|
||||
}
|
||||
|
||||
for i, m := range methods {
|
||||
if trace && i > 0 {
|
||||
p.tracef("\n")
|
||||
}
|
||||
|
||||
p.pos(m)
|
||||
name := m.Name()
|
||||
p.string(name)
|
||||
if !exported(name) {
|
||||
p.pkg(m.Pkg(), false)
|
||||
}
|
||||
|
||||
sig := m.Type().(*types.Signature)
|
||||
p.paramList(types.NewTuple(sig.Recv()), false)
|
||||
p.paramList(sig.Params(), sig.Variadic())
|
||||
p.paramList(sig.Results(), false)
|
||||
p.int(0) // dummy value for go:nointerface pragma - ignored by importer
|
||||
}
|
||||
|
||||
if trace && methods != nil {
|
||||
p.tracef("<\n} ")
|
||||
}
|
||||
}
|
||||
|
||||
type methodsByName []*types.Func
|
||||
|
||||
func (x methodsByName) Len() int { return len(x) }
|
||||
func (x methodsByName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||
func (x methodsByName) Less(i, j int) bool { return x[i].Name() < x[j].Name() }
|
||||
|
||||
func (p *exporter) fieldList(t *types.Struct) {
|
||||
if trace && t.NumFields() > 0 {
|
||||
p.tracef("fields {>\n")
|
||||
defer p.tracef("<\n} ")
|
||||
}
|
||||
|
||||
p.int(t.NumFields())
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
if trace && i > 0 {
|
||||
p.tracef("\n")
|
||||
}
|
||||
p.field(t.Field(i))
|
||||
p.string(t.Tag(i))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *exporter) field(f *types.Var) {
|
||||
if !f.IsField() {
|
||||
panic(internalError("field expected"))
|
||||
}
|
||||
|
||||
p.pos(f)
|
||||
p.fieldName(f)
|
||||
p.typ(f.Type())
|
||||
}
|
||||
|
||||
func (p *exporter) iface(t *types.Interface) {
|
||||
// TODO(gri): enable importer to load embedded interfaces,
|
||||
// then emit Embeddeds and ExplicitMethods separately here.
|
||||
p.int(0)
|
||||
|
||||
n := t.NumMethods()
|
||||
if trace && n > 0 {
|
||||
p.tracef("methods {>\n")
|
||||
defer p.tracef("<\n} ")
|
||||
}
|
||||
p.int(n)
|
||||
for i := 0; i < n; i++ {
|
||||
if trace && i > 0 {
|
||||
p.tracef("\n")
|
||||
}
|
||||
p.method(t.Method(i))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *exporter) method(m *types.Func) {
|
||||
sig := m.Type().(*types.Signature)
|
||||
if sig.Recv() == nil {
|
||||
panic(internalError("method expected"))
|
||||
}
|
||||
|
||||
p.pos(m)
|
||||
p.string(m.Name())
|
||||
if m.Name() != "_" && !ast.IsExported(m.Name()) {
|
||||
p.pkg(m.Pkg(), false)
|
||||
}
|
||||
|
||||
// interface method; no need to encode receiver.
|
||||
p.paramList(sig.Params(), sig.Variadic())
|
||||
p.paramList(sig.Results(), false)
|
||||
}
|
||||
|
||||
func (p *exporter) fieldName(f *types.Var) {
|
||||
name := f.Name()
|
||||
|
||||
if f.Anonymous() {
|
||||
// anonymous field - we distinguish between 3 cases:
|
||||
// 1) field name matches base type name and is exported
|
||||
// 2) field name matches base type name and is not exported
|
||||
// 3) field name doesn't match base type name (alias name)
|
||||
bname := basetypeName(f.Type())
|
||||
if name == bname {
|
||||
if ast.IsExported(name) {
|
||||
name = "" // 1) we don't need to know the field name or package
|
||||
} else {
|
||||
name = "?" // 2) use unexported name "?" to force package export
|
||||
}
|
||||
} else {
|
||||
// 3) indicate alias and export name as is
|
||||
// (this requires an extra "@" but this is a rare case)
|
||||
p.string("@")
|
||||
}
|
||||
}
|
||||
|
||||
p.string(name)
|
||||
if name != "" && !ast.IsExported(name) {
|
||||
p.pkg(f.Pkg(), false)
|
||||
}
|
||||
}
|
||||
|
||||
func basetypeName(typ types.Type) string {
|
||||
switch typ := deref(typ).(type) {
|
||||
case *types.Basic:
|
||||
return typ.Name()
|
||||
case *types.Named:
|
||||
return typ.Obj().Name()
|
||||
default:
|
||||
return "" // unnamed type
|
||||
}
|
||||
}
|
||||
|
||||
func (p *exporter) paramList(params *types.Tuple, variadic bool) {
|
||||
// use negative length to indicate unnamed parameters
|
||||
// (look at the first parameter only since either all
|
||||
// names are present or all are absent)
|
||||
n := params.Len()
|
||||
if n > 0 && params.At(0).Name() == "" {
|
||||
n = -n
|
||||
}
|
||||
p.int(n)
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
q := params.At(i)
|
||||
t := q.Type()
|
||||
if variadic && i == params.Len()-1 {
|
||||
t = &dddSlice{t.(*types.Slice).Elem()}
|
||||
}
|
||||
p.typ(t)
|
||||
if n > 0 {
|
||||
name := q.Name()
|
||||
p.string(name)
|
||||
if name != "_" {
|
||||
p.pkg(q.Pkg(), false)
|
||||
}
|
||||
}
|
||||
p.string("") // no compiler-specific info
|
||||
}
|
||||
}
|
||||
|
||||
func (p *exporter) value(x constant.Value) {
|
||||
if trace {
|
||||
p.tracef("= ")
|
||||
}
|
||||
|
||||
switch x.Kind() {
|
||||
case constant.Bool:
|
||||
tag := falseTag
|
||||
if constant.BoolVal(x) {
|
||||
tag = trueTag
|
||||
}
|
||||
p.tag(tag)
|
||||
|
||||
case constant.Int:
|
||||
if v, exact := constant.Int64Val(x); exact {
|
||||
// common case: x fits into an int64 - use compact encoding
|
||||
p.tag(int64Tag)
|
||||
p.int64(v)
|
||||
return
|
||||
}
|
||||
// uncommon case: large x - use float encoding
|
||||
// (powers of 2 will be encoded efficiently with exponent)
|
||||
p.tag(floatTag)
|
||||
p.float(constant.ToFloat(x))
|
||||
|
||||
case constant.Float:
|
||||
p.tag(floatTag)
|
||||
p.float(x)
|
||||
|
||||
case constant.Complex:
|
||||
p.tag(complexTag)
|
||||
p.float(constant.Real(x))
|
||||
p.float(constant.Imag(x))
|
||||
|
||||
case constant.String:
|
||||
p.tag(stringTag)
|
||||
p.string(constant.StringVal(x))
|
||||
|
||||
case constant.Unknown:
|
||||
// package contains type errors
|
||||
p.tag(unknownTag)
|
||||
|
||||
default:
|
||||
panic(internalErrorf("unexpected value %v (%T)", x, x))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *exporter) float(x constant.Value) {
|
||||
if x.Kind() != constant.Float {
|
||||
panic(internalErrorf("unexpected constant %v, want float", x))
|
||||
}
|
||||
// extract sign (there is no -0)
|
||||
sign := constant.Sign(x)
|
||||
if sign == 0 {
|
||||
// x == 0
|
||||
p.int(0)
|
||||
return
|
||||
}
|
||||
// x != 0
|
||||
|
||||
var f big.Float
|
||||
if v, exact := constant.Float64Val(x); exact {
|
||||
// float64
|
||||
f.SetFloat64(v)
|
||||
} else if num, denom := constant.Num(x), constant.Denom(x); num.Kind() == constant.Int {
|
||||
// TODO(gri): add big.Rat accessor to constant.Value.
|
||||
r := valueToRat(num)
|
||||
f.SetRat(r.Quo(r, valueToRat(denom)))
|
||||
} else {
|
||||
// Value too large to represent as a fraction => inaccessible.
|
||||
// TODO(gri): add big.Float accessor to constant.Value.
|
||||
f.SetFloat64(math.MaxFloat64) // FIXME
|
||||
}
|
||||
|
||||
// extract exponent such that 0.5 <= m < 1.0
|
||||
var m big.Float
|
||||
exp := f.MantExp(&m)
|
||||
|
||||
// extract mantissa as *big.Int
|
||||
// - set exponent large enough so mant satisfies mant.IsInt()
|
||||
// - get *big.Int from mant
|
||||
m.SetMantExp(&m, int(m.MinPrec()))
|
||||
mant, acc := m.Int(nil)
|
||||
if acc != big.Exact {
|
||||
panic(internalError("internal error"))
|
||||
}
|
||||
|
||||
p.int(sign)
|
||||
p.int(exp)
|
||||
p.string(string(mant.Bytes()))
|
||||
}
|
||||
|
||||
func valueToRat(x constant.Value) *big.Rat {
|
||||
// Convert little-endian to big-endian.
|
||||
// I can't believe this is necessary.
|
||||
bytes := constant.Bytes(x)
|
||||
for i := 0; i < len(bytes)/2; i++ {
|
||||
bytes[i], bytes[len(bytes)-1-i] = bytes[len(bytes)-1-i], bytes[i]
|
||||
}
|
||||
return new(big.Rat).SetInt(new(big.Int).SetBytes(bytes))
|
||||
}
|
||||
|
||||
func (p *exporter) bool(b bool) bool {
|
||||
if trace {
|
||||
p.tracef("[")
|
||||
defer p.tracef("= %v] ", b)
|
||||
}
|
||||
|
||||
x := 0
|
||||
if b {
|
||||
x = 1
|
||||
}
|
||||
p.int(x)
|
||||
return b
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Low-level encoders
|
||||
|
||||
func (p *exporter) index(marker byte, index int) {
|
||||
if index < 0 {
|
||||
panic(internalError("invalid index < 0"))
|
||||
}
|
||||
if debugFormat {
|
||||
p.marker('t')
|
||||
}
|
||||
if trace {
|
||||
p.tracef("%c%d ", marker, index)
|
||||
}
|
||||
p.rawInt64(int64(index))
|
||||
}
|
||||
|
||||
func (p *exporter) tag(tag int) {
|
||||
if tag >= 0 {
|
||||
panic(internalError("invalid tag >= 0"))
|
||||
}
|
||||
if debugFormat {
|
||||
p.marker('t')
|
||||
}
|
||||
if trace {
|
||||
p.tracef("%s ", tagString[-tag])
|
||||
}
|
||||
p.rawInt64(int64(tag))
|
||||
}
|
||||
|
||||
func (p *exporter) int(x int) {
|
||||
p.int64(int64(x))
|
||||
}
|
||||
|
||||
func (p *exporter) int64(x int64) {
|
||||
if debugFormat {
|
||||
p.marker('i')
|
||||
}
|
||||
if trace {
|
||||
p.tracef("%d ", x)
|
||||
}
|
||||
p.rawInt64(x)
|
||||
}
|
||||
|
||||
func (p *exporter) string(s string) {
|
||||
if debugFormat {
|
||||
p.marker('s')
|
||||
}
|
||||
if trace {
|
||||
p.tracef("%q ", s)
|
||||
}
|
||||
// if we saw the string before, write its index (>= 0)
|
||||
// (the empty string is mapped to 0)
|
||||
if i, ok := p.strIndex[s]; ok {
|
||||
p.rawInt64(int64(i))
|
||||
return
|
||||
}
|
||||
// otherwise, remember string and write its negative length and bytes
|
||||
p.strIndex[s] = len(p.strIndex)
|
||||
p.rawInt64(-int64(len(s)))
|
||||
for i := 0; i < len(s); i++ {
|
||||
p.rawByte(s[i])
|
||||
}
|
||||
}
|
||||
|
||||
// marker emits a marker byte and position information which makes
|
||||
// it easy for a reader to detect if it is "out of sync". Used for
|
||||
// debugFormat format only.
|
||||
func (p *exporter) marker(m byte) {
|
||||
p.rawByte(m)
|
||||
// Enable this for help tracking down the location
|
||||
// of an incorrect marker when running in debugFormat.
|
||||
if false && trace {
|
||||
p.tracef("#%d ", p.written)
|
||||
}
|
||||
p.rawInt64(int64(p.written))
|
||||
}
|
||||
|
||||
// rawInt64 should only be used by low-level encoders.
|
||||
func (p *exporter) rawInt64(x int64) {
|
||||
var tmp [binary.MaxVarintLen64]byte
|
||||
n := binary.PutVarint(tmp[:], x)
|
||||
for i := 0; i < n; i++ {
|
||||
p.rawByte(tmp[i])
|
||||
}
|
||||
}
|
||||
|
||||
// rawStringln should only be used to emit the initial version string.
|
||||
func (p *exporter) rawStringln(s string) {
|
||||
for i := 0; i < len(s); i++ {
|
||||
p.rawByte(s[i])
|
||||
}
|
||||
p.rawByte('\n')
|
||||
}
|
||||
|
||||
// rawByte is the bottleneck interface to write to p.out.
|
||||
// rawByte escapes b as follows (any encoding does that
|
||||
// hides '$'):
|
||||
//
|
||||
// '$' => '|' 'S'
|
||||
// '|' => '|' '|'
|
||||
//
|
||||
// Necessary so other tools can find the end of the
|
||||
// export data by searching for "$$".
|
||||
// rawByte should only be used by low-level encoders.
|
||||
func (p *exporter) rawByte(b byte) {
|
||||
switch b {
|
||||
case '$':
|
||||
// write '$' as '|' 'S'
|
||||
b = 'S'
|
||||
fallthrough
|
||||
case '|':
|
||||
// write '|' as '|' '|'
|
||||
p.out.WriteByte('|')
|
||||
p.written++
|
||||
}
|
||||
p.out.WriteByte(b)
|
||||
p.written++
|
||||
}
|
||||
|
||||
// tracef is like fmt.Printf but it rewrites the format string
|
||||
// to take care of indentation.
|
||||
func (p *exporter) tracef(format string, args ...interface{}) {
|
||||
if strings.ContainsAny(format, "<>\n") {
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(format); i++ {
|
||||
// no need to deal with runes
|
||||
ch := format[i]
|
||||
switch ch {
|
||||
case '>':
|
||||
p.indent++
|
||||
continue
|
||||
case '<':
|
||||
p.indent--
|
||||
continue
|
||||
}
|
||||
buf.WriteByte(ch)
|
||||
if ch == '\n' {
|
||||
for j := p.indent; j > 0; j-- {
|
||||
buf.WriteString(". ")
|
||||
}
|
||||
}
|
||||
}
|
||||
format = buf.String()
|
||||
}
|
||||
fmt.Printf(format, args...)
|
||||
}
|
||||
|
||||
// Debugging support.
|
||||
// (tagString is only used when tracing is enabled)
|
||||
var tagString = [...]string{
|
||||
// Packages
|
||||
-packageTag: "package",
|
||||
|
||||
// Types
|
||||
-namedTag: "named type",
|
||||
-arrayTag: "array",
|
||||
-sliceTag: "slice",
|
||||
-dddTag: "ddd",
|
||||
-structTag: "struct",
|
||||
-pointerTag: "pointer",
|
||||
-signatureTag: "signature",
|
||||
-interfaceTag: "interface",
|
||||
-mapTag: "map",
|
||||
-chanTag: "chan",
|
||||
|
||||
// Values
|
||||
-falseTag: "false",
|
||||
-trueTag: "true",
|
||||
-int64Tag: "int64",
|
||||
-floatTag: "float",
|
||||
-fractionTag: "fraction",
|
||||
-complexTag: "complex",
|
||||
-stringTag: "string",
|
||||
-unknownTag: "unknown",
|
||||
|
||||
// Type aliases
|
||||
-aliasTag: "alias",
|
||||
}
|
96
vendor/golang.org/x/tools/go/internal/gcimporter/bexport19_test.go
generated
vendored
Normal file
96
vendor/golang.org/x/tools/go/internal/gcimporter/bexport19_test.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright 2016 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.9
|
||||
|
||||
package gcimporter_test
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/internal/gcimporter"
|
||||
)
|
||||
|
||||
const src = `
|
||||
package p
|
||||
|
||||
type (
|
||||
T0 = int32
|
||||
T1 = struct{}
|
||||
T2 = struct{ T1 }
|
||||
Invalid = foo // foo is undeclared
|
||||
)
|
||||
`
|
||||
|
||||
func checkPkg(t *testing.T, pkg *types.Package, label string) {
|
||||
T1 := types.NewStruct(nil, nil)
|
||||
T2 := types.NewStruct([]*types.Var{types.NewField(0, pkg, "T1", T1, true)}, nil)
|
||||
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
typ types.Type
|
||||
}{
|
||||
{"T0", types.Typ[types.Int32]},
|
||||
{"T1", T1},
|
||||
{"T2", T2},
|
||||
{"Invalid", types.Typ[types.Invalid]},
|
||||
} {
|
||||
obj := pkg.Scope().Lookup(test.name)
|
||||
if obj == nil {
|
||||
t.Errorf("%s: %s not found", label, test.name)
|
||||
continue
|
||||
}
|
||||
tname, _ := obj.(*types.TypeName)
|
||||
if tname == nil {
|
||||
t.Errorf("%s: %v not a type name", label, obj)
|
||||
continue
|
||||
}
|
||||
if !tname.IsAlias() {
|
||||
t.Errorf("%s: %v: not marked as alias", label, tname)
|
||||
continue
|
||||
}
|
||||
if got := tname.Type(); !types.Identical(got, test.typ) {
|
||||
t.Errorf("%s: %v: got %v; want %v", label, tname, got, test.typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTypeAliases(t *testing.T) {
|
||||
// parse and typecheck
|
||||
fset1 := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset1, "p.go", src, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var conf types.Config
|
||||
pkg1, err := conf.Check("p", fset1, []*ast.File{f}, nil)
|
||||
if err == nil {
|
||||
// foo in undeclared in src; we should see an error
|
||||
t.Fatal("invalid source type-checked without error")
|
||||
}
|
||||
if pkg1 == nil {
|
||||
// despite incorrect src we should see a (partially) type-checked package
|
||||
t.Fatal("nil package returned")
|
||||
}
|
||||
checkPkg(t, pkg1, "export")
|
||||
|
||||
// export
|
||||
exportdata, err := gcimporter.BExportData(fset1, pkg1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// import
|
||||
imports := make(map[string]*types.Package)
|
||||
fset2 := token.NewFileSet()
|
||||
_, pkg2, err := gcimporter.BImportData(fset2, imports, exportdata, pkg1.Path())
|
||||
if err != nil {
|
||||
t.Fatalf("BImportData(%s): %v", pkg1.Path(), err)
|
||||
}
|
||||
checkPkg(t, pkg2, "import")
|
||||
}
|
335
vendor/golang.org/x/tools/go/internal/gcimporter/bexport_test.go
generated
vendored
Normal file
335
vendor/golang.org/x/tools/go/internal/gcimporter/bexport_test.go
generated
vendored
Normal file
@@ -0,0 +1,335 @@
|
||||
// Copyright 2016 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_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/constant"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/tools/go/buildutil"
|
||||
"golang.org/x/tools/go/internal/gcimporter"
|
||||
"golang.org/x/tools/go/loader"
|
||||
)
|
||||
|
||||
func TestBExportData_stdlib(t *testing.T) {
|
||||
if runtime.Compiler == "gccgo" {
|
||||
t.Skip("gccgo standard library is inaccessible")
|
||||
}
|
||||
if runtime.GOOS == "android" {
|
||||
t.Skipf("incomplete std lib on %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
// Load, parse and type-check the program.
|
||||
ctxt := build.Default // copy
|
||||
ctxt.GOPATH = "" // disable GOPATH
|
||||
conf := loader.Config{
|
||||
Build: &ctxt,
|
||||
AllowErrors: true,
|
||||
}
|
||||
for _, path := range buildutil.AllPackages(conf.Build) {
|
||||
conf.Import(path)
|
||||
}
|
||||
|
||||
// Create a package containing type and value errors to ensure
|
||||
// they are properly encoded/decoded.
|
||||
f, err := conf.ParseFile("haserrors/haserrors.go", `package haserrors
|
||||
const UnknownValue = "" + 0
|
||||
type UnknownType undefined
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
conf.CreateFromFiles("haserrors", f)
|
||||
|
||||
prog, err := conf.Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Load failed: %v", err)
|
||||
}
|
||||
|
||||
numPkgs := len(prog.AllPackages)
|
||||
if want := 248; numPkgs < want {
|
||||
t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want)
|
||||
}
|
||||
|
||||
for pkg, info := range prog.AllPackages {
|
||||
if info.Files == nil {
|
||||
continue // empty directory
|
||||
}
|
||||
exportdata, err := gcimporter.BExportData(conf.Fset, pkg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
imports := make(map[string]*types.Package)
|
||||
fset2 := token.NewFileSet()
|
||||
n, pkg2, err := gcimporter.BImportData(fset2, imports, exportdata, pkg.Path())
|
||||
if err != nil {
|
||||
t.Errorf("BImportData(%s): %v", pkg.Path(), err)
|
||||
continue
|
||||
}
|
||||
if n != len(exportdata) {
|
||||
t.Errorf("BImportData(%s) decoded %d bytes, want %d",
|
||||
pkg.Path(), n, len(exportdata))
|
||||
}
|
||||
|
||||
// Compare the packages' corresponding members.
|
||||
for _, name := range pkg.Scope().Names() {
|
||||
if !ast.IsExported(name) {
|
||||
continue
|
||||
}
|
||||
obj1 := pkg.Scope().Lookup(name)
|
||||
obj2 := pkg2.Scope().Lookup(name)
|
||||
if obj2 == nil {
|
||||
t.Errorf("%s.%s not found, want %s", pkg.Path(), name, obj1)
|
||||
continue
|
||||
}
|
||||
|
||||
fl1 := fileLine(conf.Fset, obj1)
|
||||
fl2 := fileLine(fset2, obj2)
|
||||
if fl1 != fl2 {
|
||||
t.Errorf("%s.%s: got posn %s, want %s",
|
||||
pkg.Path(), name, fl2, fl1)
|
||||
}
|
||||
|
||||
if err := equalObj(obj1, obj2); err != nil {
|
||||
t.Errorf("%s.%s: %s\ngot: %s\nwant: %s",
|
||||
pkg.Path(), name, err, obj2, obj1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fileLine(fset *token.FileSet, obj types.Object) string {
|
||||
posn := fset.Position(obj.Pos())
|
||||
return fmt.Sprintf("%s:%d", posn.Filename, posn.Line)
|
||||
}
|
||||
|
||||
// equalObj reports how x and y differ. They are assumed to belong to
|
||||
// different universes so cannot be compared directly.
|
||||
func equalObj(x, y types.Object) error {
|
||||
if reflect.TypeOf(x) != reflect.TypeOf(y) {
|
||||
return fmt.Errorf("%T vs %T", x, y)
|
||||
}
|
||||
xt := x.Type()
|
||||
yt := y.Type()
|
||||
switch x.(type) {
|
||||
case *types.Var, *types.Func:
|
||||
// ok
|
||||
case *types.Const:
|
||||
xval := x.(*types.Const).Val()
|
||||
yval := y.(*types.Const).Val()
|
||||
// Use string comparison for floating-point values since rounding is permitted.
|
||||
if constant.Compare(xval, token.NEQ, yval) &&
|
||||
!(xval.Kind() == constant.Float && xval.String() == yval.String()) {
|
||||
return fmt.Errorf("unequal constants %s vs %s", xval, yval)
|
||||
}
|
||||
case *types.TypeName:
|
||||
xt = xt.Underlying()
|
||||
yt = yt.Underlying()
|
||||
default:
|
||||
return fmt.Errorf("unexpected %T", x)
|
||||
}
|
||||
return equalType(xt, yt)
|
||||
}
|
||||
|
||||
func equalType(x, y types.Type) error {
|
||||
if reflect.TypeOf(x) != reflect.TypeOf(y) {
|
||||
return fmt.Errorf("unequal kinds: %T vs %T", x, y)
|
||||
}
|
||||
switch x := x.(type) {
|
||||
case *types.Interface:
|
||||
y := y.(*types.Interface)
|
||||
// TODO(gri): enable separate emission of Embedded interfaces
|
||||
// and ExplicitMethods then use this logic.
|
||||
// if x.NumEmbeddeds() != y.NumEmbeddeds() {
|
||||
// return fmt.Errorf("unequal number of embedded interfaces: %d vs %d",
|
||||
// x.NumEmbeddeds(), y.NumEmbeddeds())
|
||||
// }
|
||||
// for i := 0; i < x.NumEmbeddeds(); i++ {
|
||||
// xi := x.Embedded(i)
|
||||
// yi := y.Embedded(i)
|
||||
// if xi.String() != yi.String() {
|
||||
// return fmt.Errorf("mismatched %th embedded interface: %s vs %s",
|
||||
// i, xi, yi)
|
||||
// }
|
||||
// }
|
||||
// if x.NumExplicitMethods() != y.NumExplicitMethods() {
|
||||
// return fmt.Errorf("unequal methods: %d vs %d",
|
||||
// x.NumExplicitMethods(), y.NumExplicitMethods())
|
||||
// }
|
||||
// for i := 0; i < x.NumExplicitMethods(); i++ {
|
||||
// xm := x.ExplicitMethod(i)
|
||||
// ym := y.ExplicitMethod(i)
|
||||
// if xm.Name() != ym.Name() {
|
||||
// return fmt.Errorf("mismatched %th method: %s vs %s", i, xm, ym)
|
||||
// }
|
||||
// if err := equalType(xm.Type(), ym.Type()); err != nil {
|
||||
// return fmt.Errorf("mismatched %s method: %s", xm.Name(), err)
|
||||
// }
|
||||
// }
|
||||
if x.NumMethods() != y.NumMethods() {
|
||||
return fmt.Errorf("unequal methods: %d vs %d",
|
||||
x.NumMethods(), y.NumMethods())
|
||||
}
|
||||
for i := 0; i < x.NumMethods(); i++ {
|
||||
xm := x.Method(i)
|
||||
ym := y.Method(i)
|
||||
if xm.Name() != ym.Name() {
|
||||
return fmt.Errorf("mismatched %dth method: %s vs %s", i, xm, ym)
|
||||
}
|
||||
if err := equalType(xm.Type(), ym.Type()); err != nil {
|
||||
return fmt.Errorf("mismatched %s method: %s", xm.Name(), err)
|
||||
}
|
||||
}
|
||||
case *types.Array:
|
||||
y := y.(*types.Array)
|
||||
if x.Len() != y.Len() {
|
||||
return fmt.Errorf("unequal array lengths: %d vs %d", x.Len(), y.Len())
|
||||
}
|
||||
if err := equalType(x.Elem(), y.Elem()); err != nil {
|
||||
return fmt.Errorf("array elements: %s", err)
|
||||
}
|
||||
case *types.Basic:
|
||||
y := y.(*types.Basic)
|
||||
if x.Kind() != y.Kind() {
|
||||
return fmt.Errorf("unequal basic types: %s vs %s", x, y)
|
||||
}
|
||||
case *types.Chan:
|
||||
y := y.(*types.Chan)
|
||||
if x.Dir() != y.Dir() {
|
||||
return fmt.Errorf("unequal channel directions: %d vs %d", x.Dir(), y.Dir())
|
||||
}
|
||||
if err := equalType(x.Elem(), y.Elem()); err != nil {
|
||||
return fmt.Errorf("channel elements: %s", err)
|
||||
}
|
||||
case *types.Map:
|
||||
y := y.(*types.Map)
|
||||
if err := equalType(x.Key(), y.Key()); err != nil {
|
||||
return fmt.Errorf("map keys: %s", err)
|
||||
}
|
||||
if err := equalType(x.Elem(), y.Elem()); err != nil {
|
||||
return fmt.Errorf("map values: %s", err)
|
||||
}
|
||||
case *types.Named:
|
||||
y := y.(*types.Named)
|
||||
if x.String() != y.String() {
|
||||
return fmt.Errorf("unequal named types: %s vs %s", x, y)
|
||||
}
|
||||
case *types.Pointer:
|
||||
y := y.(*types.Pointer)
|
||||
if err := equalType(x.Elem(), y.Elem()); err != nil {
|
||||
return fmt.Errorf("pointer elements: %s", err)
|
||||
}
|
||||
case *types.Signature:
|
||||
y := y.(*types.Signature)
|
||||
if err := equalType(x.Params(), y.Params()); err != nil {
|
||||
return fmt.Errorf("parameters: %s", err)
|
||||
}
|
||||
if err := equalType(x.Results(), y.Results()); err != nil {
|
||||
return fmt.Errorf("results: %s", err)
|
||||
}
|
||||
if x.Variadic() != y.Variadic() {
|
||||
return fmt.Errorf("unequal varidicity: %t vs %t",
|
||||
x.Variadic(), y.Variadic())
|
||||
}
|
||||
if (x.Recv() != nil) != (y.Recv() != nil) {
|
||||
return fmt.Errorf("unequal receivers: %s vs %s", x.Recv(), y.Recv())
|
||||
}
|
||||
if x.Recv() != nil {
|
||||
// TODO(adonovan): fix: this assertion fires for interface methods.
|
||||
// The type of the receiver of an interface method is a named type
|
||||
// if the Package was loaded from export data, or an unnamed (interface)
|
||||
// type if the Package was produced by type-checking ASTs.
|
||||
// if err := equalType(x.Recv().Type(), y.Recv().Type()); err != nil {
|
||||
// return fmt.Errorf("receiver: %s", err)
|
||||
// }
|
||||
}
|
||||
case *types.Slice:
|
||||
y := y.(*types.Slice)
|
||||
if err := equalType(x.Elem(), y.Elem()); err != nil {
|
||||
return fmt.Errorf("slice elements: %s", err)
|
||||
}
|
||||
case *types.Struct:
|
||||
y := y.(*types.Struct)
|
||||
if x.NumFields() != y.NumFields() {
|
||||
return fmt.Errorf("unequal struct fields: %d vs %d",
|
||||
x.NumFields(), y.NumFields())
|
||||
}
|
||||
for i := 0; i < x.NumFields(); i++ {
|
||||
xf := x.Field(i)
|
||||
yf := y.Field(i)
|
||||
if xf.Name() != yf.Name() {
|
||||
return fmt.Errorf("mismatched fields: %s vs %s", xf, yf)
|
||||
}
|
||||
if err := equalType(xf.Type(), yf.Type()); err != nil {
|
||||
return fmt.Errorf("struct field %s: %s", xf.Name(), err)
|
||||
}
|
||||
if x.Tag(i) != y.Tag(i) {
|
||||
return fmt.Errorf("struct field %s has unequal tags: %q vs %q",
|
||||
xf.Name(), x.Tag(i), y.Tag(i))
|
||||
}
|
||||
}
|
||||
case *types.Tuple:
|
||||
y := y.(*types.Tuple)
|
||||
if x.Len() != y.Len() {
|
||||
return fmt.Errorf("unequal tuple lengths: %d vs %d", x.Len(), y.Len())
|
||||
}
|
||||
for i := 0; i < x.Len(); i++ {
|
||||
if err := equalType(x.At(i).Type(), y.At(i).Type()); err != nil {
|
||||
return fmt.Errorf("tuple element %d: %s", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestVeryLongFile tests the position of an import object declared in
|
||||
// a very long input file. Line numbers greater than maxlines are
|
||||
// reported as line 1, not garbage or token.NoPos.
|
||||
func TestVeryLongFile(t *testing.T) {
|
||||
// parse and typecheck
|
||||
longFile := "package foo" + strings.Repeat("\n", 123456) + "var X int"
|
||||
fset1 := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset1, "foo.go", longFile, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var conf types.Config
|
||||
pkg, err := conf.Check("foo", fset1, []*ast.File{f}, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// export
|
||||
exportdata, err := gcimporter.BExportData(fset1, pkg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// import
|
||||
imports := make(map[string]*types.Package)
|
||||
fset2 := token.NewFileSet()
|
||||
_, pkg2, err := gcimporter.BImportData(fset2, imports, exportdata, pkg.Path())
|
||||
if err != nil {
|
||||
t.Fatalf("BImportData(%s): %v", pkg.Path(), err)
|
||||
}
|
||||
|
||||
// compare
|
||||
posn1 := fset1.Position(pkg.Scope().Lookup("X").Pos())
|
||||
posn2 := fset2.Position(pkg2.Scope().Lookup("X").Pos())
|
||||
if want := "foo.go:1:1"; posn2.String() != want {
|
||||
t.Errorf("X position = %s, want %s (orig was %s)",
|
||||
posn2, want, posn1)
|
||||
}
|
||||
}
|
1028
vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go
generated
vendored
Normal file
1028
vendor/golang.org/x/tools/go/internal/gcimporter/bimport.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
93
vendor/golang.org/x/tools/go/internal/gcimporter/exportdata.go
generated
vendored
Normal file
93
vendor/golang.org/x/tools/go/internal/gcimporter/exportdata.go
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
// 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 is a copy of $GOROOT/src/go/internal/gcimporter/exportdata.go.
|
||||
|
||||
// This file implements FindExportData.
|
||||
|
||||
package gcimporter
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"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 = fmt.Errorf("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. The hdr result
|
||||
// is the string before the export data, either "$$" or "$$B".
|
||||
//
|
||||
func FindExportData(r *bufio.Reader) (hdr string, err error) {
|
||||
// Read first line to make sure this is an object file.
|
||||
line, err := r.ReadSlice('\n')
|
||||
if err != nil {
|
||||
err = fmt.Errorf("can't find export data (%v)", err)
|
||||
return
|
||||
}
|
||||
|
||||
if string(line) == "!<arch>\n" {
|
||||
// Archive file. Scan to __.PKGDEF.
|
||||
var name string
|
||||
if name, _, err = readGopackHeader(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// First entry should be __.PKGDEF.
|
||||
if name != "__.PKGDEF" {
|
||||
err = fmt.Errorf("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 {
|
||||
err = fmt.Errorf("can't find export data (%v)", err)
|
||||
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 = fmt.Errorf("not a Go object file")
|
||||
return
|
||||
}
|
||||
|
||||
// Skip over object header to export data.
|
||||
// Begins after first line starting with $$.
|
||||
for line[0] != '$' {
|
||||
if line, err = r.ReadSlice('\n'); err != nil {
|
||||
err = fmt.Errorf("can't find export data (%v)", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
hdr = string(line)
|
||||
|
||||
return
|
||||
}
|
1051
vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go
generated
vendored
Normal file
1051
vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
134
vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter11_test.go
generated
vendored
Normal file
134
vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter11_test.go
generated
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright 2018 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.11
|
||||
|
||||
package gcimporter
|
||||
|
||||
import (
|
||||
"go/types"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var importedObjectTests = []struct {
|
||||
name string
|
||||
want string
|
||||
}{
|
||||
// non-interfaces
|
||||
{"crypto.Hash", "type Hash uint"},
|
||||
{"go/ast.ObjKind", "type ObjKind int"},
|
||||
{"go/types.Qualifier", "type Qualifier func(*Package) string"},
|
||||
{"go/types.Comparable", "func Comparable(T Type) bool"},
|
||||
{"math.Pi", "const Pi untyped float"},
|
||||
{"math.Sin", "func Sin(x float64) float64"},
|
||||
{"go/ast.NotNilFilter", "func NotNilFilter(_ string, v reflect.Value) bool"},
|
||||
{"go/internal/gcimporter.BImportData", "func BImportData(fset *go/token.FileSet, imports map[string]*go/types.Package, data []byte, path string) (_ int, pkg *go/types.Package, err error)"},
|
||||
|
||||
// interfaces
|
||||
{"context.Context", "type Context interface{Deadline() (deadline time.Time, ok bool); Done() <-chan struct{}; Err() error; Value(key interface{}) interface{}}"},
|
||||
{"crypto.Decrypter", "type Decrypter interface{Decrypt(rand io.Reader, msg []byte, opts DecrypterOpts) (plaintext []byte, err error); Public() PublicKey}"},
|
||||
{"encoding.BinaryMarshaler", "type BinaryMarshaler interface{MarshalBinary() (data []byte, err error)}"},
|
||||
{"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
|
||||
{"io.ReadWriter", "type ReadWriter interface{Reader; Writer}"},
|
||||
{"go/ast.Node", "type Node interface{End() go/token.Pos; Pos() go/token.Pos}"},
|
||||
{"go/types.Type", "type Type interface{String() string; Underlying() Type}"},
|
||||
}
|
||||
|
||||
func TestImportedTypes(t *testing.T) {
|
||||
skipSpecialPlatforms(t)
|
||||
|
||||
// This package only handles gc export data.
|
||||
if runtime.Compiler != "gc" {
|
||||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
for _, test := range importedObjectTests {
|
||||
s := strings.Split(test.name, ".")
|
||||
if len(s) != 2 {
|
||||
t.Fatal("inconsistent test data")
|
||||
}
|
||||
importPath := s[0]
|
||||
objName := s[1]
|
||||
|
||||
pkg, err := Import(make(map[string]*types.Package), importPath, ".")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
obj := pkg.Scope().Lookup(objName)
|
||||
if obj == nil {
|
||||
t.Errorf("%s: object not found", test.name)
|
||||
continue
|
||||
}
|
||||
|
||||
got := types.ObjectString(obj, types.RelativeTo(pkg))
|
||||
if got != test.want {
|
||||
t.Errorf("%s: got %q; want %q", test.name, got, test.want)
|
||||
}
|
||||
|
||||
if named, _ := obj.Type().(*types.Named); named != nil {
|
||||
verifyInterfaceMethodRecvs(t, named, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// verifyInterfaceMethodRecvs verifies that method receiver types
|
||||
// are named if the methods belong to a named interface type.
|
||||
func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) {
|
||||
// avoid endless recursion in case of an embedding bug that lead to a cycle
|
||||
if level > 10 {
|
||||
t.Errorf("%s: embeds itself", named)
|
||||
return
|
||||
}
|
||||
|
||||
iface, _ := named.Underlying().(*types.Interface)
|
||||
if iface == nil {
|
||||
return // not an interface
|
||||
}
|
||||
|
||||
// check explicitly declared methods
|
||||
for i := 0; i < iface.NumExplicitMethods(); i++ {
|
||||
m := iface.ExplicitMethod(i)
|
||||
recv := m.Type().(*types.Signature).Recv()
|
||||
if recv == nil {
|
||||
t.Errorf("%s: missing receiver type", m)
|
||||
continue
|
||||
}
|
||||
if recv.Type() != named {
|
||||
t.Errorf("%s: got recv type %s; want %s", m, recv.Type(), named)
|
||||
}
|
||||
}
|
||||
|
||||
// check embedded interfaces (if they are named, too)
|
||||
for i := 0; i < iface.NumEmbeddeds(); i++ {
|
||||
// embedding of interfaces cannot have cycles; recursion will terminate
|
||||
if etype, _ := iface.EmbeddedType(i).(*types.Named); etype != nil {
|
||||
verifyInterfaceMethodRecvs(t, etype, level+1)
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestIssue25301(t *testing.T) {
|
||||
skipSpecialPlatforms(t)
|
||||
|
||||
// This package only handles gc export data.
|
||||
if runtime.Compiler != "gc" {
|
||||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
// On windows, we have to set the -D option for the compiler to avoid having a drive
|
||||
// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("avoid dealing with relative paths/drive letters on windows")
|
||||
}
|
||||
|
||||
if f := compile(t, "testdata", "issue25301.go"); f != "" {
|
||||
defer os.Remove(f)
|
||||
}
|
||||
|
||||
importPkg(t, "./testdata/issue25301")
|
||||
}
|
484
vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter_test.go
generated
vendored
Normal file
484
vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter_test.go
generated
vendored
Normal file
@@ -0,0 +1,484 @@
|
||||
// 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 is a copy of $GOROOT/src/go/internal/gcimporter/gcimporter_test.go,
|
||||
// adjusted to make it build with code from (std lib) internal/testenv copied.
|
||||
|
||||
package gcimporter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/types"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// The following three functions (Builder, HasGoBuild, MustHaveGoBuild) were
|
||||
// copied from $GOROOT/src/internal/testenv since that package is not available
|
||||
// in x/tools.
|
||||
|
||||
// Builder reports the name of the builder running this test
|
||||
// (for example, "linux-amd64" or "windows-386-gce").
|
||||
// If the test is not running on the build infrastructure,
|
||||
// Builder returns the empty string.
|
||||
func Builder() string {
|
||||
return os.Getenv("GO_BUILDER_NAME")
|
||||
}
|
||||
|
||||
// HasGoBuild reports whether the current system can build programs with ``go build''
|
||||
// and then run them with os.StartProcess or exec.Command.
|
||||
func HasGoBuild() bool {
|
||||
switch runtime.GOOS {
|
||||
case "android", "nacl":
|
||||
return false
|
||||
case "darwin":
|
||||
if strings.HasPrefix(runtime.GOARCH, "arm") {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MustHaveGoBuild checks that the current system can build programs with ``go build''
|
||||
// and then run them with os.StartProcess or exec.Command.
|
||||
// If not, MustHaveGoBuild calls t.Skip with an explanation.
|
||||
func MustHaveGoBuild(t *testing.T) {
|
||||
if !HasGoBuild() {
|
||||
t.Skipf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// skipSpecialPlatforms causes the test to be skipped for platforms where
|
||||
// builders (build.golang.org) don't have access to compiled packages for
|
||||
// import.
|
||||
func skipSpecialPlatforms(t *testing.T) {
|
||||
switch platform := runtime.GOOS + "-" + runtime.GOARCH; platform {
|
||||
case "nacl-amd64p32",
|
||||
"nacl-386",
|
||||
"nacl-arm",
|
||||
"darwin-arm",
|
||||
"darwin-arm64":
|
||||
t.Skipf("no compiled packages available for import on %s", platform)
|
||||
}
|
||||
}
|
||||
|
||||
func compile(t *testing.T, dirname, filename string) string {
|
||||
/* testenv. */ MustHaveGoBuild(t)
|
||||
cmd := exec.Command("go", "tool", "compile", filename)
|
||||
cmd.Dir = dirname
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Logf("%s", out)
|
||||
t.Fatalf("go tool compile %s failed: %s", filename, err)
|
||||
}
|
||||
// filename should end with ".go"
|
||||
return filepath.Join(dirname, filename[:len(filename)-2]+"o")
|
||||
}
|
||||
|
||||
func testPath(t *testing.T, path, srcDir string) *types.Package {
|
||||
t0 := time.Now()
|
||||
pkg, err := Import(make(map[string]*types.Package), path, srcDir)
|
||||
if err != nil {
|
||||
t.Errorf("testPath(%s): %s", path, err)
|
||||
return nil
|
||||
}
|
||||
t.Logf("testPath(%s): %v", path, time.Since(t0))
|
||||
return pkg
|
||||
}
|
||||
|
||||
const maxTime = 30 * time.Second
|
||||
|
||||
func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
|
||||
dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
|
||||
list, err := ioutil.ReadDir(dirname)
|
||||
if err != nil {
|
||||
t.Fatalf("testDir(%s): %s", dirname, err)
|
||||
}
|
||||
for _, f := range list {
|
||||
if time.Now().After(endTime) {
|
||||
t.Log("testing time used up")
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case !f.IsDir():
|
||||
// try extensions
|
||||
for _, ext := range pkgExts {
|
||||
if strings.HasSuffix(f.Name(), ext) {
|
||||
name := f.Name()[0 : len(f.Name())-len(ext)] // remove extension
|
||||
if testPath(t, filepath.Join(dir, name), dir) != nil {
|
||||
nimports++
|
||||
}
|
||||
}
|
||||
}
|
||||
case f.IsDir():
|
||||
nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const testfile = "exports.go"
|
||||
|
||||
func TestImportTestdata(t *testing.T) {
|
||||
// This package only handles gc export data.
|
||||
if runtime.Compiler != "gc" {
|
||||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
if outFn := compile(t, "testdata", testfile); outFn != "" {
|
||||
defer os.Remove(outFn)
|
||||
}
|
||||
|
||||
// filename should end with ".go"
|
||||
filename := testfile[:len(testfile)-3]
|
||||
if pkg := testPath(t, "./testdata/"+filename, "."); pkg != nil {
|
||||
// The package's Imports list must include all packages
|
||||
// explicitly imported by testfile, plus all packages
|
||||
// referenced indirectly via exported objects in testfile.
|
||||
// With the textual export format (when run against Go1.6),
|
||||
// the list may also include additional packages that are
|
||||
// not strictly required for import processing alone (they
|
||||
// are exported to err "on the safe side").
|
||||
// For now, we just test the presence of a few packages
|
||||
// that we know are there for sure.
|
||||
got := fmt.Sprint(pkg.Imports())
|
||||
for _, want := range []string{"go/ast", "go/token"} {
|
||||
if !strings.Contains(got, want) {
|
||||
t.Errorf(`Package("exports").Imports() = %s, does not contain %s`, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionHandling(t *testing.T) {
|
||||
skipSpecialPlatforms(t) // we really only need to exclude nacl platforms, but this is fine
|
||||
|
||||
// This package only handles gc export data.
|
||||
if runtime.Compiler != "gc" {
|
||||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
const dir = "./testdata/versions"
|
||||
list, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, f := range list {
|
||||
name := f.Name()
|
||||
if !strings.HasSuffix(name, ".a") {
|
||||
continue // not a package file
|
||||
}
|
||||
if strings.Contains(name, "corrupted") {
|
||||
continue // don't process a leftover corrupted file
|
||||
}
|
||||
pkgpath := "./" + name[:len(name)-2]
|
||||
|
||||
if testing.Verbose() {
|
||||
t.Logf("importing %s", name)
|
||||
}
|
||||
|
||||
// test that export data can be imported
|
||||
_, err := Import(make(map[string]*types.Package), pkgpath, dir)
|
||||
if err != nil {
|
||||
// ok to fail if it fails with a newer version error for select files
|
||||
if strings.Contains(err.Error(), "newer version") {
|
||||
switch name {
|
||||
case "test_go1.11_999b.a", "test_go1.11_999i.a":
|
||||
continue
|
||||
}
|
||||
// fall through
|
||||
}
|
||||
t.Errorf("import %q failed: %v", pkgpath, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// create file with corrupted export data
|
||||
// 1) read file
|
||||
data, err := ioutil.ReadFile(filepath.Join(dir, name))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 2) find export data
|
||||
i := bytes.Index(data, []byte("\n$$B\n")) + 5
|
||||
j := bytes.Index(data[i:], []byte("\n$$\n")) + i
|
||||
if i < 0 || j < 0 || i > j {
|
||||
t.Fatalf("export data section not found (i = %d, j = %d)", i, j)
|
||||
}
|
||||
// 3) corrupt the data (increment every 7th byte)
|
||||
for k := j - 13; k >= i; k -= 7 {
|
||||
data[k]++
|
||||
}
|
||||
// 4) write the file
|
||||
pkgpath += "_corrupted"
|
||||
filename := filepath.Join(dir, pkgpath) + ".a"
|
||||
ioutil.WriteFile(filename, data, 0666)
|
||||
defer os.Remove(filename)
|
||||
|
||||
// test that importing the corrupted file results in an error
|
||||
_, err = Import(make(map[string]*types.Package), pkgpath, dir)
|
||||
if err == nil {
|
||||
t.Errorf("import corrupted %q succeeded", pkgpath)
|
||||
} else if msg := err.Error(); !strings.Contains(msg, "version skew") {
|
||||
t.Errorf("import %q error incorrect (%s)", pkgpath, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestImportStdLib(t *testing.T) {
|
||||
skipSpecialPlatforms(t)
|
||||
|
||||
// This package only handles gc export data.
|
||||
if runtime.Compiler != "gc" {
|
||||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
dt := maxTime
|
||||
if testing.Short() && /* testenv. */ Builder() == "" {
|
||||
dt = 10 * time.Millisecond
|
||||
}
|
||||
nimports := testDir(t, "", time.Now().Add(dt)) // installed packages
|
||||
t.Logf("tested %d imports", nimports)
|
||||
}
|
||||
|
||||
func TestIssue5815(t *testing.T) {
|
||||
skipSpecialPlatforms(t)
|
||||
|
||||
// This package only handles gc export data.
|
||||
if runtime.Compiler != "gc" {
|
||||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
pkg := importPkg(t, "strings")
|
||||
|
||||
scope := pkg.Scope()
|
||||
for _, name := range scope.Names() {
|
||||
obj := scope.Lookup(name)
|
||||
if obj.Pkg() == nil {
|
||||
t.Errorf("no pkg for %s", obj)
|
||||
}
|
||||
if tname, _ := obj.(*types.TypeName); tname != nil {
|
||||
named := tname.Type().(*types.Named)
|
||||
for i := 0; i < named.NumMethods(); i++ {
|
||||
m := named.Method(i)
|
||||
if m.Pkg() == nil {
|
||||
t.Errorf("no pkg for %s", m)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Smoke test to ensure that imported methods get the correct package.
|
||||
func TestCorrectMethodPackage(t *testing.T) {
|
||||
skipSpecialPlatforms(t)
|
||||
|
||||
// This package only handles gc export data.
|
||||
if runtime.Compiler != "gc" {
|
||||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
imports := make(map[string]*types.Package)
|
||||
_, err := Import(imports, "net/http", ".")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type()
|
||||
mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex
|
||||
sel := mset.Lookup(nil, "Lock")
|
||||
lock := sel.Obj().(*types.Func)
|
||||
if got, want := lock.Pkg().Path(), "sync"; got != want {
|
||||
t.Errorf("got package path %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue13566(t *testing.T) {
|
||||
skipSpecialPlatforms(t)
|
||||
|
||||
// This package only handles gc export data.
|
||||
if runtime.Compiler != "gc" {
|
||||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
// On windows, we have to set the -D option for the compiler to avoid having a drive
|
||||
// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("avoid dealing with relative paths/drive letters on windows")
|
||||
}
|
||||
|
||||
if f := compile(t, "testdata", "a.go"); f != "" {
|
||||
defer os.Remove(f)
|
||||
}
|
||||
if f := compile(t, "testdata", "b.go"); f != "" {
|
||||
defer os.Remove(f)
|
||||
}
|
||||
|
||||
// import must succeed (test for issue at hand)
|
||||
pkg := importPkg(t, "./testdata/b")
|
||||
|
||||
// make sure all indirectly imported packages have names
|
||||
for _, imp := range pkg.Imports() {
|
||||
if imp.Name() == "" {
|
||||
t.Errorf("no name for %s package", imp.Path())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue13898(t *testing.T) {
|
||||
skipSpecialPlatforms(t)
|
||||
|
||||
// This package only handles gc export data.
|
||||
if runtime.Compiler != "gc" {
|
||||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
// import go/internal/gcimporter which imports go/types partially
|
||||
imports := make(map[string]*types.Package)
|
||||
_, err := Import(imports, "go/internal/gcimporter", ".")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// look for go/types package
|
||||
var goTypesPkg *types.Package
|
||||
for path, pkg := range imports {
|
||||
if path == "go/types" {
|
||||
goTypesPkg = pkg
|
||||
break
|
||||
}
|
||||
}
|
||||
if goTypesPkg == nil {
|
||||
t.Fatal("go/types not found")
|
||||
}
|
||||
|
||||
// look for go/types.Object type
|
||||
obj := lookupObj(t, goTypesPkg.Scope(), "Object")
|
||||
typ, ok := obj.Type().(*types.Named)
|
||||
if !ok {
|
||||
t.Fatalf("go/types.Object type is %v; wanted named type", typ)
|
||||
}
|
||||
|
||||
// lookup go/types.Object.Pkg method
|
||||
m, index, indirect := types.LookupFieldOrMethod(typ, false, nil, "Pkg")
|
||||
if m == nil {
|
||||
t.Fatalf("go/types.Object.Pkg not found (index = %v, indirect = %v)", index, indirect)
|
||||
}
|
||||
|
||||
// the method must belong to go/types
|
||||
if m.Pkg().Path() != "go/types" {
|
||||
t.Fatalf("found %v; want go/types", m.Pkg())
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue15517(t *testing.T) {
|
||||
skipSpecialPlatforms(t)
|
||||
|
||||
// This package only handles gc export data.
|
||||
if runtime.Compiler != "gc" {
|
||||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
// On windows, we have to set the -D option for the compiler to avoid having a drive
|
||||
// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("avoid dealing with relative paths/drive letters on windows")
|
||||
}
|
||||
|
||||
if f := compile(t, "testdata", "p.go"); f != "" {
|
||||
defer os.Remove(f)
|
||||
}
|
||||
|
||||
// Multiple imports of p must succeed without redeclaration errors.
|
||||
// We use an import path that's not cleaned up so that the eventual
|
||||
// file path for the package is different from the package path; this
|
||||
// will expose the error if it is present.
|
||||
//
|
||||
// (Issue: Both the textual and the binary importer used the file path
|
||||
// of the package to be imported as key into the shared packages map.
|
||||
// However, the binary importer then used the package path to identify
|
||||
// the imported package to mark it as complete; effectively marking the
|
||||
// wrong package as complete. By using an "unclean" package path, the
|
||||
// file and package path are different, exposing the problem if present.
|
||||
// The same issue occurs with vendoring.)
|
||||
imports := make(map[string]*types.Package)
|
||||
for i := 0; i < 3; i++ {
|
||||
if _, err := Import(imports, "./././testdata/p", "."); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue15920(t *testing.T) {
|
||||
skipSpecialPlatforms(t)
|
||||
|
||||
// This package only handles gc export data.
|
||||
if runtime.Compiler != "gc" {
|
||||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
// On windows, we have to set the -D option for the compiler to avoid having a drive
|
||||
// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("avoid dealing with relative paths/drive letters on windows")
|
||||
}
|
||||
|
||||
if f := compile(t, "testdata", "issue15920.go"); f != "" {
|
||||
defer os.Remove(f)
|
||||
}
|
||||
|
||||
importPkg(t, "./testdata/issue15920")
|
||||
}
|
||||
|
||||
func TestIssue20046(t *testing.T) {
|
||||
skipSpecialPlatforms(t)
|
||||
|
||||
// This package only handles gc export data.
|
||||
if runtime.Compiler != "gc" {
|
||||
t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
|
||||
}
|
||||
|
||||
// On windows, we have to set the -D option for the compiler to avoid having a drive
|
||||
// letter and an illegal ':' in the import path - just skip it (see also issue #3483).
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("avoid dealing with relative paths/drive letters on windows")
|
||||
}
|
||||
|
||||
if f := compile(t, "testdata", "issue20046.go"); f != "" {
|
||||
defer os.Remove(f)
|
||||
}
|
||||
|
||||
// "./issue20046".V.M must exist
|
||||
pkg := importPkg(t, "./testdata/issue20046")
|
||||
obj := lookupObj(t, pkg.Scope(), "V")
|
||||
if m, index, indirect := types.LookupFieldOrMethod(obj.Type(), false, nil, "M"); m == nil {
|
||||
t.Fatalf("V.M not found (index = %v, indirect = %v)", index, indirect)
|
||||
}
|
||||
}
|
||||
|
||||
func importPkg(t *testing.T, path string) *types.Package {
|
||||
pkg, err := Import(make(map[string]*types.Package), path, ".")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return pkg
|
||||
}
|
||||
|
||||
func lookupObj(t *testing.T, scope *types.Scope, name string) types.Object {
|
||||
if obj := scope.Lookup(name); obj != nil {
|
||||
return obj
|
||||
}
|
||||
t.Fatalf("%s not found", name)
|
||||
return nil
|
||||
}
|
598
vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go
generated
vendored
Normal file
598
vendor/golang.org/x/tools/go/internal/gcimporter/iimport.go
generated
vendored
Normal file
@@ -0,0 +1,598 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
// Indexed package import.
|
||||
// See cmd/compile/internal/gc/iexport.go for the export data format.
|
||||
|
||||
// This file is a copy of $GOROOT/src/go/internal/gcimporter/iimport.go.
|
||||
|
||||
package gcimporter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type intReader struct {
|
||||
*bytes.Reader
|
||||
path string
|
||||
}
|
||||
|
||||
func (r *intReader) int64() int64 {
|
||||
i, err := binary.ReadVarint(r.Reader)
|
||||
if err != nil {
|
||||
errorf("import %q: read varint error: %v", r.path, err)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func (r *intReader) uint64() uint64 {
|
||||
i, err := binary.ReadUvarint(r.Reader)
|
||||
if err != nil {
|
||||
errorf("import %q: read varint error: %v", r.path, err)
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
const predeclReserved = 32
|
||||
|
||||
type itag uint64
|
||||
|
||||
const (
|
||||
// Types
|
||||
definedType itag = iota
|
||||
pointerType
|
||||
sliceType
|
||||
arrayType
|
||||
chanType
|
||||
mapType
|
||||
signatureType
|
||||
structType
|
||||
interfaceType
|
||||
)
|
||||
|
||||
// IImportData imports a package from the serialized package data
|
||||
// and returns the number of bytes consumed and a reference to the package.
|
||||
// If the export data version is not recognized or the format is otherwise
|
||||
// compromised, an error is returned.
|
||||
func IImportData(fset *token.FileSet, imports map[string]*types.Package, data []byte, path string) (_ int, pkg *types.Package, err error) {
|
||||
const currentVersion = 0
|
||||
version := -1
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
if version > currentVersion {
|
||||
err = fmt.Errorf("cannot import %q (%v), export data is newer version - update tool", path, e)
|
||||
} else {
|
||||
err = fmt.Errorf("cannot import %q (%v), possibly version skew - reinstall package", path, e)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
r := &intReader{bytes.NewReader(data), path}
|
||||
|
||||
version = int(r.uint64())
|
||||
switch version {
|
||||
case currentVersion:
|
||||
default:
|
||||
errorf("unknown iexport format version %d", version)
|
||||
}
|
||||
|
||||
sLen := int64(r.uint64())
|
||||
dLen := int64(r.uint64())
|
||||
|
||||
whence, _ := r.Seek(0, io.SeekCurrent)
|
||||
stringData := data[whence : whence+sLen]
|
||||
declData := data[whence+sLen : whence+sLen+dLen]
|
||||
r.Seek(sLen+dLen, io.SeekCurrent)
|
||||
|
||||
p := iimporter{
|
||||
ipath: path,
|
||||
|
||||
stringData: stringData,
|
||||
stringCache: make(map[uint64]string),
|
||||
pkgCache: make(map[uint64]*types.Package),
|
||||
|
||||
declData: declData,
|
||||
pkgIndex: make(map[*types.Package]map[string]uint64),
|
||||
typCache: make(map[uint64]types.Type),
|
||||
|
||||
fake: fakeFileSet{
|
||||
fset: fset,
|
||||
files: make(map[string]*token.File),
|
||||
},
|
||||
}
|
||||
|
||||
for i, pt := range predeclared {
|
||||
p.typCache[uint64(i)] = pt
|
||||
}
|
||||
|
||||
pkgList := make([]*types.Package, r.uint64())
|
||||
for i := range pkgList {
|
||||
pkgPathOff := r.uint64()
|
||||
pkgPath := p.stringAt(pkgPathOff)
|
||||
pkgName := p.stringAt(r.uint64())
|
||||
_ = r.uint64() // package height; unused by go/types
|
||||
|
||||
if pkgPath == "" {
|
||||
pkgPath = path
|
||||
}
|
||||
pkg := imports[pkgPath]
|
||||
if pkg == nil {
|
||||
pkg = types.NewPackage(pkgPath, pkgName)
|
||||
imports[pkgPath] = pkg
|
||||
} else if pkg.Name() != pkgName {
|
||||
errorf("conflicting names %s and %s for package %q", pkg.Name(), pkgName, path)
|
||||
}
|
||||
|
||||
p.pkgCache[pkgPathOff] = pkg
|
||||
|
||||
nameIndex := make(map[string]uint64)
|
||||
for nSyms := r.uint64(); nSyms > 0; nSyms-- {
|
||||
name := p.stringAt(r.uint64())
|
||||
nameIndex[name] = r.uint64()
|
||||
}
|
||||
|
||||
p.pkgIndex[pkg] = nameIndex
|
||||
pkgList[i] = pkg
|
||||
}
|
||||
|
||||
localpkg := pkgList[0]
|
||||
|
||||
names := make([]string, 0, len(p.pkgIndex[localpkg]))
|
||||
for name := range p.pkgIndex[localpkg] {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
for _, name := range names {
|
||||
p.doDecl(localpkg, name)
|
||||
}
|
||||
|
||||
for _, typ := range p.interfaceList {
|
||||
typ.Complete()
|
||||
}
|
||||
|
||||
// record all referenced packages as imports
|
||||
list := append(([]*types.Package)(nil), pkgList[1:]...)
|
||||
sort.Sort(byPath(list))
|
||||
localpkg.SetImports(list)
|
||||
|
||||
// package was imported completely and without errors
|
||||
localpkg.MarkComplete()
|
||||
|
||||
consumed, _ := r.Seek(0, io.SeekCurrent)
|
||||
return int(consumed), localpkg, nil
|
||||
}
|
||||
|
||||
type iimporter struct {
|
||||
ipath string
|
||||
|
||||
stringData []byte
|
||||
stringCache map[uint64]string
|
||||
pkgCache map[uint64]*types.Package
|
||||
|
||||
declData []byte
|
||||
pkgIndex map[*types.Package]map[string]uint64
|
||||
typCache map[uint64]types.Type
|
||||
|
||||
fake fakeFileSet
|
||||
interfaceList []*types.Interface
|
||||
}
|
||||
|
||||
func (p *iimporter) doDecl(pkg *types.Package, name string) {
|
||||
// See if we've already imported this declaration.
|
||||
if obj := pkg.Scope().Lookup(name); obj != nil {
|
||||
return
|
||||
}
|
||||
|
||||
off, ok := p.pkgIndex[pkg][name]
|
||||
if !ok {
|
||||
errorf("%v.%v not in index", pkg, name)
|
||||
}
|
||||
|
||||
r := &importReader{p: p, currPkg: pkg}
|
||||
r.declReader.Reset(p.declData[off:])
|
||||
|
||||
r.obj(name)
|
||||
}
|
||||
|
||||
func (p *iimporter) stringAt(off uint64) string {
|
||||
if s, ok := p.stringCache[off]; ok {
|
||||
return s
|
||||
}
|
||||
|
||||
slen, n := binary.Uvarint(p.stringData[off:])
|
||||
if n <= 0 {
|
||||
errorf("varint failed")
|
||||
}
|
||||
spos := off + uint64(n)
|
||||
s := string(p.stringData[spos : spos+slen])
|
||||
p.stringCache[off] = s
|
||||
return s
|
||||
}
|
||||
|
||||
func (p *iimporter) pkgAt(off uint64) *types.Package {
|
||||
if pkg, ok := p.pkgCache[off]; ok {
|
||||
return pkg
|
||||
}
|
||||
path := p.stringAt(off)
|
||||
errorf("missing package %q in %q", path, p.ipath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *iimporter) typAt(off uint64, base *types.Named) types.Type {
|
||||
if t, ok := p.typCache[off]; ok && (base == nil || !isInterface(t)) {
|
||||
return t
|
||||
}
|
||||
|
||||
if off < predeclReserved {
|
||||
errorf("predeclared type missing from cache: %v", off)
|
||||
}
|
||||
|
||||
r := &importReader{p: p}
|
||||
r.declReader.Reset(p.declData[off-predeclReserved:])
|
||||
t := r.doType(base)
|
||||
|
||||
if base == nil || !isInterface(t) {
|
||||
p.typCache[off] = t
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
type importReader struct {
|
||||
p *iimporter
|
||||
declReader bytes.Reader
|
||||
currPkg *types.Package
|
||||
prevFile string
|
||||
prevLine int64
|
||||
}
|
||||
|
||||
func (r *importReader) obj(name string) {
|
||||
tag := r.byte()
|
||||
pos := r.pos()
|
||||
|
||||
switch tag {
|
||||
case 'A':
|
||||
typ := r.typ()
|
||||
|
||||
r.declare(types.NewTypeName(pos, r.currPkg, name, typ))
|
||||
|
||||
case 'C':
|
||||
typ, val := r.value()
|
||||
|
||||
r.declare(types.NewConst(pos, r.currPkg, name, typ, val))
|
||||
|
||||
case 'F':
|
||||
sig := r.signature(nil)
|
||||
|
||||
r.declare(types.NewFunc(pos, r.currPkg, name, sig))
|
||||
|
||||
case 'T':
|
||||
// Types can be recursive. We need to setup a stub
|
||||
// declaration before recursing.
|
||||
obj := types.NewTypeName(pos, r.currPkg, name, nil)
|
||||
named := types.NewNamed(obj, nil, nil)
|
||||
r.declare(obj)
|
||||
|
||||
underlying := r.p.typAt(r.uint64(), named).Underlying()
|
||||
named.SetUnderlying(underlying)
|
||||
|
||||
if !isInterface(underlying) {
|
||||
for n := r.uint64(); n > 0; n-- {
|
||||
mpos := r.pos()
|
||||
mname := r.ident()
|
||||
recv := r.param()
|
||||
msig := r.signature(recv)
|
||||
|
||||
named.AddMethod(types.NewFunc(mpos, r.currPkg, mname, msig))
|
||||
}
|
||||
}
|
||||
|
||||
case 'V':
|
||||
typ := r.typ()
|
||||
|
||||
r.declare(types.NewVar(pos, r.currPkg, name, typ))
|
||||
|
||||
default:
|
||||
errorf("unexpected tag: %v", tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *importReader) declare(obj types.Object) {
|
||||
obj.Pkg().Scope().Insert(obj)
|
||||
}
|
||||
|
||||
func (r *importReader) value() (typ types.Type, val constant.Value) {
|
||||
typ = r.typ()
|
||||
|
||||
switch b := typ.Underlying().(*types.Basic); b.Info() & types.IsConstType {
|
||||
case types.IsBoolean:
|
||||
val = constant.MakeBool(r.bool())
|
||||
|
||||
case types.IsString:
|
||||
val = constant.MakeString(r.string())
|
||||
|
||||
case types.IsInteger:
|
||||
val = r.mpint(b)
|
||||
|
||||
case types.IsFloat:
|
||||
val = r.mpfloat(b)
|
||||
|
||||
case types.IsComplex:
|
||||
re := r.mpfloat(b)
|
||||
im := r.mpfloat(b)
|
||||
val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
|
||||
|
||||
default:
|
||||
errorf("unexpected type %v", typ) // panics
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func intSize(b *types.Basic) (signed bool, maxBytes uint) {
|
||||
if (b.Info() & types.IsUntyped) != 0 {
|
||||
return true, 64
|
||||
}
|
||||
|
||||
switch b.Kind() {
|
||||
case types.Float32, types.Complex64:
|
||||
return true, 3
|
||||
case types.Float64, types.Complex128:
|
||||
return true, 7
|
||||
}
|
||||
|
||||
signed = (b.Info() & types.IsUnsigned) == 0
|
||||
switch b.Kind() {
|
||||
case types.Int8, types.Uint8:
|
||||
maxBytes = 1
|
||||
case types.Int16, types.Uint16:
|
||||
maxBytes = 2
|
||||
case types.Int32, types.Uint32:
|
||||
maxBytes = 4
|
||||
default:
|
||||
maxBytes = 8
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (r *importReader) mpint(b *types.Basic) constant.Value {
|
||||
signed, maxBytes := intSize(b)
|
||||
|
||||
maxSmall := 256 - maxBytes
|
||||
if signed {
|
||||
maxSmall = 256 - 2*maxBytes
|
||||
}
|
||||
if maxBytes == 1 {
|
||||
maxSmall = 256
|
||||
}
|
||||
|
||||
n, _ := r.declReader.ReadByte()
|
||||
if uint(n) < maxSmall {
|
||||
v := int64(n)
|
||||
if signed {
|
||||
v >>= 1
|
||||
if n&1 != 0 {
|
||||
v = ^v
|
||||
}
|
||||
}
|
||||
return constant.MakeInt64(v)
|
||||
}
|
||||
|
||||
v := -n
|
||||
if signed {
|
||||
v = -(n &^ 1) >> 1
|
||||
}
|
||||
if v < 1 || uint(v) > maxBytes {
|
||||
errorf("weird decoding: %v, %v => %v", n, signed, v)
|
||||
}
|
||||
|
||||
buf := make([]byte, v)
|
||||
io.ReadFull(&r.declReader, buf)
|
||||
|
||||
// convert to little endian
|
||||
// TODO(gri) go/constant should have a more direct conversion function
|
||||
// (e.g., once it supports a big.Float based implementation)
|
||||
for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
|
||||
buf[i], buf[j] = buf[j], buf[i]
|
||||
}
|
||||
|
||||
x := constant.MakeFromBytes(buf)
|
||||
if signed && n&1 != 0 {
|
||||
x = constant.UnaryOp(token.SUB, x, 0)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func (r *importReader) mpfloat(b *types.Basic) constant.Value {
|
||||
x := r.mpint(b)
|
||||
if constant.Sign(x) == 0 {
|
||||
return x
|
||||
}
|
||||
|
||||
exp := r.int64()
|
||||
switch {
|
||||
case exp > 0:
|
||||
x = constant.Shift(x, token.SHL, uint(exp))
|
||||
case exp < 0:
|
||||
d := constant.Shift(constant.MakeInt64(1), token.SHL, uint(-exp))
|
||||
x = constant.BinaryOp(x, token.QUO, d)
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
func (r *importReader) ident() string {
|
||||
return r.string()
|
||||
}
|
||||
|
||||
func (r *importReader) qualifiedIdent() (*types.Package, string) {
|
||||
name := r.string()
|
||||
pkg := r.pkg()
|
||||
return pkg, name
|
||||
}
|
||||
|
||||
func (r *importReader) pos() token.Pos {
|
||||
delta := r.int64()
|
||||
if delta != deltaNewFile {
|
||||
r.prevLine += delta
|
||||
} else if l := r.int64(); l == -1 {
|
||||
r.prevLine += deltaNewFile
|
||||
} else {
|
||||
r.prevFile = r.string()
|
||||
r.prevLine = l
|
||||
}
|
||||
|
||||
if r.prevFile == "" && r.prevLine == 0 {
|
||||
return token.NoPos
|
||||
}
|
||||
|
||||
return r.p.fake.pos(r.prevFile, int(r.prevLine))
|
||||
}
|
||||
|
||||
func (r *importReader) typ() types.Type {
|
||||
return r.p.typAt(r.uint64(), nil)
|
||||
}
|
||||
|
||||
func isInterface(t types.Type) bool {
|
||||
_, ok := t.(*types.Interface)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *importReader) pkg() *types.Package { return r.p.pkgAt(r.uint64()) }
|
||||
func (r *importReader) string() string { return r.p.stringAt(r.uint64()) }
|
||||
|
||||
func (r *importReader) doType(base *types.Named) types.Type {
|
||||
switch k := r.kind(); k {
|
||||
default:
|
||||
errorf("unexpected kind tag in %q: %v", r.p.ipath, k)
|
||||
return nil
|
||||
|
||||
case definedType:
|
||||
pkg, name := r.qualifiedIdent()
|
||||
r.p.doDecl(pkg, name)
|
||||
return pkg.Scope().Lookup(name).(*types.TypeName).Type()
|
||||
case pointerType:
|
||||
return types.NewPointer(r.typ())
|
||||
case sliceType:
|
||||
return types.NewSlice(r.typ())
|
||||
case arrayType:
|
||||
n := r.uint64()
|
||||
return types.NewArray(r.typ(), int64(n))
|
||||
case chanType:
|
||||
dir := chanDir(int(r.uint64()))
|
||||
return types.NewChan(dir, r.typ())
|
||||
case mapType:
|
||||
return types.NewMap(r.typ(), r.typ())
|
||||
case signatureType:
|
||||
r.currPkg = r.pkg()
|
||||
return r.signature(nil)
|
||||
|
||||
case structType:
|
||||
r.currPkg = r.pkg()
|
||||
|
||||
fields := make([]*types.Var, r.uint64())
|
||||
tags := make([]string, len(fields))
|
||||
for i := range fields {
|
||||
fpos := r.pos()
|
||||
fname := r.ident()
|
||||
ftyp := r.typ()
|
||||
emb := r.bool()
|
||||
tag := r.string()
|
||||
|
||||
fields[i] = types.NewField(fpos, r.currPkg, fname, ftyp, emb)
|
||||
tags[i] = tag
|
||||
}
|
||||
return types.NewStruct(fields, tags)
|
||||
|
||||
case interfaceType:
|
||||
r.currPkg = r.pkg()
|
||||
|
||||
embeddeds := make([]types.Type, r.uint64())
|
||||
for i := range embeddeds {
|
||||
_ = r.pos()
|
||||
embeddeds[i] = r.typ()
|
||||
}
|
||||
|
||||
methods := make([]*types.Func, r.uint64())
|
||||
for i := range methods {
|
||||
mpos := r.pos()
|
||||
mname := r.ident()
|
||||
|
||||
// TODO(mdempsky): Matches bimport.go, but I
|
||||
// don't agree with this.
|
||||
var recv *types.Var
|
||||
if base != nil {
|
||||
recv = types.NewVar(token.NoPos, r.currPkg, "", base)
|
||||
}
|
||||
|
||||
msig := r.signature(recv)
|
||||
methods[i] = types.NewFunc(mpos, r.currPkg, mname, msig)
|
||||
}
|
||||
|
||||
typ := newInterface(methods, embeddeds)
|
||||
r.p.interfaceList = append(r.p.interfaceList, typ)
|
||||
return typ
|
||||
}
|
||||
}
|
||||
|
||||
func (r *importReader) kind() itag {
|
||||
return itag(r.uint64())
|
||||
}
|
||||
|
||||
func (r *importReader) signature(recv *types.Var) *types.Signature {
|
||||
params := r.paramList()
|
||||
results := r.paramList()
|
||||
variadic := params.Len() > 0 && r.bool()
|
||||
return types.NewSignature(recv, params, results, variadic)
|
||||
}
|
||||
|
||||
func (r *importReader) paramList() *types.Tuple {
|
||||
xs := make([]*types.Var, r.uint64())
|
||||
for i := range xs {
|
||||
xs[i] = r.param()
|
||||
}
|
||||
return types.NewTuple(xs...)
|
||||
}
|
||||
|
||||
func (r *importReader) param() *types.Var {
|
||||
pos := r.pos()
|
||||
name := r.ident()
|
||||
typ := r.typ()
|
||||
return types.NewParam(pos, r.currPkg, name, typ)
|
||||
}
|
||||
|
||||
func (r *importReader) bool() bool {
|
||||
return r.uint64() != 0
|
||||
}
|
||||
|
||||
func (r *importReader) int64() int64 {
|
||||
n, err := binary.ReadVarint(&r.declReader)
|
||||
if err != nil {
|
||||
errorf("readVarint: %v", err)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (r *importReader) uint64() uint64 {
|
||||
n, err := binary.ReadUvarint(&r.declReader)
|
||||
if err != nil {
|
||||
errorf("readUvarint: %v", err)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (r *importReader) byte() byte {
|
||||
x, err := r.declReader.ReadByte()
|
||||
if err != nil {
|
||||
errorf("declReader.ReadByte: %v", err)
|
||||
}
|
||||
return x
|
||||
}
|
13
vendor/golang.org/x/tools/go/internal/gcimporter/isAlias18.go
generated
vendored
Normal file
13
vendor/golang.org/x/tools/go/internal/gcimporter/isAlias18.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2017 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.9
|
||||
|
||||
package gcimporter
|
||||
|
||||
import "go/types"
|
||||
|
||||
func isAlias(obj *types.TypeName) bool {
|
||||
return false // there are no type aliases before Go 1.9
|
||||
}
|
13
vendor/golang.org/x/tools/go/internal/gcimporter/isAlias19.go
generated
vendored
Normal file
13
vendor/golang.org/x/tools/go/internal/gcimporter/isAlias19.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2017 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.9
|
||||
|
||||
package gcimporter
|
||||
|
||||
import "go/types"
|
||||
|
||||
func isAlias(obj *types.TypeName) bool {
|
||||
return obj.IsAlias()
|
||||
}
|
21
vendor/golang.org/x/tools/go/internal/gcimporter/newInterface10.go
generated
vendored
Normal file
21
vendor/golang.org/x/tools/go/internal/gcimporter/newInterface10.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2018 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.11
|
||||
|
||||
package gcimporter
|
||||
|
||||
import "go/types"
|
||||
|
||||
func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface {
|
||||
named := make([]*types.Named, len(embeddeds))
|
||||
for i, e := range embeddeds {
|
||||
var ok bool
|
||||
named[i], ok = e.(*types.Named)
|
||||
if !ok {
|
||||
panic("embedding of non-defined interfaces in interfaces is not supported before Go 1.11")
|
||||
}
|
||||
}
|
||||
return types.NewInterface(methods, named)
|
||||
}
|
13
vendor/golang.org/x/tools/go/internal/gcimporter/newInterface11.go
generated
vendored
Normal file
13
vendor/golang.org/x/tools/go/internal/gcimporter/newInterface11.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2018 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.11
|
||||
|
||||
package gcimporter
|
||||
|
||||
import "go/types"
|
||||
|
||||
func newInterface(methods []*types.Func, embeddeds []types.Type) *types.Interface {
|
||||
return types.NewInterfaceType(methods, embeddeds)
|
||||
}
|
14
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/a.go
generated
vendored
Normal file
14
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/a.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// Input for TestIssue13566
|
||||
|
||||
package a
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type A struct {
|
||||
a *A
|
||||
json json.RawMessage
|
||||
}
|
11
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/b.go
generated
vendored
Normal file
11
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/b.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// Input for TestIssue13566
|
||||
|
||||
package b
|
||||
|
||||
import "./a"
|
||||
|
||||
type A a.A
|
89
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/exports.go
generated
vendored
Normal file
89
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/exports.go
generated
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
// 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 is used to generate an object file which
|
||||
// serves as test file for gcimporter_test.go.
|
||||
|
||||
package exports
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
)
|
||||
|
||||
// Issue 3682: Correctly read dotted identifiers from export data.
|
||||
const init1 = 0
|
||||
|
||||
func init() {}
|
||||
|
||||
const (
|
||||
C0 int = 0
|
||||
C1 = 3.14159265
|
||||
C2 = 2.718281828i
|
||||
C3 = -123.456e-789
|
||||
C4 = +123.456E+789
|
||||
C5 = 1234i
|
||||
C6 = "foo\n"
|
||||
C7 = `bar\n`
|
||||
)
|
||||
|
||||
type (
|
||||
T1 int
|
||||
T2 [10]int
|
||||
T3 []int
|
||||
T4 *int
|
||||
T5 chan int
|
||||
T6a chan<- int
|
||||
T6b chan (<-chan int)
|
||||
T6c chan<- (chan int)
|
||||
T7 <-chan *ast.File
|
||||
T8 struct{}
|
||||
T9 struct {
|
||||
a int
|
||||
b, c float32
|
||||
d []string `go:"tag"`
|
||||
}
|
||||
T10 struct {
|
||||
T8
|
||||
T9
|
||||
_ *T10
|
||||
}
|
||||
T11 map[int]string
|
||||
T12 interface{}
|
||||
T13 interface {
|
||||
m1()
|
||||
m2(int) float32
|
||||
}
|
||||
T14 interface {
|
||||
T12
|
||||
T13
|
||||
m3(x ...struct{}) []T9
|
||||
}
|
||||
T15 func()
|
||||
T16 func(int)
|
||||
T17 func(x int)
|
||||
T18 func() float32
|
||||
T19 func() (x float32)
|
||||
T20 func(...interface{})
|
||||
T21 struct{ next *T21 }
|
||||
T22 struct{ link *T23 }
|
||||
T23 struct{ link *T22 }
|
||||
T24 *T24
|
||||
T25 *T26
|
||||
T26 *T27
|
||||
T27 *T25
|
||||
T28 func(T28) T28
|
||||
)
|
||||
|
||||
var (
|
||||
V0 int
|
||||
V1 = -991.0
|
||||
)
|
||||
|
||||
func F1() {}
|
||||
func F2(x int) {}
|
||||
func F3() int { return 0 }
|
||||
func F4() float32 { return 0 }
|
||||
func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10)
|
||||
|
||||
func (p *T1) M1()
|
11
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/issue15920.go
generated
vendored
Normal file
11
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/issue15920.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2016 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 p
|
||||
|
||||
// The underlying type of Error is the underlying type of error.
|
||||
// Make sure we can import this again without problems.
|
||||
type Error error
|
||||
|
||||
func F() Error { return nil }
|
9
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/issue20046.go
generated
vendored
Normal file
9
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/issue20046.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2017 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 p
|
||||
|
||||
var V interface {
|
||||
M()
|
||||
}
|
17
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/issue25301.go
generated
vendored
Normal file
17
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/issue25301.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2018 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 issue25301
|
||||
|
||||
type (
|
||||
A = interface {
|
||||
M()
|
||||
}
|
||||
T interface {
|
||||
A
|
||||
}
|
||||
S struct{}
|
||||
)
|
||||
|
||||
func (S) M() { println("m") }
|
13
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/p.go
generated
vendored
Normal file
13
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/p.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
// Input for TestIssue15517
|
||||
|
||||
package p
|
||||
|
||||
const C = 0
|
||||
|
||||
var V int
|
||||
|
||||
func F() {}
|
30
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test.go
generated
vendored
Normal file
30
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2016 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 is a copy of $GOROOT/src/go/internal/gcimporter/testdata/versions.test.go.
|
||||
|
||||
// To create a test case for a new export format version,
|
||||
// build this package with the latest compiler and store
|
||||
// the resulting .a file appropriately named in the versions
|
||||
// directory. The VersionHandling test will pick it up.
|
||||
//
|
||||
// In the testdata/versions:
|
||||
//
|
||||
// go build -o test_go1.$X_$Y.a test.go
|
||||
//
|
||||
// with $X = Go version and $Y = export format version
|
||||
// (add 'b' or 'i' to distinguish between binary and
|
||||
// indexed format starting with 1.11 as long as both
|
||||
// formats are supported).
|
||||
//
|
||||
// Make sure this source is extended such that it exercises
|
||||
// whatever export format change has taken place.
|
||||
|
||||
package test
|
||||
|
||||
// Any release before and including Go 1.7 didn't encode
|
||||
// the package for a blank struct field.
|
||||
type BlankField struct {
|
||||
_ int
|
||||
}
|
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.11_0i.a
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.11_0i.a
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.11_6b.a
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.11_6b.a
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.11_999b.a
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.11_999b.a
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.11_999i.a
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.11_999i.a
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.7_0.a
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.7_0.a
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.7_1.a
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.7_1.a
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.8_4.a
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.8_4.a
generated
vendored
Normal file
Binary file not shown.
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.8_5.a
generated
vendored
Normal file
BIN
vendor/golang.org/x/tools/go/internal/gcimporter/testdata/versions/test_go1.8_5.a
generated
vendored
Normal file
Binary file not shown.
207
vendor/golang.org/x/tools/go/loader/cgo.go
generated
vendored
Normal file
207
vendor/golang.org/x/tools/go/loader/cgo.go
generated
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
// 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
|
||||
|
||||
// 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, guru, 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
|
||||
}
|
||||
display := filepath.Join(bp.Dir, cgoDisplayFiles[i])
|
||||
f, err := parser.ParseFile(fset, display, rd, mode)
|
||||
rd.Close()
|
||||
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:
|
||||
// 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 {
|
||||
pcCFLAGS, err := pkgConfigFlags(bp)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
cgoCPPFLAGS = append(cgoCPPFLAGS, pcCFLAGS...)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
39
vendor/golang.org/x/tools/go/loader/cgo_pkgconfig.go
generated
vendored
Normal file
39
vendor/golang.org/x/tools/go/loader/cgo_pkgconfig.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// pkgConfig runs pkg-config with the specified arguments and returns the flags it prints.
|
||||
func pkgConfig(mode string, pkgs []string) (flags []string, err error) {
|
||||
cmd := exec.Command("pkg-config", append([]string{mode}, pkgs...)...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
s := fmt.Sprintf("%s failed: %v", strings.Join(cmd.Args, " "), err)
|
||||
if len(out) > 0 {
|
||||
s = fmt.Sprintf("%s: %s", s, out)
|
||||
}
|
||||
return nil, errors.New(s)
|
||||
}
|
||||
if len(out) > 0 {
|
||||
flags = strings.Fields(string(out))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// pkgConfigFlags calls pkg-config if needed and returns the cflags
|
||||
// needed to build the package.
|
||||
func pkgConfigFlags(p *build.Package) (cflags []string, err error) {
|
||||
if len(p.CgoPkgConfig) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return pkgConfig("--cflags", p.CgoPkgConfig)
|
||||
}
|
205
vendor/golang.org/x/tools/go/loader/doc.go
generated
vendored
Normal file
205
vendor/golang.org/x/tools/go/loader/doc.go
generated
vendored
Normal file
@@ -0,0 +1,205 @@
|
||||
// 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
|
||||
//
|
||||
// The WORKSPACE is the set of packages accessible to the loader. The
|
||||
// workspace is defined by Config.Build, a *build.Context. The
|
||||
// default context treats subdirectories of $GOROOT and $GOPATH as
|
||||
// packages, but this behavior may be overridden.
|
||||
//
|
||||
// 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. Every importable package is uniquely identified by its
|
||||
// PACKAGE PATH or just PATH, a string such as "fmt", "encoding/json",
|
||||
// or "cmd/vendor/golang.org/x/arch/x86/x86asm". A package path
|
||||
// typically denotes a subdirectory of the workspace.
|
||||
//
|
||||
// An import declaration uses an IMPORT PATH to refer to a package.
|
||||
// Most import declarations use the package path as the import path.
|
||||
//
|
||||
// Due to VENDORING (https://golang.org/s/go15vendor), the
|
||||
// interpretation of an import path may depend on the directory in which
|
||||
// it appears. To resolve an import path to a package path, go/build
|
||||
// must search the enclosing directories for a subdirectory named
|
||||
// "vendor".
|
||||
//
|
||||
// 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 with path "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 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 non-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.
|
179
vendor/golang.org/x/tools/go/loader/example_test.go
generated
vendored
Normal file
179
vendor/golang.org/x/tools/go/loader/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
// 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.
|
||||
|
||||
// +build go1.8,!go1.9 // TODO(adonovan) determine which versions we need to test here
|
||||
// +build !windows
|
||||
|
||||
package loader_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/loader"
|
||||
)
|
||||
|
||||
func printProgram(prog *loader.Program) {
|
||||
// Created packages are the initial packages specified by a call
|
||||
// to CreateFromFilenames or CreateFromFiles.
|
||||
var names []string
|
||||
for _, info := range prog.Created {
|
||||
names = append(names, info.Pkg.Path())
|
||||
}
|
||||
fmt.Printf("created: %s\n", names)
|
||||
|
||||
// Imported packages are the initial packages specified by a
|
||||
// call to Import or ImportWithTests.
|
||||
names = nil
|
||||
for _, info := range prog.Imported {
|
||||
if strings.Contains(info.Pkg.Path(), "internal") {
|
||||
continue // skip, to reduce fragility
|
||||
}
|
||||
names = append(names, info.Pkg.Path())
|
||||
}
|
||||
sort.Strings(names)
|
||||
fmt.Printf("imported: %s\n", names)
|
||||
|
||||
// InitialPackages contains the union of created and imported.
|
||||
names = nil
|
||||
for _, info := range prog.InitialPackages() {
|
||||
names = append(names, info.Pkg.Path())
|
||||
}
|
||||
sort.Strings(names)
|
||||
fmt.Printf("initial: %s\n", names)
|
||||
|
||||
// AllPackages contains all initial packages and their dependencies.
|
||||
names = nil
|
||||
for pkg := range prog.AllPackages {
|
||||
names = append(names, pkg.Path())
|
||||
}
|
||||
sort.Strings(names)
|
||||
fmt.Printf("all: %s\n", names)
|
||||
}
|
||||
|
||||
func printFilenames(fset *token.FileSet, info *loader.PackageInfo) {
|
||||
var names []string
|
||||
for _, f := range info.Files {
|
||||
names = append(names, filepath.Base(fset.File(f.Pos()).Name()))
|
||||
}
|
||||
fmt.Printf("%s.Files: %s\n", info.Pkg.Path(), names)
|
||||
}
|
||||
|
||||
// This example loads a set of packages and all of their dependencies
|
||||
// from a typical command-line. FromArgs parses a command line and
|
||||
// makes calls to the other methods of Config shown in the examples that
|
||||
// follow.
|
||||
func ExampleConfig_FromArgs() {
|
||||
args := []string{"mytool", "unicode/utf8", "errors", "runtime", "--", "foo", "bar"}
|
||||
const wantTests = false
|
||||
|
||||
var conf loader.Config
|
||||
rest, err := conf.FromArgs(args[1:], wantTests)
|
||||
prog, err := conf.Load()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Printf("rest: %s\n", rest)
|
||||
printProgram(prog)
|
||||
// Output:
|
||||
// rest: [foo bar]
|
||||
// created: []
|
||||
// imported: [errors runtime unicode/utf8]
|
||||
// initial: [errors runtime unicode/utf8]
|
||||
// all: [errors runtime runtime/internal/atomic runtime/internal/sys unicode/utf8 unsafe]
|
||||
}
|
||||
|
||||
// This example creates and type-checks a single package (without tests)
|
||||
// from a list of filenames, and loads all of its dependencies.
|
||||
// (The input files are actually only a small part of the math/cmplx package.)
|
||||
func ExampleConfig_CreateFromFilenames() {
|
||||
var conf loader.Config
|
||||
conf.CreateFromFilenames("math/cmplx",
|
||||
filepath.Join(runtime.GOROOT(), "src/math/cmplx/abs.go"),
|
||||
filepath.Join(runtime.GOROOT(), "src/math/cmplx/sin.go"))
|
||||
prog, err := conf.Load()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
printProgram(prog)
|
||||
// Output:
|
||||
// created: [math/cmplx]
|
||||
// imported: []
|
||||
// initial: [math/cmplx]
|
||||
// all: [math math/cmplx unsafe]
|
||||
}
|
||||
|
||||
// In the examples below, for stability, the chosen packages are
|
||||
// relatively small, platform-independent, and low-level (and thus
|
||||
// infrequently changing).
|
||||
// The strconv package has internal and external tests.
|
||||
|
||||
const hello = `package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, world.")
|
||||
}
|
||||
`
|
||||
|
||||
// This example creates and type-checks a package from a list of
|
||||
// already-parsed files, and loads all its dependencies.
|
||||
func ExampleConfig_CreateFromFiles() {
|
||||
var conf loader.Config
|
||||
f, err := conf.ParseFile("hello.go", hello)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
conf.CreateFromFiles("hello", f)
|
||||
prog, err := conf.Load()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
printProgram(prog)
|
||||
printFilenames(prog.Fset, prog.Package("strconv"))
|
||||
// Output:
|
||||
// created: [hello]
|
||||
// imported: []
|
||||
// initial: [hello]
|
||||
// all: [errors fmt hello internal/race io math os reflect runtime runtime/internal/atomic runtime/internal/sys strconv sync sync/atomic syscall time unicode/utf8 unsafe]
|
||||
// strconv.Files: [atob.go atof.go atoi.go decimal.go doc.go extfloat.go ftoa.go isprint.go itoa.go quote.go]
|
||||
}
|
||||
|
||||
// This example imports three packages, including the tests for one of
|
||||
// them, and loads all their dependencies.
|
||||
func ExampleConfig_Import() {
|
||||
// ImportWithTest("strconv") causes strconv to include
|
||||
// internal_test.go, and creates an external test package,
|
||||
// strconv_test.
|
||||
// (Compare with the example of CreateFromFiles.)
|
||||
|
||||
var conf loader.Config
|
||||
conf.Import("unicode/utf8")
|
||||
conf.Import("errors")
|
||||
conf.ImportWithTests("strconv")
|
||||
prog, err := conf.Load()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
printProgram(prog)
|
||||
printFilenames(prog.Fset, prog.Package("strconv"))
|
||||
printFilenames(prog.Fset, prog.Package("strconv_test"))
|
||||
// Output:
|
||||
// created: [strconv_test]
|
||||
// imported: [errors strconv unicode/utf8]
|
||||
// initial: [errors strconv strconv_test unicode/utf8]
|
||||
// all: [bufio bytes errors flag fmt internal/race io log math math/rand os reflect runtime runtime/debug runtime/internal/atomic runtime/internal/sys runtime/trace sort strconv strconv_test strings sync sync/atomic syscall testing time unicode unicode/utf8 unsafe]
|
||||
// strconv.Files: [atob.go atof.go atoi.go decimal.go doc.go extfloat.go ftoa.go isprint.go itoa.go quote.go internal_test.go]
|
||||
// strconv_test.Files: [atob_test.go atof_test.go atoi_test.go decimal_test.go example_test.go fp_test.go ftoa_test.go itoa_test.go quote_test.go strconv_test.go]
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user