x/tools: go fix

The command was run twice, the second time to clean up after
the first e.g. by removing x := x. No problems observed.

Most fixes are from typefor, stditerators, buildtag, which
do not have known issues. (Several bugs in other analyzers
have been fixed since we last vendored x/tools into go fix.)

Also, use (*types.Func).Signature throughout.

For golang/go#71859

Change-Id: Icb9ba713315c469a2e307243b2d11bcd809d134f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/718504
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/go/analysis/checker/checker.go b/go/analysis/checker/checker.go
index 455be3f..14f5225 100644
--- a/go/analysis/checker/checker.go
+++ b/go/analysis/checker/checker.go
@@ -490,7 +490,7 @@
 	switch obj := obj.(type) {
 	case *types.Func:
 		return obj.Exported() && obj.Pkg() == pkg ||
-			obj.Type().(*types.Signature).Recv() != nil
+			obj.Signature().Recv() != nil
 	case *types.Var:
 		if obj.IsField() {
 			return true
diff --git a/go/analysis/passes/cgocall/cgocall.go b/go/analysis/passes/cgocall/cgocall.go
index bf1202b..54b8062 100644
--- a/go/analysis/passes/cgocall/cgocall.go
+++ b/go/analysis/passes/cgocall/cgocall.go
@@ -350,8 +350,8 @@
 	case *types.Array:
 		return typeOKForCgoCall(t.Elem(), m)
 	case *types.Struct:
-		for i := 0; i < t.NumFields(); i++ {
-			if !typeOKForCgoCall(t.Field(i).Type(), m) {
+		for field := range t.Fields() {
+			if !typeOKForCgoCall(field.Type(), m) {
 				return false
 			}
 		}
diff --git a/go/analysis/passes/copylock/copylock.go b/go/analysis/passes/copylock/copylock.go
index 4190cc5..208602f 100644
--- a/go/analysis/passes/copylock/copylock.go
+++ b/go/analysis/passes/copylock/copylock.go
@@ -328,8 +328,8 @@
 
 	ttyp, ok := typ.Underlying().(*types.Tuple)
 	if ok {
-		for i := 0; i < ttyp.Len(); i++ {
-			subpath := lockPath(tpkg, ttyp.At(i).Type(), seen)
+		for v := range ttyp.Variables() {
+			subpath := lockPath(tpkg, v.Type(), seen)
 			if subpath != nil {
 				return append(subpath, typ.String())
 			}
diff --git a/go/analysis/passes/deepequalerrors/deepequalerrors.go b/go/analysis/passes/deepequalerrors/deepequalerrors.go
index 5e3d1a3..32087cd 100644
--- a/go/analysis/passes/deepequalerrors/deepequalerrors.go
+++ b/go/analysis/passes/deepequalerrors/deepequalerrors.go
@@ -96,8 +96,8 @@
 		case *types.Map:
 			return check(t.Key()) || check(t.Elem())
 		case *types.Struct:
-			for i := 0; i < t.NumFields(); i++ {
-				if check(t.Field(i).Type()) {
+			for field := range t.Fields() {
+				if check(field.Type()) {
 					return true
 				}
 			}
diff --git a/go/analysis/passes/httpmux/httpmux.go b/go/analysis/passes/httpmux/httpmux.go
index a4f00e2..7c8008a 100644
--- a/go/analysis/passes/httpmux/httpmux.go
+++ b/go/analysis/passes/httpmux/httpmux.go
@@ -84,7 +84,7 @@
 	if !isMethodNamed(fn, "net/http", "Handle", "HandleFunc") {
 		return false
 	}
-	recv := fn.Type().(*types.Signature).Recv() // isMethodNamed() -> non-nil
+	recv := fn.Signature().Recv() // isMethodNamed() -> non-nil
 	isPtr, named := typesinternal.ReceiverNamed(recv)
 	return isPtr && typesinternal.IsTypeNamed(named, "net/http", "ServeMux")
 }
@@ -100,7 +100,7 @@
 	if f.Pkg() == nil || f.Pkg().Path() != pkgPath {
 		return false // not at pkgPath
 	}
-	if f.Type().(*types.Signature).Recv() == nil {
+	if f.Signature().Recv() == nil {
 		return false // not a method
 	}
 	return slices.Contains(names, f.Name())
diff --git a/go/analysis/passes/inline/inline.go b/go/analysis/passes/inline/inline.go
index 0ba9a09..2fe1ead 100644
--- a/go/analysis/passes/inline/inline.go
+++ b/go/analysis/passes/inline/inline.go
@@ -416,8 +416,8 @@
 			visit(t.Key())
 			visit(t.Elem())
 		case *types.Struct:
-			for i := range t.NumFields() {
-				visit(t.Field(i).Type())
+			for field := range t.Fields() {
+				visit(field.Type())
 			}
 		case *types.Signature:
 			// Ignore the receiver: although it may be present, it has no meaning
@@ -430,11 +430,11 @@
 			visit(t.Params())
 			visit(t.Results())
 		case *types.Interface:
-			for i := range t.NumEmbeddeds() {
-				visit(t.EmbeddedType(i))
+			for etyp := range t.EmbeddedTypes() {
+				visit(etyp)
 			}
-			for i := range t.NumExplicitMethods() {
-				visit(t.ExplicitMethod(i).Type())
+			for method := range t.ExplicitMethods() {
+				visit(method.Type())
 			}
 		case *types.Tuple:
 			for v := range t.Variables() {
diff --git a/go/analysis/passes/inspect/inspect.go b/go/analysis/passes/inspect/inspect.go
index ee1972f..aae5d25 100644
--- a/go/analysis/passes/inspect/inspect.go
+++ b/go/analysis/passes/inspect/inspect.go
@@ -41,7 +41,7 @@
 	URL:              "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/inspect",
 	Run:              run,
 	RunDespiteErrors: true,
-	ResultType:       reflect.TypeOf(new(inspector.Inspector)),
+	ResultType:       reflect.TypeFor[*inspector.Inspector](),
 }
 
 func run(pass *analysis.Pass) (any, error) {
diff --git a/go/analysis/passes/loopclosure/loopclosure.go b/go/analysis/passes/loopclosure/loopclosure.go
index fa764e7..8aeaeee 100644
--- a/go/analysis/passes/loopclosure/loopclosure.go
+++ b/go/analysis/passes/loopclosure/loopclosure.go
@@ -363,7 +363,7 @@
 	if f == nil || f.Name() != method {
 		return false
 	}
-	recv := f.Type().(*types.Signature).Recv()
+	recv := f.Signature().Recv()
 	if recv == nil {
 		return false
 	}
diff --git a/go/analysis/passes/lostcancel/lostcancel.go b/go/analysis/passes/lostcancel/lostcancel.go
index dfaecf5..72248be 100644
--- a/go/analysis/passes/lostcancel/lostcancel.go
+++ b/go/analysis/passes/lostcancel/lostcancel.go
@@ -316,8 +316,8 @@
 }
 
 func tupleContains(tuple *types.Tuple, v *types.Var) bool {
-	for i := 0; i < tuple.Len(); i++ {
-		if tuple.At(i) == v {
+	for v0 := range tuple.Variables() {
+		if v0 == v {
 			return true
 		}
 	}
diff --git a/go/analysis/passes/printf/types.go b/go/analysis/passes/printf/types.go
index f7e50f9..8aa3962 100644
--- a/go/analysis/passes/printf/types.go
+++ b/go/analysis/passes/printf/types.go
@@ -204,8 +204,7 @@
 	case *types.Struct:
 		// report whether all the elements of the struct match the expected type. For
 		// instance, with "%d" all the elements must be printable with the "%d" format.
-		for i := 0; i < typ.NumFields(); i++ {
-			typf := typ.Field(i)
+		for typf := range typ.Fields() {
 			if !m.match(typf.Type(), false) {
 				return false
 			}
diff --git a/go/analysis/passes/slog/slog.go b/go/analysis/passes/slog/slog.go
index 2cb91c7..985be27 100644
--- a/go/analysis/passes/slog/slog.go
+++ b/go/analysis/passes/slog/slog.go
@@ -168,7 +168,7 @@
 //	"slog.Logger.With" (instead of "(*log/slog.Logger).With")
 func shortName(fn *types.Func) string {
 	var r string
-	if recv := fn.Type().(*types.Signature).Recv(); recv != nil {
+	if recv := fn.Signature().Recv(); recv != nil {
 		if _, named := typesinternal.ReceiverNamed(recv); named != nil {
 			r = named.Obj().Name()
 		} else {
@@ -188,7 +188,7 @@
 		return 0, false
 	}
 	var recvName string // by default a slog package function
-	if recv := fn.Type().(*types.Signature).Recv(); recv != nil {
+	if recv := fn.Signature().Recv(); recv != nil {
 		_, named := typesinternal.ReceiverNamed(recv)
 		if named == nil {
 			return 0, false // anon struct/interface
diff --git a/go/analysis/passes/testinggoroutine/util.go b/go/analysis/passes/testinggoroutine/util.go
index db2e5f7..b56191b 100644
--- a/go/analysis/passes/testinggoroutine/util.go
+++ b/go/analysis/passes/testinggoroutine/util.go
@@ -44,7 +44,7 @@
 	if f.Pkg() == nil || f.Pkg().Path() != pkgPath {
 		return false
 	}
-	if f.Type().(*types.Signature).Recv() == nil {
+	if f.Signature().Recv() == nil {
 		return false
 	}
 	return slices.Contains(names, f.Name())
diff --git a/go/analysis/passes/unmarshal/unmarshal.go b/go/analysis/passes/unmarshal/unmarshal.go
index 4de48c8..d2d9b37 100644
--- a/go/analysis/passes/unmarshal/unmarshal.go
+++ b/go/analysis/passes/unmarshal/unmarshal.go
@@ -57,7 +57,7 @@
 		// Classify the callee (without allocating memory).
 		argidx := -1
 
-		recv := fn.Type().(*types.Signature).Recv()
+		recv := fn.Signature().Recv()
 		if fn.Name() == "Unmarshal" && recv == nil {
 			// "encoding/json".Unmarshal
 			// "encoding/xml".Unmarshal
diff --git a/go/analysis/passes/unusedresult/unusedresult.go b/go/analysis/passes/unusedresult/unusedresult.go
index 74cc83b..d1fa81c 100644
--- a/go/analysis/passes/unusedresult/unusedresult.go
+++ b/go/analysis/passes/unusedresult/unusedresult.go
@@ -150,7 +150,7 @@
 		if !ok {
 			return // e.g. var or builtin
 		}
-		if sig := fn.Type().(*types.Signature); sig.Recv() != nil {
+		if sig := fn.Signature(); sig.Recv() != nil {
 			// method (e.g. foo.String())
 			if types.Identical(sig, sigNoArgsStringResult) {
 				if stringMethods[fn.Name()] {
diff --git a/go/callgraph/rta/rta.go b/go/callgraph/rta/rta.go
index 224c0b9..442a226 100644
--- a/go/callgraph/rta/rta.go
+++ b/go/callgraph/rta/rta.go
@@ -469,9 +469,9 @@
 	}
 
 	// Recursion over signatures of each exported method.
-	for i := 0; i < mset.Len(); i++ {
-		if mset.At(i).Obj().Exported() {
-			sig := mset.At(i).Type().(*types.Signature)
+	for method := range mset.Methods() {
+		if method.Obj().Exported() {
+			sig := method.Type().(*types.Signature)
 			r.addRuntimeType(sig.Params(), true)  // skip the Tuple itself
 			r.addRuntimeType(sig.Results(), true) // skip the Tuple itself
 		}
@@ -541,8 +541,8 @@
 func fingerprint(mset *types.MethodSet) uint64 {
 	var space [64]byte
 	var mask uint64
-	for i := 0; i < mset.Len(); i++ {
-		method := mset.At(i).Obj()
+	for method := range mset.Methods() {
+		method := method.Obj()
 		sig := method.Type().(*types.Signature)
 		sum := crc32.ChecksumIEEE(fmt.Appendf(space[:], "%s/%d/%d",
 			method.Id(),
diff --git a/go/callgraph/static/static.go b/go/callgraph/static/static.go
index 948ce9a..84a95ac 100644
--- a/go/callgraph/static/static.go
+++ b/go/callgraph/static/static.go
@@ -72,8 +72,8 @@
 	methodsOf := func(T types.Type) {
 		if !types.IsInterface(T) {
 			mset := prog.MethodSets.MethodSet(T)
-			for i := 0; i < mset.Len(); i++ {
-				visit(cg.CreateNode(prog.MethodValue(mset.At(i))))
+			for method := range mset.Methods() {
+				visit(cg.CreateNode(prog.MethodValue(method)))
 			}
 		}
 	}
diff --git a/go/ssa/builder.go b/go/ssa/builder.go
index 162f3ec..a75257c 100644
--- a/go/ssa/builder.go
+++ b/go/ssa/builder.go
@@ -2994,8 +2994,8 @@
 	fn.source = fn.parent.source
 	fn.startBody()
 	params := fn.Signature.Params()
-	for i := 0; i < params.Len(); i++ {
-		fn.addParamVar(params.At(i))
+	for v := range params.Variables() {
+		fn.addParamVar(v)
 	}
 
 	// Initial targets
diff --git a/go/ssa/instantiate_test.go b/go/ssa/instantiate_test.go
index 32c3a9a..c82196f 100644
--- a/go/ssa/instantiate_test.go
+++ b/go/ssa/instantiate_test.go
@@ -207,8 +207,8 @@
 func tparams(f *ssa.Function) string {
 	tplist := f.TypeParams()
 	var tps []string
-	for i := 0; i < tplist.Len(); i++ {
-		tps = append(tps, tplist.At(i).String())
+	for tparam := range tplist.TypeParams() {
+		tps = append(tps, tparam.String())
 	}
 	return fmt.Sprint(tps)
 }
diff --git a/go/ssa/interp/reflect.go b/go/ssa/interp/reflect.go
index da9938e..7c549ab 100644
--- a/go/ssa/interp/reflect.go
+++ b/go/ssa/interp/reflect.go
@@ -542,8 +542,8 @@
 
 		// delete bodies of the old methods
 		mset := i.prog.MethodSets.MethodSet(rV)
-		for j := 0; j < mset.Len(); j++ {
-			i.prog.MethodValue(mset.At(j)).Blocks = nil
+		for method := range mset.Methods() {
+			i.prog.MethodValue(method).Blocks = nil
 		}
 
 		tEface := types.NewInterface(nil, nil).Complete()
diff --git a/go/ssa/ssautil/visit.go b/go/ssa/ssautil/visit.go
index b4feb42..7300d2b 100644
--- a/go/ssa/ssautil/visit.go
+++ b/go/ssa/ssautil/visit.go
@@ -74,8 +74,8 @@
 	methodsOf := func(T types.Type) {
 		if !types.IsInterface(T) {
 			mset := prog.MethodSets.MethodSet(T)
-			for i := 0; i < mset.Len(); i++ {
-				function(prog.MethodValue(mset.At(i)))
+			for method := range mset.Methods() {
+				function(prog.MethodValue(method))
 			}
 		}
 	}
diff --git a/go/ssa/subst.go b/go/ssa/subst.go
index a4b1026..5799a07 100644
--- a/go/ssa/subst.go
+++ b/go/ssa/subst.go
@@ -352,8 +352,7 @@
 
 		// Copy and substitute type params.
 		var newTParams []*types.TypeParam
-		for i := 0; i < tparams.Len(); i++ {
-			cur := tparams.At(i)
+		for cur := range tparams.TypeParams() {
 			cobj := cur.Obj()
 			cname := types.NewTypeName(cobj.Pos(), cobj.Pkg(), cobj.Name(), nil)
 			ntp := types.NewTypeParam(cname, nil)
@@ -488,8 +487,7 @@
 		obj := types.NewTypeName(tname.Pos(), tname.Pkg(), tname.Name(), nil)
 		fresh := types.NewNamed(obj, nil, nil)
 		var newTParams []*types.TypeParam
-		for i := 0; i < tparams.Len(); i++ {
-			cur := tparams.At(i)
+		for cur := range tparams.TypeParams() {
 			cobj := cur.Obj()
 			cname := types.NewTypeName(cobj.Pos(), cobj.Pkg(), cobj.Name(), nil)
 			ntp := types.NewTypeParam(cname, nil)
diff --git a/go/ssa/util.go b/go/ssa/util.go
index 932eb6c..42f9621 100644
--- a/go/ssa/util.go
+++ b/go/ssa/util.go
@@ -121,7 +121,7 @@
 
 // recvType returns the receiver type of method obj.
 func recvType(obj *types.Func) types.Type {
-	return obj.Type().(*types.Signature).Recv().Type()
+	return obj.Signature().Recv().Type()
 }
 
 // fieldOf returns the index'th field of the (core type of) a struct type;
@@ -200,7 +200,7 @@
 // receiverTypeArgs returns the type arguments to a method's receiver.
 // Returns an empty list if the receiver does not have type arguments.
 func receiverTypeArgs(method *types.Func) []types.Type {
-	recv := method.Type().(*types.Signature).Recv()
+	recv := method.Signature().Recv()
 	_, named := typesinternal.ReceiverNamed(recv)
 	if named == nil {
 		return nil // recv is anonymous struct/interface
@@ -221,8 +221,8 @@
 func recvAsFirstArg(sig *types.Signature) *types.Signature {
 	params := make([]*types.Var, 0, 1+sig.Params().Len())
 	params = append(params, sig.Recv())
-	for i := 0; i < sig.Params().Len(); i++ {
-		params = append(params, sig.Params().At(i))
+	for v := range sig.Params().Variables() {
+		params = append(params, v)
 	}
 	return types.NewSignatureType(nil, nil, nil, types.NewTuple(params...), sig.Results(), sig.Variadic())
 }
diff --git a/go/types/internal/play/play.go b/go/types/internal/play/play.go
index 3d00922..89ed975 100644
--- a/go/types/internal/play/play.go
+++ b/go/types/internal/play/play.go
@@ -304,7 +304,7 @@
 		origin = obj.Origin()
 
 	case *types.Func:
-		if recv := obj.Type().(*types.Signature).Recv(); recv != nil {
+		if recv := obj.Signature().Recv(); recv != nil {
 			kind = fmt.Sprintf("method (with recv %v)", recv.Type())
 		}
 		origin = obj.Origin()
diff --git a/go/types/objectpath/objectpath.go b/go/types/objectpath/objectpath.go
index 6c0c749..6646bf5 100644
--- a/go/types/objectpath/objectpath.go
+++ b/go/types/objectpath/objectpath.go
@@ -249,7 +249,7 @@
 
 	case *types.Func:
 		// A func, if not package-level, must be a method.
-		if recv := obj.Type().(*types.Signature).Recv(); recv == nil {
+		if recv := obj.Signature().Recv(); recv == nil {
 			return "", fmt.Errorf("func is not a method: %v", obj)
 		}
 
@@ -405,7 +405,7 @@
 		return "", false
 	}
 
-	_, named := typesinternal.ReceiverNamed(meth.Type().(*types.Signature).Recv())
+	_, named := typesinternal.ReceiverNamed(meth.Signature().Recv())
 	if named == nil {
 		return "", false
 	}
diff --git a/go/types/typeutil/map.go b/go/types/typeutil/map.go
index f035a0b..3662457 100644
--- a/go/types/typeutil/map.go
+++ b/go/types/typeutil/map.go
@@ -304,8 +304,7 @@
 	case *types.Named:
 		hash := h.hashTypeName(t.Obj())
 		targs := t.TypeArgs()
-		for i := 0; i < targs.Len(); i++ {
-			targ := targs.At(i)
+		for targ := range targs.Types() {
 			hash += 2 * h.hash(targ)
 		}
 		return hash
diff --git a/gopls/internal/analysis/simplifycompositelit/simplifycompositelit.go b/gopls/internal/analysis/simplifycompositelit/simplifycompositelit.go
index 3e54dc2..a883922 100644
--- a/gopls/internal/analysis/simplifycompositelit/simplifycompositelit.go
+++ b/gopls/internal/analysis/simplifycompositelit/simplifycompositelit.go
@@ -198,8 +198,8 @@
 
 // Values/types for special cases.
 var (
-	identType     = reflect.TypeOf((*ast.Ident)(nil))
-	objectPtrType = reflect.TypeOf((*ast.Object)(nil))
-	positionType  = reflect.TypeOf(token.NoPos)
-	callExprType  = reflect.TypeOf((*ast.CallExpr)(nil))
+	identType     = reflect.TypeFor[*ast.Ident]()
+	objectPtrType = reflect.TypeFor[*ast.Object]()
+	positionType  = reflect.TypeFor[token.Pos]()
+	callExprType  = reflect.TypeFor[*ast.CallExpr]()
 )
diff --git a/gopls/internal/analysis/unusedparams/unusedparams.go b/gopls/internal/analysis/unusedparams/unusedparams.go
index bcbbfda..663c36f 100644
--- a/gopls/internal/analysis/unusedparams/unusedparams.go
+++ b/gopls/internal/analysis/unusedparams/unusedparams.go
@@ -96,8 +96,7 @@
 				// generics makes it tricky, and this conservative
 				// heuristic is close enough.)
 				t := pass.TypesInfo.TypeOf(n).(*types.Interface)
-				for i := 0; i < t.NumExplicitMethods(); i++ {
-					m := t.ExplicitMethod(i)
+				for m := range t.ExplicitMethods() {
 					if !m.Exported() && m.Name() != "_" {
 						unexportedIMethodNames[m.Name()] = true
 					}
diff --git a/gopls/internal/cache/constraints_test.go b/gopls/internal/cache/constraints_test.go
index 23c9f39..6936d6c 100644
--- a/gopls/internal/cache/constraints_test.go
+++ b/gopls/internal/cache/constraints_test.go
@@ -2,9 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-//go:build go1.16
-// +build go1.16
-
 package cache
 
 import (
diff --git a/gopls/internal/cache/parsego/parse.go b/gopls/internal/cache/parsego/parse.go
index d832c88..1a00967 100644
--- a/gopls/internal/cache/parsego/parse.go
+++ b/gopls/internal/cache/parsego/parse.go
@@ -840,7 +840,7 @@
 	return exprStmt.X, nil
 }
 
-var tokenPosType = reflect.TypeOf(token.NoPos)
+var tokenPosType = reflect.TypeFor[token.Pos]()
 
 // offsetPositions applies an offset to the positions in an ast.Node.
 func offsetPositions(tok *token.File, n ast.Node, offset token.Pos) {
diff --git a/gopls/internal/doc/generate/generate.go b/gopls/internal/doc/generate/generate.go
index 5bf8e71..5b31ce9 100644
--- a/gopls/internal/doc/generate/generate.go
+++ b/gopls/internal/doc/generate/generate.go
@@ -200,13 +200,12 @@
 
 	var opts []*doc.Option
 	optsStruct := optsType.Type().Underlying().(*types.Struct)
-	for i := 0; i < optsStruct.NumFields(); i++ {
+	for typesField := range optsStruct.Fields() {
 		// The types field gives us the type.
-		typesField := optsStruct.Field(i)
 
 		// If the field name ends with "Options", assume it is a struct with
 		// additional options and process it recursively.
-		if h := strings.TrimSuffix(typesField.Name(), "Options"); h != typesField.Name() {
+		if h, ok := strings.CutSuffix(typesField.Name(), "Options"); ok {
 			// Keep track of the parent structs.
 			if hierarchy != "" {
 				h = hierarchy + "." + h
diff --git a/gopls/internal/golang/completion/completion.go b/gopls/internal/golang/completion/completion.go
index 6c28122..f6d2277 100644
--- a/gopls/internal/golang/completion/completion.go
+++ b/gopls/internal/golang/completion/completion.go
@@ -1119,8 +1119,7 @@
 				_, named := typesinternal.ReceiverNamed(recv)
 				if named != nil {
 					if recvStruct, ok := named.Underlying().(*types.Struct); ok {
-						for i := range recvStruct.NumFields() {
-							field := recvStruct.Field(i)
+						for field := range recvStruct.Fields() {
 							c.deepState.enqueue(candidate{obj: field, score: lowScore})
 						}
 					}
@@ -1619,14 +1618,14 @@
 		c.methodSetCache[methodSetKey{typ, addressable}] = mset
 	}
 
-	for i := range mset.Len() {
-		obj := mset.At(i).Obj()
+	for method := range mset.Methods() {
+		obj := method.Obj()
 		// to the other side of the cb() queue?
 		if c.tooNew(obj) {
 			continue // std method too new for file's Go version
 		}
 		cb(candidate{
-			obj:         mset.At(i).Obj(),
+			obj:         method.Obj(),
 			score:       stdScore,
 			imp:         imp,
 			addressable: addressable || isPointer(typ),
@@ -2175,8 +2174,8 @@
 		// value side. The expected type of the value will be determined from the key.
 		if clInfo.kv != nil {
 			if key, ok := clInfo.kv.Key.(*ast.Ident); ok {
-				for i := range t.NumFields() {
-					if field := t.Field(i); field.Name() == key.Name {
+				for field := range t.Fields() {
+					if field.Name() == key.Name {
 						return field.Type()
 					}
 				}
@@ -2842,8 +2841,8 @@
 	// call. Record the assignees so we can favor function
 	// calls that return matching values.
 	if len(node.Args) <= 1 && exprIdx == 0 {
-		for i := range sig.Params().Len() {
-			inf.assignees = append(inf.assignees, sig.Params().At(i).Type())
+		for v := range sig.Params().Variables() {
+			inf.assignees = append(inf.assignees, v.Type())
 		}
 
 		// Record that we may be completing into variadic parameters.
diff --git a/gopls/internal/golang/completion/format.go b/gopls/internal/golang/completion/format.go
index 5c9d81c..9f5d277 100644
--- a/gopls/internal/golang/completion/format.go
+++ b/gopls/internal/golang/completion/format.go
@@ -398,33 +398,33 @@
 		case *types.Slice:
 			visit(t.Elem())
 		case *types.Interface:
-			for i := range t.NumExplicitMethods() {
-				visit(t.ExplicitMethod(i).Type())
+			for method := range t.ExplicitMethods() {
+				visit(method.Type())
 			}
-			for i := range t.NumEmbeddeds() {
-				visit(t.EmbeddedType(i))
+			for etyp := range t.EmbeddedTypes() {
+				visit(etyp)
 			}
 		case *types.Union:
-			for i := range t.Len() {
-				visit(t.Term(i).Type())
+			for term := range t.Terms() {
+				visit(term.Type())
 			}
 		case *types.Signature:
 			if tp := t.TypeParams(); tp != nil {
 				// Generic signatures only appear as the type of generic
 				// function declarations, so this isn't really reachable.
-				for i := range tp.Len() {
-					visit(tp.At(i).Constraint())
+				for tparam := range tp.TypeParams() {
+					visit(tparam.Constraint())
 				}
 			}
 			visit(t.Params())
 			visit(t.Results())
 		case *types.Tuple:
-			for i := range t.Len() {
-				visit(t.At(i).Type())
+			for v := range t.Variables() {
+				visit(v.Type())
 			}
 		case *types.Struct:
-			for i := range t.NumFields() {
-				visit(t.Field(i).Type())
+			for field := range t.Fields() {
+				visit(field.Type())
 			}
 		case *types.TypeParam:
 			free[t] = true
@@ -432,8 +432,8 @@
 			visit(types.Unalias(t))
 		case *types.Named:
 			targs := t.TypeArgs()
-			for i := range targs.Len() {
-				visit(targs.At(i))
+			for t0 := range targs.Types() {
+				visit(t0)
 			}
 		case *types.Basic:
 			// nop
@@ -446,8 +446,7 @@
 
 	// Perform induction through constraints.
 restart:
-	for i := range sig.TypeParams().Len() {
-		tp := sig.TypeParams().At(i)
+	for tp := range sig.TypeParams().TypeParams() {
 		if free[tp] {
 			n := len(free)
 			visit(tp.Constraint())
diff --git a/gopls/internal/golang/completion/fuzz.go b/gopls/internal/golang/completion/fuzz.go
index 9e3bb7b..34c6d50 100644
--- a/gopls/internal/golang/completion/fuzz.go
+++ b/gopls/internal/golang/completion/fuzz.go
@@ -50,8 +50,8 @@
 		}
 	}
 	if inside {
-		for i := range mset.Len() {
-			o := mset.At(i).Obj()
+		for method := range mset.Methods() {
+			o := method.Obj()
 			if o.Name() == "Failed" || o.Name() == "Name" {
 				cb(candidate{
 					obj:         o,
@@ -125,8 +125,8 @@
 			isSlice:       false,
 		}
 		c.items = append(c.items, xx)
-		for i := range mset.Len() {
-			o := mset.At(i).Obj()
+		for method := range mset.Methods() {
+			o := method.Obj()
 			if o.Name() != "Fuzz" {
 				cb(candidate{
 					obj:         o,
diff --git a/gopls/internal/golang/completion/literal.go b/gopls/internal/golang/completion/literal.go
index 572a7c4..d227e45 100644
--- a/gopls/internal/golang/completion/literal.go
+++ b/gopls/internal/golang/completion/literal.go
@@ -331,8 +331,8 @@
 		results.Len() == 1 && results.At(0).Name() != ""
 
 	var resultHasTypeParams bool
-	for i := range results.Len() {
-		if tp, ok := types.Unalias(results.At(i).Type()).(*types.TypeParam); ok && !c.typeParamInScope(tp) {
+	for v := range results.Variables() {
+		if tp, ok := types.Unalias(v.Type()).(*types.TypeParam); ok && !c.typeParamInScope(tp) {
 			resultHasTypeParams = true
 		}
 	}
@@ -570,8 +570,7 @@
 		return false
 	}
 
-	for i := range targs.Len() {
-		targ := targs.At(i)
+	for targ := range targs.Types() {
 
 		// The expansion of an alias can have free type parameters,
 		// whether or not the alias itself has type parameters:
diff --git a/gopls/internal/golang/completion/postfix_snippets.go b/gopls/internal/golang/completion/postfix_snippets.go
index e81fb67..084eafe 100644
--- a/gopls/internal/golang/completion/postfix_snippets.go
+++ b/gopls/internal/golang/completion/postfix_snippets.go
@@ -431,13 +431,9 @@
 // Tuple returns the tuple result vars if the type of X is tuple.
 func (a *postfixTmplArgs) Tuple() []*types.Var {
 	tuple, _ := a.Type.(*types.Tuple)
-	if tuple == nil {
-		return nil
-	}
-
-	typs := make([]*types.Var, 0, tuple.Len())
-	for i := range tuple.Len() {
-		typs = append(typs, tuple.At(i))
+	typs := make([]*types.Var, tuple.Len())
+	for i := range typs {
+		typs[i] = tuple.At(i)
 	}
 	return typs
 }
@@ -445,9 +441,6 @@
 // TupleLast returns the last tuple result vars if the type of X is tuple.
 func (a *postfixTmplArgs) TupleLast() *types.Var {
 	tuple, _ := a.Type.(*types.Tuple)
-	if tuple == nil {
-		return nil
-	}
 	if tuple.Len() == 0 {
 		return nil
 	}
diff --git a/gopls/internal/golang/completion/statements.go b/gopls/internal/golang/completion/statements.go
index e8b35a4..620d41a 100644
--- a/gopls/internal/golang/completion/statements.go
+++ b/gopls/internal/golang/completion/statements.go
@@ -351,8 +351,7 @@
 	}
 
 	sig := enclosingFunc.sig
-	for i := range sig.Params().Len() {
-		param := sig.Params().At(i)
+	for param := range sig.Params().Variables() {
 		if param.Name() == "_" {
 			continue
 		}
diff --git a/gopls/internal/golang/completion/util.go b/gopls/internal/golang/completion/util.go
index c907fa4..10d6325 100644
--- a/gopls/internal/golang/completion/util.go
+++ b/gopls/internal/golang/completion/util.go
@@ -47,8 +47,7 @@
 				return
 			}
 
-			for i := range T.NumFields() {
-				f := T.Field(i)
+			for f := range T.Fields() {
 				fn(f)
 				if f.Anonymous() {
 					seen.Set(T, true)
@@ -84,8 +83,8 @@
 	case *types.Signature:
 		return typeIsValid(typ.Params()) && typeIsValid(typ.Results())
 	case *types.Tuple:
-		for i := range typ.Len() {
-			if !typeIsValid(typ.At(i).Type()) {
+		for v := range typ.Variables() {
+			if !typeIsValid(v.Type()) {
 				return false
 			}
 		}
@@ -241,8 +240,7 @@
 
 // fieldsAccessible returns whether s has at least one field accessible by p.
 func fieldsAccessible(s *types.Struct, p *types.Package) bool {
-	for i := range s.NumFields() {
-		f := s.Field(i)
+	for f := range s.Fields() {
 		if f.Exported() || f.Pkg() == p {
 			return true
 		}
diff --git a/gopls/internal/golang/hover.go b/gopls/internal/golang/hover.go
index b5a1620..a9e9b0c 100644
--- a/gopls/internal/golang/hover.go
+++ b/gopls/internal/golang/hover.go
@@ -19,6 +19,7 @@
 	"go/version"
 	"io/fs"
 	"path/filepath"
+	"slices"
 	"sort"
 	"strconv"
 	"strings"
@@ -719,8 +720,8 @@
 						scopeObj, _ := obj.Pkg().Scope().Lookup(typeName.Name).(*types.TypeName)
 						if scopeObj != nil {
 							if st, _ := scopeObj.Type().Underlying().(*types.Struct); st != nil {
-								for i := 0; i < st.NumFields(); i++ {
-									if obj == st.Field(i) {
+								for field := range st.Fields() {
+									if obj == field {
 										recv = scopeObj
 									}
 								}
@@ -1806,8 +1807,7 @@
 			return
 		}
 	fieldloop:
-		for i := 0; i < tStruct.NumFields(); i++ {
-			f := tStruct.Field(i)
+		for f := range tStruct.Fields() {
 
 			// Handle recursion through anonymous fields.
 			if f.Anonymous() {
@@ -1868,10 +1868,7 @@
 
 		// wasted space (struct types)
 		if tStruct, ok := obj.Type().Underlying().(*types.Struct); ok && is[*types.TypeName](obj) && size > 0 {
-			var fields []*types.Var
-			for i := 0; i < tStruct.NumFields(); i++ {
-				fields = append(fields, tStruct.Field(i))
-			}
+			fields := slices.Collect(tStruct.Fields())
 			if len(fields) > 0 {
 				// Sort into descending (most compact) order
 				// and recompute size of entire struct.
diff --git a/gopls/internal/golang/implementation.go b/gopls/internal/golang/implementation.go
index 1dd05ee..d1fd590 100644
--- a/gopls/internal/golang/implementation.go
+++ b/gopls/internal/golang/implementation.go
@@ -434,8 +434,8 @@
 			// the methodID's types.Package, which we don't know.
 			// We could recursively search pkg.Imports for it,
 			// but it's easier to walk the method set.
-			for i := 0; i < mset.Len(); i++ {
-				m := mset.At(i).Obj()
+			for method := range mset.Methods() {
+				m := method.Obj()
 				if m.Pos() == id.Pos() {
 					continue // avoid self-comparison of query method
 				}
@@ -505,8 +505,8 @@
 	// would lead to divergence with the global (fingerprint-based)
 	// algorithm, which operates only on methodsets.
 	ymset := msets.MethodSet(y)
-	for i := range ymset.Len() {
-		ym := ymset.At(i).Obj().(*types.Func)
+	for method := range ymset.Methods() {
+		ym := method.Obj().(*types.Func)
 
 		xobj, _, _ := types.LookupFieldOrMethod(x, false, ym.Pkg(), ym.Name())
 		xm, ok := xobj.(*types.Func)
diff --git a/gopls/internal/golang/inlay_hint.go b/gopls/internal/golang/inlay_hint.go
index a596550..dee0b68 100644
--- a/gopls/internal/golang/inlay_hint.go
+++ b/gopls/internal/golang/inlay_hint.go
@@ -212,8 +212,8 @@
 			continue
 		}
 		var args []string
-		for i := 0; i < inst.TypeArgs.Len(); i++ {
-			args = append(args, inst.TypeArgs.At(i).String())
+		for t := range inst.TypeArgs.Types() {
+			args = append(args, t.String())
 		}
 		if len(args) == 0 {
 			continue
diff --git a/gopls/internal/golang/pkgdoc.go b/gopls/internal/golang/pkgdoc.go
index 663f3ff..0adb7d3 100644
--- a/gopls/internal/golang/pkgdoc.go
+++ b/gopls/internal/golang/pkgdoc.go
@@ -78,7 +78,7 @@
 		// External test packages don't have /pkg doc pages,
 		// so instead show the doc for the package under test.
 		// (This named-based heuristic is imperfect.)
-		if forTest := strings.TrimSuffix(pkg.Path(), "_test"); forTest != pkg.Path() {
+		if forTest, ok := strings.CutSuffix(pkg.Path(), "_test"); ok {
 			return PackagePath(forTest), "", makeTitle("package", nil, filepath.Base(forTest))
 		}
 
diff --git a/gopls/internal/golang/rename_check.go b/gopls/internal/golang/rename_check.go
index ac2f8d1..6d2e521 100644
--- a/gopls/internal/golang/rename_check.go
+++ b/gopls/internal/golang/rename_check.go
@@ -460,8 +460,8 @@
 			// This struct is not a named type.
 			// We need only check for direct (non-promoted) field/field conflicts.
 			T := r.pkg.TypesInfo().Types[tStruct].Type.Underlying().(*types.Struct)
-			for i := 0; i < T.NumFields(); i++ {
-				if prev := T.Field(i); prev.Name() == r.to {
+			for prev := range T.Fields() {
+				if prev.Name() == r.to {
 					r.errorf(from.Pos(), "renaming this field %q to %q",
 						from.Name(), r.to)
 					r.errorf(prev.Pos(), "\twould conflict with this field")
diff --git a/gopls/internal/golang/stubmethods/stubcalledfunc.go b/gopls/internal/golang/stubmethods/stubcalledfunc.go
index 3b935db..677a57d 100644
--- a/gopls/internal/golang/stubmethods/stubcalledfunc.go
+++ b/gopls/internal/golang/stubmethods/stubcalledfunc.go
@@ -106,8 +106,8 @@
 	// Otherwise, use lowercase for the first letter of the object.
 	recvName := strings.ToLower(fmt.Sprintf("%.1s", recv.Name()))
 	if named, ok := types.Unalias(si.Receiver).(*types.Named); ok {
-		for i := 0; i < named.NumMethods(); i++ {
-			if recv := named.Method(i).Type().(*types.Signature).Recv(); recv.Name() != "" {
+		for method := range named.Methods() {
+			if recv := method.Signature().Recv(); recv.Name() != "" {
 				recvName = recv.Name()
 				break
 			}
@@ -185,8 +185,8 @@
 		// This is the case where another function call returning multiple
 		// results is used as an argument.
 		case *types.Tuple:
-			for ti := 0; ti < t.Len(); ti++ {
-				appendParam(arg, t.At(ti).Type())
+			for v := range t.Variables() {
+				appendParam(arg, v.Type())
 			}
 		default:
 			appendParam(arg, t)
diff --git a/gopls/internal/golang/stubmethods/stubmethods.go b/gopls/internal/golang/stubmethods/stubmethods.go
index f9e2c7a..e37473c 100644
--- a/gopls/internal/golang/stubmethods/stubmethods.go
+++ b/gopls/internal/golang/stubmethods/stubmethods.go
@@ -89,8 +89,8 @@
 	// Record all direct methods of the current object
 	concreteFuncs := make(map[string]struct{})
 	if named, ok := types.Unalias(si.Concrete).(*types.Named); ok {
-		for i := 0; i < named.NumMethods(); i++ {
-			concreteFuncs[named.Method(i).Name()] = struct{}{}
+		for method := range named.Methods() {
+			concreteFuncs[method.Name()] = struct{}{}
 		}
 	}
 
@@ -107,8 +107,7 @@
 		concreteStruct, isStruct = typesinternal.Origin(si.Concrete).Underlying().(*types.Struct)
 	)
 
-	for i := 0; i < ifaceType.NumMethods(); i++ {
-		imethod := ifaceType.Method(i)
+	for imethod := range ifaceType.Methods() {
 		cmethod, index, _ := types.LookupFieldOrMethod(si.Concrete, si.pointer, imethod.Pkg(), imethod.Name())
 		if cmethod == nil {
 			missing = append(missing, missingFn{fn: imethod})
@@ -163,8 +162,8 @@
 	// Otherwise, use lowercase for the first letter of the object.
 	rn := strings.ToLower(si.Concrete.Obj().Name()[0:1])
 	if named, ok := types.Unalias(si.Concrete).(*types.Named); ok {
-		for i := 0; i < named.NumMethods(); i++ {
-			if recv := named.Method(i).Type().(*types.Signature).Recv(); recv.Name() != "" {
+		for method := range named.Methods() {
+			if recv := method.Signature().Recv(); recv.Name() != "" {
 				rn = recv.Name()
 				break
 			}
@@ -173,8 +172,8 @@
 
 	// Check for receiver name conflicts
 	checkRecvName := func(tuple *types.Tuple) bool {
-		for i := 0; i < tuple.Len(); i++ {
-			if rn == tuple.At(i).Name() {
+		for v := range tuple.Variables() {
+			if rn == v.Name() {
 				return true
 			}
 		}
diff --git a/gopls/internal/golang/types_format.go b/gopls/internal/golang/types_format.go
index 472b576..f97dac0 100644
--- a/gopls/internal/golang/types_format.go
+++ b/gopls/internal/golang/types_format.go
@@ -181,8 +181,7 @@
 func NewSignature(ctx context.Context, s *cache.Snapshot, pkg *cache.Package, sig *types.Signature, comment *ast.CommentGroup, qual types.Qualifier, mq MetadataQualifier) (*signature, error) {
 	var tparams []string
 	tpList := sig.TypeParams()
-	for i := 0; i < tpList.Len(); i++ {
-		tparam := tpList.At(i)
+	for tparam := range tpList.TypeParams() {
 		// TODO: is it possible to reuse the logic from FormatVarType here?
 		s := tparam.Obj().Name() + " " + tparam.Constraint().String()
 		tparams = append(tparams, s)
diff --git a/gopls/internal/lsprpc/autostart_posix.go b/gopls/internal/lsprpc/autostart_posix.go
index 6aeac3e..fc22f96 100644
--- a/gopls/internal/lsprpc/autostart_posix.go
+++ b/gopls/internal/lsprpc/autostart_posix.go
@@ -3,7 +3,6 @@
 // license that can be found in the LICENSE file.
 
 //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
 
 package lsprpc
 
diff --git a/gopls/internal/protocol/command/commandmeta/meta.go b/gopls/internal/protocol/command/commandmeta/meta.go
index 7c3a3ac..50db7a2 100644
--- a/gopls/internal/protocol/command/commandmeta/meta.go
+++ b/gopls/internal/protocol/command/commandmeta/meta.go
@@ -68,8 +68,7 @@
 	// Load command metadata corresponding to each interface method.
 	var commands []*Command
 	loader := fieldLoader{make(map[types.Object]*Field)}
-	for i := 0; i < obj.NumMethods(); i++ {
-		m := obj.Method(i)
+	for m := range obj.Methods() {
 		c, err := loader.loadMethod(pkg, m)
 		if err != nil {
 			return nil, fmt.Errorf("loading %s: %v", m.Name(), err)
diff --git a/gopls/internal/protocol/command/gen/gen.go b/gopls/internal/protocol/command/gen/gen.go
index 779e6d8..0f72ef7 100644
--- a/gopls/internal/protocol/command/gen/gen.go
+++ b/gopls/internal/protocol/command/gen/gen.go
@@ -130,8 +130,8 @@
 				case *types.Slice:
 					return fallible(t.Elem())
 				case *types.Struct:
-					for i := 0; i < t.NumFields(); i++ {
-						if fallible(t.Field(i).Type()) {
+					for field := range t.Fields() {
+						if fallible(field.Type()) {
 							return true
 						}
 					}
diff --git a/gopls/internal/protocol/command/generate.go b/gopls/internal/protocol/command/generate.go
index 324bc51..445a784 100644
--- a/gopls/internal/protocol/command/generate.go
+++ b/gopls/internal/protocol/command/generate.go
@@ -3,7 +3,6 @@
 // license that can be found in the LICENSE file.
 
 //go:build ignore
-// +build ignore
 
 // The generate command generates command_gen.go from a combination of
 // static and dynamic analysis of the command package.
diff --git a/gopls/internal/protocol/uri_test.go b/gopls/internal/protocol/uri_test.go
index cad71dd..076c145 100644
--- a/gopls/internal/protocol/uri_test.go
+++ b/gopls/internal/protocol/uri_test.go
@@ -3,7 +3,6 @@
 // license that can be found in the LICENSE file.
 
 //go:build !windows
-// +build !windows
 
 package protocol_test
 
diff --git a/gopls/internal/protocol/uri_windows_test.go b/gopls/internal/protocol/uri_windows_test.go
index 0847116..bf19c48 100644
--- a/gopls/internal/protocol/uri_windows_test.go
+++ b/gopls/internal/protocol/uri_windows_test.go
@@ -3,7 +3,6 @@
 // license that can be found in the LICENSE file.
 
 //go:build windows
-// +build windows
 
 package protocol_test
 
diff --git a/gopls/internal/telemetry/telemetry_test.go b/gopls/internal/telemetry/telemetry_test.go
index f41769f..63a809c 100644
--- a/gopls/internal/telemetry/telemetry_test.go
+++ b/gopls/internal/telemetry/telemetry_test.go
@@ -3,7 +3,6 @@
 // license that can be found in the LICENSE file.
 
 //go:build go1.21 && !openbsd && !js && !wasip1 && !solaris && !android && !386
-// +build go1.21,!openbsd,!js,!wasip1,!solaris,!android,!386
 
 package telemetry_test
 
diff --git a/gopls/internal/test/integration/misc/generate_test.go b/gopls/internal/test/integration/misc/generate_test.go
index daa6599..cb325d8 100644
--- a/gopls/internal/test/integration/misc/generate_test.go
+++ b/gopls/internal/test/integration/misc/generate_test.go
@@ -5,7 +5,6 @@
 // TODO(rfindley): figure out why go generate fails on android builders.
 
 //go:build !android
-// +build !android
 
 package misc
 
diff --git a/gopls/internal/test/integration/workspace/quickfix_test.go b/gopls/internal/test/integration/workspace/quickfix_test.go
index 9410984..916fbf0 100644
--- a/gopls/internal/test/integration/workspace/quickfix_test.go
+++ b/gopls/internal/test/integration/workspace/quickfix_test.go
@@ -488,7 +488,7 @@
 			t.Fatalf("got %v, want 2 quick fixes", fixes)
 		}
 		good := 0
-		failures := ""
+		var failures strings.Builder
 		for _, f := range fixes {
 			ti := f.Title
 			// these may be overly white-space sensitive
@@ -496,11 +496,11 @@
 				ti == "Add import:  \"failure.com/baz\"" {
 				good++
 			} else {
-				failures += ti
+				failures.WriteString(ti)
 			}
 		}
 		if good != 2 {
-			t.Errorf("failed to find\n%q, got\n%q\n%q", failures, fixes[0].Title,
+			t.Errorf("failed to find\n%q, got\n%q\n%q", failures.String(), fixes[0].Title,
 				fixes[1].Title)
 		}
 
diff --git a/gopls/internal/test/marker/marker_test.go b/gopls/internal/test/marker/marker_test.go
index fe8277b..e850148 100644
--- a/gopls/internal/test/marker/marker_test.go
+++ b/gopls/internal/test/marker/marker_test.go
@@ -1201,9 +1201,9 @@
 
 // Types with special handling.
 var (
-	goldenType        = reflect.TypeOf(&Golden{})
-	markerType        = reflect.TypeOf(marker{})
-	stringMatcherType = reflect.TypeOf(stringMatcher{})
+	goldenType        = reflect.TypeFor[*Golden]()
+	markerType        = reflect.TypeFor[marker]()
+	stringMatcherType = reflect.TypeFor[stringMatcher]()
 )
 
 // Custom conversions.
@@ -1213,8 +1213,8 @@
 //
 // Converters should return an error rather than calling marker.errorf().
 var customConverters = map[reflect.Type]func(marker, any) (any, error){
-	reflect.TypeOf(protocol.Location{}): converter(convertLocation),
-	reflect.TypeOf(completionLabel("")): converter(convertCompletionLabel),
+	reflect.TypeFor[protocol.Location](): converter(convertLocation),
+	reflect.TypeFor[completionLabel]():   converter(convertCompletionLabel),
 }
 
 // converter transforms a typed argument conversion function to an untyped
diff --git a/gopls/internal/util/fingerprint/fingerprint.go b/gopls/internal/util/fingerprint/fingerprint.go
index b279003..20517f1 100644
--- a/gopls/internal/util/fingerprint/fingerprint.go
+++ b/gopls/internal/util/fingerprint/fingerprint.go
@@ -113,9 +113,9 @@
 				buf.WriteString(tname.Name())
 			}
 			if targs != nil {
-				for i := range targs.Len() {
+				for t0 := range targs.Types() {
 					buf.WriteByte(' ')
-					print(targs.At(i))
+					print(t0)
 				}
 				buf.WriteString(")")
 			}
@@ -149,9 +149,9 @@
 
 		case *types.Tuple:
 			buf.WriteString("(tuple")
-			for i := range t.Len() {
+			for v := range t.Variables() {
 				buf.WriteByte(' ')
-				print(t.At(i).Type())
+				print(v.Type())
 			}
 			buf.WriteByte(')')
 
diff --git a/gopls/internal/util/frob/frob.go b/gopls/internal/util/frob/frob.go
index e5670a2..6ef157b 100644
--- a/gopls/internal/util/frob/frob.go
+++ b/gopls/internal/util/frob/frob.go
@@ -45,7 +45,7 @@
 func CodecFor[T any]() Codec[T] {
 	frobsMu.Lock()
 	defer frobsMu.Unlock()
-	return Codec[T]{frobFor(reflect.TypeOf((*T)(nil)).Elem())}
+	return Codec[T]{frobFor(reflect.TypeFor[T]())}
 }
 
 func (codec Codec[T]) Encode(v T) []byte          { return codec.frob.Encode(v) }
diff --git a/gopls/internal/util/typesutil/typesutil.go b/gopls/internal/util/typesutil/typesutil.go
index b6046dc..5558c67 100644
--- a/gopls/internal/util/typesutil/typesutil.go
+++ b/gopls/internal/util/typesutil/typesutil.go
@@ -110,8 +110,8 @@
 		retsig := sig.Results()
 		// Append all return declarations' type
 		if len(returnstmt.Results) == 1 {
-			for i := 0; i < retsig.Len(); i++ {
-				t := retsig.At(i).Type()
+			for v := range retsig.Variables() {
+				t := v.Type()
 				typs = append(typs, validType(t))
 			}
 			break
diff --git a/gopls/internal/vulncheck/copier.go b/gopls/internal/vulncheck/copier.go
index ade5a5f..6ef6ac7 100644
--- a/gopls/internal/vulncheck/copier.go
+++ b/gopls/internal/vulncheck/copier.go
@@ -3,7 +3,6 @@
 // license that can be found in the LICENSE file.
 
 //go:build ignore
-// +build ignore
 
 //go:generate go run ./copier.go
 
diff --git a/internal/analysisinternal/typeindex/typeindex.go b/internal/analysisinternal/typeindex/typeindex.go
index bba21c6..776d186 100644
--- a/internal/analysisinternal/typeindex/typeindex.go
+++ b/internal/analysisinternal/typeindex/typeindex.go
@@ -29,5 +29,5 @@
 	},
 	RunDespiteErrors: true,
 	Requires:         []*analysis.Analyzer{inspect.Analyzer},
-	ResultType:       reflect.TypeOf(new(typeindex.Index)),
+	ResultType:       reflect.TypeFor[*typeindex.Index](),
 }
diff --git a/internal/facts/imports.go b/internal/facts/imports.go
index cc9383e..324010b 100644
--- a/internal/facts/imports.go
+++ b/internal/facts/imports.go
@@ -53,8 +53,8 @@
 		case typesinternal.NamedOrAlias: // *types.{Named,Alias}
 			// Add the type arguments if this is an instance.
 			if targs := T.TypeArgs(); targs.Len() > 0 {
-				for i := 0; i < targs.Len(); i++ {
-					addType(targs.At(i))
+				for t := range targs.Types() {
+					addType(t)
 				}
 			}
 
@@ -70,8 +70,8 @@
 				// common aspects
 				addObj(T.Obj())
 				if tparams := T.TypeParams(); tparams.Len() > 0 {
-					for i := 0; i < tparams.Len(); i++ {
-						addType(tparams.At(i))
+					for tparam := range tparams.TypeParams() {
+						addType(tparam)
 					}
 				}
 
@@ -81,8 +81,8 @@
 					addType(aliases.Rhs(T))
 				case *types.Named:
 					addType(T.Underlying())
-					for i := 0; i < T.NumMethods(); i++ {
-						addObj(T.Method(i))
+					for method := range T.Methods() {
+						addObj(method)
 					}
 				}
 			}
@@ -101,28 +101,28 @@
 			addType(T.Params())
 			addType(T.Results())
 			if tparams := T.TypeParams(); tparams != nil {
-				for i := 0; i < tparams.Len(); i++ {
-					addType(tparams.At(i))
+				for tparam := range tparams.TypeParams() {
+					addType(tparam)
 				}
 			}
 		case *types.Struct:
-			for i := 0; i < T.NumFields(); i++ {
-				addObj(T.Field(i))
+			for field := range T.Fields() {
+				addObj(field)
 			}
 		case *types.Tuple:
-			for i := 0; i < T.Len(); i++ {
-				addObj(T.At(i))
+			for v := range T.Variables() {
+				addObj(v)
 			}
 		case *types.Interface:
-			for i := 0; i < T.NumMethods(); i++ {
-				addObj(T.Method(i))
+			for method := range T.Methods() {
+				addObj(method)
 			}
-			for i := 0; i < T.NumEmbeddeds(); i++ {
-				addType(T.EmbeddedType(i)) // walk Embedded for implicits
+			for etyp := range T.EmbeddedTypes() {
+				addType(etyp) // walk Embedded for implicits
 			}
 		case *types.Union:
-			for i := 0; i < T.Len(); i++ {
-				addType(T.Term(i).Type())
+			for term := range T.Terms() {
+				addType(term.Type())
 			}
 		case *types.TypeParam:
 			if !typs[T] {
diff --git a/internal/gcimporter/bexport_test.go b/internal/gcimporter/bexport_test.go
index fb18a55..7967240 100644
--- a/internal/gcimporter/bexport_test.go
+++ b/internal/gcimporter/bexport_test.go
@@ -259,8 +259,8 @@
 			methods = append(methods, iface.Method(i))
 		}
 		var embeddeds []types.Type
-		for i := 0; i < iface.NumEmbeddeds(); i++ {
-			embeddeds = append(embeddeds, iface.EmbeddedType(i))
+		for etyp := range iface.EmbeddedTypes() {
+			embeddeds = append(embeddeds, etyp)
 		}
 		return types.NewInterfaceType(methods, embeddeds)
 	}
diff --git a/internal/gcimporter/gcimporter_test.go b/internal/gcimporter/gcimporter_test.go
index 9dc65fa..749253b 100644
--- a/internal/gcimporter/gcimporter_test.go
+++ b/internal/gcimporter/gcimporter_test.go
@@ -502,8 +502,7 @@
 	}
 
 	// check explicitly declared methods
-	for i := 0; i < iface.NumExplicitMethods(); i++ {
-		m := iface.ExplicitMethod(i)
+	for m := range iface.ExplicitMethods() {
 		recv := m.Type().(*types.Signature).Recv()
 		if recv == nil {
 			t.Errorf("%s: missing receiver type", m)
@@ -515,9 +514,9 @@
 	}
 
 	// check embedded interfaces (if they are named, too)
-	for i := 0; i < iface.NumEmbeddeds(); i++ {
+	for etyp := range iface.EmbeddedTypes() {
 		// embedding of interfaces cannot have cycles; recursion will terminate
-		if etype, _ := types.Unalias(iface.EmbeddedType(i)).(*types.Named); etype != nil {
+		if etype, _ := types.Unalias(etyp).(*types.Named); etype != nil {
 			verifyInterfaceMethodRecvs(t, etype, level+1)
 		}
 	}
@@ -538,8 +537,7 @@
 		}
 		if tname, _ := obj.(*types.TypeName); tname != nil {
 			named := types.Unalias(tname.Type()).(*types.Named)
-			for i := 0; i < named.NumMethods(); i++ {
-				m := named.Method(i)
+			for m := range named.Methods() {
 				if m.Pkg() == nil {
 					t.Errorf("no pkg for %s", m)
 				}
diff --git a/internal/gcimporter/iexport.go b/internal/gcimporter/iexport.go
index 4a4357d..8299efb 100644
--- a/internal/gcimporter/iexport.go
+++ b/internal/gcimporter/iexport.go
@@ -829,8 +829,7 @@
 			// their name must be qualified before exporting recv.
 			if rparams := sig.RecvTypeParams(); rparams.Len() > 0 {
 				prefix := obj.Name() + "." + m.Name()
-				for i := 0; i < rparams.Len(); i++ {
-					rparam := rparams.At(i)
+				for rparam := range rparams.TypeParams() {
 					name := tparamExportName(prefix, rparam)
 					w.p.tparamNames[rparam.Obj()] = name
 				}
@@ -1223,20 +1222,19 @@
 
 func (w *exportWriter) typeList(ts *types.TypeList, pkg *types.Package) {
 	w.uint64(uint64(ts.Len()))
-	for i := 0; i < ts.Len(); i++ {
-		w.typ(ts.At(i), pkg)
+	for t := range ts.Types() {
+		w.typ(t, pkg)
 	}
 }
 
 func (w *exportWriter) tparamList(prefix string, list *types.TypeParamList, pkg *types.Package) {
 	ll := uint64(list.Len())
 	w.uint64(ll)
-	for i := 0; i < list.Len(); i++ {
-		tparam := list.At(i)
+	for tparam := range list.TypeParams() {
 		// Set the type parameter exportName before exporting its type.
 		exportName := tparamExportName(prefix, tparam)
 		w.p.tparamNames[tparam.Obj()] = exportName
-		w.typ(list.At(i), pkg)
+		w.typ(tparam, pkg)
 	}
 }
 
diff --git a/internal/refactor/inline/callee.go b/internal/refactor/inline/callee.go
index ab2979d..ce5beb2 100644
--- a/internal/refactor/inline/callee.go
+++ b/internal/refactor/inline/callee.go
@@ -441,11 +441,11 @@
 		if sig.Recv() != nil {
 			params = append(params, newParamInfo(sig.Recv(), false))
 		}
-		for i := 0; i < sig.Params().Len(); i++ {
-			params = append(params, newParamInfo(sig.Params().At(i), false))
+		for v := range sig.Params().Variables() {
+			params = append(params, newParamInfo(v, false))
 		}
-		for i := 0; i < sig.Results().Len(); i++ {
-			results = append(results, newParamInfo(sig.Results().At(i), true))
+		for v := range sig.Results().Variables() {
+			results = append(results, newParamInfo(v, true))
 		}
 	}
 
@@ -517,8 +517,8 @@
 	paramInfos := make(map[*types.TypeName]*paramInfo)
 	var params []*paramInfo
 	collect := func(tpl *types.TypeParamList) {
-		for i := range tpl.Len() {
-			typeName := tpl.At(i).Obj()
+		for tparam := range tpl.TypeParams() {
+			typeName := tparam.Obj()
 			info := &paramInfo{Name: typeName.Name()}
 			params = append(params, info)
 			paramInfos[typeName] = info
@@ -659,8 +659,7 @@
 				return true, types.IsInterface(under.Elem()), false
 			case *types.Struct: // Struct{k: expr}
 				if id, _ := kv.Key.(*ast.Ident); id != nil {
-					for fi := range under.NumFields() {
-						field := under.Field(fi)
+					for field := range under.Fields() {
 						if info.Uses[id] == field {
 							return true, types.IsInterface(field.Type()), false
 						}
diff --git a/internal/typeparams/common_test.go b/internal/typeparams/common_test.go
index 3cbd741..be67859 100644
--- a/internal/typeparams/common_test.go
+++ b/internal/typeparams/common_test.go
@@ -185,8 +185,7 @@
 	if mset.Len() == 0 {
 		t.Errorf("NewMethodSet(*T) is empty")
 	}
-	for i := 0; i < mset.Len(); i++ {
-		sel := mset.At(i)
+	for sel := range mset.Methods() {
 		m := sel.Obj().(*types.Func)
 
 		// TODO(adonovan): check the consistency property required to fix #60634.
diff --git a/internal/typeparams/normalize.go b/internal/typeparams/normalize.go
index f49802b..8d13f12 100644
--- a/internal/typeparams/normalize.go
+++ b/internal/typeparams/normalize.go
@@ -160,8 +160,7 @@
 		// The term set of an interface is the intersection of the term sets of its
 		// embedded types.
 		tset.terms = allTermlist
-		for i := 0; i < u.NumEmbeddeds(); i++ {
-			embedded := u.EmbeddedType(i)
+		for embedded := range u.EmbeddedTypes() {
 			if _, ok := embedded.Underlying().(*types.TypeParam); ok {
 				return nil, fmt.Errorf("invalid embedded type %T", embedded)
 			}
@@ -174,8 +173,7 @@
 	case *types.Union:
 		// The term set of a union is the union of term sets of its terms.
 		tset.terms = nil
-		for i := 0; i < u.Len(); i++ {
-			t := u.Term(i)
+		for t := range u.Terms() {
 			var terms termlist
 			switch t.Type().Underlying().(type) {
 			case *types.Interface:
diff --git a/internal/typesinternal/element.go b/internal/typesinternal/element.go
index 4957f02..5fe4d8a 100644
--- a/internal/typesinternal/element.go
+++ b/internal/typesinternal/element.go
@@ -35,8 +35,8 @@
 
 		// Recursion over signatures of each method.
 		tmset := msets.MethodSet(T)
-		for i := 0; i < tmset.Len(); i++ {
-			sig := tmset.At(i).Type().(*types.Signature)
+		for method := range tmset.Methods() {
+			sig := method.Type().(*types.Signature)
 			// It is tempting to call visit(sig, false)
 			// but, as noted in golang.org/cl/65450043,
 			// the Signature.Recv field is ignored by
diff --git a/internal/typesinternal/isnamed.go b/internal/typesinternal/isnamed.go
index f2affec..e0d63c4 100644
--- a/internal/typesinternal/isnamed.go
+++ b/internal/typesinternal/isnamed.go
@@ -48,7 +48,7 @@
 	return ok &&
 		IsPackageLevel(obj) &&
 		f.Pkg().Path() == pkgPath &&
-		f.Type().(*types.Signature).Recv() == nil &&
+		f.Signature().Recv() == nil &&
 		slices.Contains(names, f.Name())
 }
 
@@ -60,7 +60,7 @@
 // which is important for the performance of syntax matching.
 func IsMethodNamed(obj types.Object, pkgPath string, typeName string, names ...string) bool {
 	if fn, ok := obj.(*types.Func); ok {
-		if recv := fn.Type().(*types.Signature).Recv(); recv != nil {
+		if recv := fn.Signature().Recv(); recv != nil {
 			_, T := ReceiverNamed(recv)
 			return T != nil &&
 				IsTypeNamed(T, pkgPath, typeName) &&
diff --git a/internal/typesinternal/zerovalue.go b/internal/typesinternal/zerovalue.go
index 453bba2..d612a71 100644
--- a/internal/typesinternal/zerovalue.go
+++ b/internal/typesinternal/zerovalue.go
@@ -258,12 +258,12 @@
 
 	case *types.Signature:
 		var params []*ast.Field
-		for i := 0; i < t.Params().Len(); i++ {
+		for v := range t.Params().Variables() {
 			params = append(params, &ast.Field{
-				Type: TypeExpr(t.Params().At(i).Type(), qual),
+				Type: TypeExpr(v.Type(), qual),
 				Names: []*ast.Ident{
 					{
-						Name: t.Params().At(i).Name(),
+						Name: v.Name(),
 					},
 				},
 			})
@@ -273,9 +273,9 @@
 			last.Type = &ast.Ellipsis{Elt: last.Type.(*ast.ArrayType).Elt}
 		}
 		var returns []*ast.Field
-		for i := 0; i < t.Results().Len(); i++ {
+		for v := range t.Results().Variables() {
 			returns = append(returns, &ast.Field{
-				Type: TypeExpr(t.Results().At(i).Type(), qual),
+				Type: TypeExpr(v.Type(), qual),
 			})
 		}
 		return &ast.FuncType{
@@ -315,8 +315,8 @@
 		if hasTypeArgs, ok := t.(interface{ TypeArgs() *types.TypeList }); ok {
 			if typeArgs := hasTypeArgs.TypeArgs(); typeArgs != nil && typeArgs.Len() > 0 {
 				var indices []ast.Expr
-				for i := range typeArgs.Len() {
-					indices = append(indices, TypeExpr(typeArgs.At(i), qual))
+				for t0 := range typeArgs.Types() {
+					indices = append(indices, TypeExpr(t0, qual))
 				}
 				expr = &ast.IndexListExpr{
 					X:       expr,
diff --git a/refactor/eg/eg.go b/refactor/eg/eg.go
index 8de1fd7..6defdeb 100644
--- a/refactor/eg/eg.go
+++ b/refactor/eg/eg.go
@@ -210,8 +210,8 @@
 	}
 
 	wildcards := make(map[*types.Var]bool)
-	for i := 0; i < beforeSig.Params().Len(); i++ {
-		wildcards[beforeSig.Params().At(i)] = true
+	for v := range beforeSig.Params().Variables() {
+		wildcards[v] = true
 	}
 
 	// checkExprTypes returns an error if Tb (type of before()) is not
diff --git a/refactor/rename/check.go b/refactor/rename/check.go
index f41213a..6400123 100644
--- a/refactor/rename/check.go
+++ b/refactor/rename/check.go
@@ -445,8 +445,8 @@
 		// This struct is not a defined type. (It may be an alias.)
 		// We need only check for direct (non-promoted) field/field conflicts.
 		T := info.Types[tStruct].Type.Underlying().(*types.Struct)
-		for i := 0; i < T.NumFields(); i++ {
-			if prev := T.Field(i); prev.Name() == r.to {
+		for field := range T.Fields() {
+			if prev := field; prev.Name() == r.to {
 				r.errorf(from.Pos(), "renaming this field %q to %q",
 					from.Name(), r.to)
 				r.errorf(prev.Pos(), "\twould conflict with this field")
@@ -828,7 +828,7 @@
 
 // recv returns the method's receiver.
 func recv(meth *types.Func) *types.Var {
-	return meth.Type().(*types.Signature).Recv()
+	return meth.Signature().Recv()
 }
 
 // someUse returns an arbitrary use of obj within info.
diff --git a/refactor/rename/rename.go b/refactor/rename/rename.go
index 6e6dcbf..a2c2241 100644
--- a/refactor/rename/rename.go
+++ b/refactor/rename/rename.go
@@ -325,7 +325,7 @@
 	// to preserve assignability.
 	for _, obj := range fromObjects {
 		if obj, ok := obj.(*types.Func); ok {
-			recv := obj.Type().(*types.Signature).Recv()
+			recv := obj.Signature().Recv()
 			if recv != nil && types.IsInterface(recv.Type()) {
 				r.changeMethods = true
 				break
diff --git a/refactor/rename/util.go b/refactor/rename/util.go
index cb7cea3..ce1809d 100644
--- a/refactor/rename/util.go
+++ b/refactor/rename/util.go
@@ -26,7 +26,7 @@
 			return "field"
 		}
 	case *types.Func:
-		if obj.Type().(*types.Signature).Recv() != nil {
+		if obj.Signature().Recv() != nil {
 			return "method"
 		}
 	}
diff --git a/refactor/satisfy/find.go b/refactor/satisfy/find.go
index 93d0483..6d23aa6 100644
--- a/refactor/satisfy/find.go
+++ b/refactor/satisfy/find.go
@@ -172,8 +172,8 @@
 		// f(g()) call where g has multiple results?
 		f.expr(args[0])
 		// unpack the tuple
-		for i := 0; i < tuple.Len(); i++ {
-			argtypes = append(argtypes, tuple.At(i).Type())
+		for v := range tuple.Variables() {
+			argtypes = append(argtypes, v.Type())
 		}
 	} else {
 		for _, arg := range args {