GoPLS Viewer

Home|gopls/refactor/rename/check.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 rename
6
7// This file defines the safety checks for each kind of renaming.
8
9import (
10    "fmt"
11    "go/ast"
12    "go/token"
13    "go/types"
14
15    "golang.org/x/tools/go/loader"
16    "golang.org/x/tools/refactor/satisfy"
17)
18
19// errorf reports an error (e.g. conflict) and prevents file modification.
20func (r *renamererrorf(pos token.Posformat stringargs ...interface{}) {
21    r.hadConflicts = true
22    reportError(r.iprog.Fset.Position(pos), fmt.Sprintf(formatargs...))
23}
24
25// check performs safety checks of the renaming of the 'from' object to r.to.
26func (r *renamercheck(from types.Object) {
27    if r.objsToUpdate[from] {
28        return
29    }
30    r.objsToUpdate[from] = true
31
32    // NB: order of conditions is important.
33    if from_ok := from.(*types.PkgName); ok {
34        r.checkInFileBlock(from_)
35    } else if from_ok := from.(*types.Label); ok {
36        r.checkLabel(from_)
37    } else if isPackageLevel(from) {
38        r.checkInPackageBlock(from)
39    } else if vok := from.(*types.Var); ok && v.IsField() {
40        r.checkStructField(v)
41    } else if fok := from.(*types.Func); ok && recv(f) != nil {
42        r.checkMethod(f)
43    } else if isLocal(from) {
44        r.checkInLocalScope(from)
45    } else {
46        r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n",
47            objectKind(from), from)
48    }
49}
50
51// checkInFileBlock performs safety checks for renames of objects in the file block,
52// i.e. imported package names.
53func (r *renamercheckInFileBlock(from *types.PkgName) {
54    // Check import name is not "init".
55    if r.to == "init" {
56        r.errorf(from.Pos(), "%q is not a valid imported package name"r.to)
57    }
58
59    // Check for conflicts between file and package block.
60    if prev := from.Pkg().Scope().Lookup(r.to); prev != nil {
61        r.errorf(from.Pos(), "renaming this %s %q to %q would conflict",
62            objectKind(from), from.Name(), r.to)
63        r.errorf(prev.Pos(), "\twith this package member %s",
64            objectKind(prev))
65        return // since checkInPackageBlock would report redundant errors
66    }
67
68    // Check for conflicts in lexical scope.
69    r.checkInLexicalScope(fromr.packages[from.Pkg()])
70
71    // Finally, modify ImportSpec syntax to add or remove the Name as needed.
72    infopath_ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
73    if from.Imported().Name() == r.to {
74        // ImportSpec.Name not needed
75        path[1].(*ast.ImportSpec).Name = nil
76    } else {
77        // ImportSpec.Name needed
78        if spec := path[1].(*ast.ImportSpec); spec.Name == nil {
79            spec.Name = &ast.Ident{NamePosspec.Path.Pos(), Namer.to}
80            info.Defs[spec.Name] = from
81        }
82    }
83}
84
85// checkInPackageBlock performs safety checks for renames of
86// func/var/const/type objects in the package block.
87func (r *renamercheckInPackageBlock(from types.Object) {
88    // Check that there are no references to the name from another
89    // package if the renaming would make it unexported.
90    if ast.IsExported(from.Name()) && !ast.IsExported(r.to) {
91        for pkginfo := range r.packages {
92            if pkg == from.Pkg() {
93                continue
94            }
95            if id := someUse(infofrom); id != nil &&
96                !r.checkExport(idpkgfrom) {
97                break
98            }
99        }
100    }
101
102    info := r.packages[from.Pkg()]
103
104    // Check that in the package block, "init" is a function, and never referenced.
105    if r.to == "init" {
106        kind := objectKind(from)
107        if kind == "func" {
108            // Reject if intra-package references to it exist.
109            for idobj := range info.Uses {
110                if obj == from {
111                    r.errorf(from.Pos(),
112                        "renaming this func %q to %q would make it a package initializer",
113                        from.Name(), r.to)
114                    r.errorf(id.Pos(), "\tbut references to it exist")
115                    break
116                }
117            }
118        } else {
119            r.errorf(from.Pos(), "you cannot have a %s at package level named %q",
120                kindr.to)
121        }
122    }
123
124    // Check for conflicts between package block and all file blocks.
125    for _f := range info.Files {
126        fileScope := info.Info.Scopes[f]
127        bprev := fileScope.LookupParent(r.totoken.NoPos)
128        if b == fileScope {
129            r.errorf(from.Pos(), "renaming this %s %q to %q would conflict",
130                objectKind(from), from.Name(), r.to)
131            r.errorf(prev.Pos(), "\twith this %s",
132                objectKind(prev))
133            return // since checkInPackageBlock would report redundant errors
134        }
135    }
136
137    // Check for conflicts in lexical scope.
138    if from.Exported() {
139        for _info := range r.packages {
140            r.checkInLexicalScope(frominfo)
141        }
142    } else {
143        r.checkInLexicalScope(frominfo)
144    }
145}
146
147func (r *renamercheckInLocalScope(from types.Object) {
148    info := r.packages[from.Pkg()]
149
150    // Is this object an implicit local var for a type switch?
151    // Each case has its own var, whose position is the decl of y,
152    // but Ident in that decl does not appear in the Uses map.
153    //
154    //   switch y := x.(type) {     // Defs[Ident(y)] is undefined
155    //   case int:    print(y)       // Implicits[CaseClause(int)]    = Var(y_int)
156    //   case string: print(y)       // Implicits[CaseClause(string)] = Var(y_string)
157    //   }
158    //
159    var isCaseVar bool
160    for syntaxobj := range info.Implicits {
161        if _ok := syntax.(*ast.CaseClause); ok && obj.Pos() == from.Pos() {
162            isCaseVar = true
163            r.check(obj)
164        }
165    }
166
167    r.checkInLexicalScope(frominfo)
168
169    // Finally, if this was a type switch, change the variable y.
170    if isCaseVar {
171        _path_ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
172        path[0].(*ast.Ident).Name = r.to // path is [Ident AssignStmt TypeSwitchStmt...]
173    }
174}
175
176// checkInLexicalScope performs safety checks that a renaming does not
177// change the lexical reference structure of the specified package.
178//
179// For objects in lexical scope, there are three kinds of conflicts:
180// same-, sub-, and super-block conflicts.  We will illustrate all three
181// using this example:
182//
183//    var x int
184//    var z int
185//
186//    func f(y int) {
187//        print(x)
188//        print(y)
189//    }
190//
191// Renaming x to z encounters a SAME-BLOCK CONFLICT, because an object
192// with the new name already exists, defined in the same lexical block
193// as the old object.
194//
195// Renaming x to y encounters a SUB-BLOCK CONFLICT, because there exists
196// a reference to x from within (what would become) a hole in its scope.
197// The definition of y in an (inner) sub-block would cast a shadow in
198// the scope of the renamed variable.
199//
200// Renaming y to x encounters a SUPER-BLOCK CONFLICT.  This is the
201// converse situation: there is an existing definition of the new name
202// (x) in an (enclosing) super-block, and the renaming would create a
203// hole in its scope, within which there exist references to it.  The
204// new name casts a shadow in scope of the existing definition of x in
205// the super-block.
206//
207// Removing the old name (and all references to it) is always safe, and
208// requires no checks.
209func (r *renamercheckInLexicalScope(from types.Objectinfo *loader.PackageInfo) {
210    b := from.Parent() // the block defining the 'from' object
211    if b != nil {
212        toBlockto := b.LookupParent(r.tofrom.Parent().End())
213        if toBlock == b {
214            // same-block conflict
215            r.errorf(from.Pos(), "renaming this %s %q to %q",
216                objectKind(from), from.Name(), r.to)
217            r.errorf(to.Pos(), "\tconflicts with %s in same block",
218                objectKind(to))
219            return
220        } else if toBlock != nil {
221            // Check for super-block conflict.
222            // The name r.to is defined in a superblock.
223            // Is that name referenced from within this block?
224            forEachLexicalRef(infoto, func(id *ast.Identblock *types.Scopebool {
225                _obj := lexicalLookup(blockfrom.Name(), id.Pos())
226                if obj == from {
227                    // super-block conflict
228                    r.errorf(from.Pos(), "renaming this %s %q to %q",
229                        objectKind(from), from.Name(), r.to)
230                    r.errorf(id.Pos(), "\twould shadow this reference")
231                    r.errorf(to.Pos(), "\tto the %s declared here",
232                        objectKind(to))
233                    return false // stop
234                }
235                return true
236            })
237        }
238    }
239
240    // Check for sub-block conflict.
241    // Is there an intervening definition of r.to between
242    // the block defining 'from' and some reference to it?
243    forEachLexicalRef(infofrom, func(id *ast.Identblock *types.Scopebool {
244        // Find the block that defines the found reference.
245        // It may be an ancestor.
246        fromBlock_ := lexicalLookup(blockfrom.Name(), id.Pos())
247
248        // See what r.to would resolve to in the same scope.
249        toBlockto := lexicalLookup(blockr.toid.Pos())
250        if to != nil {
251            // sub-block conflict
252            if deeper(toBlockfromBlock) {
253                r.errorf(from.Pos(), "renaming this %s %q to %q",
254                    objectKind(from), from.Name(), r.to)
255                r.errorf(id.Pos(), "\twould cause this reference to become shadowed")
256                r.errorf(to.Pos(), "\tby this intervening %s definition",
257                    objectKind(to))
258                return false // stop
259            }
260        }
261        return true
262    })
263
264    // Renaming a type that is used as an embedded field
265    // requires renaming the field too. e.g.
266    //     type T int // if we rename this to U..
267    //     var s struct {T}
268    //     print(s.T) // ...this must change too
269    if _ok := from.(*types.TypeName); ok {
270        for idobj := range info.Uses {
271            if obj == from {
272                if field := info.Defs[id]; field != nil {
273                    r.check(field)
274                }
275            }
276        }
277    }
278}
279
280// lexicalLookup is like (*types.Scope).LookupParent but respects the
281// environment visible at pos.  It assumes the relative position
282// information is correct with each file.
283func lexicalLookup(block *types.Scopename stringpos token.Pos) (*types.Scopetypes.Object) {
284    for b := blockb != nilb = b.Parent() {
285        obj := b.Lookup(name)
286        // The scope of a package-level object is the entire package,
287        // so ignore pos in that case.
288        // No analogous clause is needed for file-level objects
289        // since no reference can appear before an import decl.
290        if obj != nil && (b == obj.Pkg().Scope() || obj.Pos() < pos) {
291            return bobj
292        }
293    }
294    return nilnil
295}
296
297// deeper reports whether block x is lexically deeper than y.
298func deeper(xy *types.Scopebool {
299    if x == y || x == nil {
300        return false
301    } else if y == nil {
302        return true
303    } else {
304        return deeper(x.Parent(), y.Parent())
305    }
306}
307
308// forEachLexicalRef calls fn(id, block) for each identifier id in package
309// info that is a reference to obj in lexical scope.  block is the
310// lexical block enclosing the reference.  If fn returns false the
311// iteration is terminated and findLexicalRefs returns false.
312func forEachLexicalRef(info *loader.PackageInfoobj types.Objectfn func(id *ast.Identblock *types.Scopeboolbool {
313    ok := true
314    var stack []ast.Node
315
316    var visit func(n ast.Nodebool
317    visit = func(n ast.Nodebool {
318        if n == nil {
319            stack = stack[:len(stack)-1// pop
320            return false
321        }
322        if !ok {
323            return false // bail out
324        }
325
326        stack = append(stackn// push
327        switch n := n.(type) {
328        case *ast.Ident:
329            if info.Uses[n] == obj {
330                block := enclosingBlock(&info.Infostack)
331                if !fn(nblock) {
332                    ok = false
333                }
334            }
335            return visit(nil// pop stack
336
337        case *ast.SelectorExpr:
338            // don't visit n.Sel
339            ast.Inspect(n.Xvisit)
340            return visit(nil// pop stack, don't descend
341
342        case *ast.CompositeLit:
343            // Handle recursion ourselves for struct literals
344            // so we don't visit field identifiers.
345            tv := info.Types[n]
346            if _ok := deref(tv.Type).Underlying().(*types.Struct); ok {
347                if n.Type != nil {
348                    ast.Inspect(n.Typevisit)
349                }
350                for _elt := range n.Elts {
351                    if kvok := elt.(*ast.KeyValueExpr); ok {
352                        ast.Inspect(kv.Valuevisit)
353                    } else {
354                        ast.Inspect(eltvisit)
355                    }
356                }
357                return visit(nil// pop stack, don't descend
358            }
359        }
360        return true
361    }
362
363    for _f := range info.Files {
364        ast.Inspect(fvisit)
365        if len(stack) != 0 {
366            panic(stack)
367        }
368        if !ok {
369            break
370        }
371    }
372    return ok
373}
374
375// enclosingBlock returns the innermost block enclosing the specified
376// AST node, specified in the form of a path from the root of the file,
377// [file...n].
378func enclosingBlock(info *types.Infostack []ast.Node) *types.Scope {
379    for i := range stack {
380        n := stack[len(stack)-1-i]
381        // For some reason, go/types always associates a
382        // function's scope with its FuncType.
383        // TODO(adonovan): feature or a bug?
384        switch f := n.(type) {
385        case *ast.FuncDecl:
386            n = f.Type
387        case *ast.FuncLit:
388            n = f.Type
389        }
390        if b := info.Scopes[n]; b != nil {
391            return b
392        }
393    }
394    panic("no Scope for *ast.File")
395}
396
397func (r *renamercheckLabel(label *types.Label) {
398    // Check there are no identical labels in the function's label block.
399    // (Label blocks don't nest, so this is easy.)
400    if prev := label.Parent().Lookup(r.to); prev != nil {
401        r.errorf(label.Pos(), "renaming this label %q to %q"label.Name(), prev.Name())
402        r.errorf(prev.Pos(), "\twould conflict with this one")
403    }
404}
405
406// checkStructField checks that the field renaming will not cause
407// conflicts at its declaration, or ambiguity or changes to any selection.
408func (r *renamercheckStructField(from *types.Var) {
409    // Check that the struct declaration is free of field conflicts,
410    // and field/method conflicts.
411
412    // go/types offers no easy way to get from a field (or interface
413    // method) to its declaring struct (or interface), so we must
414    // ascend the AST.
415    infopath_ := r.iprog.PathEnclosingInterval(from.Pos(), from.Pos())
416    // path matches this pattern:
417    // [Ident SelectorExpr? StarExpr? Field FieldList StructType ParenExpr* ... File]
418
419    // Ascend to FieldList.
420    var i int
421    for {
422        if _ok := path[i].(*ast.FieldList); ok {
423            break
424        }
425        i++
426    }
427    i++
428    tStruct := path[i].(*ast.StructType)
429    i++
430    // Ascend past parens (unlikely).
431    for {
432        _ok := path[i].(*ast.ParenExpr)
433        if !ok {
434            break
435        }
436        i++
437    }
438    if specok := path[i].(*ast.TypeSpec); ok {
439        // This struct is also a named type.
440        // We must check for direct (non-promoted) field/field
441        // and method/field conflicts.
442        named := info.Defs[spec.Name].Type()
443        previndices_ := types.LookupFieldOrMethod(namedtrueinfo.Pkgr.to)
444        if len(indices) == 1 {
445            r.errorf(from.Pos(), "renaming this field %q to %q",
446                from.Name(), r.to)
447            r.errorf(prev.Pos(), "\twould conflict with this %s",
448                objectKind(prev))
449            return // skip checkSelections to avoid redundant errors
450        }
451    } else {
452        // This struct is not a named type.
453        // We need only check for direct (non-promoted) field/field conflicts.
454        T := info.Types[tStruct].Type.Underlying().(*types.Struct)
455        for i := 0i < T.NumFields(); i++ {
456            if prev := T.Field(i); prev.Name() == r.to {
457                r.errorf(from.Pos(), "renaming this field %q to %q",
458                    from.Name(), r.to)
459                r.errorf(prev.Pos(), "\twould conflict with this field")
460                return // skip checkSelections to avoid redundant errors
461            }
462        }
463    }
464
465    // Renaming an anonymous field requires renaming the type too. e.g.
466    //     print(s.T)       // if we rename T to U,
467    //     type T int       // this and
468    //     var s struct {T} // this must change too.
469    if from.Anonymous() {
470        if namedok := from.Type().(*types.Named); ok {
471            r.check(named.Obj())
472        } else if namedok := deref(from.Type()).(*types.Named); ok {
473            r.check(named.Obj())
474        }
475    }
476
477    // Check integrity of existing (field and method) selections.
478    r.checkSelections(from)
479}
480
481// checkSelection checks that all uses and selections that resolve to
482// the specified object would continue to do so after the renaming.
483func (r *renamercheckSelections(from types.Object) {
484    for pkginfo := range r.packages {
485        if id := someUse(infofrom); id != nil {
486            if !r.checkExport(idpkgfrom) {
487                return
488            }
489        }
490
491        for syntaxsel := range info.Selections {
492            // There may be extant selections of only the old
493            // name or only the new name, so we must check both.
494            // (If neither, the renaming is sound.)
495            //
496            // In both cases, we wish to compare the lengths
497            // of the implicit field path (Selection.Index)
498            // to see if the renaming would change it.
499            //
500            // If a selection that resolves to 'from', when renamed,
501            // would yield a path of the same or shorter length,
502            // this indicates ambiguity or a changed referent,
503            // analogous to same- or sub-block lexical conflict.
504            //
505            // If a selection using the name 'to' would
506            // yield a path of the same or shorter length,
507            // this indicates ambiguity or shadowing,
508            // analogous to same- or super-block lexical conflict.
509
510            // TODO(adonovan): fix: derive from Types[syntax.X].Mode
511            // TODO(adonovan): test with pointer, value, addressable value.
512            isAddressable := true
513
514            if sel.Obj() == from {
515                if objindices_ := types.LookupFieldOrMethod(sel.Recv(), isAddressablefrom.Pkg(), r.to); obj != nil {
516                    // Renaming this existing selection of
517                    // 'from' may block access to an existing
518                    // type member named 'to'.
519                    delta := len(indices) - len(sel.Index())
520                    if delta > 0 {
521                        continue // no ambiguity
522                    }
523                    r.selectionConflict(fromdeltasyntaxobj)
524                    return
525                }
526
527            } else if sel.Obj().Name() == r.to {
528                if objindices_ := types.LookupFieldOrMethod(sel.Recv(), isAddressablefrom.Pkg(), from.Name()); obj == from {
529                    // Renaming 'from' may cause this existing
530                    // selection of the name 'to' to change
531                    // its meaning.
532                    delta := len(indices) - len(sel.Index())
533                    if delta > 0 {
534                        continue //  no ambiguity
535                    }
536                    r.selectionConflict(from, -deltasyntaxsel.Obj())
537                    return
538                }
539            }
540        }
541    }
542}
543
544func (r *renamerselectionConflict(from types.Objectdelta intsyntax *ast.SelectorExprobj types.Object) {
545    r.errorf(from.Pos(), "renaming this %s %q to %q",
546        objectKind(from), from.Name(), r.to)
547
548    switch {
549    case delta < 0:
550        // analogous to sub-block conflict
551        r.errorf(syntax.Sel.Pos(),
552            "\twould change the referent of this selection")
553        r.errorf(obj.Pos(), "\tof this %s"objectKind(obj))
554    case delta == 0:
555        // analogous to same-block conflict
556        r.errorf(syntax.Sel.Pos(),
557            "\twould make this reference ambiguous")
558        r.errorf(obj.Pos(), "\twith this %s"objectKind(obj))
559    case delta > 0:
560        // analogous to super-block conflict
561        r.errorf(syntax.Sel.Pos(),
562            "\twould shadow this selection")
563        r.errorf(obj.Pos(), "\tof the %s declared here",
564            objectKind(obj))
565    }
566}
567
568// checkMethod performs safety checks for renaming a method.
569// There are three hazards:
570//   - declaration conflicts
571//   - selection ambiguity/changes
572//   - entailed renamings of assignable concrete/interface types.
573//
574// We reject renamings initiated at concrete methods if it would
575// change the assignability relation.  For renamings of abstract
576// methods, we rename all methods transitively coupled to it via
577// assignability.
578func (r *renamercheckMethod(from *types.Func) {
579    // e.g. error.Error
580    if from.Pkg() == nil {
581        r.errorf(from.Pos(), "you cannot rename built-in method %s"from)
582        return
583    }
584
585    // ASSIGNABILITY: We reject renamings of concrete methods that
586    // would break a 'satisfy' constraint; but renamings of abstract
587    // methods are allowed to proceed, and we rename affected
588    // concrete and abstract methods as necessary.  It is the
589    // initial method that determines the policy.
590
591    // Check for conflict at point of declaration.
592    // Check to ensure preservation of assignability requirements.
593    R := recv(from).Type()
594    if isInterface(R) {
595        // Abstract method
596
597        // declaration
598        prev__ := types.LookupFieldOrMethod(Rfalsefrom.Pkg(), r.to)
599        if prev != nil {
600            r.errorf(from.Pos(), "renaming this interface method %q to %q",
601                from.Name(), r.to)
602            r.errorf(prev.Pos(), "\twould conflict with this method")
603            return
604        }
605
606        // Check all interfaces that embed this one for
607        // declaration conflicts too.
608        for _info := range r.packages {
609            // Start with named interface types (better errors)
610            for _obj := range info.Defs {
611                if objok := obj.(*types.TypeName); ok && isInterface(obj.Type()) {
612                    f__ := types.LookupFieldOrMethod(
613                        obj.Type(), falsefrom.Pkg(), from.Name())
614                    if f == nil {
615                        continue
616                    }
617                    t__ := types.LookupFieldOrMethod(
618                        obj.Type(), falsefrom.Pkg(), r.to)
619                    if t == nil {
620                        continue
621                    }
622                    r.errorf(from.Pos(), "renaming this interface method %q to %q",
623                        from.Name(), r.to)
624                    r.errorf(t.Pos(), "\twould conflict with this method")
625                    r.errorf(obj.Pos(), "\tin named interface type %q"obj.Name())
626                }
627            }
628
629            // Now look at all literal interface types (includes named ones again).
630            for etv := range info.Types {
631                if eok := e.(*ast.InterfaceType); ok {
632                    _ = e
633                    _ = tv.Type.(*types.Interface)
634                    // TODO(adonovan): implement same check as above.
635                }
636            }
637        }
638
639        // assignability
640        //
641        // Find the set of concrete or abstract methods directly
642        // coupled to abstract method 'from' by some
643        // satisfy.Constraint, and rename them too.
644        for key := range r.satisfy() {
645            // key = (lhs, rhs) where lhs is always an interface.
646
647            lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name())
648            if lsel == nil {
649                continue
650            }
651            rmethods := r.msets.MethodSet(key.RHS)
652            rsel := rmethods.Lookup(from.Pkg(), from.Name())
653            if rsel == nil {
654                continue
655            }
656
657            // If both sides have a method of this name,
658            // and one of them is m, the other must be coupled.
659            var coupled *types.Func
660            switch from {
661            case lsel.Obj():
662                coupled = rsel.Obj().(*types.Func)
663            case rsel.Obj():
664                coupled = lsel.Obj().(*types.Func)
665            default:
666                continue
667            }
668
669            // We must treat concrete-to-interface
670            // constraints like an implicit selection C.f of
671            // each interface method I.f, and check that the
672            // renaming leaves the selection unchanged and
673            // unambiguous.
674            //
675            // Fun fact: the implicit selection of C.f
676            //     type I interface{f()}
677            //     type C struct{I}
678            //     func (C) g()
679            //      var _ I = C{} // here
680            // yields abstract method I.f.  This can make error
681            // messages less than obvious.
682            //
683            if !isInterface(key.RHS) {
684                // The logic below was derived from checkSelections.
685
686                rtosel := rmethods.Lookup(from.Pkg(), r.to)
687                if rtosel != nil {
688                    rto := rtosel.Obj().(*types.Func)
689                    delta := len(rsel.Index()) - len(rtosel.Index())
690                    if delta < 0 {
691                        continue // no ambiguity
692                    }
693
694                    // TODO(adonovan): record the constraint's position.
695                    keyPos := token.NoPos
696
697                    r.errorf(from.Pos(), "renaming this method %q to %q",
698                        from.Name(), r.to)
699                    if delta == 0 {
700                        // analogous to same-block conflict
701                        r.errorf(keyPos"\twould make the %s method of %s invoked via interface %s ambiguous",
702                            r.tokey.RHSkey.LHS)
703                        r.errorf(rto.Pos(), "\twith (%s).%s",
704                            recv(rto).Type(), r.to)
705                    } else {
706                        // analogous to super-block conflict
707                        r.errorf(keyPos"\twould change the %s method of %s invoked via interface %s",
708                            r.tokey.RHSkey.LHS)
709                        r.errorf(coupled.Pos(), "\tfrom (%s).%s",
710                            recv(coupled).Type(), r.to)
711                        r.errorf(rto.Pos(), "\tto (%s).%s",
712                            recv(rto).Type(), r.to)
713                    }
714                    return // one error is enough
715                }
716            }
717
718            if !r.changeMethods {
719                // This should be unreachable.
720                r.errorf(from.Pos(), "internal error: during renaming of abstract method %s"from)
721                r.errorf(coupled.Pos(), "\tchangedMethods=false, coupled method=%s"coupled)
722                r.errorf(from.Pos(), "\tPlease file a bug report")
723                return
724            }
725
726            // Rename the coupled method to preserve assignability.
727            r.check(coupled)
728        }
729    } else {
730        // Concrete method
731
732        // declaration
733        previndices_ := types.LookupFieldOrMethod(Rtruefrom.Pkg(), r.to)
734        if prev != nil && len(indices) == 1 {
735            r.errorf(from.Pos(), "renaming this method %q to %q",
736                from.Name(), r.to)
737            r.errorf(prev.Pos(), "\twould conflict with this %s",
738                objectKind(prev))
739            return
740        }
741
742        // assignability
743        //
744        // Find the set of abstract methods coupled to concrete
745        // method 'from' by some satisfy.Constraint, and rename
746        // them too.
747        //
748        // Coupling may be indirect, e.g. I.f <-> C.f via type D.
749        //
750        //     type I interface {f()}
751        //    type C int
752        //    type (C) f()
753        //    type D struct{C}
754        //    var _ I = D{}
755        //
756        for key := range r.satisfy() {
757            // key = (lhs, rhs) where lhs is always an interface.
758            if isInterface(key.RHS) {
759                continue
760            }
761            rsel := r.msets.MethodSet(key.RHS).Lookup(from.Pkg(), from.Name())
762            if rsel == nil || rsel.Obj() != from {
763                continue // rhs does not have the method
764            }
765            lsel := r.msets.MethodSet(key.LHS).Lookup(from.Pkg(), from.Name())
766            if lsel == nil {
767                continue
768            }
769            imeth := lsel.Obj().(*types.Func)
770
771            // imeth is the abstract method (e.g. I.f)
772            // and key.RHS is the concrete coupling type (e.g. D).
773            if !r.changeMethods {
774                r.errorf(from.Pos(), "renaming this method %q to %q",
775                    from.Name(), r.to)
776                var pos token.Pos
777                var iface string
778
779                I := recv(imeth).Type()
780                if namedok := I.(*types.Named); ok {
781                    pos = named.Obj().Pos()
782                    iface = "interface " + named.Obj().Name()
783                } else {
784                    pos = from.Pos()
785                    iface = I.String()
786                }
787                r.errorf(pos"\twould make %s no longer assignable to %s",
788                    key.RHSiface)
789                r.errorf(imeth.Pos(), "\t(rename %s.%s if you intend to change both types)",
790                    Ifrom.Name())
791                return // one error is enough
792            }
793
794            // Rename the coupled interface method to preserve assignability.
795            r.check(imeth)
796        }
797    }
798
799    // Check integrity of existing (field and method) selections.
800    // We skip this if there were errors above, to avoid redundant errors.
801    r.checkSelections(from)
802}
803
804func (r *renamercheckExport(id *ast.Identpkg *types.Packagefrom types.Objectbool {
805    // Reject cross-package references if r.to is unexported.
806    // (Such references may be qualified identifiers or field/method
807    // selections.)
808    if !ast.IsExported(r.to) && pkg != from.Pkg() {
809        r.errorf(from.Pos(),
810            "renaming this %s %q to %q would make it unexported",
811            objectKind(from), from.Name(), r.to)
812        r.errorf(id.Pos(), "\tbreaking references from packages such as %q",
813            pkg.Path())
814        return false
815    }
816    return true
817}
818
819// satisfy returns the set of interface satisfaction constraints.
820func (r *renamersatisfy() map[satisfy.Constraint]bool {
821    if r.satisfyConstraints == nil {
822        // Compute on demand: it's expensive.
823        var f satisfy.Finder
824        for _info := range r.packages {
825            f.Find(&info.Infoinfo.Files)
826        }
827        r.satisfyConstraints = f.Result
828    }
829    return r.satisfyConstraints
830}
831
832// -- helpers ----------------------------------------------------------
833
834// recv returns the method's receiver.
835func recv(meth *types.Func) *types.Var {
836    return meth.Type().(*types.Signature).Recv()
837}
838
839// someUse returns an arbitrary use of obj within info.
840func someUse(info *loader.PackageInfoobj types.Object) *ast.Ident {
841    for ido := range info.Uses {
842        if o == obj {
843            return id
844        }
845    }
846    return nil
847}
848
849// -- Plundered from golang.org/x/tools/go/ssa -----------------
850
851func isInterface(T types.Typebool { return types.IsInterface(T) }
852
853func deref(typ types.Typetypes.Type {
854    if p_ := typ.(*types.Pointer); p != nil {
855        return p.Elem()
856    }
857    return typ
858}
859
MembersX
renamer.checkSelections.RangeStmt_15134.BlockStmt.RangeStmt_15275.BlockStmt.isAddressable
renamer.checkMethod
renamer.satisfy.BlockStmt.f
renamer.checkInLocalScope.r
forEachLexicalRef.fn
renamer.checkStructField.r
someUse.info
token
renamer.errorf
renamer.checkInPackageBlock.RangeStmt_3843.BlockStmt.b
renamer.checkInLexicalScope.info
renamer.checkInPackageBlock.BlockStmt.RangeStmt_2984.BlockStmt.id
renamer.checkInPackageBlock.BlockStmt.RangeStmt_4302.info
renamer.checkStructField._
someUse.RangeStmt_26386.id
renamer.checkSelections.RangeStmt_15134.BlockStmt.RangeStmt_15275.sel
renamer.checkInPackageBlock.from
forEachLexicalRef
forEachLexicalRef.stack
enclosingBlock.stack
forEachLexicalRef.RangeStmt_11422.f
renamer.checkStructField.BlockStmt.i
someUse.RangeStmt_26386.o
renamer.checkInLocalScope.BlockStmt.path
renamer.checkInLexicalScope.BlockStmt.toBlock
renamer.checkInLexicalScope.BlockStmt.RangeStmt_8883.id
forEachLexicalRef.BlockStmt.BlockStmt.BlockStmt.block
renamer.checkStructField.from
renamer.checkMethod.BlockStmt.RangeStmt_19350.BlockStmt.RangeStmt_20121.tv
renamer.checkMethod.BlockStmt.RangeStmt_20494.BlockStmt.BlockStmt.BlockStmt.keyPos
renamer.errorf.args
renamer.checkInLocalScope.RangeStmt_4964.syntax
renamer.checkInLexicalScope.BlockStmt.BlockStmt.BlockStmt._
renamer.checkLabel.prev
renamer.checkMethod.BlockStmt.RangeStmt_20494.BlockStmt.lsel
recv
renamer.checkInLocalScope.isCaseVar
deeper.x
forEachLexicalRef.info
forEachLexicalRef.BlockStmt.BlockStmt.BlockStmt.RangeStmt_11175.elt
fmt
someUse.obj
renamer.errorf.r
renamer.checkStructField.BlockStmt._
renamer.checkSelections.r
renamer.checkMethod.BlockStmt.RangeStmt_23775.BlockStmt.BlockStmt.pos
renamer.checkMethod.BlockStmt.RangeStmt_19350.BlockStmt.RangeStmt_20121.e
renamer.check.r
renamer.checkInFileBlock.r
renamer.checkInPackageBlock.BlockStmt.RangeStmt_2984.info
renamer.checkMethod.BlockStmt.RangeStmt_19350.info
renamer.satisfy.r
renamer.checkInLocalScope.from
renamer.checkInLocalScope.BlockStmt._
renamer.checkInLexicalScope.BlockStmt.RangeStmt_8883.obj
renamer.checkExport.r
renamer.checkMethod.BlockStmt.RangeStmt_23775.key
someUse
ast
renamer.check.from
renamer.checkInPackageBlock
renamer.checkInLexicalScope.BlockStmt.BlockStmt.BlockStmt.obj
renamer.checkMethod.r
satisfy
deeper
enclosingBlock
renamer.selectionConflict.from
deref.typ
deeper.y
renamer.checkStructField.BlockStmt.indices
renamer.checkStructField.BlockStmt.BlockStmt.prev
isInterface.T
renamer.checkMethod.BlockStmt.prev
renamer.checkMethod.BlockStmt.RangeStmt_19350.BlockStmt.RangeStmt_19442.obj
renamer.checkMethod.BlockStmt.RangeStmt_23775.BlockStmt.rsel
renamer.checkExport.pkg
renamer.checkInPackageBlock.BlockStmt.BlockStmt.RangeStmt_3410.id
lexicalLookup.block
enclosingBlock.RangeStmt_11790.i
renamer.checkLabel
renamer.checkExport.from
renamer.satisfy
renamer.checkInPackageBlock.BlockStmt.RangeStmt_2984.pkg
renamer.checkInLexicalScope.BlockStmt._
renamer.checkSelections
renamer.checkSelections.from
renamer.checkSelections.RangeStmt_15134.BlockStmt.RangeStmt_15275.BlockStmt.BlockStmt.indices
renamer.checkMethod.BlockStmt.RangeStmt_20494.BlockStmt.BlockStmt.rtosel
renamer.checkMethod.BlockStmt.RangeStmt_23775.BlockStmt.lsel
renamer.satisfy.BlockStmt.RangeStmt_25915.info
types
renamer.checkInPackageBlock.BlockStmt.BlockStmt.RangeStmt_3410.obj
lexicalLookup
renamer.checkLabel.r
deref
renamer.checkInLexicalScope.b
renamer.selectionConflict.delta
renamer.checkExport.id
renamer.checkInPackageBlock.RangeStmt_3843.f
renamer.checkInLexicalScope.BlockStmt.fromBlock
renamer.checkStructField.info
renamer.checkMethod.BlockStmt.RangeStmt_20494.BlockStmt.rmethods
renamer.checkExport
renamer.checkInFileBlock.from
renamer.checkInPackageBlock.RangeStmt_3843.BlockStmt.prev
renamer.checkSelections.RangeStmt_15134.BlockStmt.id
renamer.checkMethod.R
renamer.selectionConflict.r
renamer.checkInFileBlock.prev
renamer.checkInFileBlock.info
renamer.checkInFileBlock.path
renamer.checkInPackageBlock.r
renamer.errorf.pos
lexicalLookup.b
renamer.selectionConflict.obj
renamer.checkMethod.BlockStmt.RangeStmt_20494.BlockStmt.coupled
recv.meth
renamer.checkInFileBlock._
renamer.checkInLocalScope.RangeStmt_4964.obj
lexicalLookup.name
renamer.checkStructField.BlockStmt.prev
lexicalLookup.pos
renamer.checkSelections.RangeStmt_15134.BlockStmt.RangeStmt_15275.BlockStmt.BlockStmt.obj
renamer.checkMethod.BlockStmt.RangeStmt_19350.BlockStmt.RangeStmt_19442.BlockStmt.BlockStmt.f
isInterface
lexicalLookup.BlockStmt.obj
renamer.checkStructField
renamer.checkMethod.from
renamer.checkMethod.BlockStmt.RangeStmt_20494.key
renamer.checkInFileBlock
renamer.checkLabel.label
renamer.checkStructField.BlockStmt.named
renamer.errorf.format
renamer.checkInLexicalScope.r
forEachLexicalRef.ok
enclosingBlock.info
renamer.checkInLocalScope
renamer.checkStructField.i
renamer.selectionConflict
renamer.checkMethod.BlockStmt.RangeStmt_19350.BlockStmt.RangeStmt_19442.BlockStmt.BlockStmt._
renamer.checkMethod.BlockStmt.RangeStmt_23775.BlockStmt.BlockStmt.iface
renamer.checkInLexicalScope.from
forEachLexicalRef.obj
renamer.checkSelections.RangeStmt_15134.BlockStmt.RangeStmt_15275.syntax
renamer.checkMethod.BlockStmt.RangeStmt_20494.BlockStmt.rsel
renamer.checkMethod.BlockStmt._
renamer.checkMethod.BlockStmt.RangeStmt_19350.BlockStmt.RangeStmt_19442.BlockStmt.BlockStmt.t
renamer.checkMethod.BlockStmt.indices
loader
renamer.checkInLexicalScope.BlockStmt.to
renamer.checkSelections.RangeStmt_15134.info
renamer.checkSelections.RangeStmt_15134.BlockStmt.RangeStmt_15275.BlockStmt.BlockStmt._
renamer.checkSelections.RangeStmt_15134.pkg
renamer.selectionConflict.syntax
renamer.checkMethod.BlockStmt.RangeStmt_23775.BlockStmt.BlockStmt.I
renamer.check
renamer.checkInPackageBlock.BlockStmt.kind
renamer.checkInLexicalScope
renamer.checkStructField.path
Members
X