GoPLS Viewer

Home|gopls/refactor/eg/match.go
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.
4
5package eg
6
7import (
8    "fmt"
9    "go/ast"
10    "go/constant"
11    "go/token"
12    "go/types"
13    "log"
14    "os"
15    "reflect"
16
17    "golang.org/x/tools/go/ast/astutil"
18)
19
20// matchExpr reports whether pattern x matches y.
21//
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
26// equivalent.
27//
28// A wildcard appearing more than once in the pattern must
29// consistently match the same tree.
30func (tr *TransformermatchExpr(xy ast.Exprbool {
31    if x == nil && y == nil {
32        return true
33    }
34    if x == nil || y == nil {
35        return false
36    }
37    x = unparen(x)
38    y = unparen(y)
39
40    // Is x a wildcard?  (a reference to a 'before' parameter)
41    if xobjok := tr.wildcardObj(x); ok {
42        return tr.matchWildcard(xobjy)
43    }
44
45    // Object identifiers (including pkg-qualified ones)
46    // are handled semantically, not syntactically.
47    xobj := isRef(xtr.info)
48    yobj := isRef(ytr.info)
49    if xobj != nil {
50        return xobj == yobj
51    }
52    if yobj != nil {
53        return false
54    }
55
56    // TODO(adonovan): audit: we cannot assume these ast.Exprs
57    // contain non-nil pointers.  e.g. ImportSpec.Name may be a
58    // nil *ast.Ident.
59
60    if reflect.TypeOf(x) != reflect.TypeOf(y) {
61        return false
62    }
63    switch x := x.(type) {
64    case *ast.Ident:
65        log.Fatalf("unexpected Ident: %s"astString(tr.fsetx))
66
67    case *ast.BasicLit:
68        y := y.(*ast.BasicLit)
69        xval := constant.MakeFromLiteral(x.Valuex.Kind0)
70        yval := constant.MakeFromLiteral(y.Valuey.Kind0)
71        return constant.Compare(xvaltoken.EQLyval)
72
73    case *ast.FuncLit:
74        // func literals (and thus statement syntax) never match.
75        return false
76
77    case *ast.CompositeLit:
78        y := y.(*ast.CompositeLit)
79        return (x.Type == nil) == (y.Type == nil) &&
80            (x.Type == nil || tr.matchType(x.Typey.Type)) &&
81            tr.matchExprs(x.Eltsy.Elts)
82
83    case *ast.SelectorExpr:
84        y := y.(*ast.SelectorExpr)
85        return tr.matchSelectorExpr(xy) &&
86            tr.info.Selections[x].Obj() == tr.info.Selections[y].Obj()
87
88    case *ast.IndexExpr:
89        y := y.(*ast.IndexExpr)
90        return tr.matchExpr(x.Xy.X) &&
91            tr.matchExpr(x.Indexy.Index)
92
93    case *ast.SliceExpr:
94        y := y.(*ast.SliceExpr)
95        return tr.matchExpr(x.Xy.X) &&
96            tr.matchExpr(x.Lowy.Low) &&
97            tr.matchExpr(x.Highy.High) &&
98            tr.matchExpr(x.Maxy.Max) &&
99            x.Slice3 == y.Slice3
100
101    case *ast.TypeAssertExpr:
102        y := y.(*ast.TypeAssertExpr)
103        return tr.matchExpr(x.Xy.X) &&
104            tr.matchType(x.Typey.Type)
105
106    case *ast.CallExpr:
107        y := y.(*ast.CallExpr)
108        match := tr.matchExpr // function call
109        if tr.info.Types[x.Fun].IsType() {
110            match = tr.matchType // type conversion
111        }
112        return x.Ellipsis.IsValid() == y.Ellipsis.IsValid() &&
113            match(x.Funy.Fun) &&
114            tr.matchExprs(x.Argsy.Args)
115
116    case *ast.StarExpr:
117        y := y.(*ast.StarExpr)
118        return tr.matchExpr(x.Xy.X)
119
120    case *ast.UnaryExpr:
121        y := y.(*ast.UnaryExpr)
122        return x.Op == y.Op &&
123            tr.matchExpr(x.Xy.X)
124
125    case *ast.BinaryExpr:
126        y := y.(*ast.BinaryExpr)
127        return x.Op == y.Op &&
128            tr.matchExpr(x.Xy.X) &&
129            tr.matchExpr(x.Yy.Y)
130
131    case *ast.KeyValueExpr:
132        y := y.(*ast.KeyValueExpr)
133        return tr.matchExpr(x.Keyy.Key) &&
134            tr.matchExpr(x.Valuey.Value)
135    }
136
137    panic(fmt.Sprintf("unhandled AST node type: %T"x))
138}
139
140func (tr *TransformermatchExprs(xxyy []ast.Exprbool {
141    if len(xx) != len(yy) {
142        return false
143    }
144    for i := range xx {
145        if !tr.matchExpr(xx[i], yy[i]) {
146            return false
147        }
148    }
149    return true
150}
151
152// matchType reports whether the two type ASTs denote identical types.
153func (tr *TransformermatchType(xy ast.Exprbool {
154    tx := tr.info.Types[x].Type
155    ty := tr.info.Types[y].Type
156    return types.Identical(txty)
157}
158
159func (tr *TransformerwildcardObj(x ast.Expr) (*types.Varbool) {
160    if xok := x.(*ast.Ident); ok && x != nil && tr.allowWildcards {
161        if xobjok := tr.info.Uses[x].(*types.Var); ok && tr.wildcards[xobj] {
162            return xobjtrue
163        }
164    }
165    return nilfalse
166}
167
168func (tr *TransformermatchSelectorExpr(xy *ast.SelectorExprbool {
169    if xobjok := tr.wildcardObj(x.X); ok {
170        field := x.Sel.Name
171        yt := tr.info.TypeOf(y.X)
172        o__ := types.LookupFieldOrMethod(yttruetr.currentPkgfield)
173        if o != nil {
174            tr.env[xobj.Name()] = y.X // record binding
175            return true
176        }
177    }
178    return tr.matchExpr(x.Xy.X)
179}
180
181func (tr *TransformermatchWildcard(xobj *types.Vary ast.Exprbool {
182    name := xobj.Name()
183
184    if tr.verbose {
185        fmt.Fprintf(os.Stderr"%s: wildcard %s -> %s?: ",
186            tr.fset.Position(y.Pos()), nameastString(tr.fsety))
187    }
188
189    // Check that y is assignable to the declared type of the param.
190    yt := tr.info.TypeOf(y)
191    if yt == nil {
192        // y has no type.
193        // Perhaps it is an *ast.Ellipsis in [...]T{}, or
194        // an *ast.KeyValueExpr in T{k: v}.
195        // Clearly these pseudo-expressions cannot match a
196        // wildcard, but it would nice if we had a way to ignore
197        // the difference between T{v} and T{k:v} for structs.
198        return false
199    }
200    if !types.AssignableTo(ytxobj.Type()) {
201        if tr.verbose {
202            fmt.Fprintf(os.Stderr"%s not assignable to %s\n"ytxobj.Type())
203        }
204        return false
205    }
206
207    // A wildcard matches any expression.
208    // If it appears multiple times in the pattern, it must match
209    // the same expression each time.
210    if oldok := tr.env[name]; ok {
211        // found existing binding
212        tr.allowWildcards = false
213        r := tr.matchExpr(oldy)
214        if tr.verbose {
215            fmt.Fprintf(os.Stderr"%t secondary match, primary was %s\n",
216                rastString(tr.fsetold))
217        }
218        tr.allowWildcards = true
219        return r
220    }
221
222    if tr.verbose {
223        fmt.Fprintf(os.Stderr"primary match\n")
224    }
225
226    tr.env[name] = y // record binding
227    return true
228}
229
230// -- utilities --------------------------------------------------------
231
232func unparen(e ast.Exprast.Expr { return astutil.Unparen(e) }
233
234// isRef returns the object referred to by this (possibly qualified)
235// identifier, or nil if the node is not a referring identifier.
236func isRef(n ast.Nodeinfo *types.Infotypes.Object {
237    switch n := n.(type) {
238    case *ast.Ident:
239        return info.Uses[n]
240
241    case *ast.SelectorExpr:
242        if _ok := info.Selections[n]; !ok {
243            // qualified ident
244            return info.Uses[n.Sel]
245        }
246    }
247    return nil
248}
249
MembersX
Transformer.matchSelectorExpr.xobj
Transformer.matchWildcard.yt
unparen.e
Transformer.wildcardObj.x
Transformer.matchExpr
Transformer.matchExpr.y
Transformer.matchType.tx
reflect
Transformer.matchExpr.xobj
Transformer.matchExprs.xx
Transformer.matchType.x
Transformer.wildcardObj.tr
Transformer.matchSelectorExpr.BlockStmt.field
Transformer.matchSelectorExpr.BlockStmt.o
Transformer.matchWildcard.xobj
Transformer.matchExpr.tr
Transformer.matchWildcard.y
Transformer.matchExpr.BlockStmt.xval
Transformer.matchWildcard.name
isRef.n
Transformer.matchExpr.x
Transformer.matchSelectorExpr.ok
Transformer.matchWildcard.tr
Transformer.matchWildcard
Transformer.matchWildcard.BlockStmt.r
Transformer.matchType.tr
Transformer.matchSelectorExpr.tr
Transformer.matchSelectorExpr
Transformer.matchSelectorExpr.x
Transformer.matchSelectorExpr.y
unparen
Transformer.matchType
astutil
Transformer.matchExpr.yobj
Transformer.matchExpr.BlockStmt.yval
Transformer.matchExpr.BlockStmt.match
Transformer.matchType.y
Transformer.matchSelectorExpr.BlockStmt._
isRef
log
Transformer.matchExprs.tr
Transformer.matchExprs
Transformer.matchExprs.yy
Transformer.matchExprs.RangeStmt_3587.i
Transformer.matchType.ty
Transformer.wildcardObj
Transformer.matchSelectorExpr.BlockStmt.yt
Transformer.matchExpr.ok
isRef.info
Members
X