internal/lsp: check no errs for assignability rename

The satisfy package has a precondition for Finder.Find that requires
that the package has no type errors. If this is a check that we would
perform, give an error and do not rename.

Fixes golang/go#32882

Change-Id: Id44b451bf86ff883fd78a6306f2b2565ad3bdeb9
Reviewed-on: https://go-review.googlesource.com/c/tools/+/184857
Run-TryBot: Suzy Mueller <suzmue@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index 17d845c..05329ee 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -510,6 +510,8 @@
 func (r *runner) Rename(t *testing.T, data tests.Renames) {
 	ctx := context.Background()
 	for spn, newText := range data {
+		tag := fmt.Sprintf("%s-rename", newText)
+
 		uri := spn.URI()
 		filename := uri.Filename()
 		sm, err := r.mapper(uri)
@@ -529,7 +531,12 @@
 			NewName:  newText,
 		})
 		if err != nil {
-			t.Error(err)
+			renamed := string(r.data.Golden(tag, filename, func() ([]byte, error) {
+				return []byte(err.Error()), nil
+			}))
+			if err.Error() != renamed {
+				t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v\n", newText, renamed, err)
+			}
 			continue
 		}
 
@@ -556,7 +563,6 @@
 
 		got := applyEdits(string(m.Content), sedits)
 
-		tag := fmt.Sprintf("%s-rename", newText)
 		gorenamed := string(r.data.Golden(tag, filename, func() ([]byte, error) {
 			return []byte(got), nil
 		}))
diff --git a/internal/lsp/source/rename_check.go b/internal/lsp/source/rename_check.go
index 0b602f4..8d2ac92 100644
--- a/internal/lsp/source/rename_check.go
+++ b/internal/lsp/source/rename_check.go
@@ -789,6 +789,19 @@
 		// Compute on demand: it's expensive.
 		var f satisfy.Finder
 		for _, pkg := range r.packages {
+			// From satisfy.Finder documentation:
+			//
+			// The package must be free of type errors, and
+			// info.{Defs,Uses,Selections,Types} must have been populated by the
+			// type-checker.
+			//
+			// Only proceed if all packages have no errors.
+			if errs := pkg.GetErrors(); len(errs) > 0 {
+				r.errorf(token.NoPos, // we don't have a position for this error.
+					"renaming %q to %q not possible because %q has errors",
+					r.from, r.to, pkg.PkgPath())
+				return nil
+			}
 			f.Find(pkg.GetTypesInfo(), pkg.GetSyntax())
 		}
 		r.satisfyConstraints = f.Result
diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go
index d310250..4eb95de 100644
--- a/internal/lsp/source/source_test.go
+++ b/internal/lsp/source/source_test.go
@@ -479,6 +479,8 @@
 func (r *runner) Rename(t *testing.T, data tests.Renames) {
 	ctx := context.Background()
 	for spn, newText := range data {
+		tag := fmt.Sprintf("%s-rename", newText)
+
 		f, err := r.view.GetFile(ctx, spn.URI())
 		if err != nil {
 			t.Fatalf("failed for %v: %v", spn, err)
@@ -492,7 +494,12 @@
 		}
 		changes, err := ident.Rename(context.Background(), newText)
 		if err != nil {
-			t.Error(err)
+			renamed := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) {
+				return []byte(err.Error()), nil
+			}))
+			if err.Error() != renamed {
+				t.Errorf("rename failed for %s, expected:\n%v\ngot:\n%v\n", newText, renamed, err)
+			}
 			continue
 		}
 
@@ -513,7 +520,6 @@
 		}
 
 		got := applyEdits(string(data), edits)
-		tag := fmt.Sprintf("%s-rename", newText)
 		gorenamed := string(r.data.Golden(tag, spn.URI().Filename(), func() ([]byte, error) {
 			return []byte(got), nil
 		}))
