GoPLS Viewer

Home|gopls/refactor/eg/rewrite.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
7// This file defines the AST rewriting pass.
8// Most of it was plundered directly from
9// $GOROOT/src/cmd/gofmt/rewrite.go (after convergent evolution).
10
11import (
12    "fmt"
13    "go/ast"
14    "go/token"
15    "go/types"
16    "os"
17    "reflect"
18    "sort"
19    "strconv"
20    "strings"
21
22    "golang.org/x/tools/go/ast/astutil"
23)
24
25// transformItem takes a reflect.Value representing a variable of type ast.Node
26// transforms its child elements recursively with apply, and then transforms the
27// actual element if it contains an expression.
28func (tr *TransformertransformItem(rv reflect.Value) (reflect.Valuebool, map[string]ast.Expr) {
29    // don't bother if val is invalid to start with
30    if !rv.IsValid() {
31        return reflect.Value{}, falsenil
32    }
33
34    rvchangednewEnv := tr.apply(tr.transformItemrv)
35
36    e := rvToExpr(rv)
37    if e == nil {
38        return rvchangednewEnv
39    }
40
41    savedEnv := tr.env
42    tr.env = make(map[string]ast.Expr// inefficient!  Use a slice of k/v pairs
43
44    if tr.matchExpr(tr.beforee) {
45        if tr.verbose {
46            fmt.Fprintf(os.Stderr"%s matches %s",
47                astString(tr.fsettr.before), astString(tr.fsete))
48            if len(tr.env) > 0 {
49                fmt.Fprintf(os.Stderr" with:")
50                for nameast := range tr.env {
51                    fmt.Fprintf(os.Stderr" %s->%s",
52                        nameastString(tr.fsetast))
53                }
54            }
55            fmt.Fprintf(os.Stderr"\n")
56        }
57        tr.nsubsts++
58
59        // Clone the replacement tree, performing parameter substitution.
60        // We update all positions to n.Pos() to aid comment placement.
61        rv = tr.subst(tr.envreflect.ValueOf(tr.after),
62            reflect.ValueOf(e.Pos()))
63        changed = true
64        newEnv = tr.env
65    }
66    tr.env = savedEnv
67
68    return rvchangednewEnv
69}
70
71// Transform applies the transformation to the specified parsed file,
72// whose type information is supplied in info, and returns the number
73// of replacements that were made.
74//
75// It mutates the AST in place (the identity of the root node is
76// unchanged), and may add nodes for which no type information is
77// available in info.
78//
79// Derived from rewriteFile in $GOROOT/src/cmd/gofmt/rewrite.go.
80func (tr *TransformerTransform(info *types.Infopkg *types.Packagefile *ast.Fileint {
81    if !tr.seenInfos[info] {
82        tr.seenInfos[info] = true
83        mergeTypeInfo(tr.infoinfo)
84    }
85    tr.currentPkg = pkg
86    tr.nsubsts = 0
87
88    if tr.verbose {
89        fmt.Fprintf(os.Stderr"before: %s\n"astString(tr.fsettr.before))
90        fmt.Fprintf(os.Stderr"after: %s\n"astString(tr.fsettr.after))
91        fmt.Fprintf(os.Stderr"afterStmts: %s\n"tr.afterStmts)
92    }
93
94    ochanged_ := tr.apply(tr.transformItemreflect.ValueOf(file))
95    if changed {
96        panic("BUG")
97    }
98    file2 := o.Interface().(*ast.File)
99
100    // By construction, the root node is unchanged.
101    if file != file2 {
102        panic("BUG")
103    }
104
105    // Add any necessary imports.
106    // TODO(adonovan): remove no-longer needed imports too.
107    if tr.nsubsts > 0 {
108        pkgs := make(map[string]*types.Package)
109        for obj := range tr.importedObjs {
110            pkgs[obj.Pkg().Path()] = obj.Pkg()
111        }
112
113        for _imp := range file.Imports {
114            path_ := strconv.Unquote(imp.Path.Value)
115            delete(pkgspath)
116        }
117        delete(pkgspkg.Path()) // don't import self
118
119        // NB: AddImport may completely replace the AST!
120        // It thus renders info and tr.info no longer relevant to file.
121        var paths []string
122        for path := range pkgs {
123            paths = append(pathspath)
124        }
125        sort.Strings(paths)
126        for _path := range paths {
127            astutil.AddImport(tr.fsetfilepath)
128        }
129    }
130
131    tr.currentPkg = nil
132
133    return tr.nsubsts
134}
135
136// setValue is a wrapper for x.SetValue(y); it protects
137// the caller from panics if x cannot be changed to y.
138func setValue(xy reflect.Value) {
139    // don't bother if y is invalid to start with
140    if !y.IsValid() {
141        return
142    }
143    defer func() {
144        if x := recover(); x != nil {
145            if sok := x.(string); ok &&
146                (strings.Contains(s"type mismatch") || strings.Contains(s"not assignable")) {
147                // x cannot be set to y - ignore this rewrite
148                return
149            }
150            panic(x)
151        }
152    }()
153    x.Set(y)
154}
155
156// Values/types for special cases.
157var (
158    objectPtrNil = reflect.ValueOf((*ast.Object)(nil))
159    scopePtrNil  = reflect.ValueOf((*ast.Scope)(nil))
160
161    identType        = reflect.TypeOf((*ast.Ident)(nil))
162    selectorExprType = reflect.TypeOf((*ast.SelectorExpr)(nil))
163    objectPtrType    = reflect.TypeOf((*ast.Object)(nil))
164    statementType    = reflect.TypeOf((*ast.Stmt)(nil)).Elem()
165    positionType     = reflect.TypeOf(token.NoPos)
166    scopePtrType     = reflect.TypeOf((*ast.Scope)(nil))
167)
168
169// apply replaces each AST field x in val with f(x), returning val.
170// To avoid extra conversions, f operates on the reflect.Value form.
171// f takes a reflect.Value representing the variable to modify of type ast.Node.
172// It returns a reflect.Value containing the transformed value of type ast.Node,
173// whether any change was made, and a map of identifiers to ast.Expr (so we can
174// do contextually correct substitutions in the parent statements).
175func (tr *Transformerapply(f func(reflect.Value) (reflect.Valuebool, map[string]ast.Expr), val reflect.Value) (reflect.Valuebool, map[string]ast.Expr) {
176    if !val.IsValid() {
177        return reflect.Value{}, falsenil
178    }
179
180    // *ast.Objects introduce cycles and are likely incorrect after
181    // rewrite; don't follow them but replace with nil instead
182    if val.Type() == objectPtrType {
183        return objectPtrNilfalsenil
184    }
185
186    // similarly for scopes: they are likely incorrect after a rewrite;
187    // replace them with nil
188    if val.Type() == scopePtrType {
189        return scopePtrNilfalsenil
190    }
191
192    switch v := reflect.Indirect(val); v.Kind() {
193    case reflect.Slice:
194        // no possible rewriting of statements.
195        if v.Type().Elem() != statementType {
196            changed := false
197            var envp map[string]ast.Expr
198            for i := 0i < v.Len(); i++ {
199                e := v.Index(i)
200                olocalchangedenv := f(e)
201                if localchanged {
202                    changed = true
203                    // we clobber envp here,
204                    // which means if we have two successive
205                    // replacements inside the same statement
206                    // we will only generate the setup for one of them.
207                    envp = env
208                }
209                setValue(eo)
210            }
211            return valchangedenvp
212        }
213
214        // statements are rewritten.
215        var out []ast.Stmt
216        for i := 0i < v.Len(); i++ {
217            e := v.Index(i)
218            ochangedenv := f(e)
219            if changed {
220                for _s := range tr.afterStmts {
221                    t := tr.subst(envreflect.ValueOf(s), reflect.Value{}).Interface()
222                    out = append(outt.(ast.Stmt))
223                }
224            }
225            setValue(eo)
226            out = append(oute.Interface().(ast.Stmt))
227        }
228        return reflect.ValueOf(out), falsenil
229    case reflect.Struct:
230        changed := false
231        var envp map[string]ast.Expr
232        for i := 0i < v.NumField(); i++ {
233            e := v.Field(i)
234            olocalchangedenv := f(e)
235            if localchanged {
236                changed = true
237                envp = env
238            }
239            setValue(eo)
240        }
241        return valchangedenvp
242    case reflect.Interface:
243        e := v.Elem()
244        ochangedenv := f(e)
245        setValue(vo)
246        return valchangedenv
247    }
248    return valfalsenil
249}
250
251// subst returns a copy of (replacement) pattern with values from env
252// substituted in place of wildcards and pos used as the position of
253// tokens from the pattern.  if env == nil, subst returns a copy of
254// pattern and doesn't change the line number information.
255func (tr *Transformersubst(env map[string]ast.Exprpatternpos reflect.Valuereflect.Value {
256    if !pattern.IsValid() {
257        return reflect.Value{}
258    }
259
260    // *ast.Objects introduce cycles and are likely incorrect after
261    // rewrite; don't follow them but replace with nil instead
262    if pattern.Type() == objectPtrType {
263        return objectPtrNil
264    }
265
266    // similarly for scopes: they are likely incorrect after a rewrite;
267    // replace them with nil
268    if pattern.Type() == scopePtrType {
269        return scopePtrNil
270    }
271
272    // Wildcard gets replaced with map value.
273    if env != nil && pattern.Type() == identType {
274        id := pattern.Interface().(*ast.Ident)
275        if oldok := env[id.Name]; ok {
276            return tr.subst(nilreflect.ValueOf(old), reflect.Value{})
277        }
278    }
279
280    // Emit qualified identifiers in the pattern by appropriate
281    // (possibly qualified) identifier in the input.
282    //
283    // The template cannot contain dot imports, so all identifiers
284    // for imported objects are explicitly qualified.
285    //
286    // We assume (unsoundly) that there are no dot or named
287    // imports in the input code, nor are any imported package
288    // names shadowed, so the usual normal qualified identifier
289    // syntax may be used.
290    // TODO(adonovan): fix: avoid this assumption.
291    //
292    // A refactoring may be applied to a package referenced by the
293    // template.  Objects belonging to the current package are
294    // denoted by unqualified identifiers.
295    //
296    if tr.importedObjs != nil && pattern.Type() == selectorExprType {
297        obj := isRef(pattern.Interface().(*ast.SelectorExpr), tr.info)
298        if obj != nil {
299            if selok := tr.importedObjs[obj]; ok {
300                var id ast.Expr
301                if obj.Pkg() == tr.currentPkg {
302                    id = sel.Sel // unqualified
303                } else {
304                    id = sel // pkg-qualified
305                }
306
307                // Return a clone of id.
308                saved := tr.importedObjs
309                tr.importedObjs = nil // break cycle
310                r := tr.subst(nilreflect.ValueOf(id), pos)
311                tr.importedObjs = saved
312                return r
313            }
314        }
315    }
316
317    if pos.IsValid() && pattern.Type() == positionType {
318        // use new position only if old position was valid in the first place
319        if old := pattern.Interface().(token.Pos); !old.IsValid() {
320            return pattern
321        }
322        return pos
323    }
324
325    // Otherwise copy.
326    switch p := patternp.Kind() {
327    case reflect.Slice:
328        v := reflect.MakeSlice(p.Type(), p.Len(), p.Len())
329        for i := 0i < p.Len(); i++ {
330            v.Index(i).Set(tr.subst(envp.Index(i), pos))
331        }
332        return v
333
334    case reflect.Struct:
335        v := reflect.New(p.Type()).Elem()
336        for i := 0i < p.NumField(); i++ {
337            v.Field(i).Set(tr.subst(envp.Field(i), pos))
338        }
339        return v
340
341    case reflect.Ptr:
342        v := reflect.New(p.Type()).Elem()
343        if elem := p.Elem(); elem.IsValid() {
344            v.Set(tr.subst(envelempos).Addr())
345        }
346
347        // Duplicate type information for duplicated ast.Expr.
348        // All ast.Node implementations are *structs,
349        // so this case catches them all.
350        if e := rvToExpr(v); e != nil {
351            updateTypeInfo(tr.infoep.Interface().(ast.Expr))
352        }
353        return v
354
355    case reflect.Interface:
356        v := reflect.New(p.Type()).Elem()
357        if elem := p.Elem(); elem.IsValid() {
358            v.Set(tr.subst(envelempos))
359        }
360        return v
361    }
362
363    return pattern
364}
365
366// -- utilities -------------------------------------------------------
367
368func rvToExpr(rv reflect.Valueast.Expr {
369    if rv.CanInterface() {
370        if eok := rv.Interface().(ast.Expr); ok {
371            return e
372        }
373    }
374    return nil
375}
376
377// updateTypeInfo duplicates type information for the existing AST old
378// so that it also applies to duplicated AST new.
379func updateTypeInfo(info *types.Infonewold ast.Expr) {
380    switch new := new.(type) {
381    case *ast.Ident:
382        orig := old.(*ast.Ident)
383        if objok := info.Defs[orig]; ok {
384            info.Defs[new] = obj
385        }
386        if objok := info.Uses[orig]; ok {
387            info.Uses[new] = obj
388        }
389
390    case *ast.SelectorExpr:
391        orig := old.(*ast.SelectorExpr)
392        if selok := info.Selections[orig]; ok {
393            info.Selections[new] = sel
394        }
395    }
396
397    if tvok := info.Types[old]; ok {
398        info.Types[new] = tv
399    }
400}
401
MembersX
Transformer.apply
Transformer.apply.BlockStmt.BlockStmt.BlockStmt.localchanged
Transformer.apply.BlockStmt.e
Transformer.subst
Transformer.subst.BlockStmt.elem
Transformer.transformItem.BlockStmt.BlockStmt.BlockStmt.RangeStmt_1323.ast
Transformer.Transform.pkg
Transformer.Transform.changed
Transformer.transformItem.changed
Transformer.subst.pos
Transformer.subst.p
Transformer.Transform.BlockStmt.RangeStmt_3476.path
Transformer.apply.val
Transformer.apply.BlockStmt.BlockStmt.BlockStmt.env
Transformer.apply.BlockStmt.BlockStmt.localchanged
Transformer.subst.BlockStmt.BlockStmt.BlockStmt.id
Transformer.transformItem
Transformer.transformItem.newEnv
Transformer.Transform.BlockStmt.RangeStmt_3096.BlockStmt._
updateTypeInfo.new
Transformer.subst.BlockStmt.BlockStmt.BlockStmt.saved
Transformer.Transform.file
Transformer.Transform._
Transformer.apply.BlockStmt.BlockStmt.changed
Transformer.subst.pattern
rvToExpr
Transformer.transformItem.BlockStmt.BlockStmt.BlockStmt.RangeStmt_1323.name
Transformer.Transform.BlockStmt.RangeStmt_3096.BlockStmt.path
setValue
rvToExpr.rv
setValue.x
Transformer.apply.BlockStmt.BlockStmt.env
Transformer.subst.BlockStmt.BlockStmt.BlockStmt.r
setValue.BlockStmt.x
Transformer.apply.f
Transformer.apply.BlockStmt.BlockStmt.BlockStmt.o
Transformer.apply.BlockStmt.BlockStmt.BlockStmt.RangeStmt_6343.s
Transformer.transformItem.e
Transformer.Transform.BlockStmt.pkgs
Transformer.Transform.BlockStmt.RangeStmt_3096.imp
Transformer.subst.BlockStmt.i
Transformer.transformItem.rv
Transformer.Transform
Transformer.apply.BlockStmt.BlockStmt.BlockStmt.RangeStmt_6343.BlockStmt.t
updateTypeInfo
updateTypeInfo.info
Transformer.Transform.BlockStmt.RangeStmt_3392.path
Transformer.apply.BlockStmt.out
Transformer.apply.BlockStmt.o
Transformer.Transform.tr
Transformer.apply.BlockStmt.i
Transformer.apply.BlockStmt.BlockStmt.e
Transformer.apply.BlockStmt.BlockStmt.envp
Transformer.apply.BlockStmt.env
Transformer.subst.env
Transformer.subst.BlockStmt.obj
Transformer.transformItem.tr
Transformer.Transform.BlockStmt.RangeStmt_3016.obj
Transformer.apply.v
updateTypeInfo.old
strconv
Transformer.apply.BlockStmt.BlockStmt.i
Transformer.subst.BlockStmt.v
Transformer.subst.BlockStmt.e
Transformer.Transform.info
Transformer.Transform.o
Transformer.apply.BlockStmt.changed
setValue.y
Transformer.apply.tr
Transformer.apply.BlockStmt.BlockStmt.BlockStmt.e
Transformer.apply.BlockStmt.BlockStmt.o
Transformer.apply.BlockStmt.envp
sort
Transformer.transformItem.savedEnv
Transformer.Transform.BlockStmt.paths
Transformer.subst.tr
Members
X