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 | |
5 | package rename |
6 | |
7 | // This file defines the safety checks for each kind of renaming. |
8 | |
9 | import ( |
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. |
20 | func (r *renamer) errorf(pos token.Pos, format string, args ...interface{}) { |
21 | r.hadConflicts = true |
22 | reportError(r.iprog.Fset.Position(pos), fmt.Sprintf(format, args...)) |
23 | } |
24 | |
25 | // check performs safety checks of the renaming of the 'from' object to r.to. |
26 | func (r *renamer) check(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 v, ok := from.(*types.Var); ok && v.IsField() { |
40 | r.checkStructField(v) |
41 | } else if f, ok := 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. |
53 | func (r *renamer) checkInFileBlock(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(from, r.packages[from.Pkg()]) |
70 | |
71 | // Finally, modify ImportSpec syntax to add or remove the Name as needed. |
72 | info, path, _ := 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{NamePos: spec.Path.Pos(), Name: r.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. |
87 | func (r *renamer) checkInPackageBlock(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 pkg, info := range r.packages { |
92 | if pkg == from.Pkg() { |
93 | continue |
94 | } |
95 | if id := someUse(info, from); id != nil && |
96 | !r.checkExport(id, pkg, from) { |
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 id, obj := 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 | kind, r.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 | b, prev := fileScope.LookupParent(r.to, token.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(from, info) |
141 | } |
142 | } else { |
143 | r.checkInLexicalScope(from, info) |
144 | } |
145 | } |
146 | |
147 | func (r *renamer) checkInLocalScope(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 syntax, obj := 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(from, info) |
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. |
209 | func (r *renamer) checkInLexicalScope(from types.Object, info *loader.PackageInfo) { |
210 | b := from.Parent() // the block defining the 'from' object |
211 | if b != nil { |
212 | toBlock, to := b.LookupParent(r.to, from.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(info, to, func(id *ast.Ident, block *types.Scope) bool { |
225 | _, obj := lexicalLookup(block, from.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(info, from, func(id *ast.Ident, block *types.Scope) bool { |
244 | // Find the block that defines the found reference. |
245 | // It may be an ancestor. |
246 | fromBlock, _ := lexicalLookup(block, from.Name(), id.Pos()) |
247 | |
248 | // See what r.to would resolve to in the same scope. |
249 | toBlock, to := lexicalLookup(block, r.to, id.Pos()) |
250 | if to != nil { |
251 | // sub-block conflict |
252 | if deeper(toBlock, fromBlock) { |
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 id, obj := 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. |
283 | func lexicalLookup(block *types.Scope, name string, pos token.Pos) (*types.Scope, types.Object) { |
284 | for b := block; b != nil; b = 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 b, obj |
292 | } |
293 | } |
294 | return nil, nil |
295 | } |
296 | |
297 | // deeper reports whether block x is lexically deeper than y. |
298 | func deeper(x, y *types.Scope) bool { |
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. |
312 | func forEachLexicalRef(info *loader.PackageInfo, obj types.Object, fn func(id *ast.Ident, block *types.Scope) bool) bool { |
313 | ok := true |
314 | var stack []ast.Node |
315 | |
316 | var visit func(n ast.Node) bool |
317 | visit = func(n ast.Node) bool { |
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(stack, n) // push |
327 | switch n := n.(type) { |
328 | case *ast.Ident: |
329 | if info.Uses[n] == obj { |
330 | block := enclosingBlock(&info.Info, stack) |
331 | if !fn(n, block) { |
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.X, visit) |
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.Type, visit) |
349 | } |
350 | for _, elt := range n.Elts { |
351 | if kv, ok := elt.(*ast.KeyValueExpr); ok { |
352 | ast.Inspect(kv.Value, visit) |
353 | } else { |
354 | ast.Inspect(elt, visit) |
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(f, visit) |
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]. |
378 | func enclosingBlock(info *types.Info, stack []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 | |
397 | func (r *renamer) checkLabel(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. |
408 | func (r *renamer) checkStructField(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 | info, path, _ := 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 spec, ok := 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 | prev, indices, _ := types.LookupFieldOrMethod(named, true, info.Pkg, r.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 := 0; i < 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 named, ok := from.Type().(*types.Named); ok { |
471 | r.check(named.Obj()) |
472 | } else if named, ok := 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. |
483 | func (r *renamer) checkSelections(from types.Object) { |
484 | for pkg, info := range r.packages { |
485 | if id := someUse(info, from); id != nil { |
486 | if !r.checkExport(id, pkg, from) { |
487 | return |
488 | } |
489 | } |
490 | |
491 | for syntax, sel := 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 obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.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(from, delta, syntax, obj) |
524 | return |
525 | } |
526 | |
527 | } else if sel.Obj().Name() == r.to { |
528 | if obj, indices, _ := types.LookupFieldOrMethod(sel.Recv(), isAddressable, from.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, -delta, syntax, sel.Obj()) |
537 | return |
538 | } |
539 | } |
540 | } |
541 | } |
542 | } |
543 | |
544 | func (r *renamer) selectionConflict(from types.Object, delta int, syntax *ast.SelectorExpr, obj 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. |
578 | func (r *renamer) checkMethod(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(R, false, from.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 obj, ok := obj.(*types.TypeName); ok && isInterface(obj.Type()) { |
612 | f, _, _ := types.LookupFieldOrMethod( |
613 | obj.Type(), false, from.Pkg(), from.Name()) |
614 | if f == nil { |
615 | continue |
616 | } |
617 | t, _, _ := types.LookupFieldOrMethod( |
618 | obj.Type(), false, from.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 e, tv := range info.Types { |
631 | if e, ok := 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.to, key.RHS, key.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.to, key.RHS, key.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 | prev, indices, _ := types.LookupFieldOrMethod(R, true, from.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 named, ok := 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.RHS, iface) |
789 | r.errorf(imeth.Pos(), "\t(rename %s.%s if you intend to change both types)", |
790 | I, from.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 | |
804 | func (r *renamer) checkExport(id *ast.Ident, pkg *types.Package, from types.Object) bool { |
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. |
820 | func (r *renamer) satisfy() 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.Info, info.Files) |
826 | } |
827 | r.satisfyConstraints = f.Result |
828 | } |
829 | return r.satisfyConstraints |
830 | } |
831 | |
832 | // -- helpers ---------------------------------------------------------- |
833 | |
834 | // recv returns the method's receiver. |
835 | func 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. |
840 | func someUse(info *loader.PackageInfo, obj types.Object) *ast.Ident { |
841 | for id, o := 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 | |
851 | func isInterface(T types.Type) bool { return types.IsInterface(T) } |
852 | |
853 | func deref(typ types.Type) types.Type { |
854 | if p, _ := typ.(*types.Pointer); p != nil { |
855 | return p.Elem() |
856 | } |
857 | return typ |
858 | } |
859 |
Members