diff --git a/internal/lsp/testdata/rename/a/random.go.golden b/internal/lsp/testdata/rename/a/random.go.golden
index 427b7d2..0ad12fe 100644
--- a/internal/lsp/testdata/rename/a/random.go.golden
+++ b/internal/lsp/testdata/rename/a/random.go.golden
@@ -42,6 +42,138 @@
 	}
 }
 
+-- fmt2-rename --
+package a
+
+import (
+	lg "log"
+	"fmt"
+	fmt2 "fmt"
+)
+
+func Random() int {
+	y := 6 + 7
+	return y
+}
+
+func Random2(y int) int { //@rename("y", "z")
+	return y
+}
+
+type Pos struct {
+	x, y int
+}
+
+func (p *Pos) Sum() int {
+	return p.x + p.y //@rename("x", "myX")
+}
+
+func _() {
+	var p Pos   //@rename("p", "pos")
+	_ = p.Sum() //@rename("Sum", "GetSum")
+}
+
+func sw() {
+	var x interface{}
+
+	switch y := x.(type) { //@rename("y", "y0")
+	case int:
+		fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format")
+	case string:
+		lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log")
+	default:
+		fmt2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2")
+	}
+}
+
+-- format-rename --
+package a
+
+import (
+	lg "log"
+	format "fmt"
+	f2 "fmt"
+)
+
+func Random() int {
+	y := 6 + 7
+	return y
+}
+
+func Random2(y int) int { //@rename("y", "z")
+	return y
+}
+
+type Pos struct {
+	x, y int
+}
+
+func (p *Pos) Sum() int {
+	return p.x + p.y //@rename("x", "myX")
+}
+
+func _() {
+	var p Pos   //@rename("p", "pos")
+	_ = p.Sum() //@rename("Sum", "GetSum")
+}
+
+func sw() {
+	var x interface{}
+
+	switch y := x.(type) { //@rename("y", "y0")
+	case int:
+		format.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format")
+	case string:
+		lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log")
+	default:
+		f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2")
+	}
+}
+
+-- log-rename --
+package a
+
+import (
+	"log"
+	"fmt"
+	f2 "fmt"
+)
+
+func Random() int {
+	y := 6 + 7
+	return y
+}
+
+func Random2(y int) int { //@rename("y", "z")
+	return y
+}
+
+type Pos struct {
+	x, y int
+}
+
+func (p *Pos) Sum() int {
+	return p.x + p.y //@rename("x", "myX")
+}
+
+func _() {
+	var p Pos   //@rename("p", "pos")
+	_ = p.Sum() //@rename("Sum", "GetSum")
+}
+
+func sw() {
+	var x interface{}
+
+	switch y := x.(type) { //@rename("y", "y0")
+	case int:
+		fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format")
+	case string:
+		log.Printf("%s", y) //@rename("y", "y2"),rename("lg","log")
+	default:
+		f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2")
+	}
+}
+
 -- myX-rename --
 package a
 
@@ -130,50 +262,6 @@
 	}
 }
 
--- z-rename --
-package a
-
-import (
-	lg "log"
-	"fmt"
-	f2 "fmt"
-)
-
-func Random() int {
-	y := 6 + 7
-	return y
-}
-
-func Random2(z int) int { //@rename("y", "z")
-	return z
-}
-
-type Pos struct {
-	x, y int
-}
-
-func (p *Pos) Sum() int {
-	return p.x + p.y //@rename("x", "myX")
-}
-
-func _() {
-	var p Pos   //@rename("p", "pos")
-	_ = p.Sum() //@rename("Sum", "GetSum")
-}
-
-func sw() {
-	var x interface{}
-
-	switch y := x.(type) { //@rename("y", "y0")
-	case int:
-		fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format")
-	case string:
-		lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log")
-	default:
-		f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2")
-	}
-}
-
 -- y0-rename --
 package a
 
@@ -350,55 +438,11 @@
 	}
 }
 
