Update and fix staticcheck
This commit is contained in:
242
vendor/honnef.co/go/tools/pattern/convert.go
vendored
Normal file
242
vendor/honnef.co/go/tools/pattern/convert.go
vendored
Normal file
@ -0,0 +1,242 @@
|
||||
package pattern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var astTypes = map[string]reflect.Type{
|
||||
"Ellipsis": reflect.TypeOf(ast.Ellipsis{}),
|
||||
"RangeStmt": reflect.TypeOf(ast.RangeStmt{}),
|
||||
"AssignStmt": reflect.TypeOf(ast.AssignStmt{}),
|
||||
"IndexExpr": reflect.TypeOf(ast.IndexExpr{}),
|
||||
"Ident": reflect.TypeOf(ast.Ident{}),
|
||||
"ValueSpec": reflect.TypeOf(ast.ValueSpec{}),
|
||||
"GenDecl": reflect.TypeOf(ast.GenDecl{}),
|
||||
"BinaryExpr": reflect.TypeOf(ast.BinaryExpr{}),
|
||||
"ForStmt": reflect.TypeOf(ast.ForStmt{}),
|
||||
"ArrayType": reflect.TypeOf(ast.ArrayType{}),
|
||||
"DeferStmt": reflect.TypeOf(ast.DeferStmt{}),
|
||||
"MapType": reflect.TypeOf(ast.MapType{}),
|
||||
"ReturnStmt": reflect.TypeOf(ast.ReturnStmt{}),
|
||||
"SliceExpr": reflect.TypeOf(ast.SliceExpr{}),
|
||||
"StarExpr": reflect.TypeOf(ast.StarExpr{}),
|
||||
"UnaryExpr": reflect.TypeOf(ast.UnaryExpr{}),
|
||||
"SendStmt": reflect.TypeOf(ast.SendStmt{}),
|
||||
"SelectStmt": reflect.TypeOf(ast.SelectStmt{}),
|
||||
"ImportSpec": reflect.TypeOf(ast.ImportSpec{}),
|
||||
"IfStmt": reflect.TypeOf(ast.IfStmt{}),
|
||||
"GoStmt": reflect.TypeOf(ast.GoStmt{}),
|
||||
"Field": reflect.TypeOf(ast.Field{}),
|
||||
"SelectorExpr": reflect.TypeOf(ast.SelectorExpr{}),
|
||||
"StructType": reflect.TypeOf(ast.StructType{}),
|
||||
"KeyValueExpr": reflect.TypeOf(ast.KeyValueExpr{}),
|
||||
"FuncType": reflect.TypeOf(ast.FuncType{}),
|
||||
"FuncLit": reflect.TypeOf(ast.FuncLit{}),
|
||||
"FuncDecl": reflect.TypeOf(ast.FuncDecl{}),
|
||||
"ChanType": reflect.TypeOf(ast.ChanType{}),
|
||||
"CallExpr": reflect.TypeOf(ast.CallExpr{}),
|
||||
"CaseClause": reflect.TypeOf(ast.CaseClause{}),
|
||||
"CommClause": reflect.TypeOf(ast.CommClause{}),
|
||||
"CompositeLit": reflect.TypeOf(ast.CompositeLit{}),
|
||||
"EmptyStmt": reflect.TypeOf(ast.EmptyStmt{}),
|
||||
"SwitchStmt": reflect.TypeOf(ast.SwitchStmt{}),
|
||||
"TypeSwitchStmt": reflect.TypeOf(ast.TypeSwitchStmt{}),
|
||||
"TypeAssertExpr": reflect.TypeOf(ast.TypeAssertExpr{}),
|
||||
"TypeSpec": reflect.TypeOf(ast.TypeSpec{}),
|
||||
"InterfaceType": reflect.TypeOf(ast.InterfaceType{}),
|
||||
"BranchStmt": reflect.TypeOf(ast.BranchStmt{}),
|
||||
"IncDecStmt": reflect.TypeOf(ast.IncDecStmt{}),
|
||||
"BasicLit": reflect.TypeOf(ast.BasicLit{}),
|
||||
}
|
||||
|
||||
func ASTToNode(node interface{}) Node {
|
||||
switch node := node.(type) {
|
||||
case *ast.File:
|
||||
panic("cannot convert *ast.File to Node")
|
||||
case nil:
|
||||
return Nil{}
|
||||
case string:
|
||||
return String(node)
|
||||
case token.Token:
|
||||
return Token(node)
|
||||
case *ast.ExprStmt:
|
||||
return ASTToNode(node.X)
|
||||
case *ast.BlockStmt:
|
||||
if node == nil {
|
||||
return Nil{}
|
||||
}
|
||||
return ASTToNode(node.List)
|
||||
case *ast.FieldList:
|
||||
if node == nil {
|
||||
return Nil{}
|
||||
}
|
||||
return ASTToNode(node.List)
|
||||
case *ast.BasicLit:
|
||||
if node == nil {
|
||||
return Nil{}
|
||||
}
|
||||
case *ast.ParenExpr:
|
||||
return ASTToNode(node.X)
|
||||
}
|
||||
|
||||
if node, ok := node.(ast.Node); ok {
|
||||
name := reflect.TypeOf(node).Elem().Name()
|
||||
T, ok := structNodes[name]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("internal error: unhandled type %T", node))
|
||||
}
|
||||
|
||||
if reflect.ValueOf(node).IsNil() {
|
||||
return Nil{}
|
||||
}
|
||||
v := reflect.ValueOf(node).Elem()
|
||||
objs := make([]Node, T.NumField())
|
||||
for i := 0; i < T.NumField(); i++ {
|
||||
f := v.FieldByName(T.Field(i).Name)
|
||||
objs[i] = ASTToNode(f.Interface())
|
||||
}
|
||||
|
||||
n, err := populateNode(name, objs, false)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("internal error: %s", err))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
s := reflect.ValueOf(node)
|
||||
if s.Kind() == reflect.Slice {
|
||||
if s.Len() == 0 {
|
||||
return List{}
|
||||
}
|
||||
if s.Len() == 1 {
|
||||
return ASTToNode(s.Index(0).Interface())
|
||||
}
|
||||
|
||||
tail := List{}
|
||||
for i := s.Len() - 1; i >= 0; i-- {
|
||||
head := ASTToNode(s.Index(i).Interface())
|
||||
l := List{
|
||||
Head: head,
|
||||
Tail: tail,
|
||||
}
|
||||
tail = l
|
||||
}
|
||||
return tail
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("internal error: unhandled type %T", node))
|
||||
}
|
||||
|
||||
func NodeToAST(node Node, state State) interface{} {
|
||||
switch node := node.(type) {
|
||||
case Binding:
|
||||
v, ok := state[node.Name]
|
||||
if !ok {
|
||||
// really we want to return an error here
|
||||
panic("XXX")
|
||||
}
|
||||
switch v := v.(type) {
|
||||
case types.Object:
|
||||
return &ast.Ident{Name: v.Name()}
|
||||
default:
|
||||
return v
|
||||
}
|
||||
case Builtin, Any, Object, Function, Not, Or:
|
||||
panic("XXX")
|
||||
case List:
|
||||
if (node == List{}) {
|
||||
return []ast.Node{}
|
||||
}
|
||||
x := []ast.Node{NodeToAST(node.Head, state).(ast.Node)}
|
||||
x = append(x, NodeToAST(node.Tail, state).([]ast.Node)...)
|
||||
return x
|
||||
case Token:
|
||||
return token.Token(node)
|
||||
case String:
|
||||
return string(node)
|
||||
case Nil:
|
||||
return nil
|
||||
}
|
||||
|
||||
name := reflect.TypeOf(node).Name()
|
||||
T, ok := astTypes[name]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("internal error: unhandled type %T", node))
|
||||
}
|
||||
v := reflect.ValueOf(node)
|
||||
out := reflect.New(T)
|
||||
for i := 0; i < T.NumField(); i++ {
|
||||
fNode := v.FieldByName(T.Field(i).Name)
|
||||
if (fNode == reflect.Value{}) {
|
||||
continue
|
||||
}
|
||||
fAST := out.Elem().FieldByName(T.Field(i).Name)
|
||||
switch fAST.Type().Kind() {
|
||||
case reflect.Slice:
|
||||
c := reflect.ValueOf(NodeToAST(fNode.Interface().(Node), state))
|
||||
if c.Kind() != reflect.Slice {
|
||||
// it's a single node in the pattern, we have to wrap
|
||||
// it in a slice
|
||||
slice := reflect.MakeSlice(fAST.Type(), 1, 1)
|
||||
slice.Index(0).Set(c)
|
||||
c = slice
|
||||
}
|
||||
switch fAST.Interface().(type) {
|
||||
case []ast.Node:
|
||||
switch cc := c.Interface().(type) {
|
||||
case []ast.Node:
|
||||
fAST.Set(c)
|
||||
case []ast.Expr:
|
||||
var slice []ast.Node
|
||||
for _, el := range cc {
|
||||
slice = append(slice, el)
|
||||
}
|
||||
fAST.Set(reflect.ValueOf(slice))
|
||||
default:
|
||||
panic("XXX")
|
||||
}
|
||||
case []ast.Expr:
|
||||
switch cc := c.Interface().(type) {
|
||||
case []ast.Node:
|
||||
var slice []ast.Expr
|
||||
for _, el := range cc {
|
||||
slice = append(slice, el.(ast.Expr))
|
||||
}
|
||||
fAST.Set(reflect.ValueOf(slice))
|
||||
case []ast.Expr:
|
||||
fAST.Set(c)
|
||||
default:
|
||||
panic("XXX")
|
||||
}
|
||||
default:
|
||||
panic("XXX")
|
||||
}
|
||||
case reflect.Int:
|
||||
c := reflect.ValueOf(NodeToAST(fNode.Interface().(Node), state))
|
||||
switch c.Kind() {
|
||||
case reflect.String:
|
||||
tok, ok := tokensByString[c.Interface().(string)]
|
||||
if !ok {
|
||||
// really we want to return an error here
|
||||
panic("XXX")
|
||||
}
|
||||
fAST.SetInt(int64(tok))
|
||||
case reflect.Int:
|
||||
fAST.Set(c)
|
||||
default:
|
||||
panic(fmt.Sprintf("internal error: unexpected kind %s", c.Kind()))
|
||||
}
|
||||
default:
|
||||
r := NodeToAST(fNode.Interface().(Node), state)
|
||||
if r != nil {
|
||||
fAST.Set(reflect.ValueOf(r))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out.Interface().(ast.Node)
|
||||
}
|
273
vendor/honnef.co/go/tools/pattern/doc.go
vendored
Normal file
273
vendor/honnef.co/go/tools/pattern/doc.go
vendored
Normal file
@ -0,0 +1,273 @@
|
||||
/*
|
||||
Package pattern implements a simple language for pattern matching Go ASTs.
|
||||
|
||||
Design decisions and trade-offs
|
||||
|
||||
The language is designed specifically for the task of filtering ASTs
|
||||
to simplify the implementation of analyses in staticcheck.
|
||||
It is also intended to be trivial to parse and execute.
|
||||
|
||||
To that end, we make certain decisions that make the language more
|
||||
suited to its task, while making certain queries infeasible.
|
||||
|
||||
Furthermore, it is fully expected that the majority of analyses will still require ordinary Go code
|
||||
to further process the filtered AST, to make use of type information and to enforce complex invariants.
|
||||
It is not our goal to design a scripting language for writing entire checks in.
|
||||
|
||||
The language
|
||||
|
||||
At its core, patterns are a representation of Go ASTs, allowing for the use of placeholders to enable pattern matching.
|
||||
Their syntax is inspired by LISP and Haskell, but unlike LISP, the core unit of patterns isn't the list, but the node.
|
||||
There is a fixed set of nodes, identified by name, and with the exception of the Or node, all nodes have a fixed number of arguments.
|
||||
In addition to nodes, there are atoms, which represent basic units such as strings or the nil value.
|
||||
|
||||
Pattern matching is implemented via bindings, represented by the Binding node.
|
||||
A Binding can match nodes and associate them with names, to later recall the nodes.
|
||||
This allows for expressing "this node must be equal to that node" constraints.
|
||||
|
||||
To simplify writing and reading patterns, a small amount of additional syntax exists on top of nodes and atoms.
|
||||
This additional syntax doesn't add any new features of its own, it simply provides shortcuts to creating nodes and atoms.
|
||||
|
||||
To show an example of a pattern, first consider this snippet of Go code:
|
||||
|
||||
if x := fn(); x != nil {
|
||||
for _, v := range x {
|
||||
println(v, x)
|
||||
}
|
||||
}
|
||||
|
||||
The corresponding AST expressed as an idiomatic pattern would look as follows:
|
||||
|
||||
(IfStmt
|
||||
(AssignStmt (Ident "x") ":=" (CallExpr (Ident "fn") []))
|
||||
(BinaryExpr (Ident "x") "!=" (Ident "nil"))
|
||||
(RangeStmt
|
||||
(Ident "_") (Ident "v") ":=" (Ident "x")
|
||||
(CallExpr (Ident "println") [(Ident "v") (Ident "x")]))
|
||||
nil)
|
||||
|
||||
Two things are worth noting about this representation.
|
||||
First, the [el1 el2 ...] syntax is a short-hand for creating lists.
|
||||
It is a short-hand for el1:el2:[], which itself is a short-hand for (List el1 (List el2 (List nil nil)).
|
||||
Second, note the absence of a lot of lists in places that normally accept lists.
|
||||
For example, assignment assigns a number of right-hands to a number of left-hands, yet our AssignStmt is lacking any form of list.
|
||||
This is due to the fact that a single node can match a list of exactly one element.
|
||||
Thus, the two following forms have identical matching behavior:
|
||||
|
||||
(AssignStmt (Ident "x") ":=" (CallExpr (Ident "fn") []))
|
||||
(AssignStmt [(Ident "x")] ":=" [(CallExpr (Ident "fn") [])])
|
||||
|
||||
This section serves as an overview of the language's syntax.
|
||||
More in-depth explanations of the matching behavior as well as an exhaustive list of node types follows in the coming sections.
|
||||
|
||||
Pattern matching
|
||||
|
||||
TODO write about pattern matching
|
||||
|
||||
- inspired by haskell syntax, but much, much simpler and naive
|
||||
|
||||
Node types
|
||||
|
||||
The language contains two kinds of nodes: those that map to nodes in the AST, and those that implement additional logic.
|
||||
|
||||
Nodes that map directly to AST nodes are named identically to the types in the go/ast package.
|
||||
What follows is an exhaustive list of these nodes:
|
||||
|
||||
(ArrayType len elt)
|
||||
(AssignStmt lhs tok rhs)
|
||||
(BasicLit kind value)
|
||||
(BinaryExpr x op y)
|
||||
(BranchStmt tok label)
|
||||
(CallExpr fun args)
|
||||
(CaseClause list body)
|
||||
(ChanType dir value)
|
||||
(CommClause comm body)
|
||||
(CompositeLit type elts)
|
||||
(DeferStmt call)
|
||||
(Ellipsis elt)
|
||||
(EmptyStmt)
|
||||
(Field names type tag)
|
||||
(ForStmt init cond post body)
|
||||
(FuncDecl recv name type body)
|
||||
(FuncLit type body)
|
||||
(FuncType params results)
|
||||
(GenDecl specs)
|
||||
(GoStmt call)
|
||||
(Ident name)
|
||||
(IfStmt init cond body else)
|
||||
(ImportSpec name path)
|
||||
(IncDecStmt x tok)
|
||||
(IndexExpr x index)
|
||||
(InterfaceType methods)
|
||||
(KeyValueExpr key value)
|
||||
(MapType key value)
|
||||
(RangeStmt key value tok x body)
|
||||
(ReturnStmt results)
|
||||
(SelectStmt body)
|
||||
(SelectorExpr x sel)
|
||||
(SendStmt chan value)
|
||||
(SliceExpr x low high max)
|
||||
(StarExpr x)
|
||||
(StructType fields)
|
||||
(SwitchStmt init tag body)
|
||||
(TypeAssertExpr)
|
||||
(TypeSpec name type)
|
||||
(TypeSwitchStmt init assign body)
|
||||
(UnaryExpr op x)
|
||||
(ValueSpec names type values)
|
||||
|
||||
Additionally, there are the String, Token and nil atoms.
|
||||
Strings are double-quoted string literals, as in (Ident "someName").
|
||||
Tokens are also represented as double-quoted string literals, but are converted to token.Token values in contexts that require tokens,
|
||||
such as in (BinaryExpr x "<" y), where "<" is transparently converted to token.LSS during matching.
|
||||
The keyword 'nil' denotes the nil value, which represents the absence of any value.
|
||||
|
||||
We also defines the (List head tail) node, which is used to represent sequences of elements as a singly linked list.
|
||||
The head is a single element, and the tail is the remainder of the list.
|
||||
For example,
|
||||
|
||||
(List "foo" (List "bar" (List "baz" (List nil nil))))
|
||||
|
||||
represents a list of three elements, "foo", "bar" and "baz". There is dedicated syntax for writing lists, which looks as follows:
|
||||
|
||||
["foo" "bar" "baz"]
|
||||
|
||||
This syntax is itself syntactic sugar for the following form:
|
||||
|
||||
"foo":"bar":"baz":[]
|
||||
|
||||
This form is of particular interest for pattern matching, as it allows matching on the head and tail. For example,
|
||||
|
||||
"foo":"bar":_
|
||||
|
||||
would match any list with at least two elements, where the first two elements are "foo" and "bar". This is equivalent to writing
|
||||
|
||||
(List "foo" (List "bar" _))
|
||||
|
||||
Note that it is not possible to match from the end of the list.
|
||||
That is, there is no way to express a query such as "a list of any length where the last element is foo".
|
||||
|
||||
Note that unlike in LISP, nil and empty lists are distinct from one another.
|
||||
In patterns, with respect to lists, nil is akin to Go's untyped nil.
|
||||
It will match a nil ast.Node, but it will not match a nil []ast.Expr. Nil will, however, match pointers to named types such as *ast.Ident.
|
||||
Similarly, lists are akin to Go's
|
||||
slices. An empty list will match both a nil and an empty []ast.Expr, but it will not match a nil ast.Node.
|
||||
|
||||
Due to the difference between nil and empty lists, an empty list is represented as (List nil nil), i.e. a list with no head or tail.
|
||||
Similarly, a list of one element is represented as (List el (List nil nil)). Unlike in LISP, it cannot be represented by (List el nil).
|
||||
|
||||
Finally, there are nodes that implement special logic or matching behavior.
|
||||
|
||||
(Any) matches any value. The underscore (_) maps to this node, making the following two forms equivalent:
|
||||
|
||||
(Ident _)
|
||||
(Ident (Any))
|
||||
|
||||
(Builtin name) matches a built-in identifier or function by name.
|
||||
This is a type-aware variant of (Ident name).
|
||||
Instead of only comparing the name, it resolves the object behind the name and makes sure it's a pre-declared identifier.
|
||||
|
||||
For example, in the following piece of code
|
||||
|
||||
func fn() {
|
||||
println(true)
|
||||
true := false
|
||||
println(true)
|
||||
}
|
||||
|
||||
the pattern
|
||||
|
||||
(Builtin "true")
|
||||
|
||||
will match exactly once, on the first use of 'true' in the function.
|
||||
Subsequent occurrences of 'true' no longer refer to the pre-declared identifier.
|
||||
|
||||
(Object name) matches an identifier by name, but yields the
|
||||
types.Object it refers to.
|
||||
|
||||
(Function name) matches ast.Idents and ast.SelectorExprs that refer to a function with a given fully qualified name.
|
||||
For example, "net/url.PathEscape" matches the PathEscape function in the net/url package,
|
||||
and "(net/url.EscapeError).Error" refers to the Error method on the net/url.EscapeError type,
|
||||
either on an instance of the type, or on the type itself.
|
||||
|
||||
For example, the following patterns match the following lines of code:
|
||||
|
||||
(CallExpr (Function "fmt.Println") _) // pattern 1
|
||||
(CallExpr (Function "(net/url.EscapeError).Error") _) // pattern 2
|
||||
|
||||
fmt.Println("hello, world") // matches pattern 1
|
||||
var x url.EscapeError
|
||||
x.Error() // matches pattern 2
|
||||
(url.EscapeError).Error(x) // also matches pattern 2
|
||||
|
||||
(Binding name node) creates or uses a binding.
|
||||
Bindings work like variable assignments, allowing referring to already matched nodes.
|
||||
As an example, bindings are necessary to match self-assignment of the form "x = x",
|
||||
since we need to express that the right-hand side is identical to the left-hand side.
|
||||
|
||||
If a binding's node is not nil, the matcher will attempt to match a node according to the pattern.
|
||||
If a binding's node is nil, the binding will either recall an existing value, or match the Any node.
|
||||
It is an error to provide a non-nil node to a binding that has already been bound.
|
||||
|
||||
Referring back to the earlier example, the following pattern will match self-assignment of idents:
|
||||
|
||||
(AssignStmt (Binding "lhs" (Ident _)) "=" (Binding "lhs" nil))
|
||||
|
||||
Because bindings are a crucial component of pattern matching, there is special syntax for creating and recalling bindings.
|
||||
Lower-case names refer to bindings. If standing on its own, the name "foo" will be equivalent to (Binding "foo" nil).
|
||||
If a name is followed by an at-sign (@) then it will create a binding for the node that follows.
|
||||
Together, this allows us to rewrite the earlier example as follows:
|
||||
|
||||
(AssignStmt lhs@(Ident _) "=" lhs)
|
||||
|
||||
(Or nodes...) is a variadic node that tries matching each node until one succeeds. For example, the following pattern matches all idents of name "foo" or "bar":
|
||||
|
||||
(Ident (Or "foo" "bar"))
|
||||
|
||||
We could also have written
|
||||
|
||||
(Or (Ident "foo") (Ident "bar"))
|
||||
|
||||
and achieved the same result. We can also mix different kinds of nodes:
|
||||
|
||||
(Or (Ident "foo") (CallExpr (Ident "bar") _))
|
||||
|
||||
When using bindings inside of nodes used inside Or, all or none of the bindings will be bound.
|
||||
That is, partially matched nodes that ultimately failed to match will not produce any bindings observable outside of the matching attempt.
|
||||
We can thus write
|
||||
|
||||
(Or (Ident name) (CallExpr name))
|
||||
|
||||
and 'name' will either be a String if the first option matched, or an Ident or SelectorExpr if the second option matched.
|
||||
|
||||
(Not node)
|
||||
|
||||
The Not node negates a match. For example, (Not (Ident _)) will match all nodes that aren't identifiers.
|
||||
|
||||
ChanDir(0)
|
||||
|
||||
Automatic unnesting of AST nodes
|
||||
|
||||
The Go AST has several types of nodes that wrap other nodes.
|
||||
To simplify matching, we automatically unwrap some of these nodes.
|
||||
|
||||
These nodes are ExprStmt (for using expressions in a statement context),
|
||||
ParenExpr (for parenthesized expressions),
|
||||
DeclStmt (for declarations in a statement context),
|
||||
and LabeledStmt (for labeled statements).
|
||||
|
||||
Thus, the query
|
||||
|
||||
(FuncLit _ [(CallExpr _ _)]
|
||||
|
||||
will match a function literal containing a single function call,
|
||||
even though in the actual Go AST, the CallExpr is nested inside an ExprStmt,
|
||||
as function bodies are made up of sequences of statements.
|
||||
|
||||
On the flip-side, there is no way to specifically match these wrapper nodes.
|
||||
For example, there is no way of searching for unnecessary parentheses, like in the following piece of Go code:
|
||||
|
||||
((x)) += 2
|
||||
|
||||
*/
|
||||
package pattern
|
50
vendor/honnef.co/go/tools/pattern/fuzz.go
vendored
Normal file
50
vendor/honnef.co/go/tools/pattern/fuzz.go
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
// +build gofuzz
|
||||
|
||||
package pattern
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
goparser "go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var files []*ast.File
|
||||
|
||||
func init() {
|
||||
fset := token.NewFileSet()
|
||||
filepath.Walk("/usr/lib/go/src", func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
// XXX error handling
|
||||
panic(err)
|
||||
}
|
||||
if !strings.HasSuffix(path, ".go") {
|
||||
return nil
|
||||
}
|
||||
f, err := goparser.ParseFile(fset, path, nil, 0)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
files = append(files, f)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
p := &Parser{}
|
||||
pat, err := p.Parse(string(data))
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "internal error") {
|
||||
panic(err)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
_ = pat.Root.String()
|
||||
|
||||
for _, f := range files {
|
||||
Match(pat.Root, f)
|
||||
}
|
||||
return 1
|
||||
}
|
221
vendor/honnef.co/go/tools/pattern/lexer.go
vendored
Normal file
221
vendor/honnef.co/go/tools/pattern/lexer.go
vendored
Normal file
@ -0,0 +1,221 @@
|
||||
package pattern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type lexer struct {
|
||||
f *token.File
|
||||
|
||||
input string
|
||||
start int
|
||||
pos int
|
||||
width int
|
||||
items chan item
|
||||
}
|
||||
|
||||
type itemType int
|
||||
|
||||
const eof = -1
|
||||
|
||||
const (
|
||||
itemError itemType = iota
|
||||
itemLeftParen
|
||||
itemRightParen
|
||||
itemLeftBracket
|
||||
itemRightBracket
|
||||
itemTypeName
|
||||
itemVariable
|
||||
itemAt
|
||||
itemColon
|
||||
itemBlank
|
||||
itemString
|
||||
itemEOF
|
||||
)
|
||||
|
||||
func (typ itemType) String() string {
|
||||
switch typ {
|
||||
case itemError:
|
||||
return "ERROR"
|
||||
case itemLeftParen:
|
||||
return "("
|
||||
case itemRightParen:
|
||||
return ")"
|
||||
case itemLeftBracket:
|
||||
return "["
|
||||
case itemRightBracket:
|
||||
return "]"
|
||||
case itemTypeName:
|
||||
return "TYPE"
|
||||
case itemVariable:
|
||||
return "VAR"
|
||||
case itemAt:
|
||||
return "@"
|
||||
case itemColon:
|
||||
return ":"
|
||||
case itemBlank:
|
||||
return "_"
|
||||
case itemString:
|
||||
return "STRING"
|
||||
case itemEOF:
|
||||
return "EOF"
|
||||
default:
|
||||
return fmt.Sprintf("itemType(%d)", typ)
|
||||
}
|
||||
}
|
||||
|
||||
type item struct {
|
||||
typ itemType
|
||||
val string
|
||||
pos int
|
||||
}
|
||||
|
||||
type stateFn func(*lexer) stateFn
|
||||
|
||||
func (l *lexer) run() {
|
||||
for state := lexStart; state != nil; {
|
||||
state = state(l)
|
||||
}
|
||||
close(l.items)
|
||||
}
|
||||
|
||||
func (l *lexer) emitValue(t itemType, value string) {
|
||||
l.items <- item{t, value, l.start}
|
||||
l.start = l.pos
|
||||
}
|
||||
|
||||
func (l *lexer) emit(t itemType) {
|
||||
l.items <- item{t, l.input[l.start:l.pos], l.start}
|
||||
l.start = l.pos
|
||||
}
|
||||
|
||||
func lexStart(l *lexer) stateFn {
|
||||
switch r := l.next(); {
|
||||
case r == eof:
|
||||
l.emit(itemEOF)
|
||||
return nil
|
||||
case unicode.IsSpace(r):
|
||||
l.ignore()
|
||||
case r == '(':
|
||||
l.emit(itemLeftParen)
|
||||
case r == ')':
|
||||
l.emit(itemRightParen)
|
||||
case r == '[':
|
||||
l.emit(itemLeftBracket)
|
||||
case r == ']':
|
||||
l.emit(itemRightBracket)
|
||||
case r == '@':
|
||||
l.emit(itemAt)
|
||||
case r == ':':
|
||||
l.emit(itemColon)
|
||||
case r == '_':
|
||||
l.emit(itemBlank)
|
||||
case r == '"':
|
||||
l.backup()
|
||||
return lexString
|
||||
case unicode.IsUpper(r):
|
||||
l.backup()
|
||||
return lexType
|
||||
case unicode.IsLower(r):
|
||||
l.backup()
|
||||
return lexVariable
|
||||
default:
|
||||
return l.errorf("unexpected character %c", r)
|
||||
}
|
||||
return lexStart
|
||||
}
|
||||
|
||||
func (l *lexer) next() (r rune) {
|
||||
if l.pos >= len(l.input) {
|
||||
l.width = 0
|
||||
return eof
|
||||
}
|
||||
r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
|
||||
|
||||
if r == '\n' {
|
||||
l.f.AddLine(l.pos)
|
||||
}
|
||||
|
||||
l.pos += l.width
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (l *lexer) ignore() {
|
||||
l.start = l.pos
|
||||
}
|
||||
|
||||
func (l *lexer) backup() {
|
||||
l.pos -= l.width
|
||||
}
|
||||
|
||||
func (l *lexer) errorf(format string, args ...interface{}) stateFn {
|
||||
// TODO(dh): emit position information in errors
|
||||
l.items <- item{
|
||||
itemError,
|
||||
fmt.Sprintf(format, args...),
|
||||
l.start,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isAlphaNumeric(r rune) bool {
|
||||
return r >= '0' && r <= '9' ||
|
||||
r >= 'a' && r <= 'z' ||
|
||||
r >= 'A' && r <= 'Z'
|
||||
}
|
||||
|
||||
func lexString(l *lexer) stateFn {
|
||||
l.next() // skip quote
|
||||
escape := false
|
||||
|
||||
var runes []rune
|
||||
for {
|
||||
switch r := l.next(); r {
|
||||
case eof:
|
||||
return l.errorf("unterminated string")
|
||||
case '"':
|
||||
if !escape {
|
||||
l.emitValue(itemString, string(runes))
|
||||
return lexStart
|
||||
} else {
|
||||
runes = append(runes, '"')
|
||||
escape = false
|
||||
}
|
||||
case '\\':
|
||||
if escape {
|
||||
runes = append(runes, '\\')
|
||||
escape = false
|
||||
} else {
|
||||
escape = true
|
||||
}
|
||||
default:
|
||||
runes = append(runes, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func lexType(l *lexer) stateFn {
|
||||
l.next()
|
||||
for {
|
||||
if !isAlphaNumeric(l.next()) {
|
||||
l.backup()
|
||||
l.emit(itemTypeName)
|
||||
return lexStart
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func lexVariable(l *lexer) stateFn {
|
||||
l.next()
|
||||
for {
|
||||
if !isAlphaNumeric(l.next()) {
|
||||
l.backup()
|
||||
l.emit(itemVariable)
|
||||
return lexStart
|
||||
}
|
||||
}
|
||||
}
|
513
vendor/honnef.co/go/tools/pattern/match.go
vendored
Normal file
513
vendor/honnef.co/go/tools/pattern/match.go
vendored
Normal file
@ -0,0 +1,513 @@
|
||||
package pattern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"reflect"
|
||||
|
||||
"honnef.co/go/tools/lint"
|
||||
)
|
||||
|
||||
var tokensByString = map[string]Token{
|
||||
"INT": Token(token.INT),
|
||||
"FLOAT": Token(token.FLOAT),
|
||||
"IMAG": Token(token.IMAG),
|
||||
"CHAR": Token(token.CHAR),
|
||||
"STRING": Token(token.STRING),
|
||||
"+": Token(token.ADD),
|
||||
"-": Token(token.SUB),
|
||||
"*": Token(token.MUL),
|
||||
"/": Token(token.QUO),
|
||||
"%": Token(token.REM),
|
||||
"&": Token(token.AND),
|
||||
"|": Token(token.OR),
|
||||
"^": Token(token.XOR),
|
||||
"<<": Token(token.SHL),
|
||||
">>": Token(token.SHR),
|
||||
"&^": Token(token.AND_NOT),
|
||||
"+=": Token(token.ADD_ASSIGN),
|
||||
"-=": Token(token.SUB_ASSIGN),
|
||||
"*=": Token(token.MUL_ASSIGN),
|
||||
"/=": Token(token.QUO_ASSIGN),
|
||||
"%=": Token(token.REM_ASSIGN),
|
||||
"&=": Token(token.AND_ASSIGN),
|
||||
"|=": Token(token.OR_ASSIGN),
|
||||
"^=": Token(token.XOR_ASSIGN),
|
||||
"<<=": Token(token.SHL_ASSIGN),
|
||||
">>=": Token(token.SHR_ASSIGN),
|
||||
"&^=": Token(token.AND_NOT_ASSIGN),
|
||||
"&&": Token(token.LAND),
|
||||
"||": Token(token.LOR),
|
||||
"<-": Token(token.ARROW),
|
||||
"++": Token(token.INC),
|
||||
"--": Token(token.DEC),
|
||||
"==": Token(token.EQL),
|
||||
"<": Token(token.LSS),
|
||||
">": Token(token.GTR),
|
||||
"=": Token(token.ASSIGN),
|
||||
"!": Token(token.NOT),
|
||||
"!=": Token(token.NEQ),
|
||||
"<=": Token(token.LEQ),
|
||||
">=": Token(token.GEQ),
|
||||
":=": Token(token.DEFINE),
|
||||
"...": Token(token.ELLIPSIS),
|
||||
"IMPORT": Token(token.IMPORT),
|
||||
"VAR": Token(token.VAR),
|
||||
"TYPE": Token(token.TYPE),
|
||||
"CONST": Token(token.CONST),
|
||||
}
|
||||
|
||||
func maybeToken(node Node) (Node, bool) {
|
||||
if node, ok := node.(String); ok {
|
||||
if tok, ok := tokensByString[string(node)]; ok {
|
||||
return tok, true
|
||||
}
|
||||
return node, false
|
||||
}
|
||||
return node, false
|
||||
}
|
||||
|
||||
func isNil(v interface{}) bool {
|
||||
if v == nil {
|
||||
return true
|
||||
}
|
||||
if _, ok := v.(Nil); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type matcher interface {
|
||||
Match(*Matcher, interface{}) (interface{}, bool)
|
||||
}
|
||||
|
||||
type State = map[string]interface{}
|
||||
|
||||
type Matcher struct {
|
||||
TypesInfo *types.Info
|
||||
State State
|
||||
}
|
||||
|
||||
func (m *Matcher) fork() *Matcher {
|
||||
state := make(State, len(m.State))
|
||||
for k, v := range m.State {
|
||||
state[k] = v
|
||||
}
|
||||
return &Matcher{
|
||||
TypesInfo: m.TypesInfo,
|
||||
State: state,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Matcher) merge(mc *Matcher) {
|
||||
m.State = mc.State
|
||||
}
|
||||
|
||||
func (m *Matcher) Match(a Node, b ast.Node) bool {
|
||||
m.State = State{}
|
||||
_, ok := match(m, a, b)
|
||||
return ok
|
||||
}
|
||||
|
||||
func Match(a Node, b ast.Node) (*Matcher, bool) {
|
||||
m := &Matcher{}
|
||||
ret := m.Match(a, b)
|
||||
return m, ret
|
||||
}
|
||||
|
||||
// Match two items, which may be (Node, AST) or (AST, AST)
|
||||
func match(m *Matcher, l, r interface{}) (interface{}, bool) {
|
||||
if _, ok := r.(Node); ok {
|
||||
panic("Node mustn't be on right side of match")
|
||||
}
|
||||
|
||||
switch l := l.(type) {
|
||||
case *ast.ParenExpr:
|
||||
return match(m, l.X, r)
|
||||
case *ast.ExprStmt:
|
||||
return match(m, l.X, r)
|
||||
case *ast.DeclStmt:
|
||||
return match(m, l.Decl, r)
|
||||
case *ast.LabeledStmt:
|
||||
return match(m, l.Stmt, r)
|
||||
case *ast.BlockStmt:
|
||||
return match(m, l.List, r)
|
||||
case *ast.FieldList:
|
||||
return match(m, l.List, r)
|
||||
}
|
||||
|
||||
switch r := r.(type) {
|
||||
case *ast.ParenExpr:
|
||||
return match(m, l, r.X)
|
||||
case *ast.ExprStmt:
|
||||
return match(m, l, r.X)
|
||||
case *ast.DeclStmt:
|
||||
return match(m, l, r.Decl)
|
||||
case *ast.LabeledStmt:
|
||||
return match(m, l, r.Stmt)
|
||||
case *ast.BlockStmt:
|
||||
if r == nil {
|
||||
return match(m, l, nil)
|
||||
}
|
||||
return match(m, l, r.List)
|
||||
case *ast.FieldList:
|
||||
if r == nil {
|
||||
return match(m, l, nil)
|
||||
}
|
||||
return match(m, l, r.List)
|
||||
case *ast.BasicLit:
|
||||
if r == nil {
|
||||
return match(m, l, nil)
|
||||
}
|
||||
}
|
||||
|
||||
if l, ok := l.(matcher); ok {
|
||||
return l.Match(m, r)
|
||||
}
|
||||
|
||||
if l, ok := l.(Node); ok {
|
||||
// Matching of pattern with concrete value
|
||||
return matchNodeAST(m, l, r)
|
||||
}
|
||||
|
||||
if l == nil || r == nil {
|
||||
return nil, l == r
|
||||
}
|
||||
|
||||
{
|
||||
ln, ok1 := l.(ast.Node)
|
||||
rn, ok2 := r.(ast.Node)
|
||||
if ok1 && ok2 {
|
||||
return matchAST(m, ln, rn)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
obj, ok := l.(types.Object)
|
||||
if ok {
|
||||
switch r := r.(type) {
|
||||
case *ast.Ident:
|
||||
return obj, obj == m.TypesInfo.ObjectOf(r)
|
||||
case *ast.SelectorExpr:
|
||||
return obj, obj == m.TypesInfo.ObjectOf(r.Sel)
|
||||
default:
|
||||
return obj, false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ln, ok1 := l.([]ast.Expr)
|
||||
rn, ok2 := r.([]ast.Expr)
|
||||
if ok1 || ok2 {
|
||||
if ok1 && !ok2 {
|
||||
rn = []ast.Expr{r.(ast.Expr)}
|
||||
} else if !ok1 && ok2 {
|
||||
ln = []ast.Expr{l.(ast.Expr)}
|
||||
}
|
||||
|
||||
if len(ln) != len(rn) {
|
||||
return nil, false
|
||||
}
|
||||
for i, ll := range ln {
|
||||
if _, ok := match(m, ll, rn[i]); !ok {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return r, true
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ln, ok1 := l.([]ast.Stmt)
|
||||
rn, ok2 := r.([]ast.Stmt)
|
||||
if ok1 || ok2 {
|
||||
if ok1 && !ok2 {
|
||||
rn = []ast.Stmt{r.(ast.Stmt)}
|
||||
} else if !ok1 && ok2 {
|
||||
ln = []ast.Stmt{l.(ast.Stmt)}
|
||||
}
|
||||
|
||||
if len(ln) != len(rn) {
|
||||
return nil, false
|
||||
}
|
||||
for i, ll := range ln {
|
||||
if _, ok := match(m, ll, rn[i]); !ok {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
return r, true
|
||||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unsupported comparison: %T and %T", l, r))
|
||||
}
|
||||
|
||||
// Match a Node with an AST node
|
||||
func matchNodeAST(m *Matcher, a Node, b interface{}) (interface{}, bool) {
|
||||
switch b := b.(type) {
|
||||
case []ast.Stmt:
|
||||
// 'a' is not a List or we'd be using its Match
|
||||
// implementation.
|
||||
|
||||
if len(b) != 1 {
|
||||
return nil, false
|
||||
}
|
||||
return match(m, a, b[0])
|
||||
case []ast.Expr:
|
||||
// 'a' is not a List or we'd be using its Match
|
||||
// implementation.
|
||||
|
||||
if len(b) != 1 {
|
||||
return nil, false
|
||||
}
|
||||
return match(m, a, b[0])
|
||||
case ast.Node:
|
||||
ra := reflect.ValueOf(a)
|
||||
rb := reflect.ValueOf(b).Elem()
|
||||
|
||||
if ra.Type().Name() != rb.Type().Name() {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
for i := 0; i < ra.NumField(); i++ {
|
||||
af := ra.Field(i)
|
||||
fieldName := ra.Type().Field(i).Name
|
||||
bf := rb.FieldByName(fieldName)
|
||||
if (bf == reflect.Value{}) {
|
||||
panic(fmt.Sprintf("internal error: could not find field %s in type %t when comparing with %T", fieldName, b, a))
|
||||
}
|
||||
ai := af.Interface()
|
||||
bi := bf.Interface()
|
||||
if ai == nil {
|
||||
return b, bi == nil
|
||||
}
|
||||
if _, ok := match(m, ai.(Node), bi); !ok {
|
||||
return b, false
|
||||
}
|
||||
}
|
||||
return b, true
|
||||
case nil:
|
||||
return nil, a == Nil{}
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled type %T", b))
|
||||
}
|
||||
}
|
||||
|
||||
// Match two AST nodes
|
||||
func matchAST(m *Matcher, a, b ast.Node) (interface{}, bool) {
|
||||
ra := reflect.ValueOf(a)
|
||||
rb := reflect.ValueOf(b)
|
||||
|
||||
if ra.Type() != rb.Type() {
|
||||
return nil, false
|
||||
}
|
||||
if ra.IsNil() || rb.IsNil() {
|
||||
return rb, ra.IsNil() == rb.IsNil()
|
||||
}
|
||||
|
||||
ra = ra.Elem()
|
||||
rb = rb.Elem()
|
||||
for i := 0; i < ra.NumField(); i++ {
|
||||
af := ra.Field(i)
|
||||
bf := rb.Field(i)
|
||||
if af.Type() == rtTokPos || af.Type() == rtObject || af.Type() == rtCommentGroup {
|
||||
continue
|
||||
}
|
||||
|
||||
switch af.Kind() {
|
||||
case reflect.Slice:
|
||||
if af.Len() != bf.Len() {
|
||||
return nil, false
|
||||
}
|
||||
for j := 0; j < af.Len(); j++ {
|
||||
if _, ok := match(m, af.Index(j).Interface().(ast.Node), bf.Index(j).Interface().(ast.Node)); !ok {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
case reflect.String:
|
||||
if af.String() != bf.String() {
|
||||
return nil, false
|
||||
}
|
||||
case reflect.Int:
|
||||
if af.Int() != bf.Int() {
|
||||
return nil, false
|
||||
}
|
||||
case reflect.Bool:
|
||||
if af.Bool() != bf.Bool() {
|
||||
return nil, false
|
||||
}
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
if _, ok := match(m, af.Interface(), bf.Interface()); !ok {
|
||||
return nil, false
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("internal error: unhandled kind %s (%T)", af.Kind(), af.Interface()))
|
||||
}
|
||||
}
|
||||
return b, true
|
||||
}
|
||||
|
||||
func (b Binding) Match(m *Matcher, node interface{}) (interface{}, bool) {
|
||||
if isNil(b.Node) {
|
||||
v, ok := m.State[b.Name]
|
||||
if ok {
|
||||
// Recall value
|
||||
return match(m, v, node)
|
||||
}
|
||||
// Matching anything
|
||||
b.Node = Any{}
|
||||
}
|
||||
|
||||
// Store value
|
||||
if _, ok := m.State[b.Name]; ok {
|
||||
panic(fmt.Sprintf("binding already created: %s", b.Name))
|
||||
}
|
||||
new, ret := match(m, b.Node, node)
|
||||
if ret {
|
||||
m.State[b.Name] = new
|
||||
}
|
||||
return new, ret
|
||||
}
|
||||
|
||||
func (Any) Match(m *Matcher, node interface{}) (interface{}, bool) {
|
||||
return node, true
|
||||
}
|
||||
|
||||
func (l List) Match(m *Matcher, node interface{}) (interface{}, bool) {
|
||||
v := reflect.ValueOf(node)
|
||||
if v.Kind() == reflect.Slice {
|
||||
if isNil(l.Head) {
|
||||
return node, v.Len() == 0
|
||||
}
|
||||
if v.Len() == 0 {
|
||||
return nil, false
|
||||
}
|
||||
// OPT(dh): don't check the entire tail if head didn't match
|
||||
_, ok1 := match(m, l.Head, v.Index(0).Interface())
|
||||
_, ok2 := match(m, l.Tail, v.Slice(1, v.Len()).Interface())
|
||||
return node, ok1 && ok2
|
||||
}
|
||||
// Our empty list does not equal an untyped Go nil. This way, we can
|
||||
// tell apart an if with no else and an if with an empty else.
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (s String) Match(m *Matcher, node interface{}) (interface{}, bool) {
|
||||
switch o := node.(type) {
|
||||
case token.Token:
|
||||
if tok, ok := maybeToken(s); ok {
|
||||
return match(m, tok, node)
|
||||
}
|
||||
return nil, false
|
||||
case string:
|
||||
return o, string(s) == o
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
func (tok Token) Match(m *Matcher, node interface{}) (interface{}, bool) {
|
||||
o, ok := node.(token.Token)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return o, token.Token(tok) == o
|
||||
}
|
||||
|
||||
func (Nil) Match(m *Matcher, node interface{}) (interface{}, bool) {
|
||||
return nil, isNil(node)
|
||||
}
|
||||
|
||||
func (builtin Builtin) Match(m *Matcher, node interface{}) (interface{}, bool) {
|
||||
ident, ok := node.(*ast.Ident)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
obj := m.TypesInfo.ObjectOf(ident)
|
||||
if obj != types.Universe.Lookup(ident.Name) {
|
||||
return nil, false
|
||||
}
|
||||
return match(m, builtin.Name, ident.Name)
|
||||
}
|
||||
|
||||
func (obj Object) Match(m *Matcher, node interface{}) (interface{}, bool) {
|
||||
ident, ok := node.(*ast.Ident)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
id := m.TypesInfo.ObjectOf(ident)
|
||||
_, ok = match(m, obj.Name, ident.Name)
|
||||
return id, ok
|
||||
}
|
||||
|
||||
func (fn Function) Match(m *Matcher, node interface{}) (interface{}, bool) {
|
||||
var name string
|
||||
var obj types.Object
|
||||
switch node := node.(type) {
|
||||
case *ast.Ident:
|
||||
obj = m.TypesInfo.ObjectOf(node)
|
||||
switch obj := obj.(type) {
|
||||
case *types.Func:
|
||||
name = lint.FuncName(obj)
|
||||
case *types.Builtin:
|
||||
name = obj.Name()
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
case *ast.SelectorExpr:
|
||||
var ok bool
|
||||
obj, ok = m.TypesInfo.ObjectOf(node.Sel).(*types.Func)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
name = lint.FuncName(obj.(*types.Func))
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
_, ok := match(m, fn.Name, name)
|
||||
return obj, ok
|
||||
}
|
||||
|
||||
func (or Or) Match(m *Matcher, node interface{}) (interface{}, bool) {
|
||||
for _, opt := range or.Nodes {
|
||||
mc := m.fork()
|
||||
if ret, ok := match(mc, opt, node); ok {
|
||||
m.merge(mc)
|
||||
return ret, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (not Not) Match(m *Matcher, node interface{}) (interface{}, bool) {
|
||||
_, ok := match(m, not.Node, node)
|
||||
if ok {
|
||||
return nil, false
|
||||
}
|
||||
return node, true
|
||||
}
|
||||
|
||||
var (
|
||||
// Types of fields in go/ast structs that we want to skip
|
||||
rtTokPos = reflect.TypeOf(token.Pos(0))
|
||||
rtObject = reflect.TypeOf((*ast.Object)(nil))
|
||||
rtCommentGroup = reflect.TypeOf((*ast.CommentGroup)(nil))
|
||||
)
|
||||
|
||||
var (
|
||||
_ matcher = Binding{}
|
||||
_ matcher = Any{}
|
||||
_ matcher = List{}
|
||||
_ matcher = String("")
|
||||
_ matcher = Token(0)
|
||||
_ matcher = Nil{}
|
||||
_ matcher = Builtin{}
|
||||
_ matcher = Object{}
|
||||
_ matcher = Function{}
|
||||
_ matcher = Or{}
|
||||
_ matcher = Not{}
|
||||
)
|
455
vendor/honnef.co/go/tools/pattern/parser.go
vendored
Normal file
455
vendor/honnef.co/go/tools/pattern/parser.go
vendored
Normal file
@ -0,0 +1,455 @@
|
||||
package pattern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type Pattern struct {
|
||||
Root Node
|
||||
// Relevant contains instances of ast.Node that could potentially
|
||||
// initiate a successful match of the pattern.
|
||||
Relevant []reflect.Type
|
||||
}
|
||||
|
||||
func MustParse(s string) Pattern {
|
||||
p := &Parser{AllowTypeInfo: true}
|
||||
pat, err := p.Parse(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return pat
|
||||
}
|
||||
|
||||
func roots(node Node) []reflect.Type {
|
||||
switch node := node.(type) {
|
||||
case Or:
|
||||
var out []reflect.Type
|
||||
for _, el := range node.Nodes {
|
||||
out = append(out, roots(el)...)
|
||||
}
|
||||
return out
|
||||
case Not:
|
||||
return roots(node.Node)
|
||||
case Binding:
|
||||
return roots(node.Node)
|
||||
case Nil, nil:
|
||||
// this branch is reached via bindings
|
||||
return allTypes
|
||||
default:
|
||||
Ts, ok := nodeToASTTypes[reflect.TypeOf(node)]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("internal error: unhandled type %T", node))
|
||||
}
|
||||
return Ts
|
||||
}
|
||||
}
|
||||
|
||||
var allTypes = []reflect.Type{
|
||||
reflect.TypeOf((*ast.RangeStmt)(nil)),
|
||||
reflect.TypeOf((*ast.AssignStmt)(nil)),
|
||||
reflect.TypeOf((*ast.IndexExpr)(nil)),
|
||||
reflect.TypeOf((*ast.Ident)(nil)),
|
||||
reflect.TypeOf((*ast.ValueSpec)(nil)),
|
||||
reflect.TypeOf((*ast.GenDecl)(nil)),
|
||||
reflect.TypeOf((*ast.BinaryExpr)(nil)),
|
||||
reflect.TypeOf((*ast.ForStmt)(nil)),
|
||||
reflect.TypeOf((*ast.ArrayType)(nil)),
|
||||
reflect.TypeOf((*ast.DeferStmt)(nil)),
|
||||
reflect.TypeOf((*ast.MapType)(nil)),
|
||||
reflect.TypeOf((*ast.ReturnStmt)(nil)),
|
||||
reflect.TypeOf((*ast.SliceExpr)(nil)),
|
||||
reflect.TypeOf((*ast.StarExpr)(nil)),
|
||||
reflect.TypeOf((*ast.UnaryExpr)(nil)),
|
||||
reflect.TypeOf((*ast.SendStmt)(nil)),
|
||||
reflect.TypeOf((*ast.SelectStmt)(nil)),
|
||||
reflect.TypeOf((*ast.ImportSpec)(nil)),
|
||||
reflect.TypeOf((*ast.IfStmt)(nil)),
|
||||
reflect.TypeOf((*ast.GoStmt)(nil)),
|
||||
reflect.TypeOf((*ast.Field)(nil)),
|
||||
reflect.TypeOf((*ast.SelectorExpr)(nil)),
|
||||
reflect.TypeOf((*ast.StructType)(nil)),
|
||||
reflect.TypeOf((*ast.KeyValueExpr)(nil)),
|
||||
reflect.TypeOf((*ast.FuncType)(nil)),
|
||||
reflect.TypeOf((*ast.FuncLit)(nil)),
|
||||
reflect.TypeOf((*ast.FuncDecl)(nil)),
|
||||
reflect.TypeOf((*ast.ChanType)(nil)),
|
||||
reflect.TypeOf((*ast.CallExpr)(nil)),
|
||||
reflect.TypeOf((*ast.CaseClause)(nil)),
|
||||
reflect.TypeOf((*ast.CommClause)(nil)),
|
||||
reflect.TypeOf((*ast.CompositeLit)(nil)),
|
||||
reflect.TypeOf((*ast.EmptyStmt)(nil)),
|
||||
reflect.TypeOf((*ast.SwitchStmt)(nil)),
|
||||
reflect.TypeOf((*ast.TypeSwitchStmt)(nil)),
|
||||
reflect.TypeOf((*ast.TypeAssertExpr)(nil)),
|
||||
reflect.TypeOf((*ast.TypeSpec)(nil)),
|
||||
reflect.TypeOf((*ast.InterfaceType)(nil)),
|
||||
reflect.TypeOf((*ast.BranchStmt)(nil)),
|
||||
reflect.TypeOf((*ast.IncDecStmt)(nil)),
|
||||
reflect.TypeOf((*ast.BasicLit)(nil)),
|
||||
}
|
||||
|
||||
var nodeToASTTypes = map[reflect.Type][]reflect.Type{
|
||||
reflect.TypeOf(String("")): nil,
|
||||
reflect.TypeOf(Token(0)): nil,
|
||||
reflect.TypeOf(List{}): {reflect.TypeOf((*ast.BlockStmt)(nil)), reflect.TypeOf((*ast.FieldList)(nil))},
|
||||
reflect.TypeOf(Builtin{}): {reflect.TypeOf((*ast.Ident)(nil))},
|
||||
reflect.TypeOf(Object{}): {reflect.TypeOf((*ast.Ident)(nil))},
|
||||
reflect.TypeOf(Function{}): {reflect.TypeOf((*ast.Ident)(nil)), reflect.TypeOf((*ast.SelectorExpr)(nil))},
|
||||
reflect.TypeOf(Any{}): allTypes,
|
||||
reflect.TypeOf(RangeStmt{}): {reflect.TypeOf((*ast.RangeStmt)(nil))},
|
||||
reflect.TypeOf(AssignStmt{}): {reflect.TypeOf((*ast.AssignStmt)(nil))},
|
||||
reflect.TypeOf(IndexExpr{}): {reflect.TypeOf((*ast.IndexExpr)(nil))},
|
||||
reflect.TypeOf(Ident{}): {reflect.TypeOf((*ast.Ident)(nil))},
|
||||
reflect.TypeOf(ValueSpec{}): {reflect.TypeOf((*ast.ValueSpec)(nil))},
|
||||
reflect.TypeOf(GenDecl{}): {reflect.TypeOf((*ast.GenDecl)(nil))},
|
||||
reflect.TypeOf(BinaryExpr{}): {reflect.TypeOf((*ast.BinaryExpr)(nil))},
|
||||
reflect.TypeOf(ForStmt{}): {reflect.TypeOf((*ast.ForStmt)(nil))},
|
||||
reflect.TypeOf(ArrayType{}): {reflect.TypeOf((*ast.ArrayType)(nil))},
|
||||
reflect.TypeOf(DeferStmt{}): {reflect.TypeOf((*ast.DeferStmt)(nil))},
|
||||
reflect.TypeOf(MapType{}): {reflect.TypeOf((*ast.MapType)(nil))},
|
||||
reflect.TypeOf(ReturnStmt{}): {reflect.TypeOf((*ast.ReturnStmt)(nil))},
|
||||
reflect.TypeOf(SliceExpr{}): {reflect.TypeOf((*ast.SliceExpr)(nil))},
|
||||
reflect.TypeOf(StarExpr{}): {reflect.TypeOf((*ast.StarExpr)(nil))},
|
||||
reflect.TypeOf(UnaryExpr{}): {reflect.TypeOf((*ast.UnaryExpr)(nil))},
|
||||
reflect.TypeOf(SendStmt{}): {reflect.TypeOf((*ast.SendStmt)(nil))},
|
||||
reflect.TypeOf(SelectStmt{}): {reflect.TypeOf((*ast.SelectStmt)(nil))},
|
||||
reflect.TypeOf(ImportSpec{}): {reflect.TypeOf((*ast.ImportSpec)(nil))},
|
||||
reflect.TypeOf(IfStmt{}): {reflect.TypeOf((*ast.IfStmt)(nil))},
|
||||
reflect.TypeOf(GoStmt{}): {reflect.TypeOf((*ast.GoStmt)(nil))},
|
||||
reflect.TypeOf(Field{}): {reflect.TypeOf((*ast.Field)(nil))},
|
||||
reflect.TypeOf(SelectorExpr{}): {reflect.TypeOf((*ast.SelectorExpr)(nil))},
|
||||
reflect.TypeOf(StructType{}): {reflect.TypeOf((*ast.StructType)(nil))},
|
||||
reflect.TypeOf(KeyValueExpr{}): {reflect.TypeOf((*ast.KeyValueExpr)(nil))},
|
||||
reflect.TypeOf(FuncType{}): {reflect.TypeOf((*ast.FuncType)(nil))},
|
||||
reflect.TypeOf(FuncLit{}): {reflect.TypeOf((*ast.FuncLit)(nil))},
|
||||
reflect.TypeOf(FuncDecl{}): {reflect.TypeOf((*ast.FuncDecl)(nil))},
|
||||
reflect.TypeOf(ChanType{}): {reflect.TypeOf((*ast.ChanType)(nil))},
|
||||
reflect.TypeOf(CallExpr{}): {reflect.TypeOf((*ast.CallExpr)(nil))},
|
||||
reflect.TypeOf(CaseClause{}): {reflect.TypeOf((*ast.CaseClause)(nil))},
|
||||
reflect.TypeOf(CommClause{}): {reflect.TypeOf((*ast.CommClause)(nil))},
|
||||
reflect.TypeOf(CompositeLit{}): {reflect.TypeOf((*ast.CompositeLit)(nil))},
|
||||
reflect.TypeOf(EmptyStmt{}): {reflect.TypeOf((*ast.EmptyStmt)(nil))},
|
||||
reflect.TypeOf(SwitchStmt{}): {reflect.TypeOf((*ast.SwitchStmt)(nil))},
|
||||
reflect.TypeOf(TypeSwitchStmt{}): {reflect.TypeOf((*ast.TypeSwitchStmt)(nil))},
|
||||
reflect.TypeOf(TypeAssertExpr{}): {reflect.TypeOf((*ast.TypeAssertExpr)(nil))},
|
||||
reflect.TypeOf(TypeSpec{}): {reflect.TypeOf((*ast.TypeSpec)(nil))},
|
||||
reflect.TypeOf(InterfaceType{}): {reflect.TypeOf((*ast.InterfaceType)(nil))},
|
||||
reflect.TypeOf(BranchStmt{}): {reflect.TypeOf((*ast.BranchStmt)(nil))},
|
||||
reflect.TypeOf(IncDecStmt{}): {reflect.TypeOf((*ast.IncDecStmt)(nil))},
|
||||
reflect.TypeOf(BasicLit{}): {reflect.TypeOf((*ast.BasicLit)(nil))},
|
||||
}
|
||||
|
||||
var requiresTypeInfo = map[string]bool{
|
||||
"Function": true,
|
||||
"Builtin": true,
|
||||
"Object": true,
|
||||
}
|
||||
|
||||
type Parser struct {
|
||||
// Allow nodes that rely on type information
|
||||
AllowTypeInfo bool
|
||||
|
||||
lex *lexer
|
||||
cur item
|
||||
last *item
|
||||
items chan item
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(s string) (Pattern, error) {
|
||||
p.cur = item{}
|
||||
p.last = nil
|
||||
p.items = nil
|
||||
|
||||
fset := token.NewFileSet()
|
||||
p.lex = &lexer{
|
||||
f: fset.AddFile("<input>", -1, len(s)),
|
||||
input: s,
|
||||
items: make(chan item),
|
||||
}
|
||||
go p.lex.run()
|
||||
p.items = p.lex.items
|
||||
root, err := p.node()
|
||||
if err != nil {
|
||||
// drain lexer if parsing failed
|
||||
for range p.lex.items {
|
||||
}
|
||||
return Pattern{}, err
|
||||
}
|
||||
if item := <-p.lex.items; item.typ != itemEOF {
|
||||
return Pattern{}, fmt.Errorf("unexpected token %s after end of pattern", item.typ)
|
||||
}
|
||||
return Pattern{
|
||||
Root: root,
|
||||
Relevant: roots(root),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *Parser) next() item {
|
||||
if p.last != nil {
|
||||
n := *p.last
|
||||
p.last = nil
|
||||
return n
|
||||
}
|
||||
var ok bool
|
||||
p.cur, ok = <-p.items
|
||||
if !ok {
|
||||
p.cur = item{typ: eof}
|
||||
}
|
||||
return p.cur
|
||||
}
|
||||
|
||||
func (p *Parser) rewind() {
|
||||
p.last = &p.cur
|
||||
}
|
||||
|
||||
func (p *Parser) peek() item {
|
||||
n := p.next()
|
||||
p.rewind()
|
||||
return n
|
||||
}
|
||||
|
||||
func (p *Parser) accept(typ itemType) (item, bool) {
|
||||
n := p.next()
|
||||
if n.typ == typ {
|
||||
return n, true
|
||||
}
|
||||
p.rewind()
|
||||
return item{}, false
|
||||
}
|
||||
|
||||
func (p *Parser) unexpectedToken(valid string) error {
|
||||
if p.cur.typ == itemError {
|
||||
return fmt.Errorf("error lexing input: %s", p.cur.val)
|
||||
}
|
||||
var got string
|
||||
switch p.cur.typ {
|
||||
case itemTypeName, itemVariable, itemString:
|
||||
got = p.cur.val
|
||||
default:
|
||||
got = "'" + p.cur.typ.String() + "'"
|
||||
}
|
||||
|
||||
pos := p.lex.f.Position(token.Pos(p.cur.pos))
|
||||
return fmt.Errorf("%s: expected %s, found %s", pos, valid, got)
|
||||
}
|
||||
|
||||
func (p *Parser) node() (Node, error) {
|
||||
if _, ok := p.accept(itemLeftParen); !ok {
|
||||
return nil, p.unexpectedToken("'('")
|
||||
}
|
||||
typ, ok := p.accept(itemTypeName)
|
||||
if !ok {
|
||||
return nil, p.unexpectedToken("Node type")
|
||||
}
|
||||
|
||||
var objs []Node
|
||||
for {
|
||||
if _, ok := p.accept(itemRightParen); ok {
|
||||
break
|
||||
} else {
|
||||
p.rewind()
|
||||
obj, err := p.object()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objs = append(objs, obj)
|
||||
}
|
||||
}
|
||||
|
||||
return p.populateNode(typ.val, objs)
|
||||
}
|
||||
|
||||
func populateNode(typ string, objs []Node, allowTypeInfo bool) (Node, error) {
|
||||
T, ok := structNodes[typ]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown node %s", typ)
|
||||
}
|
||||
|
||||
if !allowTypeInfo && requiresTypeInfo[typ] {
|
||||
return nil, fmt.Errorf("Node %s requires type information", typ)
|
||||
}
|
||||
|
||||
pv := reflect.New(T)
|
||||
v := pv.Elem()
|
||||
|
||||
if v.NumField() == 1 {
|
||||
f := v.Field(0)
|
||||
if f.Type().Kind() == reflect.Slice {
|
||||
// Variadic node
|
||||
f.Set(reflect.AppendSlice(f, reflect.ValueOf(objs)))
|
||||
return v.Interface().(Node), nil
|
||||
}
|
||||
}
|
||||
if len(objs) != v.NumField() {
|
||||
return nil, fmt.Errorf("tried to initialize node %s with %d values, expected %d", typ, len(objs), v.NumField())
|
||||
}
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
f := v.Field(i)
|
||||
if f.Kind() == reflect.String {
|
||||
if obj, ok := objs[i].(String); ok {
|
||||
f.Set(reflect.ValueOf(string(obj)))
|
||||
} else {
|
||||
return nil, fmt.Errorf("first argument of (Binding name node) must be string, but got %s", objs[i])
|
||||
}
|
||||
} else {
|
||||
f.Set(reflect.ValueOf(objs[i]))
|
||||
}
|
||||
}
|
||||
return v.Interface().(Node), nil
|
||||
}
|
||||
|
||||
func (p *Parser) populateNode(typ string, objs []Node) (Node, error) {
|
||||
return populateNode(typ, objs, p.AllowTypeInfo)
|
||||
}
|
||||
|
||||
var structNodes = map[string]reflect.Type{
|
||||
"Any": reflect.TypeOf(Any{}),
|
||||
"Ellipsis": reflect.TypeOf(Ellipsis{}),
|
||||
"List": reflect.TypeOf(List{}),
|
||||
"Binding": reflect.TypeOf(Binding{}),
|
||||
"RangeStmt": reflect.TypeOf(RangeStmt{}),
|
||||
"AssignStmt": reflect.TypeOf(AssignStmt{}),
|
||||
"IndexExpr": reflect.TypeOf(IndexExpr{}),
|
||||
"Ident": reflect.TypeOf(Ident{}),
|
||||
"Builtin": reflect.TypeOf(Builtin{}),
|
||||
"ValueSpec": reflect.TypeOf(ValueSpec{}),
|
||||
"GenDecl": reflect.TypeOf(GenDecl{}),
|
||||
"BinaryExpr": reflect.TypeOf(BinaryExpr{}),
|
||||
"ForStmt": reflect.TypeOf(ForStmt{}),
|
||||
"ArrayType": reflect.TypeOf(ArrayType{}),
|
||||
"DeferStmt": reflect.TypeOf(DeferStmt{}),
|
||||
"MapType": reflect.TypeOf(MapType{}),
|
||||
"ReturnStmt": reflect.TypeOf(ReturnStmt{}),
|
||||
"SliceExpr": reflect.TypeOf(SliceExpr{}),
|
||||
"StarExpr": reflect.TypeOf(StarExpr{}),
|
||||
"UnaryExpr": reflect.TypeOf(UnaryExpr{}),
|
||||
"SendStmt": reflect.TypeOf(SendStmt{}),
|
||||
"SelectStmt": reflect.TypeOf(SelectStmt{}),
|
||||
"ImportSpec": reflect.TypeOf(ImportSpec{}),
|
||||
"IfStmt": reflect.TypeOf(IfStmt{}),
|
||||
"GoStmt": reflect.TypeOf(GoStmt{}),
|
||||
"Field": reflect.TypeOf(Field{}),
|
||||
"SelectorExpr": reflect.TypeOf(SelectorExpr{}),
|
||||
"StructType": reflect.TypeOf(StructType{}),
|
||||
"KeyValueExpr": reflect.TypeOf(KeyValueExpr{}),
|
||||
"FuncType": reflect.TypeOf(FuncType{}),
|
||||
"FuncLit": reflect.TypeOf(FuncLit{}),
|
||||
"FuncDecl": reflect.TypeOf(FuncDecl{}),
|
||||
"ChanType": reflect.TypeOf(ChanType{}),
|
||||
"CallExpr": reflect.TypeOf(CallExpr{}),
|
||||
"CaseClause": reflect.TypeOf(CaseClause{}),
|
||||
"CommClause": reflect.TypeOf(CommClause{}),
|
||||
"CompositeLit": reflect.TypeOf(CompositeLit{}),
|
||||
"EmptyStmt": reflect.TypeOf(EmptyStmt{}),
|
||||
"SwitchStmt": reflect.TypeOf(SwitchStmt{}),
|
||||
"TypeSwitchStmt": reflect.TypeOf(TypeSwitchStmt{}),
|
||||
"TypeAssertExpr": reflect.TypeOf(TypeAssertExpr{}),
|
||||
"TypeSpec": reflect.TypeOf(TypeSpec{}),
|
||||
"InterfaceType": reflect.TypeOf(InterfaceType{}),
|
||||
"BranchStmt": reflect.TypeOf(BranchStmt{}),
|
||||
"IncDecStmt": reflect.TypeOf(IncDecStmt{}),
|
||||
"BasicLit": reflect.TypeOf(BasicLit{}),
|
||||
"Object": reflect.TypeOf(Object{}),
|
||||
"Function": reflect.TypeOf(Function{}),
|
||||
"Or": reflect.TypeOf(Or{}),
|
||||
"Not": reflect.TypeOf(Not{}),
|
||||
}
|
||||
|
||||
func (p *Parser) object() (Node, error) {
|
||||
n := p.next()
|
||||
switch n.typ {
|
||||
case itemLeftParen:
|
||||
p.rewind()
|
||||
node, err := p.node()
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
if p.peek().typ == itemColon {
|
||||
p.next()
|
||||
tail, err := p.object()
|
||||
if err != nil {
|
||||
return node, err
|
||||
}
|
||||
return List{Head: node, Tail: tail}, nil
|
||||
}
|
||||
return node, nil
|
||||
case itemLeftBracket:
|
||||
p.rewind()
|
||||
return p.array()
|
||||
case itemVariable:
|
||||
v := n
|
||||
if v.val == "nil" {
|
||||
return Nil{}, nil
|
||||
}
|
||||
var b Binding
|
||||
if _, ok := p.accept(itemAt); ok {
|
||||
o, err := p.node()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b = Binding{
|
||||
Name: v.val,
|
||||
Node: o,
|
||||
}
|
||||
} else {
|
||||
p.rewind()
|
||||
b = Binding{Name: v.val}
|
||||
}
|
||||
if p.peek().typ == itemColon {
|
||||
p.next()
|
||||
tail, err := p.object()
|
||||
if err != nil {
|
||||
return b, err
|
||||
}
|
||||
return List{Head: b, Tail: tail}, nil
|
||||
}
|
||||
return b, nil
|
||||
case itemBlank:
|
||||
return Any{}, nil
|
||||
case itemString:
|
||||
return String(n.val), nil
|
||||
default:
|
||||
return nil, p.unexpectedToken("object")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) array() (Node, error) {
|
||||
if _, ok := p.accept(itemLeftBracket); !ok {
|
||||
return nil, p.unexpectedToken("'['")
|
||||
}
|
||||
|
||||
var objs []Node
|
||||
for {
|
||||
if _, ok := p.accept(itemRightBracket); ok {
|
||||
break
|
||||
} else {
|
||||
p.rewind()
|
||||
obj, err := p.object()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objs = append(objs, obj)
|
||||
}
|
||||
}
|
||||
|
||||
tail := List{}
|
||||
for i := len(objs) - 1; i >= 0; i-- {
|
||||
l := List{
|
||||
Head: objs[i],
|
||||
Tail: tail,
|
||||
}
|
||||
tail = l
|
||||
}
|
||||
return tail, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Node ::= itemLeftParen itemTypeName Object* itemRightParen
|
||||
Object ::= Node | Array | Binding | itemVariable | itemBlank | itemString
|
||||
Array := itemLeftBracket Object* itemRightBracket
|
||||
Array := Object itemColon Object
|
||||
Binding ::= itemVariable itemAt Node
|
||||
*/
|
497
vendor/honnef.co/go/tools/pattern/pattern.go
vendored
Normal file
497
vendor/honnef.co/go/tools/pattern/pattern.go
vendored
Normal file
@ -0,0 +1,497 @@
|
||||
package pattern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Node = Ellipsis{}
|
||||
_ Node = Binding{}
|
||||
_ Node = RangeStmt{}
|
||||
_ Node = AssignStmt{}
|
||||
_ Node = IndexExpr{}
|
||||
_ Node = Ident{}
|
||||
_ Node = Builtin{}
|
||||
_ Node = String("")
|
||||
_ Node = Any{}
|
||||
_ Node = ValueSpec{}
|
||||
_ Node = List{}
|
||||
_ Node = GenDecl{}
|
||||
_ Node = BinaryExpr{}
|
||||
_ Node = ForStmt{}
|
||||
_ Node = ArrayType{}
|
||||
_ Node = DeferStmt{}
|
||||
_ Node = MapType{}
|
||||
_ Node = ReturnStmt{}
|
||||
_ Node = SliceExpr{}
|
||||
_ Node = StarExpr{}
|
||||
_ Node = UnaryExpr{}
|
||||
_ Node = SendStmt{}
|
||||
_ Node = SelectStmt{}
|
||||
_ Node = ImportSpec{}
|
||||
_ Node = IfStmt{}
|
||||
_ Node = GoStmt{}
|
||||
_ Node = Field{}
|
||||
_ Node = SelectorExpr{}
|
||||
_ Node = StructType{}
|
||||
_ Node = KeyValueExpr{}
|
||||
_ Node = FuncType{}
|
||||
_ Node = FuncLit{}
|
||||
_ Node = FuncDecl{}
|
||||
_ Node = Token(0)
|
||||
_ Node = ChanType{}
|
||||
_ Node = CallExpr{}
|
||||
_ Node = CaseClause{}
|
||||
_ Node = CommClause{}
|
||||
_ Node = CompositeLit{}
|
||||
_ Node = EmptyStmt{}
|
||||
_ Node = SwitchStmt{}
|
||||
_ Node = TypeSwitchStmt{}
|
||||
_ Node = TypeAssertExpr{}
|
||||
_ Node = TypeSpec{}
|
||||
_ Node = InterfaceType{}
|
||||
_ Node = BranchStmt{}
|
||||
_ Node = IncDecStmt{}
|
||||
_ Node = BasicLit{}
|
||||
_ Node = Nil{}
|
||||
_ Node = Object{}
|
||||
_ Node = Function{}
|
||||
_ Node = Not{}
|
||||
_ Node = Or{}
|
||||
)
|
||||
|
||||
type Function struct {
|
||||
Name Node
|
||||
}
|
||||
|
||||
type Token token.Token
|
||||
|
||||
type Nil struct {
|
||||
}
|
||||
|
||||
type Ellipsis struct {
|
||||
Elt Node
|
||||
}
|
||||
|
||||
type IncDecStmt struct {
|
||||
X Node
|
||||
Tok Node
|
||||
}
|
||||
|
||||
type BranchStmt struct {
|
||||
Tok Node
|
||||
Label Node
|
||||
}
|
||||
|
||||
type InterfaceType struct {
|
||||
Methods Node
|
||||
}
|
||||
|
||||
type TypeSpec struct {
|
||||
Name Node
|
||||
Type Node
|
||||
}
|
||||
|
||||
type TypeAssertExpr struct {
|
||||
X Node
|
||||
Type Node
|
||||
}
|
||||
|
||||
type TypeSwitchStmt struct {
|
||||
Init Node
|
||||
Assign Node
|
||||
Body Node
|
||||
}
|
||||
|
||||
type SwitchStmt struct {
|
||||
Init Node
|
||||
Tag Node
|
||||
Body Node
|
||||
}
|
||||
|
||||
type EmptyStmt struct {
|
||||
}
|
||||
|
||||
type CompositeLit struct {
|
||||
Type Node
|
||||
Elts Node
|
||||
}
|
||||
|
||||
type CommClause struct {
|
||||
Comm Node
|
||||
Body Node
|
||||
}
|
||||
|
||||
type CaseClause struct {
|
||||
List Node
|
||||
Body Node
|
||||
}
|
||||
|
||||
type CallExpr struct {
|
||||
Fun Node
|
||||
Args Node
|
||||
// XXX handle ellipsis
|
||||
}
|
||||
|
||||
// TODO(dh): add a ChanDir node, and a way of instantiating it.
|
||||
|
||||
type ChanType struct {
|
||||
Dir Node
|
||||
Value Node
|
||||
}
|
||||
|
||||
type FuncDecl struct {
|
||||
Recv Node
|
||||
Name Node
|
||||
Type Node
|
||||
Body Node
|
||||
}
|
||||
|
||||
type FuncLit struct {
|
||||
Type Node
|
||||
Body Node
|
||||
}
|
||||
|
||||
type FuncType struct {
|
||||
Params Node
|
||||
Results Node
|
||||
}
|
||||
|
||||
type KeyValueExpr struct {
|
||||
Key Node
|
||||
Value Node
|
||||
}
|
||||
|
||||
type StructType struct {
|
||||
Fields Node
|
||||
}
|
||||
|
||||
type SelectorExpr struct {
|
||||
X Node
|
||||
Sel Node
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
Names Node
|
||||
Type Node
|
||||
Tag Node
|
||||
}
|
||||
|
||||
type GoStmt struct {
|
||||
Call Node
|
||||
}
|
||||
|
||||
type IfStmt struct {
|
||||
Init Node
|
||||
Cond Node
|
||||
Body Node
|
||||
Else Node
|
||||
}
|
||||
|
||||
type ImportSpec struct {
|
||||
Name Node
|
||||
Path Node
|
||||
}
|
||||
|
||||
type SelectStmt struct {
|
||||
Body Node
|
||||
}
|
||||
|
||||
type ArrayType struct {
|
||||
Len Node
|
||||
Elt Node
|
||||
}
|
||||
|
||||
type DeferStmt struct {
|
||||
Call Node
|
||||
}
|
||||
|
||||
type MapType struct {
|
||||
Key Node
|
||||
Value Node
|
||||
}
|
||||
|
||||
type ReturnStmt struct {
|
||||
Results Node
|
||||
}
|
||||
|
||||
type SliceExpr struct {
|
||||
X Node
|
||||
Low Node
|
||||
High Node
|
||||
Max Node
|
||||
}
|
||||
|
||||
type StarExpr struct {
|
||||
X Node
|
||||
}
|
||||
|
||||
type UnaryExpr struct {
|
||||
Op Node
|
||||
X Node
|
||||
}
|
||||
|
||||
type SendStmt struct {
|
||||
Chan Node
|
||||
Value Node
|
||||
}
|
||||
|
||||
type Binding struct {
|
||||
Name string
|
||||
Node Node
|
||||
}
|
||||
|
||||
type RangeStmt struct {
|
||||
Key Node
|
||||
Value Node
|
||||
Tok Node
|
||||
X Node
|
||||
Body Node
|
||||
}
|
||||
|
||||
type AssignStmt struct {
|
||||
Lhs Node
|
||||
Tok Node
|
||||
Rhs Node
|
||||
}
|
||||
|
||||
type IndexExpr struct {
|
||||
X Node
|
||||
Index Node
|
||||
}
|
||||
|
||||
type Node interface {
|
||||
String() string
|
||||
isNode()
|
||||
}
|
||||
|
||||
type Ident struct {
|
||||
Name Node
|
||||
}
|
||||
|
||||
type Object struct {
|
||||
Name Node
|
||||
}
|
||||
|
||||
type Builtin struct {
|
||||
Name Node
|
||||
}
|
||||
|
||||
type String string
|
||||
|
||||
type Any struct{}
|
||||
|
||||
type ValueSpec struct {
|
||||
Names Node
|
||||
Type Node
|
||||
Values Node
|
||||
}
|
||||
|
||||
type List struct {
|
||||
Head Node
|
||||
Tail Node
|
||||
}
|
||||
|
||||
type GenDecl struct {
|
||||
Tok Node
|
||||
Specs Node
|
||||
}
|
||||
|
||||
type BasicLit struct {
|
||||
Kind Node
|
||||
Value Node
|
||||
}
|
||||
|
||||
type BinaryExpr struct {
|
||||
X Node
|
||||
Op Node
|
||||
Y Node
|
||||
}
|
||||
|
||||
type ForStmt struct {
|
||||
Init Node
|
||||
Cond Node
|
||||
Post Node
|
||||
Body Node
|
||||
}
|
||||
|
||||
type Or struct {
|
||||
Nodes []Node
|
||||
}
|
||||
|
||||
type Not struct {
|
||||
Node Node
|
||||
}
|
||||
|
||||
func stringify(n Node) string {
|
||||
v := reflect.ValueOf(n)
|
||||
var parts []string
|
||||
parts = append(parts, v.Type().Name())
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
//lint:ignore S1025 false positive in staticcheck 2019.2.3
|
||||
parts = append(parts, fmt.Sprintf("%s", v.Field(i)))
|
||||
}
|
||||
return "(" + strings.Join(parts, " ") + ")"
|
||||
}
|
||||
|
||||
func (stmt AssignStmt) String() string { return stringify(stmt) }
|
||||
func (expr IndexExpr) String() string { return stringify(expr) }
|
||||
func (id Ident) String() string { return stringify(id) }
|
||||
func (spec ValueSpec) String() string { return stringify(spec) }
|
||||
func (decl GenDecl) String() string { return stringify(decl) }
|
||||
func (lit BasicLit) String() string { return stringify(lit) }
|
||||
func (expr BinaryExpr) String() string { return stringify(expr) }
|
||||
func (stmt ForStmt) String() string { return stringify(stmt) }
|
||||
func (stmt RangeStmt) String() string { return stringify(stmt) }
|
||||
func (typ ArrayType) String() string { return stringify(typ) }
|
||||
func (stmt DeferStmt) String() string { return stringify(stmt) }
|
||||
func (typ MapType) String() string { return stringify(typ) }
|
||||
func (stmt ReturnStmt) String() string { return stringify(stmt) }
|
||||
func (expr SliceExpr) String() string { return stringify(expr) }
|
||||
func (expr StarExpr) String() string { return stringify(expr) }
|
||||
func (expr UnaryExpr) String() string { return stringify(expr) }
|
||||
func (stmt SendStmt) String() string { return stringify(stmt) }
|
||||
func (spec ImportSpec) String() string { return stringify(spec) }
|
||||
func (stmt SelectStmt) String() string { return stringify(stmt) }
|
||||
func (stmt IfStmt) String() string { return stringify(stmt) }
|
||||
func (stmt IncDecStmt) String() string { return stringify(stmt) }
|
||||
func (stmt GoStmt) String() string { return stringify(stmt) }
|
||||
func (field Field) String() string { return stringify(field) }
|
||||
func (expr SelectorExpr) String() string { return stringify(expr) }
|
||||
func (typ StructType) String() string { return stringify(typ) }
|
||||
func (expr KeyValueExpr) String() string { return stringify(expr) }
|
||||
func (typ FuncType) String() string { return stringify(typ) }
|
||||
func (lit FuncLit) String() string { return stringify(lit) }
|
||||
func (decl FuncDecl) String() string { return stringify(decl) }
|
||||
func (stmt BranchStmt) String() string { return stringify(stmt) }
|
||||
func (expr CallExpr) String() string { return stringify(expr) }
|
||||
func (clause CaseClause) String() string { return stringify(clause) }
|
||||
func (typ ChanType) String() string { return stringify(typ) }
|
||||
func (clause CommClause) String() string { return stringify(clause) }
|
||||
func (lit CompositeLit) String() string { return stringify(lit) }
|
||||
func (stmt EmptyStmt) String() string { return stringify(stmt) }
|
||||
func (typ InterfaceType) String() string { return stringify(typ) }
|
||||
func (stmt SwitchStmt) String() string { return stringify(stmt) }
|
||||
func (expr TypeAssertExpr) String() string { return stringify(expr) }
|
||||
func (spec TypeSpec) String() string { return stringify(spec) }
|
||||
func (stmt TypeSwitchStmt) String() string { return stringify(stmt) }
|
||||
func (nil Nil) String() string { return "nil" }
|
||||
func (builtin Builtin) String() string { return stringify(builtin) }
|
||||
func (obj Object) String() string { return stringify(obj) }
|
||||
func (fn Function) String() string { return stringify(fn) }
|
||||
func (el Ellipsis) String() string { return stringify(el) }
|
||||
func (not Not) String() string { return stringify(not) }
|
||||
|
||||
func (or Or) String() string {
|
||||
s := "(Or"
|
||||
for _, node := range or.Nodes {
|
||||
s += " "
|
||||
s += node.String()
|
||||
}
|
||||
s += ")"
|
||||
return s
|
||||
}
|
||||
|
||||
func isProperList(l List) bool {
|
||||
if l.Head == nil && l.Tail == nil {
|
||||
return true
|
||||
}
|
||||
switch tail := l.Tail.(type) {
|
||||
case nil:
|
||||
return false
|
||||
case List:
|
||||
return isProperList(tail)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (l List) String() string {
|
||||
if l.Head == nil && l.Tail == nil {
|
||||
return "[]"
|
||||
}
|
||||
|
||||
if isProperList(l) {
|
||||
// pretty-print the list
|
||||
var objs []string
|
||||
for l.Head != nil {
|
||||
objs = append(objs, l.Head.String())
|
||||
l = l.Tail.(List)
|
||||
}
|
||||
return fmt.Sprintf("[%s]", strings.Join(objs, " "))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%s", l.Head, l.Tail)
|
||||
}
|
||||
|
||||
func (bind Binding) String() string {
|
||||
if bind.Node == nil {
|
||||
return bind.Name
|
||||
}
|
||||
return fmt.Sprintf("%s@%s", bind.Name, bind.Node)
|
||||
}
|
||||
|
||||
func (s String) String() string { return fmt.Sprintf("%q", string(s)) }
|
||||
|
||||
func (tok Token) String() string {
|
||||
return fmt.Sprintf("%q", strings.ToUpper(token.Token(tok).String()))
|
||||
}
|
||||
|
||||
func (Any) String() string { return "_" }
|
||||
|
||||
func (AssignStmt) isNode() {}
|
||||
func (IndexExpr) isNode() {}
|
||||
func (Ident) isNode() {}
|
||||
func (ValueSpec) isNode() {}
|
||||
func (GenDecl) isNode() {}
|
||||
func (BasicLit) isNode() {}
|
||||
func (BinaryExpr) isNode() {}
|
||||
func (ForStmt) isNode() {}
|
||||
func (RangeStmt) isNode() {}
|
||||
func (ArrayType) isNode() {}
|
||||
func (DeferStmt) isNode() {}
|
||||
func (MapType) isNode() {}
|
||||
func (ReturnStmt) isNode() {}
|
||||
func (SliceExpr) isNode() {}
|
||||
func (StarExpr) isNode() {}
|
||||
func (UnaryExpr) isNode() {}
|
||||
func (SendStmt) isNode() {}
|
||||
func (ImportSpec) isNode() {}
|
||||
func (SelectStmt) isNode() {}
|
||||
func (IfStmt) isNode() {}
|
||||
func (IncDecStmt) isNode() {}
|
||||
func (GoStmt) isNode() {}
|
||||
func (Field) isNode() {}
|
||||
func (SelectorExpr) isNode() {}
|
||||
func (StructType) isNode() {}
|
||||
func (KeyValueExpr) isNode() {}
|
||||
func (FuncType) isNode() {}
|
||||
func (FuncLit) isNode() {}
|
||||
func (FuncDecl) isNode() {}
|
||||
func (BranchStmt) isNode() {}
|
||||
func (CallExpr) isNode() {}
|
||||
func (CaseClause) isNode() {}
|
||||
func (ChanType) isNode() {}
|
||||
func (CommClause) isNode() {}
|
||||
func (CompositeLit) isNode() {}
|
||||
func (EmptyStmt) isNode() {}
|
||||
func (InterfaceType) isNode() {}
|
||||
func (SwitchStmt) isNode() {}
|
||||
func (TypeAssertExpr) isNode() {}
|
||||
func (TypeSpec) isNode() {}
|
||||
func (TypeSwitchStmt) isNode() {}
|
||||
func (Nil) isNode() {}
|
||||
func (Builtin) isNode() {}
|
||||
func (Object) isNode() {}
|
||||
func (Function) isNode() {}
|
||||
func (Ellipsis) isNode() {}
|
||||
func (Or) isNode() {}
|
||||
func (List) isNode() {}
|
||||
func (String) isNode() {}
|
||||
func (Token) isNode() {}
|
||||
func (Any) isNode() {}
|
||||
func (Binding) isNode() {}
|
||||
func (Not) isNode() {}
|
Reference in New Issue
Block a user