x/tools: run slicesbackward analyzer

I renamed the loop vars in most cases, which suggests the analyzer
could use a better heuristic for the name. For example, use the LHS
of a following y := x or switch y := x.(type) statement, or use the
singular of the range operand if its name is a plural.

I also manually fixed one error due to golang/go#78629

Change-Id: I32b5f08f1adcac8761ebd8984f4bde28a05af0fd
Reviewed-on: https://go-review.googlesource.com/c/tools/+/765305
Auto-Submit: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Madeline Kalil <mkalil@google.com>
diff --git a/go/analysis/passes/httpresponse/httpresponse.go b/go/analysis/passes/httpresponse/httpresponse.go
index 37ecb65..22539de 100644
--- a/go/analysis/passes/httpresponse/httpresponse.go
+++ b/go/analysis/passes/httpresponse/httpresponse.go
@@ -9,6 +9,7 @@
 import (
 	"go/ast"
 	"go/types"
+	"slices"
 
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
@@ -144,8 +145,8 @@
 // node, along with the number of call expressions encountered.
 func restOfBlock(stack []ast.Node) ([]ast.Stmt, int) {
 	var ncalls int
-	for i := len(stack) - 1; i >= 0; i-- {
-		if b, ok := stack[i].(*ast.BlockStmt); ok {
+	for i, n := range slices.Backward(stack) {
+		if b, ok := n.(*ast.BlockStmt); ok {
 			for j, v := range b.List {
 				if v == stack[i+1] {
 					return b.List[j:], ncalls
@@ -154,7 +155,7 @@
 			break
 		}
 
-		if _, ok := stack[i].(*ast.CallExpr); ok {
+		if _, ok := n.(*ast.CallExpr); ok {
 			ncalls++
 		}
 	}
diff --git a/go/callgraph/vta/propagation.go b/go/callgraph/vta/propagation.go
index a96f82d..33ec246 100644
--- a/go/callgraph/vta/propagation.go
+++ b/go/callgraph/vta/propagation.go
@@ -155,9 +155,9 @@
 		sccToTypes[sccID] = &typeSet
 	}
 
-	for i := len(sccs) - 1; i >= 0; i-- {
+	for i, scc := range slices.Backward(sccs) {
 		nextSccs := make(map[int]empty)
-		for _, n := range sccs[i] {
+		for _, n := range scc {
 			for succ := range graph.successors(n) {
 				nextSccs[idxToSccID[succ]] = empty{}
 			}
diff --git a/gopls/internal/analysis/infertypeargs/infertypeargs.go b/gopls/internal/analysis/infertypeargs/infertypeargs.go
index 0ce43e6..5a22aef 100644
--- a/gopls/internal/analysis/infertypeargs/infertypeargs.go
+++ b/gopls/internal/analysis/infertypeargs/infertypeargs.go
@@ -8,6 +8,7 @@
 	"go/ast"
 	"go/token"
 	"go/types"
+	"slices"
 
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
@@ -74,7 +75,7 @@
 		// Start removing argument expressions from the right, and check if we can
 		// still infer the call expression.
 		required := len(indices) // number of type expressions that are required
-		for i := len(indices) - 1; i >= 0; i-- {
+		for i := range slices.Backward(indices) {
 			var fun ast.Expr
 			if i == 0 {
 				// No longer an index expression: just use the parameterized operand.
diff --git a/gopls/internal/cache/check.go b/gopls/internal/cache/check.go
index a35758c..0bd7366 100644
--- a/gopls/internal/cache/check.go
+++ b/gopls/internal/cache/check.go
@@ -17,6 +17,7 @@
 	"go/types"
 	"regexp"
 	"runtime"
+	"slices"
 	"sort"
 	"strings"
 	"sync"
@@ -1866,8 +1867,7 @@
 	// we reach the workspace.
 	var errors []*Diagnostic
 	for _, depErr := range relevantErrors {
-		for i := len(depErr.ImportStack) - 1; i >= 0; i-- {
-			item := depErr.ImportStack[i]
+		for _, item := range slices.Backward(depErr.ImportStack) {
 			if snapshot.IsWorkspacePackage(PackageID(item)) {
 				break
 			}
@@ -1905,8 +1905,7 @@
 	// Add a diagnostic to the module that contained the lowest-level import of
 	// the missing package.
 	for _, depErr := range relevantErrors {
-		for i := len(depErr.ImportStack) - 1; i >= 0; i-- {
-			item := depErr.ImportStack[i]
+		for _, item := range slices.Backward(depErr.ImportStack) {
 			mp := snapshot.Metadata(PackageID(item))
 			if mp == nil || mp.Module == nil {
 				continue
diff --git a/gopls/internal/cache/mod.go b/gopls/internal/cache/mod.go
index 940c5e0..26e6149 100644
--- a/gopls/internal/cache/mod.go
+++ b/gopls/internal/cache/mod.go
@@ -9,6 +9,7 @@
 	"errors"
 	"fmt"
 	"regexp"
+	"slices"
 	"strings"
 
 	"golang.org/x/mod/modfile"
@@ -380,8 +381,8 @@
 	var reference *modfile.Line
 	matches := moduleVersionInErrorRe.FindAllStringSubmatch(goCmdError, -1)
 
-	for i := len(matches) - 1; i >= 0; i-- {
-		ver := module.Version{Path: matches[i][1], Version: matches[i][2]}
+	for _, match := range slices.Backward(matches) {
+		ver := module.Version{Path: match[1], Version: match[2]}
 		if err := module.Check(ver.Path, ver.Version); err != nil {
 			continue
 		}
@@ -412,8 +413,8 @@
 func (s *Snapshot) goCommandDiagnostic(pm *ParsedModule, loc protocol.Location, goCmdError string) (*Diagnostic, error) {
 	matches := moduleVersionInErrorRe.FindAllStringSubmatch(goCmdError, -1)
 	var innermost *module.Version
-	for i := len(matches) - 1; i >= 0; i-- {
-		ver := module.Version{Path: matches[i][1], Version: matches[i][2]}
+	for _, match := range slices.Backward(matches) {
+		ver := module.Version{Path: match[1], Version: match[2]}
 		if err := module.Check(ver.Path, ver.Version); err != nil {
 			continue
 		}
diff --git a/gopls/internal/cmd/semantictokens.go b/gopls/internal/cmd/semantictokens.go
index 55182f3..6dd0052 100644
--- a/gopls/internal/cmd/semantictokens.go
+++ b/gopls/internal/cmd/semantictokens.go
@@ -11,6 +11,7 @@
 	"fmt"
 	"log"
 	"os"
+	"slices"
 	"unicode/utf8"
 
 	"golang.org/x/tools/gopls/internal/protocol"
@@ -148,9 +149,8 @@
 		return nil
 	}
 	lines := bytes.Split(file.mapper.Content, []byte{'\n'})
-	for i := len(marks) - 1; i >= 0; i-- {
-		mx := marks[i]
-		markLine(mx, lines)
+	for _, mark := range slices.Backward(marks) {
+		markLine(mark, lines)
 	}
 	os.Stdout.Write(bytes.Join(lines, []byte{'\n'}))
 	return nil
diff --git a/gopls/internal/golang/completion/util.go b/gopls/internal/golang/completion/util.go
index 10d6325..0b07129 100644
--- a/gopls/internal/golang/completion/util.go
+++ b/gopls/internal/golang/completion/util.go
@@ -8,6 +8,7 @@
 	"go/ast"
 	"go/token"
 	"go/types"
+	"slices"
 
 	"golang.org/x/tools/go/types/typeutil"
 	"golang.org/x/tools/gopls/internal/golang"
@@ -268,9 +269,9 @@
 		}
 	}
 
-	for i := len(blockLines) - 1; i >= 0; i-- {
-		if blockLines[i].End() < pos {
-			return blockLines[i]
+	for _, stmt := range slices.Backward(blockLines) {
+		if stmt.End() < pos {
+			return stmt
 		}
 	}
 
diff --git a/gopls/internal/golang/hover.go b/gopls/internal/golang/hover.go
index 65618fc..31942fd 100644
--- a/gopls/internal/golang/hover.go
+++ b/gopls/internal/golang/hover.go
@@ -1687,8 +1687,8 @@
 		switch n := n.(type) {
 		case *ast.Field:
 			findEnclosingDeclAndSpec := func() {
-				for i := len(stack) - 1; i >= 0; i-- {
-					switch n := stack[i].(type) {
+				for _, n := range slices.Backward(stack) {
+					switch n := n.(type) {
 					case ast.Spec:
 						spec = n
 					case ast.Decl:
diff --git a/gopls/internal/golang/semtok.go b/gopls/internal/golang/semtok.go
index 35506a2..2c8c7d5 100644
--- a/gopls/internal/golang/semtok.go
+++ b/gopls/internal/golang/semtok.go
@@ -321,8 +321,7 @@
 // strStack converts the stack to a string, for debugging and error messages.
 func (tv *tokenVisitor) strStack() string {
 	msg := []string{"["}
-	for i := len(tv.stack) - 1; i >= 0; i-- {
-		n := tv.stack[i]
+	for _, n := range slices.Backward(tv.stack) {
 		msg = append(msg, strings.TrimPrefix(fmt.Sprintf("%T", n), "*ast."))
 	}
 	if len(tv.stack) > 0 {
diff --git a/gopls/internal/server/general.go b/gopls/internal/server/general.go
index 2f05b00..831ab85 100644
--- a/gopls/internal/server/general.go
+++ b/gopls/internal/server/general.go
@@ -16,6 +16,7 @@
 	"os"
 	"path"
 	"path/filepath"
+	"slices"
 	"sort"
 	"strings"
 	"sync"
@@ -301,9 +302,9 @@
 //
 // Copied from the testenv package.
 func go1Point() int {
-	for i := len(build.Default.ReleaseTags) - 1; i >= 0; i-- {
+	for _, tag := range slices.Backward(build.Default.ReleaseTags) {
 		var version int
-		if _, err := fmt.Sscanf(build.Default.ReleaseTags[i], "go1.%d", &version); err != nil {
+		if _, err := fmt.Sscanf(tag, "go1.%d", &version); err != nil {
 			continue
 		}
 		return version
diff --git a/internal/bisect/bisect.go b/internal/bisect/bisect.go
index 7b1d112..04da97e 100644
--- a/internal/bisect/bisect.go
+++ b/internal/bisect/bisect.go
@@ -285,6 +285,7 @@
 	if m == nil {
 		return true
 	}
+	// Don't use slices.Backward here (no imports).
 	for i := len(m.list) - 1; i >= 0; i-- {
 		c := &m.list[i]
 		if id&c.mask == c.bits {
@@ -299,6 +300,7 @@
 	if m == nil {
 		return false
 	}
+	// Don't use slices.Backward here (no imports).
 	for i := len(m.list) - 1; i >= 0; i-- {
 		c := &m.list[i]
 		if id&c.mask == c.bits {
diff --git a/internal/excfg/excfg.go b/internal/excfg/excfg.go
index f312c02..135f6ac 100644
--- a/internal/excfg/excfg.go
+++ b/internal/excfg/excfg.go
@@ -151,8 +151,7 @@
 	//
 	// We do this backwards so that successor blocks are usually already visited
 	// and we can link them up eagerly.
-	for i := len(cfg.Blocks) - 1; i >= 0; i-- {
-		b := cfg.Blocks[i]
+	for _, b := range slices.Backward(cfg.Blocks) {
 		if b.Live {
 			eb.visitBlock(b)
 		}
@@ -265,9 +264,8 @@
 		next = eb.entry[cb.Succs[0].Index]
 	}
 	isIf := len(cb.Succs) == 2
-	for ni := len(cb.Nodes) - 1; ni >= 0; ni-- {
+	for _, node := range slices.Backward(cb.Nodes) {
 		eb.assertReverse()
-		node := cb.Nodes[ni]
 
 		if isIf {
 			next = eb.visitCond(node.(ast.Expr), next, eb.entry[cb.Succs[1].Index])
diff --git a/internal/gocommand/version.go b/internal/gocommand/version.go
index cce290c..d82f13a 100644
--- a/internal/gocommand/version.go
+++ b/internal/gocommand/version.go
@@ -8,6 +8,7 @@
 	"context"
 	"fmt"
 	"regexp"
+	"slices"
 	"strings"
 )
 
@@ -41,9 +42,9 @@
 	}
 	// Split up "[go1.1 go1.15]" and return highest go1.X value.
 	tags := strings.Fields(stdout[1 : len(stdout)-2])
-	for i := len(tags) - 1; i >= 0; i-- {
+	for _, tag := range slices.Backward(tags) {
 		var version int
-		if _, err := fmt.Sscanf(tags[i], "go1.%d", &version); err != nil {
+		if _, err := fmt.Sscanf(tag, "go1.%d", &version); err != nil {
 			continue
 		}
 		return version, nil
diff --git a/internal/refactor/inline/inline.go b/internal/refactor/inline/inline.go
index 6a31d3b..cbcf7b3 100644
--- a/internal/refactor/inline/inline.go
+++ b/internal/refactor/inline/inline.go
@@ -2022,8 +2022,7 @@
 		return string("RW"[btoi(effects)]) + i
 	}
 	removed := false
-	for i := len(args) - 1; i >= 0; i-- {
-		argi := args[i]
+	for i, argi := range slices.Backward(args) {
 		if sg.has(argi) && !argi.pure {
 			// i is not bound: check whether it must be bound due to hazards.
 			idx := slices.Index(effects, i)
diff --git a/internal/testenv/testenv.go b/internal/testenv/testenv.go
index 2bea513..b02de15 100644
--- a/internal/testenv/testenv.go
+++ b/internal/testenv/testenv.go
@@ -18,6 +18,7 @@
 	"path/filepath"
 	"runtime"
 	"runtime/debug"
+	"slices"
 	"strings"
 	"sync"
 	"testing"
@@ -330,9 +331,9 @@
 
 // Go1Point returns the x in Go 1.x.
 func Go1Point() int {
-	for i := len(build.Default.ReleaseTags) - 1; i >= 0; i-- {
+	for _, tag := range slices.Backward(build.Default.ReleaseTags) {
 		var version int
-		if _, err := fmt.Sscanf(build.Default.ReleaseTags[i], "go1.%d", &version); err != nil {
+		if _, err := fmt.Sscanf(tag, "go1.%d", &version); err != nil {
 			continue
 		}
 		return version