--- format-rename --
+-- z-rename --
 package a
 
 import (
 	lg "log"
-	format "fmt"
-	f2 "fmt"
-)
-
-func Random() int {
-	y := 6 + 7
-	return y
-}
-
-func Random2(y int) int { //@rename("y", "z")
-	return y
-}
-
-type Pos struct {
-	x, y int
-}
-
-func (p *Pos) Sum() int {
-	return p.x + p.y //@rename("x", "myX")
-}
-
-func _() {
-	var p Pos   //@rename("p", "pos")
-	_ = p.Sum() //@rename("Sum", "GetSum")
-}
-
-func sw() {
-	var x interface{}
-
-	switch y := x.(type) { //@rename("y", "y0")
-	case int:
-		format.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format")
-	case string:
-		lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log")
-	default:
-		f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2")
-	}
-}
-
--- log-rename --
-package a
-
-import (
-	"log"
 	"fmt"
 	f2 "fmt"
 )
@@ -408,52 +452,8 @@
 	return y
 }
 
-func Random2(y int) int { //@rename("y", "z")
-	return y
-}
-
-type Pos struct {
-	x, y int
-}
-
-func (p *Pos) Sum() int {
-	return p.x + p.y //@rename("x", "myX")
-}
-
-func _() {
-	var p Pos   //@rename("p", "pos")
-	_ = p.Sum() //@rename("Sum", "GetSum")
-}
-
-func sw() {
-	var x interface{}
-
-	switch y := x.(type) { //@rename("y", "y0")
-	case int:
-		fmt.Printf("%d", y) //@rename("y", "y1"),rename("fmt", "format")
-	case string:
-		log.Printf("%s", y) //@rename("y", "y2"),rename("lg","log")
-	default:
-		f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2")
-	}
-}
-
--- fmt2-rename --
-package a
-
-import (
-	lg "log"
-	"fmt"
-	fmt2 "fmt"
-)
-
-func Random() int {
-	y := 6 + 7
-	return y
-}
-
-func Random2(y int) int { //@rename("y", "z")
-	return y
+func Random2(z int) int { //@rename("y", "z")
+	return z
 }
 
 type Pos struct {
@@ -478,7 +478,7 @@
 	case string:
 		lg.Printf("%s", y) //@rename("y", "y2"),rename("lg","log")
 	default:
-		fmt2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2")
+		f2.Printf("%v", y) //@rename("y", "y3"),rename("f2","fmt2")
 	}
 }
 
diff --git a/internal/lsp/testdata/rename/bad/bad.go.golden b/internal/lsp/testdata/rename/bad/bad.go.golden
new file mode 100644
index 0000000..7f45813
--- /dev/null
+++ b/internal/lsp/testdata/rename/bad/bad.go.golden
@@ -0,0 +1,2 @@
+-- rFunc-rename --
+renaming "sFunc" to "rFunc" not possible because "golang.org/x/tools/internal/lsp/rename/bad" has errors
diff --git a/internal/lsp/testdata/rename/bad/bad.go.in b/internal/lsp/testdata/rename/bad/bad.go.in
new file mode 100644
index 0000000..56dbee7
--- /dev/null
+++ b/internal/lsp/testdata/rename/bad/bad.go.in
@@ -0,0 +1,8 @@
+package bad
+
+type myStruct struct {
+}
+
+func (s *myStruct) sFunc() bool { //@rename("sFunc", "rFunc")
+	return s.Bad
+}
diff --git a/internal/lsp/tests/tests.go b/internal/lsp/tests/tests.go
index 65a29f3..aa5fd60 100644
--- a/internal/lsp/tests/tests.go
+++ b/internal/lsp/tests/tests.go
@@ -34,7 +34,7 @@
 	ExpectedTypeDefinitionsCount   = 2
 	ExpectedHighlightsCount        = 2
 	ExpectedReferencesCount        = 4
-	ExpectedRenamesCount           = 11
+	ExpectedRenamesCount           = 12
 	ExpectedSymbolsCount           = 1
 	ExpectedSignaturesCount        = 21
 	ExpectedLinksCount             = 2