Files
odidere/vendor/honnef.co/go/tools/pattern/parser.go

715 lines
20 KiB
Go
Raw Normal View History

2026-06-06 01:17:20 +00:00
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
*/