gopls/internal/analysis/modernize: fix bug in slicescontains
Previously, a fix was offered to use slices.Contains(haystack, needle)
even when the slice element type was not identical to the needle,
resulting in a type error. (We really should make RunWithSuggestedFixes
verify that the fixed code is still well-typed.)
Now, we don't report a diagnostic if the types differ.
(We could insert a widening cast in one of the two cases,
but that's left for a follow-up.)
Fixes golang/go#71313
Change-Id: I41764da93085eef8ea5d30337d01fe03ee345f6d
Reviewed-on: https://go-review.googlesource.com/c/tools/+/645856
Auto-Submit: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/gopls/internal/analysis/modernize/slicescontains.go b/gopls/internal/analysis/modernize/slicescontains.go
index 062083c..dc0aa61 100644
--- a/gopls/internal/analysis/modernize/slicescontains.go
+++ b/gopls/internal/analysis/modernize/slicescontains.go
@@ -85,13 +85,33 @@
switch cond := ifStmt.Cond.(type) {
case *ast.BinaryExpr:
if cond.Op == token.EQL {
+ var elem ast.Expr
if isSliceElem(cond.X) {
funcName = "Contains"
+ elem = cond.X
arg2 = cond.Y // "if elem == needle"
} else if isSliceElem(cond.Y) {
funcName = "Contains"
+ elem = cond.Y
arg2 = cond.X // "if needle == elem"
}
+
+ // Reject if elem and needle have different types.
+ if elem != nil {
+ tElem := info.TypeOf(elem)
+ tNeedle := info.TypeOf(arg2)
+ if !types.Identical(tElem, tNeedle) {
+ // Avoid ill-typed slices.Contains([]error, any).
+ if !types.AssignableTo(tNeedle, tElem) {
+ return
+ }
+ // TODO(adonovan): relax this check to allow
+ // slices.Contains([]error, error(any)),
+ // inserting an explicit widening conversion
+ // around the needle.
+ return
+ }
+ }
}
case *ast.CallExpr:
diff --git a/gopls/internal/analysis/modernize/testdata/src/slicescontains/slicescontains.go b/gopls/internal/analysis/modernize/testdata/src/slicescontains/slicescontains.go
index ecb7371..6116ce1 100644
--- a/gopls/internal/analysis/modernize/testdata/src/slicescontains/slicescontains.go
+++ b/gopls/internal/analysis/modernize/testdata/src/slicescontains/slicescontains.go
@@ -127,3 +127,22 @@
}
func predicate(int) bool
+
+// Regression tests for bad fixes when needle
+// and haystack have different types (#71313):
+
+func nopeNeedleHaystackDifferentTypes(x any, args []error) {
+ for _, arg := range args {
+ if arg == x {
+ return
+ }
+ }
+}
+
+func nopeNeedleHaystackDifferentTypes2(x error, args []any) {
+ for _, arg := range args {
+ if arg == x {
+ return
+ }
+ }
+}
diff --git a/gopls/internal/analysis/modernize/testdata/src/slicescontains/slicescontains.go.golden b/gopls/internal/analysis/modernize/testdata/src/slicescontains/slicescontains.go.golden
index 561e42f..2d67395 100644
--- a/gopls/internal/analysis/modernize/testdata/src/slicescontains/slicescontains.go.golden
+++ b/gopls/internal/analysis/modernize/testdata/src/slicescontains/slicescontains.go.golden
@@ -83,3 +83,22 @@
}
func predicate(int) bool
+
+// Regression tests for bad fixes when needle
+// and haystack have different types (#71313):
+
+func nopeNeedleHaystackDifferentTypes(x any, args []error) {
+ for _, arg := range args {
+ if arg == x {
+ return
+ }
+ }
+}
+
+func nopeNeedleHaystackDifferentTypes2(x error, args []any) {
+ for _, arg := range args {
+ if arg == x {
+ return
+ }
+ }
+}