10 "honnef.co/go/tools/lint"
13 var tokensByString = map[string]Token{
14 "INT": Token(token.INT),
15 "FLOAT": Token(token.FLOAT),
16 "IMAG": Token(token.IMAG),
17 "CHAR": Token(token.CHAR),
18 "STRING": Token(token.STRING),
19 "+": Token(token.ADD),
20 "-": Token(token.SUB),
21 "*": Token(token.MUL),
22 "/": Token(token.QUO),
23 "%": Token(token.REM),
24 "&": Token(token.AND),
26 "^": Token(token.XOR),
27 "<<": Token(token.SHL),
28 ">>": Token(token.SHR),
29 "&^": Token(token.AND_NOT),
30 "+=": Token(token.ADD_ASSIGN),
31 "-=": Token(token.SUB_ASSIGN),
32 "*=": Token(token.MUL_ASSIGN),
33 "/=": Token(token.QUO_ASSIGN),
34 "%=": Token(token.REM_ASSIGN),
35 "&=": Token(token.AND_ASSIGN),
36 "|=": Token(token.OR_ASSIGN),
37 "^=": Token(token.XOR_ASSIGN),
38 "<<=": Token(token.SHL_ASSIGN),
39 ">>=": Token(token.SHR_ASSIGN),
40 "&^=": Token(token.AND_NOT_ASSIGN),
41 "&&": Token(token.LAND),
42 "||": Token(token.LOR),
43 "<-": Token(token.ARROW),
44 "++": Token(token.INC),
45 "--": Token(token.DEC),
46 "==": Token(token.EQL),
47 "<": Token(token.LSS),
48 ">": Token(token.GTR),
49 "=": Token(token.ASSIGN),
50 "!": Token(token.NOT),
51 "!=": Token(token.NEQ),
52 "<=": Token(token.LEQ),
53 ">=": Token(token.GEQ),
54 ":=": Token(token.DEFINE),
55 "...": Token(token.ELLIPSIS),
56 "IMPORT": Token(token.IMPORT),
57 "VAR": Token(token.VAR),
58 "TYPE": Token(token.TYPE),
59 "CONST": Token(token.CONST),
62 func maybeToken(node Node) (Node, bool) {
63 if node, ok := node.(String); ok {
64 if tok, ok := tokensByString[string(node)]; ok {
72 func isNil(v interface{}) bool {
76 if _, ok := v.(Nil); ok {
82 type matcher interface {
83 Match(*Matcher, interface{}) (interface{}, bool)
86 type State = map[string]interface{}
93 func (m *Matcher) fork() *Matcher {
94 state := make(State, len(m.State))
95 for k, v := range m.State {
99 TypesInfo: m.TypesInfo,
104 func (m *Matcher) merge(mc *Matcher) {
108 func (m *Matcher) Match(a Node, b ast.Node) bool {
110 _, ok := match(m, a, b)
114 func Match(a Node, b ast.Node) (*Matcher, bool) {
120 // Match two items, which may be (Node, AST) or (AST, AST)
121 func match(m *Matcher, l, r interface{}) (interface{}, bool) {
122 if _, ok := r.(Node); ok {
123 panic("Node mustn't be on right side of match")
126 switch l := l.(type) {
128 return match(m, l.X, r)
130 return match(m, l.X, r)
132 return match(m, l.Decl, r)
133 case *ast.LabeledStmt:
134 return match(m, l.Stmt, r)
136 return match(m, l.List, r)
138 return match(m, l.List, r)
141 switch r := r.(type) {
143 return match(m, l, r.X)
145 return match(m, l, r.X)
147 return match(m, l, r.Decl)
148 case *ast.LabeledStmt:
149 return match(m, l, r.Stmt)
152 return match(m, l, nil)
154 return match(m, l, r.List)
157 return match(m, l, nil)
159 return match(m, l, r.List)
162 return match(m, l, nil)
166 if l, ok := l.(matcher); ok {
170 if l, ok := l.(Node); ok {
171 // Matching of pattern with concrete value
172 return matchNodeAST(m, l, r)
175 if l == nil || r == nil {
180 ln, ok1 := l.(ast.Node)
181 rn, ok2 := r.(ast.Node)
183 return matchAST(m, ln, rn)
188 obj, ok := l.(types.Object)
190 switch r := r.(type) {
192 return obj, obj == m.TypesInfo.ObjectOf(r)
193 case *ast.SelectorExpr:
194 return obj, obj == m.TypesInfo.ObjectOf(r.Sel)
202 ln, ok1 := l.([]ast.Expr)
203 rn, ok2 := r.([]ast.Expr)
206 rn = []ast.Expr{r.(ast.Expr)}
207 } else if !ok1 && ok2 {
208 ln = []ast.Expr{l.(ast.Expr)}
211 if len(ln) != len(rn) {
214 for i, ll := range ln {
215 if _, ok := match(m, ll, rn[i]); !ok {
224 ln, ok1 := l.([]ast.Stmt)
225 rn, ok2 := r.([]ast.Stmt)
228 rn = []ast.Stmt{r.(ast.Stmt)}
229 } else if !ok1 && ok2 {
230 ln = []ast.Stmt{l.(ast.Stmt)}
233 if len(ln) != len(rn) {
236 for i, ll := range ln {
237 if _, ok := match(m, ll, rn[i]); !ok {
246 ln, ok1 := l.([]*ast.Field)
247 rn, ok2 := r.([]*ast.Field)
250 rn = []*ast.Field{r.(*ast.Field)}
251 } else if !ok1 && ok2 {
252 ln = []*ast.Field{l.(*ast.Field)}
255 if len(ln) != len(rn) {
258 for i, ll := range ln {
259 if _, ok := match(m, ll, rn[i]); !ok {
267 panic(fmt.Sprintf("unsupported comparison: %T and %T", l, r))
270 // Match a Node with an AST node
271 func matchNodeAST(m *Matcher, a Node, b interface{}) (interface{}, bool) {
272 switch b := b.(type) {
274 // 'a' is not a List or we'd be using its Match
280 return match(m, a, b[0])
282 // 'a' is not a List or we'd be using its Match
288 return match(m, a, b[0])
290 ra := reflect.ValueOf(a)
291 rb := reflect.ValueOf(b).Elem()
293 if ra.Type().Name() != rb.Type().Name() {
297 for i := 0; i < ra.NumField(); i++ {
299 fieldName := ra.Type().Field(i).Name
300 bf := rb.FieldByName(fieldName)
301 if (bf == reflect.Value{}) {
302 panic(fmt.Sprintf("internal error: could not find field %s in type %t when comparing with %T", fieldName, b, a))
309 if _, ok := match(m, ai.(Node), bi); !ok {
315 return nil, a == Nil{}
317 panic(fmt.Sprintf("unhandled type %T", b))
321 // Match two AST nodes
322 func matchAST(m *Matcher, a, b ast.Node) (interface{}, bool) {
323 ra := reflect.ValueOf(a)
324 rb := reflect.ValueOf(b)
326 if ra.Type() != rb.Type() {
329 if ra.IsNil() || rb.IsNil() {
330 return rb, ra.IsNil() == rb.IsNil()
335 for i := 0; i < ra.NumField(); i++ {
338 if af.Type() == rtTokPos || af.Type() == rtObject || af.Type() == rtCommentGroup {
344 if af.Len() != bf.Len() {
347 for j := 0; j < af.Len(); j++ {
348 if _, ok := match(m, af.Index(j).Interface().(ast.Node), bf.Index(j).Interface().(ast.Node)); !ok {
353 if af.String() != bf.String() {
357 if af.Int() != bf.Int() {
361 if af.Bool() != bf.Bool() {
364 case reflect.Ptr, reflect.Interface:
365 if _, ok := match(m, af.Interface(), bf.Interface()); !ok {
369 panic(fmt.Sprintf("internal error: unhandled kind %s (%T)", af.Kind(), af.Interface()))
375 func (b Binding) Match(m *Matcher, node interface{}) (interface{}, bool) {
377 v, ok := m.State[b.Name]
380 return match(m, v, node)
387 if _, ok := m.State[b.Name]; ok {
388 panic(fmt.Sprintf("binding already created: %s", b.Name))
390 new, ret := match(m, b.Node, node)
392 m.State[b.Name] = new
397 func (Any) Match(m *Matcher, node interface{}) (interface{}, bool) {
401 func (l List) Match(m *Matcher, node interface{}) (interface{}, bool) {
402 v := reflect.ValueOf(node)
403 if v.Kind() == reflect.Slice {
405 return node, v.Len() == 0
410 // OPT(dh): don't check the entire tail if head didn't match
411 _, ok1 := match(m, l.Head, v.Index(0).Interface())
412 _, ok2 := match(m, l.Tail, v.Slice(1, v.Len()).Interface())
413 return node, ok1 && ok2
415 // Our empty list does not equal an untyped Go nil. This way, we can
416 // tell apart an if with no else and an if with an empty else.
420 func (s String) Match(m *Matcher, node interface{}) (interface{}, bool) {
421 switch o := node.(type) {
423 if tok, ok := maybeToken(s); ok {
424 return match(m, tok, node)
428 return o, string(s) == o
434 func (tok Token) Match(m *Matcher, node interface{}) (interface{}, bool) {
435 o, ok := node.(token.Token)
439 return o, token.Token(tok) == o
442 func (Nil) Match(m *Matcher, node interface{}) (interface{}, bool) {
443 return nil, isNil(node)
446 func (builtin Builtin) Match(m *Matcher, node interface{}) (interface{}, bool) {
447 ident, ok := node.(*ast.Ident)
451 obj := m.TypesInfo.ObjectOf(ident)
452 if obj != types.Universe.Lookup(ident.Name) {
455 return match(m, builtin.Name, ident.Name)
458 func (obj Object) Match(m *Matcher, node interface{}) (interface{}, bool) {
459 ident, ok := node.(*ast.Ident)
464 id := m.TypesInfo.ObjectOf(ident)
465 _, ok = match(m, obj.Name, ident.Name)
469 func (fn Function) Match(m *Matcher, node interface{}) (interface{}, bool) {
472 switch node := node.(type) {
474 obj = m.TypesInfo.ObjectOf(node)
475 switch obj := obj.(type) {
477 name = lint.FuncName(obj)
483 case *ast.SelectorExpr:
485 obj, ok = m.TypesInfo.ObjectOf(node.Sel).(*types.Func)
489 name = lint.FuncName(obj.(*types.Func))
493 _, ok := match(m, fn.Name, name)
497 func (or Or) Match(m *Matcher, node interface{}) (interface{}, bool) {
498 for _, opt := range or.Nodes {
500 if ret, ok := match(mc, opt, node); ok {
508 func (not Not) Match(m *Matcher, node interface{}) (interface{}, bool) {
509 _, ok := match(m, not.Node, node)
517 // Types of fields in go/ast structs that we want to skip
518 rtTokPos = reflect.TypeOf(token.Pos(0))
519 rtObject = reflect.TypeOf((*ast.Object)(nil))
520 rtCommentGroup = reflect.TypeOf((*ast.CommentGroup)(nil))
524 _ matcher = Binding{}
527 _ matcher = String("")
530 _ matcher = Builtin{}
532 _ matcher = Function{}