Add staticcheck tool
This commit is contained in:
243
vendor/honnef.co/go/tools/pattern/convert.go
vendored
Normal file
243
vendor/honnef.co/go/tools/pattern/convert.go
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
package pattern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var astTypes = map[string]reflect.Type{
|
||||
"Ellipsis": reflect.TypeFor[ast.Ellipsis](),
|
||||
"RangeStmt": reflect.TypeFor[ast.RangeStmt](),
|
||||
"AssignStmt": reflect.TypeFor[ast.AssignStmt](),
|
||||
"IndexExpr": reflect.TypeFor[ast.IndexExpr](),
|
||||
"IndexListExpr": reflect.TypeFor[ast.IndexListExpr](),
|
||||
"Ident": reflect.TypeFor[ast.Ident](),
|
||||
"ValueSpec": reflect.TypeFor[ast.ValueSpec](),
|
||||
"GenDecl": reflect.TypeFor[ast.GenDecl](),
|
||||
"BinaryExpr": reflect.TypeFor[ast.BinaryExpr](),
|
||||
"ForStmt": reflect.TypeFor[ast.ForStmt](),
|
||||
"ArrayType": reflect.TypeFor[ast.ArrayType](),
|
||||
"DeferStmt": reflect.TypeFor[ast.DeferStmt](),
|
||||
"MapType": reflect.TypeFor[ast.MapType](),
|
||||
"ReturnStmt": reflect.TypeFor[ast.ReturnStmt](),
|
||||
"SliceExpr": reflect.TypeFor[ast.SliceExpr](),
|
||||
"StarExpr": reflect.TypeFor[ast.StarExpr](),
|
||||
"UnaryExpr": reflect.TypeFor[ast.UnaryExpr](),
|
||||
"SendStmt": reflect.TypeFor[ast.SendStmt](),
|
||||
"SelectStmt": reflect.TypeFor[ast.SelectStmt](),
|
||||
"ImportSpec": reflect.TypeFor[ast.ImportSpec](),
|
||||
"IfStmt": reflect.TypeFor[ast.IfStmt](),
|
||||
"GoStmt": reflect.TypeFor[ast.GoStmt](),
|
||||
"Field": reflect.TypeFor[ast.Field](),
|
||||
"SelectorExpr": reflect.TypeFor[ast.SelectorExpr](),
|
||||
"StructType": reflect.TypeFor[ast.StructType](),
|
||||
"KeyValueExpr": reflect.TypeFor[ast.KeyValueExpr](),
|
||||
"FuncType": reflect.TypeFor[ast.FuncType](),
|
||||
"FuncLit": reflect.TypeFor[ast.FuncLit](),
|
||||
"FuncDecl": reflect.TypeFor[ast.FuncDecl](),
|
||||
"ChanType": reflect.TypeFor[ast.ChanType](),
|
||||
"CallExpr": reflect.TypeFor[ast.CallExpr](),
|
||||
"CaseClause": reflect.TypeFor[ast.CaseClause](),
|
||||
"CommClause": reflect.TypeFor[ast.CommClause](),
|
||||
"CompositeLit": reflect.TypeFor[ast.CompositeLit](),
|
||||
"EmptyStmt": reflect.TypeFor[ast.EmptyStmt](),
|
||||
"SwitchStmt": reflect.TypeFor[ast.SwitchStmt](),
|
||||
"TypeSwitchStmt": reflect.TypeFor[ast.TypeSwitchStmt](),
|
||||
"TypeAssertExpr": reflect.TypeFor[ast.TypeAssertExpr](),
|
||||
"TypeSpec": reflect.TypeFor[ast.TypeSpec](),
|
||||
"InterfaceType": reflect.TypeFor[ast.InterfaceType](),
|
||||
"BranchStmt": reflect.TypeFor[ast.BranchStmt](),
|
||||
"IncDecStmt": reflect.TypeFor[ast.IncDecStmt](),
|
||||
"BasicLit": reflect.TypeFor[ast.BasicLit](),
|
||||
}
|
||||
|
||||
func ASTToNode(node any) 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) any {
|
||||
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, Symbol, 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)
|
||||
}
|
||||
272
vendor/honnef.co/go/tools/pattern/doc.go
vendored
Normal file
272
vendor/honnef.co/go/tools/pattern/doc.go
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
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 define 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.
|
||||
|
||||
(Symbol name) matches ast.Idents and ast.SelectorExprs that refer to a symbol 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 (Symbol "fmt.Println") _) // pattern 1
|
||||
(CallExpr (Symbol "(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
|
||||
257
vendor/honnef.co/go/tools/pattern/lexer.go
vendored
Normal file
257
vendor/honnef.co/go/tools/pattern/lexer.go
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
package pattern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"iter"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// lex returns the sequence of tokens in the input.
|
||||
func lex(f *token.File, input string) iter.Seq[item] {
|
||||
return func(yield func(item) bool) {
|
||||
lex := &lexer{
|
||||
f: f,
|
||||
input: input,
|
||||
yield: yield,
|
||||
}
|
||||
lex.run()
|
||||
}
|
||||
}
|
||||
|
||||
// lexer holds the state of a single [lex] iteration.
|
||||
type lexer struct {
|
||||
f *token.File
|
||||
|
||||
input string
|
||||
start int
|
||||
pos int
|
||||
width int
|
||||
|
||||
yield func(item) bool
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *lexer) emitValue(t itemType, value string) bool {
|
||||
ok := l.yield(item{t, value, l.start})
|
||||
l.start = l.pos
|
||||
return ok
|
||||
}
|
||||
|
||||
func (l *lexer) emit(t itemType) bool {
|
||||
ok := l.yield(item{t, l.input[l.start:l.pos], l.start})
|
||||
l.start = l.pos
|
||||
return ok
|
||||
}
|
||||
|
||||
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 == '(':
|
||||
if !l.emit(itemLeftParen) {
|
||||
return nil
|
||||
}
|
||||
case r == ')':
|
||||
if !l.emit(itemRightParen) {
|
||||
return nil
|
||||
}
|
||||
case r == '[':
|
||||
if !l.emit(itemLeftBracket) {
|
||||
return nil
|
||||
}
|
||||
case r == ']':
|
||||
if !l.emit(itemRightBracket) {
|
||||
return nil
|
||||
}
|
||||
case r == '@':
|
||||
if !l.emit(itemAt) {
|
||||
return nil
|
||||
}
|
||||
case r == ':':
|
||||
if !l.emit(itemColon) {
|
||||
return nil
|
||||
}
|
||||
case r == '_':
|
||||
if !l.emit(itemBlank) {
|
||||
return nil
|
||||
}
|
||||
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 ...any) stateFn {
|
||||
// TODO(dh): emit position information in errors
|
||||
_ = l.yield(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 {
|
||||
if !l.emitValue(itemString, string(runes)) {
|
||||
return nil
|
||||
}
|
||||
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()
|
||||
if !l.emit(itemTypeName) {
|
||||
return nil
|
||||
}
|
||||
return lexStart
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func lexVariable(l *lexer) stateFn {
|
||||
l.next()
|
||||
for {
|
||||
if !isAlphaNumeric(l.next()) {
|
||||
l.backup()
|
||||
if !l.emit(itemVariable) {
|
||||
return nil
|
||||
}
|
||||
return lexStart
|
||||
}
|
||||
}
|
||||
}
|
||||
712
vendor/honnef.co/go/tools/pattern/match.go
vendored
Normal file
712
vendor/honnef.co/go/tools/pattern/match.go
vendored
Normal file
@@ -0,0 +1,712 @@
|
||||
package pattern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
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),
|
||||
"BREAK": Token(token.BREAK),
|
||||
"CONTINUE": Token(token.CONTINUE),
|
||||
"GOTO": Token(token.GOTO),
|
||||
"FALLTHROUGH": Token(token.FALLTHROUGH),
|
||||
}
|
||||
|
||||
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 any) bool {
|
||||
if v == nil {
|
||||
return true
|
||||
}
|
||||
if _, ok := v.(Nil); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type matcher interface {
|
||||
Match(*Matcher, any) (any, bool)
|
||||
}
|
||||
|
||||
type State = map[string]any
|
||||
|
||||
type Matcher struct {
|
||||
TypesInfo *types.Info
|
||||
State State
|
||||
|
||||
bindingsMapping []string
|
||||
|
||||
setBindings []uint64
|
||||
}
|
||||
|
||||
func (m *Matcher) set(b Binding, value any) {
|
||||
m.State[b.Name] = value
|
||||
m.setBindings[len(m.setBindings)-1] |= 1 << b.idx
|
||||
}
|
||||
|
||||
func (m *Matcher) push() {
|
||||
m.setBindings = append(m.setBindings, 0)
|
||||
}
|
||||
|
||||
func (m *Matcher) pop() {
|
||||
set := m.setBindings[len(m.setBindings)-1]
|
||||
if set != 0 {
|
||||
for i := 0; i < len(m.bindingsMapping); i++ {
|
||||
if (set & (1 << i)) != 0 {
|
||||
key := m.bindingsMapping[i]
|
||||
delete(m.State, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
m.setBindings = m.setBindings[:len(m.setBindings)-1]
|
||||
}
|
||||
|
||||
func (m *Matcher) merge() {
|
||||
m.setBindings = m.setBindings[:len(m.setBindings)-1]
|
||||
}
|
||||
|
||||
func (m *Matcher) Match(a Pattern, b ast.Node) bool {
|
||||
m.bindingsMapping = a.Bindings
|
||||
m.State = State{}
|
||||
m.push()
|
||||
_, ok := match(m, a.Root, b)
|
||||
m.merge()
|
||||
if len(m.setBindings) != 0 {
|
||||
panic(fmt.Sprintf("%d entries left on the stack, expected none", len(m.setBindings)))
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
func Match(a Pattern, 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 any) (any, 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:
|
||||
if l == nil {
|
||||
return match(m, nil, r)
|
||||
} else {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(dh): the three blocks handling slices can be combined into a single block if we use reflection
|
||||
|
||||
{
|
||||
ln, ok1 := l.([]ast.Expr)
|
||||
rn, ok2 := r.([]ast.Expr)
|
||||
if ok1 || ok2 {
|
||||
if ok1 && !ok2 {
|
||||
cast, ok := r.(ast.Expr)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
rn = []ast.Expr{cast}
|
||||
} else if !ok1 && ok2 {
|
||||
cast, ok := l.(ast.Expr)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
ln = []ast.Expr{cast}
|
||||
}
|
||||
|
||||
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 {
|
||||
cast, ok := r.(ast.Stmt)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
rn = []ast.Stmt{cast}
|
||||
} else if !ok1 && ok2 {
|
||||
cast, ok := l.(ast.Stmt)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
ln = []ast.Stmt{cast}
|
||||
}
|
||||
|
||||
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.Field)
|
||||
rn, ok2 := r.([]*ast.Field)
|
||||
if ok1 || ok2 {
|
||||
if ok1 && !ok2 {
|
||||
cast, ok := r.(*ast.Field)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
rn = []*ast.Field{cast}
|
||||
} else if !ok1 && ok2 {
|
||||
cast, ok := l.(*ast.Field)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
ln = []*ast.Field{cast}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Match a Node with an AST node
|
||||
func matchNodeAST(m *Matcher, a Node, b any) (any, 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.Field:
|
||||
// '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{}
|
||||
case string, token.Token:
|
||||
// 'a' can't be a String, Token, or Binding or we'd be using their Match implementations.
|
||||
return nil, false
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled type %T", b))
|
||||
}
|
||||
}
|
||||
|
||||
// Match two AST nodes
|
||||
func matchAST(m *Matcher, a, b ast.Node) (any, 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.Pointer, 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 any) (any, 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.set(b, new)
|
||||
}
|
||||
return new, ret
|
||||
}
|
||||
|
||||
func (Any) Match(m *Matcher, node any) (any, bool) {
|
||||
return node, true
|
||||
}
|
||||
|
||||
func (l List) Match(m *Matcher, node any) (any, 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 any) (any, 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
|
||||
case types.TypeAndValue:
|
||||
return o, o.Value != nil && o.Value.String() == string(s)
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
func (tok Token) Match(m *Matcher, node any) (any, bool) {
|
||||
o, ok := node.(token.Token)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return o, token.Token(tok) == o
|
||||
}
|
||||
|
||||
func (Nil) Match(m *Matcher, node any) (any, bool) {
|
||||
if isNil(node) {
|
||||
return nil, true
|
||||
}
|
||||
v := reflect.ValueOf(node)
|
||||
switch v.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
|
||||
return nil, v.IsNil()
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
func (builtin Builtin) Match(m *Matcher, node any) (any, bool) {
|
||||
r, ok := match(m, Ident(builtin), node)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
ident := r.(*ast.Ident)
|
||||
obj := m.TypesInfo.ObjectOf(ident)
|
||||
if obj != types.Universe.Lookup(ident.Name) {
|
||||
return nil, false
|
||||
}
|
||||
return ident, true
|
||||
}
|
||||
|
||||
func (obj Object) Match(m *Matcher, node any) (any, bool) {
|
||||
r, ok := match(m, Ident(obj), node)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
ident := r.(*ast.Ident)
|
||||
|
||||
id := m.TypesInfo.ObjectOf(ident)
|
||||
_, ok = match(m, obj.Name, ident.Name)
|
||||
return id, ok
|
||||
}
|
||||
|
||||
func (fn Symbol) Match(m *Matcher, node any) (any, bool) {
|
||||
var name string
|
||||
var obj types.Object
|
||||
|
||||
base := []Node{
|
||||
Ident{Any{}},
|
||||
SelectorExpr{Any{}, Any{}},
|
||||
}
|
||||
p := Or{
|
||||
Nodes: append(base,
|
||||
IndexExpr{Or{Nodes: base}, Any{}},
|
||||
IndexListExpr{Or{Nodes: base}, Any{}})}
|
||||
|
||||
r, ok := match(m, p, node)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
fun := r.(ast.Expr)
|
||||
switch idx := fun.(type) {
|
||||
case *ast.IndexExpr:
|
||||
fun = idx.X
|
||||
case *ast.IndexListExpr:
|
||||
fun = idx.X
|
||||
}
|
||||
|
||||
switch fun := ast.Unparen(fun).(type) {
|
||||
case *ast.Ident:
|
||||
obj = m.TypesInfo.ObjectOf(fun)
|
||||
case *ast.SelectorExpr:
|
||||
obj = m.TypesInfo.ObjectOf(fun.Sel)
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
switch obj := obj.(type) {
|
||||
case *types.Func:
|
||||
// OPT(dh): optimize this similar to code.FuncName
|
||||
name = obj.FullName()
|
||||
case *types.Builtin:
|
||||
name = obj.Name()
|
||||
case *types.TypeName:
|
||||
origObj := obj
|
||||
for {
|
||||
if obj.Parent() != obj.Pkg().Scope() {
|
||||
return nil, false
|
||||
}
|
||||
name = types.TypeString(obj.Type(), nil)
|
||||
_, ok = match(m, fn.Name, name)
|
||||
if ok || !obj.IsAlias() {
|
||||
return origObj, ok
|
||||
} else {
|
||||
// FIXME(dh): we should peel away one layer of alias at a time; this is blocked on
|
||||
// github.com/golang/go/issues/66559
|
||||
switch typ := types.Unalias(obj.Type()).(type) {
|
||||
case interface{ Obj() *types.TypeName }:
|
||||
obj = typ.Obj()
|
||||
case *types.Basic:
|
||||
return match(m, fn.Name, typ.Name())
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
}
|
||||
case *types.Const, *types.Var:
|
||||
if obj.Pkg() == nil {
|
||||
return nil, false
|
||||
}
|
||||
if obj.Parent() != obj.Pkg().Scope() {
|
||||
return nil, false
|
||||
}
|
||||
name = fmt.Sprintf("%s.%s", obj.Pkg().Path(), obj.Name())
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
|
||||
_, ok = match(m, fn.Name, name)
|
||||
return obj, ok
|
||||
}
|
||||
|
||||
func (or Or) Match(m *Matcher, node any) (any, bool) {
|
||||
for _, opt := range or.Nodes {
|
||||
m.push()
|
||||
if ret, ok := match(m, opt, node); ok {
|
||||
m.merge()
|
||||
return ret, true
|
||||
} else {
|
||||
m.pop()
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (not Not) Match(m *Matcher, node any) (any, bool) {
|
||||
_, ok := match(m, not.Node, node)
|
||||
if ok {
|
||||
return nil, false
|
||||
}
|
||||
return node, true
|
||||
}
|
||||
|
||||
var integerLiteralQ = MustParse(`(Or (BasicLit "INT" _) (UnaryExpr (Or "+" "-") (IntegerLiteral _)))`)
|
||||
|
||||
func (lit IntegerLiteral) Match(m *Matcher, node any) (any, bool) {
|
||||
matched, ok := match(m, integerLiteralQ.Root, node)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
tv, ok := m.TypesInfo.Types[matched.(ast.Expr)]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
if tv.Value == nil {
|
||||
return nil, false
|
||||
}
|
||||
_, ok = match(m, lit.Value, tv)
|
||||
return matched, ok
|
||||
}
|
||||
|
||||
func (texpr TrulyConstantExpression) Match(m *Matcher, node any) (any, bool) {
|
||||
expr, ok := node.(ast.Expr)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
tv, ok := m.TypesInfo.Types[expr]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
if tv.Value == nil {
|
||||
return nil, false
|
||||
}
|
||||
truly := true
|
||||
ast.Inspect(expr, func(node ast.Node) bool {
|
||||
if _, ok := node.(*ast.Ident); ok {
|
||||
truly = false
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if !truly {
|
||||
return nil, false
|
||||
}
|
||||
_, ok = match(m, texpr.Value, tv)
|
||||
return expr, ok
|
||||
}
|
||||
|
||||
var (
|
||||
// Types of fields in go/ast structs that we want to skip
|
||||
rtTokPos = reflect.TypeFor[token.Pos]()
|
||||
//lint:ignore SA1019 It's deprecated, but we still want to skip the field.
|
||||
rtObject = reflect.TypeFor[*ast.Object]()
|
||||
rtCommentGroup = reflect.TypeFor[*ast.CommentGroup]()
|
||||
)
|
||||
|
||||
var (
|
||||
_ matcher = Binding{}
|
||||
_ matcher = Any{}
|
||||
_ matcher = List{}
|
||||
_ matcher = String("")
|
||||
_ matcher = Token(0)
|
||||
_ matcher = Nil{}
|
||||
_ matcher = Builtin{}
|
||||
_ matcher = Object{}
|
||||
_ matcher = Symbol{}
|
||||
_ matcher = Or{}
|
||||
_ matcher = Not{}
|
||||
_ matcher = IntegerLiteral{}
|
||||
_ matcher = TrulyConstantExpression{}
|
||||
)
|
||||
714
vendor/honnef.co/go/tools/pattern/parser.go
vendored
Normal file
714
vendor/honnef.co/go/tools/pattern/parser.go
vendored
Normal file
@@ -0,0 +1,714 @@
|
||||
package pattern
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"iter"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Pattern struct {
|
||||
Root Node
|
||||
// EntryNodes contains instances of ast.Node that could potentially
|
||||
// initiate a successful match of the pattern.
|
||||
EntryNodes []ast.Node
|
||||
|
||||
// SymbolsPattern is a pattern consisting or Any, Or, And, and IndexSymbol,
|
||||
// that can be used to implement fast rejection of whole packages using
|
||||
// typeindex.
|
||||
SymbolsPattern Node
|
||||
|
||||
// If non-empty, all possible candidate nodes for this pattern can be found
|
||||
// by finding all call expressions for this list of symbols.
|
||||
RootCallSymbols []IndexSymbol
|
||||
|
||||
// Mapping from binding index to binding name
|
||||
Bindings []string
|
||||
}
|
||||
|
||||
func MustParse(s string) Pattern {
|
||||
p := &Parser{AllowTypeInfo: true}
|
||||
pat, err := p.Parse(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return pat
|
||||
}
|
||||
|
||||
func symbolToIndexSymbol(name string) IndexSymbol {
|
||||
if len(name) == 0 {
|
||||
return IndexSymbol{}
|
||||
}
|
||||
if name[0] == '(' {
|
||||
end := strings.IndexAny(name, ")")
|
||||
// Ensure there's a ), and also that there are at least two more
|
||||
// characters after it, for a dot and an identifier.
|
||||
if end == -1 || end > len(name)-2 {
|
||||
return IndexSymbol{}
|
||||
}
|
||||
pathAndType := strings.TrimPrefix(name[1:end], "*")
|
||||
dot := strings.LastIndex(pathAndType, ".")
|
||||
if dot == -1 {
|
||||
return IndexSymbol{}
|
||||
}
|
||||
path := pathAndType[:dot]
|
||||
typ := pathAndType[dot+1:]
|
||||
ident := name[end+2:]
|
||||
return IndexSymbol{path, typ, ident}
|
||||
} else {
|
||||
dot := strings.LastIndex(name, ".")
|
||||
if dot == -1 {
|
||||
return IndexSymbol{"", "", name}
|
||||
}
|
||||
path := name[:dot]
|
||||
ident := name[dot+1:]
|
||||
return IndexSymbol{path, "", ident}
|
||||
}
|
||||
}
|
||||
|
||||
func collectSymbols(node Node, inSymbol bool) Node {
|
||||
and := func(c Node, out *And) {
|
||||
switch cc := c.(type) {
|
||||
case And:
|
||||
out.Nodes = append(out.Nodes, cc.Nodes...)
|
||||
case Any:
|
||||
case nil:
|
||||
default:
|
||||
out.Nodes = append(out.Nodes, c)
|
||||
}
|
||||
}
|
||||
|
||||
switch node := node.(type) {
|
||||
case Or:
|
||||
s := Or{}
|
||||
for _, el := range node.Nodes {
|
||||
c := collectSymbols(el, inSymbol)
|
||||
switch cc := c.(type) {
|
||||
case Or:
|
||||
s.Nodes = append(s.Nodes, cc.Nodes...)
|
||||
case Any:
|
||||
return Any{}
|
||||
case nil:
|
||||
default:
|
||||
s.Nodes = append(s.Nodes, c)
|
||||
}
|
||||
}
|
||||
switch len(s.Nodes) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return s.Nodes[0]
|
||||
default:
|
||||
return s
|
||||
}
|
||||
case Not, Token, nil:
|
||||
return Any{}
|
||||
case Symbol:
|
||||
return collectSymbols(node.Name, true)
|
||||
case String:
|
||||
if !inSymbol {
|
||||
return Any{}
|
||||
}
|
||||
// In logically correct patterns, all Strings that are children of
|
||||
// Symbols describe the names of symbols.
|
||||
return symbolToIndexSymbol(string(node))
|
||||
case Binding:
|
||||
return collectSymbols(node.Node, inSymbol)
|
||||
case Any:
|
||||
return Any{}
|
||||
case List:
|
||||
var out And
|
||||
and(collectSymbols(node.Head, inSymbol), &out)
|
||||
and(collectSymbols(node.Tail, inSymbol), &out)
|
||||
switch len(out.Nodes) {
|
||||
case 0:
|
||||
return Any{}
|
||||
case 1:
|
||||
return out.Nodes[0]
|
||||
default:
|
||||
return out
|
||||
}
|
||||
default:
|
||||
var out And
|
||||
rv := reflect.ValueOf(node)
|
||||
for i := range rv.NumField() {
|
||||
c := collectSymbols(rv.Field(i).Interface().(Node), inSymbol)
|
||||
and(c, &out)
|
||||
}
|
||||
switch len(out.Nodes) {
|
||||
case 0:
|
||||
return Any{}
|
||||
case 1:
|
||||
return out.Nodes[0]
|
||||
default:
|
||||
return out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func collectRootCallSymbols(node Node) []IndexSymbol {
|
||||
root, ok := node.(CallExpr)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
var names []String
|
||||
var handleSymName func(name Node) bool
|
||||
handleSymName = func(name Node) bool {
|
||||
switch name := name.(type) {
|
||||
case String:
|
||||
names = append(names, name)
|
||||
case Or:
|
||||
for _, node := range name.Nodes {
|
||||
if name, ok := node.(String); ok {
|
||||
names = append(names, name)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
case Binding:
|
||||
return handleSymName(name.Node)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
var handleRootFun func(node Node) bool
|
||||
handleRootFun = func(node Node) bool {
|
||||
switch fun := node.(type) {
|
||||
case Binding:
|
||||
return handleRootFun(fun.Node)
|
||||
case Symbol:
|
||||
return handleSymName(fun.Name)
|
||||
case Or:
|
||||
for _, node := range fun.Nodes {
|
||||
if sym, ok := node.(Symbol); !ok || !handleSymName(sym.Name) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !handleRootFun(root.Fun) {
|
||||
return nil
|
||||
}
|
||||
|
||||
out := make([]IndexSymbol, len(names))
|
||||
for i, name := range names {
|
||||
out[i] = symbolToIndexSymbol(string(name))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func collectEntryNodes(node Node, m map[reflect.Type]struct{}) {
|
||||
switch node := node.(type) {
|
||||
case Or:
|
||||
for _, el := range node.Nodes {
|
||||
collectEntryNodes(el, m)
|
||||
}
|
||||
case Not:
|
||||
collectEntryNodes(node.Node, m)
|
||||
case Binding:
|
||||
collectEntryNodes(node.Node, m)
|
||||
case Nil, nil:
|
||||
// this branch is reached via bindings
|
||||
for _, T := range allTypes {
|
||||
m[T] = struct{}{}
|
||||
}
|
||||
default:
|
||||
Ts, ok := nodeToASTTypes[reflect.TypeOf(node)]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("internal error: unhandled type %T", node))
|
||||
}
|
||||
for _, T := range Ts {
|
||||
m[T] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var allTypes = []reflect.Type{
|
||||
reflect.TypeFor[*ast.RangeStmt](),
|
||||
reflect.TypeFor[*ast.AssignStmt](),
|
||||
reflect.TypeFor[*ast.IndexExpr](),
|
||||
reflect.TypeFor[*ast.Ident](),
|
||||
reflect.TypeFor[*ast.ValueSpec](),
|
||||
reflect.TypeFor[*ast.GenDecl](),
|
||||
reflect.TypeFor[*ast.BinaryExpr](),
|
||||
reflect.TypeFor[*ast.ForStmt](),
|
||||
reflect.TypeFor[*ast.ArrayType](),
|
||||
reflect.TypeFor[*ast.DeferStmt](),
|
||||
reflect.TypeFor[*ast.MapType](),
|
||||
reflect.TypeFor[*ast.ReturnStmt](),
|
||||
reflect.TypeFor[*ast.SliceExpr](),
|
||||
reflect.TypeFor[*ast.StarExpr](),
|
||||
reflect.TypeFor[*ast.UnaryExpr](),
|
||||
reflect.TypeFor[*ast.SendStmt](),
|
||||
reflect.TypeFor[*ast.SelectStmt](),
|
||||
reflect.TypeFor[*ast.ImportSpec](),
|
||||
reflect.TypeFor[*ast.IfStmt](),
|
||||
reflect.TypeFor[*ast.GoStmt](),
|
||||
reflect.TypeFor[*ast.Field](),
|
||||
reflect.TypeFor[*ast.SelectorExpr](),
|
||||
reflect.TypeFor[*ast.StructType](),
|
||||
reflect.TypeFor[*ast.KeyValueExpr](),
|
||||
reflect.TypeFor[*ast.FuncType](),
|
||||
reflect.TypeFor[*ast.FuncLit](),
|
||||
reflect.TypeFor[*ast.FuncDecl](),
|
||||
reflect.TypeFor[*ast.ChanType](),
|
||||
reflect.TypeFor[*ast.CallExpr](),
|
||||
reflect.TypeFor[*ast.CaseClause](),
|
||||
reflect.TypeFor[*ast.CommClause](),
|
||||
reflect.TypeFor[*ast.CompositeLit](),
|
||||
reflect.TypeFor[*ast.EmptyStmt](),
|
||||
reflect.TypeFor[*ast.SwitchStmt](),
|
||||
reflect.TypeFor[*ast.TypeSwitchStmt](),
|
||||
reflect.TypeFor[*ast.TypeAssertExpr](),
|
||||
reflect.TypeFor[*ast.TypeSpec](),
|
||||
reflect.TypeFor[*ast.InterfaceType](),
|
||||
reflect.TypeFor[*ast.BranchStmt](),
|
||||
reflect.TypeFor[*ast.IncDecStmt](),
|
||||
reflect.TypeFor[*ast.BasicLit](),
|
||||
}
|
||||
|
||||
var nodeToASTTypes = map[reflect.Type][]reflect.Type{
|
||||
reflect.TypeFor[String](): nil,
|
||||
reflect.TypeFor[Token](): nil,
|
||||
reflect.TypeFor[List](): {reflect.TypeFor[*ast.BlockStmt](), reflect.TypeFor[*ast.FieldList]()},
|
||||
reflect.TypeFor[Builtin](): {reflect.TypeFor[*ast.Ident]()},
|
||||
reflect.TypeFor[Object](): {reflect.TypeFor[*ast.Ident]()},
|
||||
reflect.TypeFor[Symbol](): {reflect.TypeFor[*ast.Ident](), reflect.TypeFor[*ast.SelectorExpr]()},
|
||||
reflect.TypeFor[Any](): allTypes,
|
||||
reflect.TypeFor[RangeStmt](): {reflect.TypeFor[*ast.RangeStmt]()},
|
||||
reflect.TypeFor[AssignStmt](): {reflect.TypeFor[*ast.AssignStmt]()},
|
||||
reflect.TypeFor[IndexExpr](): {reflect.TypeFor[*ast.IndexExpr]()},
|
||||
reflect.TypeFor[Ident](): {reflect.TypeFor[*ast.Ident]()},
|
||||
reflect.TypeFor[ValueSpec](): {reflect.TypeFor[*ast.ValueSpec]()},
|
||||
reflect.TypeFor[GenDecl](): {reflect.TypeFor[*ast.GenDecl]()},
|
||||
reflect.TypeFor[BinaryExpr](): {reflect.TypeFor[*ast.BinaryExpr]()},
|
||||
reflect.TypeFor[ForStmt](): {reflect.TypeFor[*ast.ForStmt]()},
|
||||
reflect.TypeFor[ArrayType](): {reflect.TypeFor[*ast.ArrayType]()},
|
||||
reflect.TypeFor[DeferStmt](): {reflect.TypeFor[*ast.DeferStmt]()},
|
||||
reflect.TypeFor[MapType](): {reflect.TypeFor[*ast.MapType]()},
|
||||
reflect.TypeFor[ReturnStmt](): {reflect.TypeFor[*ast.ReturnStmt]()},
|
||||
reflect.TypeFor[SliceExpr](): {reflect.TypeFor[*ast.SliceExpr]()},
|
||||
reflect.TypeFor[StarExpr](): {reflect.TypeFor[*ast.StarExpr]()},
|
||||
reflect.TypeFor[UnaryExpr](): {reflect.TypeFor[*ast.UnaryExpr]()},
|
||||
reflect.TypeFor[SendStmt](): {reflect.TypeFor[*ast.SendStmt]()},
|
||||
reflect.TypeFor[SelectStmt](): {reflect.TypeFor[*ast.SelectStmt]()},
|
||||
reflect.TypeFor[ImportSpec](): {reflect.TypeFor[*ast.ImportSpec]()},
|
||||
reflect.TypeFor[IfStmt](): {reflect.TypeFor[*ast.IfStmt]()},
|
||||
reflect.TypeFor[GoStmt](): {reflect.TypeFor[*ast.GoStmt]()},
|
||||
reflect.TypeFor[Field](): {reflect.TypeFor[*ast.Field]()},
|
||||
reflect.TypeFor[SelectorExpr](): {reflect.TypeFor[*ast.SelectorExpr]()},
|
||||
reflect.TypeFor[StructType](): {reflect.TypeFor[*ast.StructType]()},
|
||||
reflect.TypeFor[KeyValueExpr](): {reflect.TypeFor[*ast.KeyValueExpr]()},
|
||||
reflect.TypeFor[FuncType](): {reflect.TypeFor[*ast.FuncType]()},
|
||||
reflect.TypeFor[FuncLit](): {reflect.TypeFor[*ast.FuncLit]()},
|
||||
reflect.TypeFor[FuncDecl](): {reflect.TypeFor[*ast.FuncDecl]()},
|
||||
reflect.TypeFor[ChanType](): {reflect.TypeFor[*ast.ChanType]()},
|
||||
reflect.TypeFor[CallExpr](): {reflect.TypeFor[*ast.CallExpr]()},
|
||||
reflect.TypeFor[CaseClause](): {reflect.TypeFor[*ast.CaseClause]()},
|
||||
reflect.TypeFor[CommClause](): {reflect.TypeFor[*ast.CommClause]()},
|
||||
reflect.TypeFor[CompositeLit](): {reflect.TypeFor[*ast.CompositeLit]()},
|
||||
reflect.TypeFor[EmptyStmt](): {reflect.TypeFor[*ast.EmptyStmt]()},
|
||||
reflect.TypeFor[SwitchStmt](): {reflect.TypeFor[*ast.SwitchStmt]()},
|
||||
reflect.TypeFor[TypeSwitchStmt](): {reflect.TypeFor[*ast.TypeSwitchStmt]()},
|
||||
reflect.TypeFor[TypeAssertExpr](): {reflect.TypeFor[*ast.TypeAssertExpr]()},
|
||||
reflect.TypeFor[TypeSpec](): {reflect.TypeFor[*ast.TypeSpec]()},
|
||||
reflect.TypeFor[InterfaceType](): {reflect.TypeFor[*ast.InterfaceType]()},
|
||||
reflect.TypeFor[BranchStmt](): {reflect.TypeFor[*ast.BranchStmt]()},
|
||||
reflect.TypeFor[IncDecStmt](): {reflect.TypeFor[*ast.IncDecStmt]()},
|
||||
reflect.TypeFor[BasicLit](): {reflect.TypeFor[*ast.BasicLit]()},
|
||||
reflect.TypeFor[IntegerLiteral](): {reflect.TypeFor[*ast.BasicLit](), reflect.TypeFor[*ast.UnaryExpr]()},
|
||||
reflect.TypeFor[TrulyConstantExpression](): allTypes, // this is an over-approximation, which is fine
|
||||
}
|
||||
|
||||
var requiresTypeInfo = map[string]bool{
|
||||
"Symbol": true,
|
||||
"Builtin": true,
|
||||
"Object": true,
|
||||
"IntegerLiteral": true,
|
||||
"TrulyConstantExpression": true,
|
||||
}
|
||||
|
||||
type Parser struct {
|
||||
// Allow nodes that rely on type information
|
||||
AllowTypeInfo bool
|
||||
|
||||
f *token.File
|
||||
cur item
|
||||
last *item
|
||||
nextItem func() (item, bool)
|
||||
|
||||
bindings map[string]int
|
||||
}
|
||||
|
||||
func (p *Parser) bindingIndex(name string) int {
|
||||
if p.bindings == nil {
|
||||
p.bindings = map[string]int{}
|
||||
}
|
||||
if idx, ok := p.bindings[name]; ok {
|
||||
return idx
|
||||
}
|
||||
idx := len(p.bindings)
|
||||
p.bindings[name] = idx
|
||||
return idx
|
||||
}
|
||||
|
||||
func (p *Parser) Parse(s string) (Pattern, error) {
|
||||
f := token.NewFileSet().AddFile("<input>", -1, len(s))
|
||||
|
||||
// Run the lexer iterator as a coroutine.
|
||||
// The parser will call 'next' to consume each item.
|
||||
// After the parser returns, we must call 'stop' to
|
||||
// terminate the coroutine.
|
||||
next, stop := iter.Pull(lex(f, s))
|
||||
defer stop()
|
||||
|
||||
p.cur = item{}
|
||||
p.last = nil
|
||||
p.f = f
|
||||
p.nextItem = next
|
||||
|
||||
// Parse.
|
||||
root, err := p.node()
|
||||
if err != nil {
|
||||
return Pattern{}, err
|
||||
}
|
||||
// Consume final EOF token.
|
||||
if item, ok := next(); !ok || item.typ != itemEOF {
|
||||
return Pattern{}, fmt.Errorf("unexpected token %s after end of pattern", item.typ)
|
||||
}
|
||||
|
||||
if len(p.bindings) > 64 {
|
||||
return Pattern{}, errors.New("encountered more than 64 bindings")
|
||||
}
|
||||
|
||||
bindings := make([]string, len(p.bindings))
|
||||
for name, idx := range p.bindings {
|
||||
bindings[idx] = name
|
||||
}
|
||||
|
||||
_, isSymbol := root.(Symbol)
|
||||
sym := collectSymbols(root, isSymbol)
|
||||
rootSyms := collectRootCallSymbols(root)
|
||||
relevantMap := map[reflect.Type]struct{}{}
|
||||
collectEntryNodes(root, relevantMap)
|
||||
relevantNodes := make([]ast.Node, 0, len(relevantMap))
|
||||
for k := range relevantMap {
|
||||
relevantNodes = append(relevantNodes, reflect.Zero(k).Interface().(ast.Node))
|
||||
}
|
||||
return Pattern{
|
||||
Root: root,
|
||||
EntryNodes: relevantNodes,
|
||||
SymbolsPattern: sym,
|
||||
RootCallSymbols: rootSyms,
|
||||
Bindings: bindings,
|
||||
}, 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.nextItem()
|
||||
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.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)
|
||||
}
|
||||
}
|
||||
|
||||
node, err := p.populateNode(typ.val, objs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if node, ok := node.(Binding); ok {
|
||||
node.idx = p.bindingIndex(node.Name)
|
||||
}
|
||||
return node, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
n := -1
|
||||
for i := 0; i < T.NumField(); i++ {
|
||||
if !T.Field(i).IsExported() {
|
||||
break
|
||||
}
|
||||
n = i
|
||||
}
|
||||
|
||||
if len(objs) != n+1 {
|
||||
return nil, fmt.Errorf("tried to initialize node %s with %d values, expected %d", typ, len(objs), n+1)
|
||||
}
|
||||
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
if !T.Field(i).IsExported() {
|
||||
break
|
||||
}
|
||||
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.TypeFor[Any](),
|
||||
"Ellipsis": reflect.TypeFor[Ellipsis](),
|
||||
"List": reflect.TypeFor[List](),
|
||||
"Binding": reflect.TypeFor[Binding](),
|
||||
"RangeStmt": reflect.TypeFor[RangeStmt](),
|
||||
"AssignStmt": reflect.TypeFor[AssignStmt](),
|
||||
"IndexExpr": reflect.TypeFor[IndexExpr](),
|
||||
"Ident": reflect.TypeFor[Ident](),
|
||||
"Builtin": reflect.TypeFor[Builtin](),
|
||||
"ValueSpec": reflect.TypeFor[ValueSpec](),
|
||||
"GenDecl": reflect.TypeFor[GenDecl](),
|
||||
"BinaryExpr": reflect.TypeFor[BinaryExpr](),
|
||||
"ForStmt": reflect.TypeFor[ForStmt](),
|
||||
"ArrayType": reflect.TypeFor[ArrayType](),
|
||||
"DeferStmt": reflect.TypeFor[DeferStmt](),
|
||||
"MapType": reflect.TypeFor[MapType](),
|
||||
"ReturnStmt": reflect.TypeFor[ReturnStmt](),
|
||||
"SliceExpr": reflect.TypeFor[SliceExpr](),
|
||||
"StarExpr": reflect.TypeFor[StarExpr](),
|
||||
"UnaryExpr": reflect.TypeFor[UnaryExpr](),
|
||||
"SendStmt": reflect.TypeFor[SendStmt](),
|
||||
"SelectStmt": reflect.TypeFor[SelectStmt](),
|
||||
"ImportSpec": reflect.TypeFor[ImportSpec](),
|
||||
"IfStmt": reflect.TypeFor[IfStmt](),
|
||||
"GoStmt": reflect.TypeFor[GoStmt](),
|
||||
"Field": reflect.TypeFor[Field](),
|
||||
"SelectorExpr": reflect.TypeFor[SelectorExpr](),
|
||||
"StructType": reflect.TypeFor[StructType](),
|
||||
"KeyValueExpr": reflect.TypeFor[KeyValueExpr](),
|
||||
"FuncType": reflect.TypeFor[FuncType](),
|
||||
"FuncLit": reflect.TypeFor[FuncLit](),
|
||||
"FuncDecl": reflect.TypeFor[FuncDecl](),
|
||||
"ChanType": reflect.TypeFor[ChanType](),
|
||||
"CallExpr": reflect.TypeFor[CallExpr](),
|
||||
"CaseClause": reflect.TypeFor[CaseClause](),
|
||||
"CommClause": reflect.TypeFor[CommClause](),
|
||||
"CompositeLit": reflect.TypeFor[CompositeLit](),
|
||||
"EmptyStmt": reflect.TypeFor[EmptyStmt](),
|
||||
"SwitchStmt": reflect.TypeFor[SwitchStmt](),
|
||||
"TypeSwitchStmt": reflect.TypeFor[TypeSwitchStmt](),
|
||||
"TypeAssertExpr": reflect.TypeFor[TypeAssertExpr](),
|
||||
"TypeSpec": reflect.TypeFor[TypeSpec](),
|
||||
"InterfaceType": reflect.TypeFor[InterfaceType](),
|
||||
"BranchStmt": reflect.TypeFor[BranchStmt](),
|
||||
"IncDecStmt": reflect.TypeFor[IncDecStmt](),
|
||||
"BasicLit": reflect.TypeFor[BasicLit](),
|
||||
"Object": reflect.TypeFor[Object](),
|
||||
"Symbol": reflect.TypeFor[Symbol](),
|
||||
"Or": reflect.TypeFor[Or](),
|
||||
"Not": reflect.TypeFor[Not](),
|
||||
"IntegerLiteral": reflect.TypeFor[IntegerLiteral](),
|
||||
"TrulyConstantExpression": reflect.TypeFor[TrulyConstantExpression](),
|
||||
}
|
||||
|
||||
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,
|
||||
idx: p.bindingIndex(v.val),
|
||||
}
|
||||
} else {
|
||||
p.rewind()
|
||||
b = Binding{
|
||||
Name: v.val,
|
||||
idx: p.bindingIndex(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:
|
||||
if p.peek().typ == itemColon {
|
||||
p.next()
|
||||
tail, err := p.object()
|
||||
if err != nil {
|
||||
return Any{}, err
|
||||
}
|
||||
return List{Head: Any{}, Tail: tail}, nil
|
||||
}
|
||||
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
|
||||
*/
|
||||
551
vendor/honnef.co/go/tools/pattern/pattern.go
vendored
Normal file
551
vendor/honnef.co/go/tools/pattern/pattern.go
vendored
Normal file
@@ -0,0 +1,551 @@
|
||||
package pattern
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Node = Ellipsis{}
|
||||
_ Node = Binding{}
|
||||
_ Node = RangeStmt{}
|
||||
_ Node = AssignStmt{}
|
||||
_ Node = IndexExpr{}
|
||||
_ Node = IndexListExpr{}
|
||||
_ 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 = Symbol{}
|
||||
_ Node = Not{}
|
||||
_ Node = Or{}
|
||||
_ Node = IntegerLiteral{}
|
||||
_ Node = TrulyConstantExpression{}
|
||||
)
|
||||
|
||||
type Symbol 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
|
||||
|
||||
idx int
|
||||
}
|
||||
|
||||
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 IndexListExpr struct {
|
||||
X Node
|
||||
Indices 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
|
||||
}
|
||||
|
||||
// An IntegerLiteral is a constant expression made up of only integer basic literals and the "+" and "-" unary operators.
|
||||
// That is, 0, -4, -+42 are all integer literals, but 1 + 2 is not.
|
||||
type IntegerLiteral struct {
|
||||
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 And struct {
|
||||
Nodes []Node
|
||||
}
|
||||
|
||||
type Not struct {
|
||||
Node Node
|
||||
}
|
||||
|
||||
// A TrulyConstantExpression is a constant expression that does not make use of any identifiers.
|
||||
// It is constant even under varying build tags.
|
||||
type TrulyConstantExpression struct {
|
||||
Value Node
|
||||
}
|
||||
|
||||
type IndexSymbol struct {
|
||||
Path string
|
||||
Type string
|
||||
Ident string
|
||||
}
|
||||
|
||||
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++ {
|
||||
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 (expr IndexListExpr) 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 Symbol) String() string { return stringify(fn) }
|
||||
func (el Ellipsis) String() string { return stringify(el) }
|
||||
func (not Not) String() string { return stringify(not) }
|
||||
func (lit IntegerLiteral) String() string { return stringify(lit) }
|
||||
func (expr TrulyConstantExpression) String() string { return stringify(expr) }
|
||||
func (sym IndexSymbol) String() string {
|
||||
return fmt.Sprintf("(IndexSymbol %q %q %q)", sym.Path, sym.Type, sym.Ident)
|
||||
}
|
||||
|
||||
func (or Or) String() string {
|
||||
var s strings.Builder
|
||||
s.WriteString("(Or")
|
||||
for _, node := range or.Nodes {
|
||||
s.WriteString(" ")
|
||||
s.WriteString(node.String())
|
||||
}
|
||||
s.WriteString(")")
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func (and And) String() string {
|
||||
var s strings.Builder
|
||||
s.WriteString("(And")
|
||||
for _, node := range and.Nodes {
|
||||
s.WriteString(" ")
|
||||
s.WriteString(node.String())
|
||||
}
|
||||
s.WriteString(")")
|
||||
return s.String()
|
||||
}
|
||||
|
||||
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 (IndexListExpr) 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 (Symbol) isNode() {}
|
||||
func (Ellipsis) isNode() {}
|
||||
func (Or) isNode() {}
|
||||
func (And) isNode() {}
|
||||
func (List) isNode() {}
|
||||
func (String) isNode() {}
|
||||
func (Token) isNode() {}
|
||||
func (Any) isNode() {}
|
||||
func (Binding) isNode() {}
|
||||
func (Not) isNode() {}
|
||||
func (IntegerLiteral) isNode() {}
|
||||
func (TrulyConstantExpression) isNode() {}
|
||||
func (IndexSymbol) isNode() {}
|
||||
Reference in New Issue
Block a user