src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

match.go (15531B)


      1 package pattern
      2 
      3 import (
      4 	"fmt"
      5 	"go/ast"
      6 	"go/token"
      7 	"go/types"
      8 	"reflect"
      9 )
     10 
     11 var tokensByString = map[string]Token{
     12 	"INT":         Token(token.INT),
     13 	"FLOAT":       Token(token.FLOAT),
     14 	"IMAG":        Token(token.IMAG),
     15 	"CHAR":        Token(token.CHAR),
     16 	"STRING":      Token(token.STRING),
     17 	"+":           Token(token.ADD),
     18 	"-":           Token(token.SUB),
     19 	"*":           Token(token.MUL),
     20 	"/":           Token(token.QUO),
     21 	"%":           Token(token.REM),
     22 	"&":           Token(token.AND),
     23 	"|":           Token(token.OR),
     24 	"^":           Token(token.XOR),
     25 	"<<":          Token(token.SHL),
     26 	">>":          Token(token.SHR),
     27 	"&^":          Token(token.AND_NOT),
     28 	"+=":          Token(token.ADD_ASSIGN),
     29 	"-=":          Token(token.SUB_ASSIGN),
     30 	"*=":          Token(token.MUL_ASSIGN),
     31 	"/=":          Token(token.QUO_ASSIGN),
     32 	"%=":          Token(token.REM_ASSIGN),
     33 	"&=":          Token(token.AND_ASSIGN),
     34 	"|=":          Token(token.OR_ASSIGN),
     35 	"^=":          Token(token.XOR_ASSIGN),
     36 	"<<=":         Token(token.SHL_ASSIGN),
     37 	">>=":         Token(token.SHR_ASSIGN),
     38 	"&^=":         Token(token.AND_NOT_ASSIGN),
     39 	"&&":          Token(token.LAND),
     40 	"||":          Token(token.LOR),
     41 	"<-":          Token(token.ARROW),
     42 	"++":          Token(token.INC),
     43 	"--":          Token(token.DEC),
     44 	"==":          Token(token.EQL),
     45 	"<":           Token(token.LSS),
     46 	">":           Token(token.GTR),
     47 	"=":           Token(token.ASSIGN),
     48 	"!":           Token(token.NOT),
     49 	"!=":          Token(token.NEQ),
     50 	"<=":          Token(token.LEQ),
     51 	">=":          Token(token.GEQ),
     52 	":=":          Token(token.DEFINE),
     53 	"...":         Token(token.ELLIPSIS),
     54 	"IMPORT":      Token(token.IMPORT),
     55 	"VAR":         Token(token.VAR),
     56 	"TYPE":        Token(token.TYPE),
     57 	"CONST":       Token(token.CONST),
     58 	"BREAK":       Token(token.BREAK),
     59 	"CONTINUE":    Token(token.CONTINUE),
     60 	"GOTO":        Token(token.GOTO),
     61 	"FALLTHROUGH": Token(token.FALLTHROUGH),
     62 }
     63 
     64 func maybeToken(node Node) (Node, bool) {
     65 	if node, ok := node.(String); ok {
     66 		if tok, ok := tokensByString[string(node)]; ok {
     67 			return tok, true
     68 		}
     69 		return node, false
     70 	}
     71 	return node, false
     72 }
     73 
     74 func isNil(v any) bool {
     75 	if v == nil {
     76 		return true
     77 	}
     78 	if _, ok := v.(Nil); ok {
     79 		return true
     80 	}
     81 	return false
     82 }
     83 
     84 type matcher interface {
     85 	Match(*Matcher, any) (any, bool)
     86 }
     87 
     88 type State = map[string]any
     89 
     90 type Matcher struct {
     91 	TypesInfo *types.Info
     92 	State     State
     93 
     94 	bindingsMapping []string
     95 
     96 	setBindings []uint64
     97 }
     98 
     99 func (m *Matcher) set(b Binding, value any) {
    100 	m.State[b.Name] = value
    101 	m.setBindings[len(m.setBindings)-1] |= 1 << b.idx
    102 }
    103 
    104 func (m *Matcher) push() {
    105 	m.setBindings = append(m.setBindings, 0)
    106 }
    107 
    108 func (m *Matcher) pop() {
    109 	set := m.setBindings[len(m.setBindings)-1]
    110 	if set != 0 {
    111 		for i := 0; i < len(m.bindingsMapping); i++ {
    112 			if (set & (1 << i)) != 0 {
    113 				key := m.bindingsMapping[i]
    114 				delete(m.State, key)
    115 			}
    116 		}
    117 	}
    118 	m.setBindings = m.setBindings[:len(m.setBindings)-1]
    119 }
    120 
    121 func (m *Matcher) merge() {
    122 	m.setBindings = m.setBindings[:len(m.setBindings)-1]
    123 }
    124 
    125 func (m *Matcher) Match(a Pattern, b ast.Node) bool {
    126 	m.bindingsMapping = a.Bindings
    127 	m.State = State{}
    128 	m.push()
    129 	_, ok := match(m, a.Root, b)
    130 	m.merge()
    131 	if len(m.setBindings) != 0 {
    132 		panic(fmt.Sprintf("%d entries left on the stack, expected none", len(m.setBindings)))
    133 	}
    134 	return ok
    135 }
    136 
    137 func Match(a Pattern, b ast.Node) (*Matcher, bool) {
    138 	m := &Matcher{}
    139 	ret := m.Match(a, b)
    140 	return m, ret
    141 }
    142 
    143 // Match two items, which may be (Node, AST) or (AST, AST)
    144 func match(m *Matcher, l, r any) (any, bool) {
    145 	if _, ok := r.(Node); ok {
    146 		panic("Node mustn't be on right side of match")
    147 	}
    148 
    149 	switch l := l.(type) {
    150 	case *ast.ParenExpr:
    151 		return match(m, l.X, r)
    152 	case *ast.ExprStmt:
    153 		return match(m, l.X, r)
    154 	case *ast.DeclStmt:
    155 		return match(m, l.Decl, r)
    156 	case *ast.LabeledStmt:
    157 		return match(m, l.Stmt, r)
    158 	case *ast.BlockStmt:
    159 		return match(m, l.List, r)
    160 	case *ast.FieldList:
    161 		if l == nil {
    162 			return match(m, nil, r)
    163 		} else {
    164 			return match(m, l.List, r)
    165 		}
    166 	}
    167 
    168 	switch r := r.(type) {
    169 	case *ast.ParenExpr:
    170 		return match(m, l, r.X)
    171 	case *ast.ExprStmt:
    172 		return match(m, l, r.X)
    173 	case *ast.DeclStmt:
    174 		return match(m, l, r.Decl)
    175 	case *ast.LabeledStmt:
    176 		return match(m, l, r.Stmt)
    177 	case *ast.BlockStmt:
    178 		if r == nil {
    179 			return match(m, l, nil)
    180 		}
    181 		return match(m, l, r.List)
    182 	case *ast.FieldList:
    183 		if r == nil {
    184 			return match(m, l, nil)
    185 		}
    186 		return match(m, l, r.List)
    187 	case *ast.BasicLit:
    188 		if r == nil {
    189 			return match(m, l, nil)
    190 		}
    191 	}
    192 
    193 	if l, ok := l.(matcher); ok {
    194 		return l.Match(m, r)
    195 	}
    196 
    197 	if l, ok := l.(Node); ok {
    198 		// Matching of pattern with concrete value
    199 		return matchNodeAST(m, l, r)
    200 	}
    201 
    202 	if l == nil || r == nil {
    203 		return nil, l == r
    204 	}
    205 
    206 	{
    207 		ln, ok1 := l.(ast.Node)
    208 		rn, ok2 := r.(ast.Node)
    209 		if ok1 && ok2 {
    210 			return matchAST(m, ln, rn)
    211 		}
    212 	}
    213 
    214 	{
    215 		obj, ok := l.(types.Object)
    216 		if ok {
    217 			switch r := r.(type) {
    218 			case *ast.Ident:
    219 				return obj, obj == m.TypesInfo.ObjectOf(r)
    220 			case *ast.SelectorExpr:
    221 				return obj, obj == m.TypesInfo.ObjectOf(r.Sel)
    222 			default:
    223 				return obj, false
    224 			}
    225 		}
    226 	}
    227 
    228 	// TODO(dh): the three blocks handling slices can be combined into a single block if we use reflection
    229 
    230 	{
    231 		ln, ok1 := l.([]ast.Expr)
    232 		rn, ok2 := r.([]ast.Expr)
    233 		if ok1 || ok2 {
    234 			if ok1 && !ok2 {
    235 				cast, ok := r.(ast.Expr)
    236 				if !ok {
    237 					return nil, false
    238 				}
    239 				rn = []ast.Expr{cast}
    240 			} else if !ok1 && ok2 {
    241 				cast, ok := l.(ast.Expr)
    242 				if !ok {
    243 					return nil, false
    244 				}
    245 				ln = []ast.Expr{cast}
    246 			}
    247 
    248 			if len(ln) != len(rn) {
    249 				return nil, false
    250 			}
    251 			for i, ll := range ln {
    252 				if _, ok := match(m, ll, rn[i]); !ok {
    253 					return nil, false
    254 				}
    255 			}
    256 			return r, true
    257 		}
    258 	}
    259 
    260 	{
    261 		ln, ok1 := l.([]ast.Stmt)
    262 		rn, ok2 := r.([]ast.Stmt)
    263 		if ok1 || ok2 {
    264 			if ok1 && !ok2 {
    265 				cast, ok := r.(ast.Stmt)
    266 				if !ok {
    267 					return nil, false
    268 				}
    269 				rn = []ast.Stmt{cast}
    270 			} else if !ok1 && ok2 {
    271 				cast, ok := l.(ast.Stmt)
    272 				if !ok {
    273 					return nil, false
    274 				}
    275 				ln = []ast.Stmt{cast}
    276 			}
    277 
    278 			if len(ln) != len(rn) {
    279 				return nil, false
    280 			}
    281 			for i, ll := range ln {
    282 				if _, ok := match(m, ll, rn[i]); !ok {
    283 					return nil, false
    284 				}
    285 			}
    286 			return r, true
    287 		}
    288 	}
    289 
    290 	{
    291 		ln, ok1 := l.([]*ast.Field)
    292 		rn, ok2 := r.([]*ast.Field)
    293 		if ok1 || ok2 {
    294 			if ok1 && !ok2 {
    295 				cast, ok := r.(*ast.Field)
    296 				if !ok {
    297 					return nil, false
    298 				}
    299 				rn = []*ast.Field{cast}
    300 			} else if !ok1 && ok2 {
    301 				cast, ok := l.(*ast.Field)
    302 				if !ok {
    303 					return nil, false
    304 				}
    305 				ln = []*ast.Field{cast}
    306 			}
    307 
    308 			if len(ln) != len(rn) {
    309 				return nil, false
    310 			}
    311 			for i, ll := range ln {
    312 				if _, ok := match(m, ll, rn[i]); !ok {
    313 					return nil, false
    314 				}
    315 			}
    316 			return r, true
    317 		}
    318 	}
    319 
    320 	return nil, false
    321 }
    322 
    323 // Match a Node with an AST node
    324 func matchNodeAST(m *Matcher, a Node, b any) (any, bool) {
    325 	switch b := b.(type) {
    326 	case []ast.Stmt:
    327 		// 'a' is not a List or we'd be using its Match
    328 		// implementation.
    329 
    330 		if len(b) != 1 {
    331 			return nil, false
    332 		}
    333 		return match(m, a, b[0])
    334 	case []ast.Expr:
    335 		// 'a' is not a List or we'd be using its Match
    336 		// implementation.
    337 
    338 		if len(b) != 1 {
    339 			return nil, false
    340 		}
    341 		return match(m, a, b[0])
    342 	case []*ast.Field:
    343 		// 'a' is not a List or we'd be using its Match
    344 		// implementation
    345 		if len(b) != 1 {
    346 			return nil, false
    347 		}
    348 		return match(m, a, b[0])
    349 	case ast.Node:
    350 		ra := reflect.ValueOf(a)
    351 		rb := reflect.ValueOf(b).Elem()
    352 
    353 		if ra.Type().Name() != rb.Type().Name() {
    354 			return nil, false
    355 		}
    356 
    357 		for i := 0; i < ra.NumField(); i++ {
    358 			af := ra.Field(i)
    359 			fieldName := ra.Type().Field(i).Name
    360 			bf := rb.FieldByName(fieldName)
    361 			if (bf == reflect.Value{}) {
    362 				panic(fmt.Sprintf("internal error: could not find field %s in type %t when comparing with %T", fieldName, b, a))
    363 			}
    364 			ai := af.Interface()
    365 			bi := bf.Interface()
    366 			if ai == nil {
    367 				return b, bi == nil
    368 			}
    369 			if _, ok := match(m, ai.(Node), bi); !ok {
    370 				return b, false
    371 			}
    372 		}
    373 		return b, true
    374 	case nil:
    375 		return nil, a == Nil{}
    376 	case string, token.Token:
    377 		// 'a' can't be a String, Token, or Binding or we'd be using their Match implementations.
    378 		return nil, false
    379 	default:
    380 		panic(fmt.Sprintf("unhandled type %T", b))
    381 	}
    382 }
    383 
    384 // Match two AST nodes
    385 func matchAST(m *Matcher, a, b ast.Node) (any, bool) {
    386 	ra := reflect.ValueOf(a)
    387 	rb := reflect.ValueOf(b)
    388 
    389 	if ra.Type() != rb.Type() {
    390 		return nil, false
    391 	}
    392 	if ra.IsNil() || rb.IsNil() {
    393 		return rb, ra.IsNil() == rb.IsNil()
    394 	}
    395 
    396 	ra = ra.Elem()
    397 	rb = rb.Elem()
    398 	for i := 0; i < ra.NumField(); i++ {
    399 		af := ra.Field(i)
    400 		bf := rb.Field(i)
    401 		if af.Type() == rtTokPos || af.Type() == rtObject || af.Type() == rtCommentGroup {
    402 			continue
    403 		}
    404 
    405 		switch af.Kind() {
    406 		case reflect.Slice:
    407 			if af.Len() != bf.Len() {
    408 				return nil, false
    409 			}
    410 			for j := 0; j < af.Len(); j++ {
    411 				if _, ok := match(m, af.Index(j).Interface().(ast.Node), bf.Index(j).Interface().(ast.Node)); !ok {
    412 					return nil, false
    413 				}
    414 			}
    415 		case reflect.String:
    416 			if af.String() != bf.String() {
    417 				return nil, false
    418 			}
    419 		case reflect.Int:
    420 			if af.Int() != bf.Int() {
    421 				return nil, false
    422 			}
    423 		case reflect.Bool:
    424 			if af.Bool() != bf.Bool() {
    425 				return nil, false
    426 			}
    427 		case reflect.Pointer, reflect.Interface:
    428 			if _, ok := match(m, af.Interface(), bf.Interface()); !ok {
    429 				return nil, false
    430 			}
    431 		default:
    432 			panic(fmt.Sprintf("internal error: unhandled kind %s (%T)", af.Kind(), af.Interface()))
    433 		}
    434 	}
    435 	return b, true
    436 }
    437 
    438 func (b Binding) Match(m *Matcher, node any) (any, bool) {
    439 	if isNil(b.Node) {
    440 		v, ok := m.State[b.Name]
    441 		if ok {
    442 			// Recall value
    443 			return match(m, v, node)
    444 		}
    445 		// Matching anything
    446 		b.Node = Any{}
    447 	}
    448 
    449 	// Store value
    450 	if _, ok := m.State[b.Name]; ok {
    451 		panic(fmt.Sprintf("binding already created: %s", b.Name))
    452 	}
    453 	new, ret := match(m, b.Node, node)
    454 	if ret {
    455 		m.set(b, new)
    456 	}
    457 	return new, ret
    458 }
    459 
    460 func (Any) Match(m *Matcher, node any) (any, bool) {
    461 	return node, true
    462 }
    463 
    464 func (l List) Match(m *Matcher, node any) (any, bool) {
    465 	v := reflect.ValueOf(node)
    466 	if v.Kind() == reflect.Slice {
    467 		if isNil(l.Head) {
    468 			return node, v.Len() == 0
    469 		}
    470 		if v.Len() == 0 {
    471 			return nil, false
    472 		}
    473 		// OPT(dh): don't check the entire tail if head didn't match
    474 		_, ok1 := match(m, l.Head, v.Index(0).Interface())
    475 		_, ok2 := match(m, l.Tail, v.Slice(1, v.Len()).Interface())
    476 		return node, ok1 && ok2
    477 	}
    478 	// Our empty list does not equal an untyped Go nil. This way, we can
    479 	// tell apart an if with no else and an if with an empty else.
    480 	return nil, false
    481 }
    482 
    483 func (s String) Match(m *Matcher, node any) (any, bool) {
    484 	switch o := node.(type) {
    485 	case token.Token:
    486 		if tok, ok := maybeToken(s); ok {
    487 			return match(m, tok, node)
    488 		}
    489 		return nil, false
    490 	case string:
    491 		return o, string(s) == o
    492 	case types.TypeAndValue:
    493 		return o, o.Value != nil && o.Value.String() == string(s)
    494 	default:
    495 		return nil, false
    496 	}
    497 }
    498 
    499 func (tok Token) Match(m *Matcher, node any) (any, bool) {
    500 	o, ok := node.(token.Token)
    501 	if !ok {
    502 		return nil, false
    503 	}
    504 	return o, token.Token(tok) == o
    505 }
    506 
    507 func (Nil) Match(m *Matcher, node any) (any, bool) {
    508 	if isNil(node) {
    509 		return nil, true
    510 	}
    511 	v := reflect.ValueOf(node)
    512 	switch v.Kind() {
    513 	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
    514 		return nil, v.IsNil()
    515 	default:
    516 		return nil, false
    517 	}
    518 }
    519 
    520 func (builtin Builtin) Match(m *Matcher, node any) (any, bool) {
    521 	r, ok := match(m, Ident(builtin), node)
    522 	if !ok {
    523 		return nil, false
    524 	}
    525 	ident := r.(*ast.Ident)
    526 	obj := m.TypesInfo.ObjectOf(ident)
    527 	if obj != types.Universe.Lookup(ident.Name) {
    528 		return nil, false
    529 	}
    530 	return ident, true
    531 }
    532 
    533 func (obj Object) Match(m *Matcher, node any) (any, bool) {
    534 	r, ok := match(m, Ident(obj), node)
    535 	if !ok {
    536 		return nil, false
    537 	}
    538 	ident := r.(*ast.Ident)
    539 
    540 	id := m.TypesInfo.ObjectOf(ident)
    541 	_, ok = match(m, obj.Name, ident.Name)
    542 	return id, ok
    543 }
    544 
    545 func (fn Symbol) Match(m *Matcher, node any) (any, bool) {
    546 	var name string
    547 	var obj types.Object
    548 
    549 	base := []Node{
    550 		Ident{Any{}},
    551 		SelectorExpr{Any{}, Any{}},
    552 	}
    553 	p := Or{
    554 		Nodes: append(base,
    555 			IndexExpr{Or{Nodes: base}, Any{}},
    556 			IndexListExpr{Or{Nodes: base}, Any{}})}
    557 
    558 	r, ok := match(m, p, node)
    559 	if !ok {
    560 		return nil, false
    561 	}
    562 
    563 	fun := r.(ast.Expr)
    564 	switch idx := fun.(type) {
    565 	case *ast.IndexExpr:
    566 		fun = idx.X
    567 	case *ast.IndexListExpr:
    568 		fun = idx.X
    569 	}
    570 
    571 	switch fun := ast.Unparen(fun).(type) {
    572 	case *ast.Ident:
    573 		obj = m.TypesInfo.ObjectOf(fun)
    574 	case *ast.SelectorExpr:
    575 		obj = m.TypesInfo.ObjectOf(fun.Sel)
    576 	default:
    577 		panic("unreachable")
    578 	}
    579 	switch obj := obj.(type) {
    580 	case *types.Func:
    581 		// OPT(dh): optimize this similar to code.FuncName
    582 		name = obj.FullName()
    583 	case *types.Builtin:
    584 		name = obj.Name()
    585 	case *types.TypeName:
    586 		origObj := obj
    587 		for {
    588 			if obj.Parent() != obj.Pkg().Scope() {
    589 				return nil, false
    590 			}
    591 			name = types.TypeString(obj.Type(), nil)
    592 			_, ok = match(m, fn.Name, name)
    593 			if ok || !obj.IsAlias() {
    594 				return origObj, ok
    595 			} else {
    596 				// FIXME(dh): we should peel away one layer of alias at a time; this is blocked on
    597 				// github.com/golang/go/issues/66559
    598 				switch typ := types.Unalias(obj.Type()).(type) {
    599 				case interface{ Obj() *types.TypeName }:
    600 					obj = typ.Obj()
    601 				case *types.Basic:
    602 					return match(m, fn.Name, typ.Name())
    603 				default:
    604 					return nil, false
    605 				}
    606 			}
    607 		}
    608 	case *types.Const, *types.Var:
    609 		if obj.Pkg() == nil {
    610 			return nil, false
    611 		}
    612 		if obj.Parent() != obj.Pkg().Scope() {
    613 			return nil, false
    614 		}
    615 		name = fmt.Sprintf("%s.%s", obj.Pkg().Path(), obj.Name())
    616 	default:
    617 		return nil, false
    618 	}
    619 
    620 	_, ok = match(m, fn.Name, name)
    621 	return obj, ok
    622 }
    623 
    624 func (or Or) Match(m *Matcher, node any) (any, bool) {
    625 	for _, opt := range or.Nodes {
    626 		m.push()
    627 		if ret, ok := match(m, opt, node); ok {
    628 			m.merge()
    629 			return ret, true
    630 		} else {
    631 			m.pop()
    632 		}
    633 	}
    634 	return nil, false
    635 }
    636 
    637 func (not Not) Match(m *Matcher, node any) (any, bool) {
    638 	_, ok := match(m, not.Node, node)
    639 	if ok {
    640 		return nil, false
    641 	}
    642 	return node, true
    643 }
    644 
    645 var integerLiteralQ = MustParse(`(Or (BasicLit "INT" _) (UnaryExpr (Or "+" "-") (IntegerLiteral _)))`)
    646 
    647 func (lit IntegerLiteral) Match(m *Matcher, node any) (any, bool) {
    648 	matched, ok := match(m, integerLiteralQ.Root, node)
    649 	if !ok {
    650 		return nil, false
    651 	}
    652 	tv, ok := m.TypesInfo.Types[matched.(ast.Expr)]
    653 	if !ok {
    654 		return nil, false
    655 	}
    656 	if tv.Value == nil {
    657 		return nil, false
    658 	}
    659 	_, ok = match(m, lit.Value, tv)
    660 	return matched, ok
    661 }
    662 
    663 func (texpr TrulyConstantExpression) Match(m *Matcher, node any) (any, bool) {
    664 	expr, ok := node.(ast.Expr)
    665 	if !ok {
    666 		return nil, false
    667 	}
    668 	tv, ok := m.TypesInfo.Types[expr]
    669 	if !ok {
    670 		return nil, false
    671 	}
    672 	if tv.Value == nil {
    673 		return nil, false
    674 	}
    675 	truly := true
    676 	ast.Inspect(expr, func(node ast.Node) bool {
    677 		if _, ok := node.(*ast.Ident); ok {
    678 			truly = false
    679 			return false
    680 		}
    681 		return true
    682 	})
    683 	if !truly {
    684 		return nil, false
    685 	}
    686 	_, ok = match(m, texpr.Value, tv)
    687 	return expr, ok
    688 }
    689 
    690 var (
    691 	// Types of fields in go/ast structs that we want to skip
    692 	rtTokPos = reflect.TypeFor[token.Pos]()
    693 	//lint:ignore SA1019 It's deprecated, but we still want to skip the field.
    694 	rtObject       = reflect.TypeFor[*ast.Object]()
    695 	rtCommentGroup = reflect.TypeFor[*ast.CommentGroup]()
    696 )
    697 
    698 var (
    699 	_ matcher = Binding{}
    700 	_ matcher = Any{}
    701 	_ matcher = List{}
    702 	_ matcher = String("")
    703 	_ matcher = Token(0)
    704 	_ matcher = Nil{}
    705 	_ matcher = Builtin{}
    706 	_ matcher = Object{}
    707 	_ matcher = Symbol{}
    708 	_ matcher = Or{}
    709 	_ matcher = Not{}
    710 	_ matcher = IntegerLiteral{}
    711 	_ matcher = TrulyConstantExpression{}
    712 )