1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
17 "golang.org/x/tools/go/ast/astutil"
20 // matchExpr reports whether pattern x matches y.
22 // If tr.allowWildcards, Idents in x that refer to parameters are
23 // treated as wildcards, and match any y that is assignable to the
24 // parameter type; matchExpr records this correspondence in tr.env.
25 // Otherwise, matchExpr simply reports whether the two trees are
28 // A wildcard appearing more than once in the pattern must
29 // consistently match the same tree.
31 func (tr *Transformer) matchExpr(x, y ast.Expr) bool {
32 if x == nil && y == nil {
35 if x == nil || y == nil {
41 // Is x a wildcard? (a reference to a 'before' parameter)
42 if xobj, ok := tr.wildcardObj(x); ok {
43 return tr.matchWildcard(xobj, y)
46 // Object identifiers (including pkg-qualified ones)
47 // are handled semantically, not syntactically.
48 xobj := isRef(x, tr.info)
49 yobj := isRef(y, tr.info)
57 // TODO(adonovan): audit: we cannot assume these ast.Exprs
58 // contain non-nil pointers. e.g. ImportSpec.Name may be a
61 if reflect.TypeOf(x) != reflect.TypeOf(y) {
64 switch x := x.(type) {
66 log.Fatalf("unexpected Ident: %s", astString(tr.fset, x))
69 y := y.(*ast.BasicLit)
70 xval := constant.MakeFromLiteral(x.Value, x.Kind, 0)
71 yval := constant.MakeFromLiteral(y.Value, y.Kind, 0)
72 return constant.Compare(xval, token.EQL, yval)
75 // func literals (and thus statement syntax) never match.
78 case *ast.CompositeLit:
79 y := y.(*ast.CompositeLit)
80 return (x.Type == nil) == (y.Type == nil) &&
81 (x.Type == nil || tr.matchType(x.Type, y.Type)) &&
82 tr.matchExprs(x.Elts, y.Elts)
84 case *ast.SelectorExpr:
85 y := y.(*ast.SelectorExpr)
86 return tr.matchSelectorExpr(x, y) &&
87 tr.info.Selections[x].Obj() == tr.info.Selections[y].Obj()
90 y := y.(*ast.IndexExpr)
91 return tr.matchExpr(x.X, y.X) &&
92 tr.matchExpr(x.Index, y.Index)
95 y := y.(*ast.SliceExpr)
96 return tr.matchExpr(x.X, y.X) &&
97 tr.matchExpr(x.Low, y.Low) &&
98 tr.matchExpr(x.High, y.High) &&
99 tr.matchExpr(x.Max, y.Max) &&
102 case *ast.TypeAssertExpr:
103 y := y.(*ast.TypeAssertExpr)
104 return tr.matchExpr(x.X, y.X) &&
105 tr.matchType(x.Type, y.Type)
108 y := y.(*ast.CallExpr)
109 match := tr.matchExpr // function call
110 if tr.info.Types[x.Fun].IsType() {
111 match = tr.matchType // type conversion
113 return x.Ellipsis.IsValid() == y.Ellipsis.IsValid() &&
114 match(x.Fun, y.Fun) &&
115 tr.matchExprs(x.Args, y.Args)
118 y := y.(*ast.StarExpr)
119 return tr.matchExpr(x.X, y.X)
122 y := y.(*ast.UnaryExpr)
123 return x.Op == y.Op &&
124 tr.matchExpr(x.X, y.X)
126 case *ast.BinaryExpr:
127 y := y.(*ast.BinaryExpr)
128 return x.Op == y.Op &&
129 tr.matchExpr(x.X, y.X) &&
130 tr.matchExpr(x.Y, y.Y)
132 case *ast.KeyValueExpr:
133 y := y.(*ast.KeyValueExpr)
134 return tr.matchExpr(x.Key, y.Key) &&
135 tr.matchExpr(x.Value, y.Value)
138 panic(fmt.Sprintf("unhandled AST node type: %T", x))
141 func (tr *Transformer) matchExprs(xx, yy []ast.Expr) bool {
142 if len(xx) != len(yy) {
146 if !tr.matchExpr(xx[i], yy[i]) {
153 // matchType reports whether the two type ASTs denote identical types.
154 func (tr *Transformer) matchType(x, y ast.Expr) bool {
155 tx := tr.info.Types[x].Type
156 ty := tr.info.Types[y].Type
157 return types.Identical(tx, ty)
160 func (tr *Transformer) wildcardObj(x ast.Expr) (*types.Var, bool) {
161 if x, ok := x.(*ast.Ident); ok && x != nil && tr.allowWildcards {
162 if xobj, ok := tr.info.Uses[x].(*types.Var); ok && tr.wildcards[xobj] {
169 func (tr *Transformer) matchSelectorExpr(x, y *ast.SelectorExpr) bool {
170 if xobj, ok := tr.wildcardObj(x.X); ok {
172 yt := tr.info.TypeOf(y.X)
173 o, _, _ := types.LookupFieldOrMethod(yt, true, tr.currentPkg, field)
175 tr.env[xobj.Name()] = y.X // record binding
179 return tr.matchExpr(x.X, y.X)
182 func (tr *Transformer) matchWildcard(xobj *types.Var, y ast.Expr) bool {
186 fmt.Fprintf(os.Stderr, "%s: wildcard %s -> %s?: ",
187 tr.fset.Position(y.Pos()), name, astString(tr.fset, y))
190 // Check that y is assignable to the declared type of the param.
191 yt := tr.info.TypeOf(y)
194 // Perhaps it is an *ast.Ellipsis in [...]T{}, or
195 // an *ast.KeyValueExpr in T{k: v}.
196 // Clearly these pseudo-expressions cannot match a
197 // wildcard, but it would nice if we had a way to ignore
198 // the difference between T{v} and T{k:v} for structs.
201 if !types.AssignableTo(yt, xobj.Type()) {
203 fmt.Fprintf(os.Stderr, "%s not assignable to %s\n", yt, xobj.Type())
208 // A wildcard matches any expression.
209 // If it appears multiple times in the pattern, it must match
210 // the same expression each time.
211 if old, ok := tr.env[name]; ok {
212 // found existing binding
213 tr.allowWildcards = false
214 r := tr.matchExpr(old, y)
216 fmt.Fprintf(os.Stderr, "%t secondary match, primary was %s\n",
217 r, astString(tr.fset, old))
219 tr.allowWildcards = true
224 fmt.Fprintf(os.Stderr, "primary match\n")
227 tr.env[name] = y // record binding
231 // -- utilities --------------------------------------------------------
233 func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
235 // isRef returns the object referred to by this (possibly qualified)
236 // identifier, or nil if the node is not a referring identifier.
237 func isRef(n ast.Node, info *types.Info) types.Object {
238 switch n := n.(type) {
242 case *ast.SelectorExpr:
243 if _, ok := info.Selections[n]; !ok {
245 return info.Uses[n.Sel]