all: merge master (ab04c19) into gopls-release-branch.0.18

Also add back the replace directive.

For golang/go#71607

Merge List:

+ 2025-02-13 ab04c1963 gopls/internal/analysis/modernize: improve rangeint transformation
+ 2025-02-13 ddd4bde5a gopls/internal/golang: avoid PackageSymbols errors with missing packages
+ 2025-02-13 44b61a1d1 x/tools: eliminate various unparen (et al) helpers
+ 2025-02-13 d0d86e40a x/tools: run gopls/internal/analysis/gofix/main.go -fix
+ 2025-02-13 2f1b076c4 x/tools: add //go:fix inline
+ 2025-02-12 86f13a91f gopls/internal/analysis/gofix: rename local
+ 2025-02-12 57629448d gopls/internal/analysis/gofix: check package visibility
+ 2025-02-12 f9aad7054 go/types/typeutil: avoid shifting uintptr by 32 on 32-bit archs

Change-Id: Iec742aa4d8dc370d050b8f33a5dd11838cdb7888
diff --git a/go.mod b/go.mod
index 8cea866..e6e31d7 100644
--- a/go.mod
+++ b/go.mod
@@ -12,3 +12,5 @@
 )
 
 require golang.org/x/sys v0.30.0 // indirect
+
+replace golang.org/x/tools => ../
diff --git a/go/analysis/checker/checker.go b/go/analysis/checker/checker.go
index 502ec92..9480873 100644
--- a/go/analysis/checker/checker.go
+++ b/go/analysis/checker/checker.go
@@ -594,7 +594,7 @@
 
 func factType(fact analysis.Fact) reflect.Type {
 	t := reflect.TypeOf(fact)
-	if t.Kind() != reflect.Ptr {
+	if t.Kind() != reflect.Pointer {
 		log.Fatalf("invalid Fact type: got %T, want pointer", fact)
 	}
 	return t
diff --git a/go/analysis/passes/copylock/copylock.go b/go/analysis/passes/copylock/copylock.go
index a9f02ac..8a21567 100644
--- a/go/analysis/passes/copylock/copylock.go
+++ b/go/analysis/passes/copylock/copylock.go
@@ -378,7 +378,7 @@
 
 // Construct a sync.Locker interface type.
 func init() {
-	nullary := types.NewSignature(nil, nil, nil, false) // func()
+	nullary := types.NewSignatureType(nil, nil, nil, nil, nil, false) // func()
 	methods := []*types.Func{
 		types.NewFunc(token.NoPos, nil, "Lock", nullary),
 		types.NewFunc(token.NoPos, nil, "Unlock", nullary),
diff --git a/go/analysis/passes/unusedresult/unusedresult.go b/go/analysis/passes/unusedresult/unusedresult.go
index d7cc1e6..e298f64 100644
--- a/go/analysis/passes/unusedresult/unusedresult.go
+++ b/go/analysis/passes/unusedresult/unusedresult.go
@@ -130,9 +130,7 @@
 }
 
 // func() string
-var sigNoArgsStringResult = types.NewSignature(nil, nil,
-	types.NewTuple(types.NewParam(token.NoPos, nil, "", types.Typ[types.String])),
-	false)
+var sigNoArgsStringResult = types.NewSignatureType(nil, nil, nil, nil, types.NewTuple(types.NewParam(token.NoPos, nil, "", types.Typ[types.String])), false)
 
 type stringSetFlag map[string]bool
 
diff --git a/go/analysis/validate.go b/go/analysis/validate.go
index 4f2c404..1453939 100644
--- a/go/analysis/validate.go
+++ b/go/analysis/validate.go
@@ -63,7 +63,7 @@
 					return fmt.Errorf("fact type %s registered by two analyzers: %v, %v",
 						t, a, prev)
 				}
-				if t.Kind() != reflect.Ptr {
+				if t.Kind() != reflect.Pointer {
 					return fmt.Errorf("%s: fact type %s is not a pointer", a, t)
 				}
 				factTypes[t] = a
diff --git a/go/ast/astutil/rewrite.go b/go/ast/astutil/rewrite.go
index 58934f7..5c8dbbb 100644
--- a/go/ast/astutil/rewrite.go
+++ b/go/ast/astutil/rewrite.go
@@ -183,7 +183,7 @@
 
 func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast.Node) {
 	// convert typed nil into untyped nil
-	if v := reflect.ValueOf(n); v.Kind() == reflect.Ptr && v.IsNil() {
+	if v := reflect.ValueOf(n); v.Kind() == reflect.Pointer && v.IsNil() {
 		n = nil
 	}
 
diff --git a/go/ast/astutil/util.go b/go/ast/astutil/util.go
index ca71e3e..c820b20 100644
--- a/go/ast/astutil/util.go
+++ b/go/ast/astutil/util.go
@@ -8,4 +8,6 @@
 
 // Unparen returns e with any enclosing parentheses stripped.
 // Deprecated: use [ast.Unparen].
+//
+//go:fix inline
 func Unparen(e ast.Expr) ast.Expr { return ast.Unparen(e) }
diff --git a/go/callgraph/vta/propagation_test.go b/go/callgraph/vta/propagation_test.go
index 492258f..3885ef2 100644
--- a/go/callgraph/vta/propagation_test.go
+++ b/go/callgraph/vta/propagation_test.go
@@ -203,7 +203,7 @@
 	a := newNamedType("A")
 	b := newNamedType("B")
 	c := newNamedType("C")
-	sig := types.NewSignature(nil, types.NewTuple(), types.NewTuple(), false)
+	sig := types.NewSignatureType(nil, nil, nil, types.NewTuple(), types.NewTuple(), false)
 
 	f1 := &ssa.Function{Signature: sig}
 	setName(f1, "F1")
diff --git a/go/internal/gccgoimporter/parser.go b/go/internal/gccgoimporter/parser.go
index f315ec4..f70946e 100644
--- a/go/internal/gccgoimporter/parser.go
+++ b/go/internal/gccgoimporter/parser.go
@@ -619,7 +619,7 @@
 			p.skipInlineBody()
 			p.expectEOL()
 
-			sig := types.NewSignature(receiver, params, results, isVariadic)
+			sig := types.NewSignatureType(receiver, nil, nil, params, results, isVariadic)
 			nt.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
 		}
 	}
@@ -800,7 +800,7 @@
 	params, isVariadic := p.parseParamList(pkg)
 	results := p.parseResultList(pkg)
 
-	*t = *types.NewSignature(nil, params, results, isVariadic)
+	*t = *types.NewSignatureType(nil, nil, nil, params, results, isVariadic)
 	return t
 }
 
diff --git a/go/packages/packages.go b/go/packages/packages.go
index c3a59b8..342f019 100644
--- a/go/packages/packages.go
+++ b/go/packages/packages.go
@@ -141,6 +141,8 @@
 	LoadAllSyntax = LoadSyntax | NeedDeps
 
 	// Deprecated: NeedExportsFile is a historical misspelling of NeedExportFile.
+	//
+	//go:fix inline
 	NeedExportsFile = NeedExportFile
 )
 
diff --git a/go/ssa/builder.go b/go/ssa/builder.go
index 4cd7126..1761dcc 100644
--- a/go/ssa/builder.go
+++ b/go/ssa/builder.go
@@ -559,7 +559,7 @@
 // literal that may reference parts of the LHS.
 func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb *storebuf) {
 	// Can we initialize it in place?
-	if e, ok := unparen(e).(*ast.CompositeLit); ok {
+	if e, ok := ast.Unparen(e).(*ast.CompositeLit); ok {
 		// A CompositeLit never evaluates to a pointer,
 		// so if the type of the location is a pointer,
 		// an &-operation is implied.
@@ -614,7 +614,7 @@
 // expr lowers a single-result expression e to SSA form, emitting code
 // to fn and returning the Value defined by the expression.
 func (b *builder) expr(fn *Function, e ast.Expr) Value {
-	e = unparen(e)
+	e = ast.Unparen(e)
 
 	tv := fn.info.Types[e]
 
@@ -704,7 +704,7 @@
 			return y
 		}
 		// Call to "intrinsic" built-ins, e.g. new, make, panic.
-		if id, ok := unparen(e.Fun).(*ast.Ident); ok {
+		if id, ok := ast.Unparen(e.Fun).(*ast.Ident); ok {
 			if obj, ok := fn.info.Uses[id].(*types.Builtin); ok {
 				if v := b.builtin(fn, obj, e.Args, fn.typ(tv.Type), e.Lparen); v != nil {
 					return v
@@ -721,7 +721,7 @@
 		switch e.Op {
 		case token.AND: // &X --- potentially escaping.
 			addr := b.addr(fn, e.X, true)
-			if _, ok := unparen(e.X).(*ast.StarExpr); ok {
+			if _, ok := ast.Unparen(e.X).(*ast.StarExpr); ok {
 				// &*p must panic if p is nil (http://golang.org/s/go12nil).
 				// For simplicity, we'll just (suboptimally) rely
 				// on the side effects of a load.
@@ -1002,7 +1002,7 @@
 	c.pos = e.Lparen
 
 	// Is this a method call?
-	if selector, ok := unparen(e.Fun).(*ast.SelectorExpr); ok {
+	if selector, ok := ast.Unparen(e.Fun).(*ast.SelectorExpr); ok {
 		sel := fn.selection(selector)
 		if sel != nil && sel.kind == types.MethodVal {
 			obj := sel.obj.(*types.Func)
@@ -1372,7 +1372,7 @@
 			// An &-operation may be implied:
 			//	map[*struct{}]bool{&struct{}{}: true}
 			wantAddr := false
-			if _, ok := unparen(e.Key).(*ast.CompositeLit); ok {
+			if _, ok := ast.Unparen(e.Key).(*ast.CompositeLit); ok {
 				wantAddr = isPointerCore(t.Key())
 			}
 
@@ -1547,9 +1547,9 @@
 	var x Value
 	switch ass := s.Assign.(type) {
 	case *ast.ExprStmt: // x.(type)
-		x = b.expr(fn, unparen(ass.X).(*ast.TypeAssertExpr).X)
+		x = b.expr(fn, ast.Unparen(ass.X).(*ast.TypeAssertExpr).X)
 	case *ast.AssignStmt: // y := x.(type)
-		x = b.expr(fn, unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
+		x = b.expr(fn, ast.Unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
 	}
 
 	done := fn.newBasicBlock("typeswitch.done")
@@ -1667,7 +1667,7 @@
 			}
 
 		case *ast.AssignStmt: // x := <-ch
-			recv := unparen(comm.Rhs[0]).(*ast.UnaryExpr)
+			recv := ast.Unparen(comm.Rhs[0]).(*ast.UnaryExpr)
 			st = &SelectState{
 				Dir:  types.RecvOnly,
 				Chan: b.expr(fn, recv.X),
@@ -1678,7 +1678,7 @@
 			}
 
 		case *ast.ExprStmt: // <-ch
-			recv := unparen(comm.X).(*ast.UnaryExpr)
+			recv := ast.Unparen(comm.X).(*ast.UnaryExpr)
 			st = &SelectState{
 				Dir:  types.RecvOnly,
 				Chan: b.expr(fn, recv.X),
diff --git a/go/ssa/emit.go b/go/ssa/emit.go
index a3d41ad..bca79ad 100644
--- a/go/ssa/emit.go
+++ b/go/ssa/emit.go
@@ -81,7 +81,7 @@
 		panic("nil")
 	}
 	var obj types.Object
-	e = unparen(e)
+	e = ast.Unparen(e)
 	if id, ok := e.(*ast.Ident); ok {
 		if isBlankIdent(id) {
 			return
diff --git a/go/ssa/interp/reflect.go b/go/ssa/interp/reflect.go
index 8259e56..22f8cde 100644
--- a/go/ssa/interp/reflect.go
+++ b/go/ssa/interp/reflect.go
@@ -231,7 +231,7 @@
 	case *types.Map:
 		return reflect.Map
 	case *types.Pointer:
-		return reflect.Ptr
+		return reflect.Pointer
 	case *types.Slice:
 		return reflect.Slice
 	case *types.Struct:
@@ -510,7 +510,7 @@
 	// that is needed is the "pointerness" of Recv.Type, and for
 	// now, we'll set it to always be false since we're only
 	// concerned with rtype.  Encapsulate this better.
-	sig := types.NewSignature(types.NewParam(token.NoPos, nil, "recv", recvType), nil, nil, false)
+	sig := types.NewSignatureType(types.NewParam(token.NoPos, nil, "recv", recvType), nil, nil, nil, nil, false)
 	fn := pkg.Prog.NewFunction(name, sig, "fake reflect method")
 	fn.Pkg = pkg
 	return fn
diff --git a/go/ssa/source.go b/go/ssa/source.go
index 055a6b1..d0cc1f4 100644
--- a/go/ssa/source.go
+++ b/go/ssa/source.go
@@ -153,7 +153,7 @@
 // the ssa.Value.)
 func (f *Function) ValueForExpr(e ast.Expr) (value Value, isAddr bool) {
 	if f.debugInfo() { // (opt)
-		e = unparen(e)
+		e = ast.Unparen(e)
 		for _, b := range f.Blocks {
 			for _, instr := range b.Instrs {
 				if ref, ok := instr.(*DebugRef); ok {
diff --git a/go/ssa/util.go b/go/ssa/util.go
index 4a056cb..2a9c9b9 100644
--- a/go/ssa/util.go
+++ b/go/ssa/util.go
@@ -35,8 +35,6 @@
 
 //// AST utilities
 
-func unparen(e ast.Expr) ast.Expr { return ast.Unparen(e) }
-
 // isBlankIdent returns true iff e is an Ident with name "_".
 // They have no associated types.Object, and thus no type.
 func isBlankIdent(e ast.Expr) bool {
@@ -195,7 +193,7 @@
 	lenParams := types.NewTuple(anonVar(T))
 	return &Builtin{
 		name: "len",
-		sig:  types.NewSignature(nil, lenParams, lenResults, false),
+		sig:  types.NewSignatureType(nil, nil, nil, lenParams, lenResults, false),
 	}
 }
 
diff --git a/go/ssa/wrappers.go b/go/ssa/wrappers.go
index d09b4f2..aeb160e 100644
--- a/go/ssa/wrappers.go
+++ b/go/ssa/wrappers.go
@@ -106,9 +106,7 @@
 			var c Call
 			c.Call.Value = &Builtin{
 				name: "ssa:wrapnilchk",
-				sig: types.NewSignature(nil,
-					types.NewTuple(anonVar(fn.method.recv), anonVar(tString), anonVar(tString)),
-					types.NewTuple(anonVar(fn.method.recv)), false),
+				sig:  types.NewSignatureType(nil, nil, nil, types.NewTuple(anonVar(fn.method.recv), anonVar(tString), anonVar(tString)), types.NewTuple(anonVar(fn.method.recv)), false),
 			}
 			c.Call.Args = []Value{
 				v,
@@ -262,7 +260,7 @@
 }
 
 func changeRecv(s *types.Signature, recv *types.Var) *types.Signature {
-	return types.NewSignature(recv, s.Params(), s.Results(), s.Variadic())
+	return types.NewSignatureType(recv, nil, nil, s.Params(), s.Results(), s.Variadic())
 }
 
 // A local version of *types.Selection.
diff --git a/go/types/objectpath/objectpath_test.go b/go/types/objectpath/objectpath_test.go
index 0805c9d..642d6da 100644
--- a/go/types/objectpath/objectpath_test.go
+++ b/go/types/objectpath/objectpath_test.go
@@ -308,7 +308,7 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	conf := types.Config{Importer: importer.For("source", nil)}
+	conf := types.Config{Importer: importer.ForCompiler(token.NewFileSet(), "source", nil)}
 	info := &types.Info{
 		Defs: make(map[*ast.Ident]types.Object),
 	}
diff --git a/go/types/typeutil/map.go b/go/types/typeutil/map.go
index 4326114..b6d542c 100644
--- a/go/types/typeutil/map.go
+++ b/go/types/typeutil/map.go
@@ -389,8 +389,13 @@
 	// path, and whether or not it is a package-level typename. It
 	// is rare for a package to define multiple local types with
 	// the same name.)
-	hash := uintptr(unsafe.Pointer(tname))
-	return uint32(hash ^ (hash >> 32))
+	ptr := uintptr(unsafe.Pointer(tname))
+	if unsafe.Sizeof(ptr) == 8 {
+		hash := uint64(ptr)
+		return uint32(hash ^ (hash >> 32))
+	} else {
+		return uint32(ptr)
+	}
 }
 
 // shallowHash computes a hash of t without looking at any of its
diff --git a/gopls/internal/analysis/gofix/gofix.go b/gopls/internal/analysis/gofix/gofix.go
index 1019243..8ec31bd 100644
--- a/gopls/internal/analysis/gofix/gofix.go
+++ b/gopls/internal/analysis/gofix/gofix.go
@@ -220,15 +220,15 @@
 		case *ast.Ident:
 			// If the identifier is a use of an inlinable constant, suggest inlining it.
 			if con, ok := pass.TypesInfo.Uses[n].(*types.Const); ok {
-				fcon, ok := inlinableConsts[con]
+				incon, ok := inlinableConsts[con]
 				if !ok {
 					var fact goFixInlineConstFact
 					if pass.ImportObjectFact(con, &fact) {
-						fcon = &fact
-						inlinableConsts[con] = fcon
+						incon = &fact
+						inlinableConsts[con] = incon
 					}
 				}
-				if fcon == nil {
+				if incon == nil {
 					continue // nope
 				}
 
@@ -248,27 +248,30 @@
 				// If the RHS is not in the current package, AddImport will handle
 				// shadowing, so we only need to worry about when both expressions
 				// are in the current package.
-				if pass.Pkg.Path() == fcon.RHSPkgPath {
+				if pass.Pkg.Path() == incon.RHSPkgPath {
 					// fcon.rhsObj is the object referred to by B in the definition of A.
 					scope := pass.TypesInfo.Scopes[curFile].Innermost(n.Pos()) // n's scope
-					_, obj := scope.LookupParent(fcon.RHSName, n.Pos())        // what "B" means in n's scope
+					_, obj := scope.LookupParent(incon.RHSName, n.Pos())       // what "B" means in n's scope
 					if obj == nil {
 						// Should be impossible: if code at n can refer to the LHS,
 						// it can refer to the RHS.
-						panic(fmt.Sprintf("no object for inlinable const %s RHS %s", n.Name, fcon.RHSName))
+						panic(fmt.Sprintf("no object for inlinable const %s RHS %s", n.Name, incon.RHSName))
 					}
-					if obj != fcon.rhsObj {
+					if obj != incon.rhsObj {
 						// "B" means something different here than at the inlinable const's scope.
 						continue
 					}
+				} else if !analysisinternal.CanImport(pass.Pkg.Path(), incon.RHSPkgPath) {
+					// If this package can't see the RHS's package, we can't inline.
+					continue
 				}
 				var (
 					importPrefix string
 					edits        []analysis.TextEdit
 				)
-				if fcon.RHSPkgPath != pass.Pkg.Path() {
+				if incon.RHSPkgPath != pass.Pkg.Path() {
 					_, importPrefix, edits = analysisinternal.AddImport(
-						pass.TypesInfo, curFile, fcon.RHSPkgName, fcon.RHSPkgPath, fcon.RHSName, n.Pos())
+						pass.TypesInfo, curFile, incon.RHSPkgName, incon.RHSPkgPath, incon.RHSName, n.Pos())
 				}
 				var (
 					pos  = n.Pos()
@@ -284,7 +287,7 @@
 				edits = append(edits, analysis.TextEdit{
 					Pos:     pos,
 					End:     end,
-					NewText: []byte(importPrefix + fcon.RHSName),
+					NewText: []byte(importPrefix + incon.RHSName),
 				})
 				pass.Report(analysis.Diagnostic{
 					Pos:     pos,
diff --git a/gopls/internal/analysis/gofix/testdata/src/a/a.go b/gopls/internal/analysis/gofix/testdata/src/a/a.go
index ae48674..4f41b9a 100644
--- a/gopls/internal/analysis/gofix/testdata/src/a/a.go
+++ b/gopls/internal/analysis/gofix/testdata/src/a/a.go
@@ -1,5 +1,7 @@
 package a
 
+import "a/internal"
+
 // Functions.
 
 func f() {
@@ -75,6 +77,9 @@
 	in8 = x
 )
 
+//go:fix inline
+const D = internal.D // want D: `goFixInline const "a/internal".D`
+
 func shadow() {
 	var x int // shadows x at package scope
 
diff --git a/gopls/internal/analysis/gofix/testdata/src/a/a.go.golden b/gopls/internal/analysis/gofix/testdata/src/a/a.go.golden
index 7d75a59..9e9cc25 100644
--- a/gopls/internal/analysis/gofix/testdata/src/a/a.go.golden
+++ b/gopls/internal/analysis/gofix/testdata/src/a/a.go.golden
@@ -1,5 +1,7 @@
 package a
 
+import "a/internal"
+
 // Functions.
 
 func f() {
@@ -75,6 +77,9 @@
 	in8 = x
 )
 
+//go:fix inline
+const D = internal.D // want D: `goFixInline const "a/internal".D`
+
 func shadow() {
 	var x int // shadows x at package scope
 
diff --git a/gopls/internal/analysis/gofix/testdata/src/a/internal/d.go b/gopls/internal/analysis/gofix/testdata/src/a/internal/d.go
new file mode 100644
index 0000000..3211d7a
--- /dev/null
+++ b/gopls/internal/analysis/gofix/testdata/src/a/internal/d.go
@@ -0,0 +1,5 @@
+// According to the go toolchain's rule about internal packages,
+// this package is visible to package a, but not package b.
+package internal
+
+const D = 1
diff --git a/gopls/internal/analysis/gofix/testdata/src/b/b.go b/gopls/internal/analysis/gofix/testdata/src/b/b.go
index 4bf9f0d..7487673 100644
--- a/gopls/internal/analysis/gofix/testdata/src/b/b.go
+++ b/gopls/internal/analysis/gofix/testdata/src/b/b.go
@@ -28,3 +28,5 @@
 	_ = a
 	_ = x
 }
+
+const d = a.D // nope: a.D refers to a constant in a package that is not visible here.
diff --git a/gopls/internal/analysis/gofix/testdata/src/b/b.go.golden b/gopls/internal/analysis/gofix/testdata/src/b/b.go.golden
index b26a05c..b3608d6 100644
--- a/gopls/internal/analysis/gofix/testdata/src/b/b.go.golden
+++ b/gopls/internal/analysis/gofix/testdata/src/b/b.go.golden
@@ -32,3 +32,5 @@
 	_ = a
 	_ = x
 }
+
+const d = a.D // nope: a.D refers to a constant in a package that is not visible here.
diff --git a/gopls/internal/analysis/modernize/rangeint.go b/gopls/internal/analysis/modernize/rangeint.go
index c36203c..2d25d6a 100644
--- a/gopls/internal/analysis/modernize/rangeint.go
+++ b/gopls/internal/analysis/modernize/rangeint.go
@@ -8,10 +8,12 @@
 	"fmt"
 	"go/ast"
 	"go/token"
+	"go/types"
 
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
+	"golang.org/x/tools/go/types/typeutil"
 	"golang.org/x/tools/internal/analysisinternal"
 	"golang.org/x/tools/internal/astutil/cursor"
 	"golang.org/x/tools/internal/astutil/edge"
@@ -98,6 +100,14 @@
 							})
 						}
 
+						// If limit is len(slice),
+						// simplify "range len(slice)" to "range slice".
+						if call, ok := limit.(*ast.CallExpr); ok &&
+							typeutil.Callee(info, call) == builtinLen &&
+							is[*types.Slice](info.TypeOf(call.Args[0]).Underlying()) {
+							limit = call.Args[0]
+						}
+
 						pass.Report(analysis.Diagnostic{
 							Pos:      init.Pos(),
 							End:      inc.End(),
diff --git a/gopls/internal/analysis/modernize/testdata/src/rangeint/rangeint.go b/gopls/internal/analysis/modernize/testdata/src/rangeint/rangeint.go
index e17dcca..a60bd5e 100644
--- a/gopls/internal/analysis/modernize/testdata/src/rangeint/rangeint.go
+++ b/gopls/internal/analysis/modernize/testdata/src/rangeint/rangeint.go
@@ -1,6 +1,6 @@
 package rangeint
 
-func _(i int, s struct{ i int }) {
+func _(i int, s struct{ i int }, slice []int) {
 	for i := 0; i < 10; i++ { // want "for loop can be modernized using range over int"
 		println(i)
 	}
@@ -9,6 +9,9 @@
 	for i := 0; i < 10; i++ { // want "for loop can be modernized using range over int"
 		// i unused within loop
 	}
+	for i := 0; i < len(slice); i++ { // want "for loop can be modernized using range over int"
+		println(slice[i])
+	}
 
 	// nope
 	for i := 0; i < 10; { // nope: missing increment
diff --git a/gopls/internal/analysis/modernize/testdata/src/rangeint/rangeint.go.golden b/gopls/internal/analysis/modernize/testdata/src/rangeint/rangeint.go.golden
index 5a76229..348f775 100644
--- a/gopls/internal/analysis/modernize/testdata/src/rangeint/rangeint.go.golden
+++ b/gopls/internal/analysis/modernize/testdata/src/rangeint/rangeint.go.golden
@@ -1,6 +1,6 @@
 package rangeint
 
-func _(i int, s struct{ i int }) {
+func _(i int, s struct{ i int }, slice []int) {
 	for i := range 10 { // want "for loop can be modernized using range over int"
 		println(i)
 	}
@@ -9,6 +9,9 @@
 	for range 10 { // want "for loop can be modernized using range over int"
 		// i unused within loop
 	}
+	for i := range slice { // want "for loop can be modernized using range over int"
+		println(slice[i])
+	}
 
 	// nope
 	for i := 0; i < 10; { // nope: missing increment
diff --git a/gopls/internal/golang/extract.go b/gopls/internal/golang/extract.go
index 2ce8979..8c8758d 100644
--- a/gopls/internal/golang/extract.go
+++ b/gopls/internal/golang/extract.go
@@ -1229,7 +1229,7 @@
 	// return value acts as an indicator for where it was defined.
 	var sel func(n *ast.SelectorExpr) (types.Object, bool)
 	sel = func(n *ast.SelectorExpr) (types.Object, bool) {
-		switch x := astutil.Unparen(n.X).(type) {
+		switch x := ast.Unparen(n.X).(type) {
 		case *ast.SelectorExpr:
 			return sel(x)
 		case *ast.Ident:
diff --git a/gopls/internal/golang/freesymbols.go b/gopls/internal/golang/freesymbols.go
index 2c9e251..3360253 100644
--- a/gopls/internal/golang/freesymbols.go
+++ b/gopls/internal/golang/freesymbols.go
@@ -342,7 +342,7 @@
 		for {
 			suffix = append(suffix, info.Uses[sel.Sel])
 
-			switch x := astutil.Unparen(sel.X).(type) {
+			switch x := ast.Unparen(sel.X).(type) {
 			case *ast.Ident:
 				return id(x, suffix)
 			default:
diff --git a/gopls/internal/golang/symbols.go b/gopls/internal/golang/symbols.go
index 14f2703..db31baa 100644
--- a/gopls/internal/golang/symbols.go
+++ b/gopls/internal/golang/symbols.go
@@ -86,17 +86,22 @@
 	ctx, done := event.Start(ctx, "source.PackageSymbols")
 	defer done()
 
-	mp, err := NarrowestMetadataForFile(ctx, snapshot, uri)
-	if err != nil {
-		return command.PackageSymbolsResult{}, err
+	pkgFiles := []protocol.DocumentURI{uri}
+
+	// golang/vscode-go#3681: do our best if the file is not in a package.
+	// TODO(rfindley): revisit this in the future once there is more graceful
+	// handling in VS Code.
+	if mp, err := NarrowestMetadataForFile(ctx, snapshot, uri); err == nil {
+		pkgFiles = mp.CompiledGoFiles
 	}
-	pkgfiles := mp.CompiledGoFiles
-	// Maps receiver name to the methods that use it
-	receiverToMethods := make(map[string][]command.PackageSymbol)
-	// Maps type symbol name to its index in symbols
-	typeSymbolToIdx := make(map[string]int)
-	var symbols []command.PackageSymbol
-	for fidx, f := range pkgfiles {
+
+	var (
+		pkgName           string
+		symbols           []command.PackageSymbol
+		receiverToMethods = make(map[string][]command.PackageSymbol) // receiver name -> methods
+		typeSymbolToIdx   = make(map[string]int)                     // type name -> index in symbols
+	)
+	for fidx, f := range pkgFiles {
 		fh, err := snapshot.ReadFile(ctx, f)
 		if err != nil {
 			return command.PackageSymbolsResult{}, err
@@ -105,6 +110,9 @@
 		if err != nil {
 			return command.PackageSymbolsResult{}, err
 		}
+		if pkgName == "" && pgf.File != nil && pgf.File.Name != nil {
+			pkgName = pgf.File.Name.Name
+		}
 		for _, decl := range pgf.File.Decls {
 			switch decl := decl.(type) {
 			case *ast.FuncDecl:
@@ -154,8 +162,8 @@
 		}
 	}
 	return command.PackageSymbolsResult{
-		PackageName: string(mp.Name),
-		Files:       pkgfiles,
+		PackageName: pkgName,
+		Files:       pkgFiles,
 		Symbols:     symbols,
 	}, nil
 
diff --git a/gopls/internal/server/command.go b/gopls/internal/server/command.go
index 2b5c282..007b8d5 100644
--- a/gopls/internal/server/command.go
+++ b/gopls/internal/server/command.go
@@ -1741,6 +1741,10 @@
 	err := c.run(ctx, commandConfig{
 		forURI: args.URI,
 	}, func(ctx context.Context, deps commandDeps) error {
+		if deps.snapshot.FileKind(deps.fh) != file.Go {
+			// golang/vscode-go#3681: fail silently, to avoid spurious error popups.
+			return nil
+		}
 		res, err := golang.PackageSymbols(ctx, deps.snapshot, args.URI)
 		if err != nil {
 			return err
diff --git a/gopls/internal/test/integration/misc/package_symbols_test.go b/gopls/internal/test/integration/misc/package_symbols_test.go
index 860264f..1e06a65 100644
--- a/gopls/internal/test/integration/misc/package_symbols_test.go
+++ b/gopls/internal/test/integration/misc/package_symbols_test.go
@@ -16,6 +16,11 @@
 
 func TestPackageSymbols(t *testing.T) {
 	const files = `
+-- go.mod --
+module example.com
+
+go 1.20
+
 -- a.go --
 package a
 
@@ -33,68 +38,74 @@
 func (s *S) M3() {}
 
 func F() {}
+-- unloaded.go --
+//go:build unloaded
+
+package a
+
+var Unloaded int
 `
 	integration.Run(t, files, func(t *testing.T, env *integration.Env) {
-		a_uri := env.Sandbox.Workdir.URI("a.go")
-		b_uri := env.Sandbox.Workdir.URI("b.go")
+		aURI := env.Sandbox.Workdir.URI("a.go")
+		bURI := env.Sandbox.Workdir.URI("b.go")
 		args, err := command.MarshalArgs(command.PackageSymbolsArgs{
-			URI: a_uri,
+			URI: aURI,
 		})
 		if err != nil {
-			t.Fatalf("failed to MarshalArgs: %v", err)
+			t.Fatal(err)
 		}
 
 		var res command.PackageSymbolsResult
 		env.ExecuteCommand(&protocol.ExecuteCommandParams{
-			Command:   "gopls.package_symbols",
+			Command:   command.PackageSymbols.String(),
 			Arguments: args,
 		}, &res)
 
 		want := command.PackageSymbolsResult{
 			PackageName: "a",
-			Files:       []protocol.DocumentURI{a_uri, b_uri},
+			Files:       []protocol.DocumentURI{aURI, bURI},
 			Symbols: []command.PackageSymbol{
-				{
-					Name: "A",
-					Kind: protocol.Variable,
-					File: 0,
-				},
-				{
-					Name: "F",
-					Kind: protocol.Function,
-					File: 1,
-				},
-				{
-					Name: "S",
-					Kind: protocol.Struct,
-					File: 0,
-					Children: []command.PackageSymbol{
-						{
-							Name: "M1",
-							Kind: protocol.Method,
-							File: 0,
-						},
-						{
-							Name: "M2",
-							Kind: protocol.Method,
-							File: 1,
-						},
-						{
-							Name: "M3",
-							Kind: protocol.Method,
-							File: 1,
-						},
-					},
-				},
-				{
-					Name: "b",
-					Kind: protocol.Variable,
-					File: 1,
-				},
+				{Name: "A", Kind: protocol.Variable, File: 0},
+				{Name: "F", Kind: protocol.Function, File: 1},
+				{Name: "S", Kind: protocol.Struct, File: 0, Children: []command.PackageSymbol{
+					{Name: "M1", Kind: protocol.Method, File: 0},
+					{Name: "M2", Kind: protocol.Method, File: 1},
+					{Name: "M3", Kind: protocol.Method, File: 1},
+				}},
+				{Name: "b", Kind: protocol.Variable, File: 1},
 			},
 		}
-		if diff := cmp.Diff(want, res, cmpopts.IgnoreFields(command.PackageSymbol{}, "Range", "SelectionRange", "Detail")); diff != "" {
-			t.Errorf("gopls.package_symbols returned unexpected diff (-want +got):\n%s", diff)
+		ignore := cmpopts.IgnoreFields(command.PackageSymbol{}, "Range", "SelectionRange", "Detail")
+		if diff := cmp.Diff(want, res, ignore); diff != "" {
+			t.Errorf("package_symbols returned unexpected diff (-want +got):\n%s", diff)
+		}
+
+		for file, want := range map[string]command.PackageSymbolsResult{
+			"go.mod": {},
+			"unloaded.go": {
+				PackageName: "a",
+				Files:       []protocol.DocumentURI{env.Sandbox.Workdir.URI("unloaded.go")},
+				Symbols: []command.PackageSymbol{
+					{Name: "Unloaded", Kind: protocol.Variable, File: 0},
+				},
+			},
+		} {
+			uri := env.Sandbox.Workdir.URI(file)
+			args, err := command.MarshalArgs(command.PackageSymbolsArgs{
+				URI: uri,
+			})
+			if err != nil {
+				t.Fatal(err)
+			}
+			var res command.PackageSymbolsResult
+			env.ExecuteCommand(&protocol.ExecuteCommandParams{
+				Command:   command.PackageSymbols.String(),
+				Arguments: args,
+			}, &res)
+
+			if diff := cmp.Diff(want, res, ignore); diff != "" {
+				t.Errorf("package_symbols returned unexpected diff (-want +got):\n%s", diff)
+			}
 		}
 	})
 }
diff --git a/internal/astutil/clone.go b/internal/astutil/clone.go
index d5ee82c..2c9b6bb 100644
--- a/internal/astutil/clone.go
+++ b/internal/astutil/clone.go
@@ -25,7 +25,7 @@
 	}
 	clone = func(x reflect.Value) reflect.Value {
 		switch x.Kind() {
-		case reflect.Ptr:
+		case reflect.Pointer:
 			if x.IsNil() {
 				return x
 			}
diff --git a/internal/gcimporter/ureader_yes.go b/internal/gcimporter/ureader_yes.go
index 522287d..37b4a39 100644
--- a/internal/gcimporter/ureader_yes.go
+++ b/internal/gcimporter/ureader_yes.go
@@ -574,7 +574,7 @@
 
 						recv := types.NewVar(fn.Pos(), fn.Pkg(), "", named)
 						typesinternal.SetVarKind(recv, typesinternal.RecvVar)
-						methods[i] = types.NewFunc(fn.Pos(), fn.Pkg(), fn.Name(), types.NewSignature(recv, sig.Params(), sig.Results(), sig.Variadic()))
+						methods[i] = types.NewFunc(fn.Pos(), fn.Pkg(), fn.Name(), types.NewSignatureType(recv, nil, nil, sig.Params(), sig.Results(), sig.Variadic()))
 					}
 
 					embeds := make([]types.Type, iface.NumEmbeddeds())
diff --git a/internal/refactor/inline/inline.go b/internal/refactor/inline/inline.go
index 96fbb8f..5430824 100644
--- a/internal/refactor/inline/inline.go
+++ b/internal/refactor/inline/inline.go
@@ -2981,7 +2981,7 @@
 	var visit func(reflect.Value)
 	visit = func(v reflect.Value) {
 		switch v.Kind() {
-		case reflect.Ptr:
+		case reflect.Pointer:
 			if v.Interface() == from {
 				found = true
 
diff --git a/internal/refactor/inline/inline_test.go b/internal/refactor/inline/inline_test.go
index 03fb5cc..3be37d5 100644
--- a/internal/refactor/inline/inline_test.go
+++ b/internal/refactor/inline/inline_test.go
@@ -1977,7 +1977,7 @@
 	var visit func(reflect.Value)
 	visit = func(v reflect.Value) {
 		switch v.Kind() {
-		case reflect.Ptr:
+		case reflect.Pointer:
 			ptr := v.UnsafePointer()
 			writeUint64(uint64(uintptr(ptr)))
 			if !v.IsNil() {
diff --git a/internal/tool/tool.go b/internal/tool/tool.go
index 46f5b87..fe2b1c2 100644
--- a/internal/tool/tool.go
+++ b/internal/tool/tool.go
@@ -250,7 +250,7 @@
 		child := value.Type().Field(i)
 		v := value.Field(i)
 		// make sure we have a pointer
-		if v.Kind() != reflect.Ptr {
+		if v.Kind() != reflect.Pointer {
 			v = v.Addr()
 		}
 		// check if that field is a flag or contains flags
@@ -289,7 +289,7 @@
 func resolve(v reflect.Value) reflect.Value {
 	for {
 		switch v.Kind() {
-		case reflect.Interface, reflect.Ptr:
+		case reflect.Interface, reflect.Pointer:
 			v = v.Elem()
 		default:
 			return v
diff --git a/refactor/eg/match.go b/refactor/eg/match.go
index 31f8af2..d85a473 100644
--- a/refactor/eg/match.go
+++ b/refactor/eg/match.go
@@ -13,8 +13,6 @@
 	"log"
 	"os"
 	"reflect"
-
-	"golang.org/x/tools/go/ast/astutil"
 )
 
 // matchExpr reports whether pattern x matches y.
@@ -34,8 +32,8 @@
 	if x == nil || y == nil {
 		return false
 	}
-	x = unparen(x)
-	y = unparen(y)
+	x = ast.Unparen(x)
+	y = ast.Unparen(y)
 
 	// Is x a wildcard?  (a reference to a 'before' parameter)
 	if xobj, ok := tr.wildcardObj(x); ok {
@@ -229,8 +227,6 @@
 
 // -- utilities --------------------------------------------------------
 
-func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
-
 // isRef returns the object referred to by this (possibly qualified)
 // identifier, or nil if the node is not a referring identifier.
 func isRef(n ast.Node, info *types.Info) types.Object {
diff --git a/refactor/eg/rewrite.go b/refactor/eg/rewrite.go
index 3f71c53..6fb1e44 100644
--- a/refactor/eg/rewrite.go
+++ b/refactor/eg/rewrite.go
@@ -338,7 +338,7 @@
 		}
 		return v
 
-	case reflect.Ptr:
+	case reflect.Pointer:
 		v := reflect.New(p.Type()).Elem()
 		if elem := p.Elem(); elem.IsValid() {
 			v.Set(tr.subst(env, elem, pos).Addr())
diff --git a/refactor/rename/spec.go b/refactor/rename/spec.go
index 1d8c32c..99068c1 100644
--- a/refactor/rename/spec.go
+++ b/refactor/rename/spec.go
@@ -155,7 +155,7 @@
 	}
 
 	if e, ok := e.(*ast.SelectorExpr); ok {
-		x := unparen(e.X)
+		x := ast.Unparen(e.X)
 
 		// Strip off star constructor, if any.
 		if star, ok := x.(*ast.StarExpr); ok {
@@ -172,7 +172,7 @@
 
 		if x, ok := x.(*ast.SelectorExpr); ok {
 			// field/method of type e.g. ("encoding/json".Decoder).Decode
-			y := unparen(x.X)
+			y := ast.Unparen(x.X)
 			if pkg := parseImportPath(y); pkg != "" {
 				spec.pkg = pkg               // e.g. "encoding/json"
 				spec.pkgMember = x.Sel.Name  // e.g. "Decoder"
diff --git a/refactor/rename/util.go b/refactor/rename/util.go
index 7c1a634..cb7cea3 100644
--- a/refactor/rename/util.go
+++ b/refactor/rename/util.go
@@ -5,7 +5,6 @@
 package rename
 
 import (
-	"go/ast"
 	"go/token"
 	"go/types"
 	"os"
@@ -14,8 +13,6 @@
 	"runtime"
 	"strings"
 	"unicode"
-
-	"golang.org/x/tools/go/ast/astutil"
 )
 
 func objectKind(obj types.Object) string {
@@ -93,8 +90,6 @@
 	return false
 }
 
-func unparen(e ast.Expr) ast.Expr { return astutil.Unparen(e) }
-
 func is[T any](x any) bool {
 	_, ok := x.(T)
 	return ok
diff --git a/refactor/satisfy/find.go b/refactor/satisfy/find.go
index 3d693aa..a897c3c 100644
--- a/refactor/satisfy/find.go
+++ b/refactor/satisfy/find.go
@@ -126,13 +126,13 @@
 
 	case *ast.CallExpr:
 		// x, err := f(args)
-		sig := coreType(f.expr(e.Fun)).(*types.Signature)
+		sig := typeparams.CoreType(f.expr(e.Fun)).(*types.Signature)
 		f.call(sig, e.Args)
 
 	case *ast.IndexExpr:
 		// y, ok := x[i]
 		x := f.expr(e.X)
-		f.assign(f.expr(e.Index), coreType(x).(*types.Map).Key())
+		f.assign(f.expr(e.Index), typeparams.CoreType(x).(*types.Map).Key())
 
 	case *ast.TypeAssertExpr:
 		// y, ok := x.(T)
@@ -213,7 +213,7 @@
 			f.expr(args[1])
 		} else {
 			// append(x, y, z)
-			tElem := coreType(s).(*types.Slice).Elem()
+			tElem := typeparams.CoreType(s).(*types.Slice).Elem()
 			for _, arg := range args[1:] {
 				f.assign(tElem, f.expr(arg))
 			}
@@ -222,7 +222,7 @@
 	case "delete":
 		m := f.expr(args[0])
 		k := f.expr(args[1])
-		f.assign(coreType(m).(*types.Map).Key(), k)
+		f.assign(typeparams.CoreType(m).(*types.Map).Key(), k)
 
 	default:
 		// ordinary call
@@ -273,7 +273,7 @@
 	if types.Identical(lhs, rhs) {
 		return
 	}
-	if !isInterface(lhs) {
+	if !types.IsInterface(lhs) {
 		return
 	}
 
@@ -354,7 +354,7 @@
 		f.sig = saved
 
 	case *ast.CompositeLit:
-		switch T := coreType(typeparams.Deref(tv.Type)).(type) {
+		switch T := typeparams.CoreType(typeparams.Deref(tv.Type)).(type) {
 		case *types.Struct:
 			for i, elem := range e.Elts {
 				if kv, ok := elem.(*ast.KeyValueExpr); ok {
@@ -405,7 +405,7 @@
 			// x[i] or m[k] -- index or lookup operation
 			x := f.expr(e.X)
 			i := f.expr(e.Index)
-			if ux, ok := coreType(x).(*types.Map); ok {
+			if ux, ok := typeparams.CoreType(x).(*types.Map); ok {
 				f.assign(ux.Key(), i)
 			}
 		}
@@ -440,7 +440,7 @@
 			// unsafe call. Treat calls to functions in unsafe like ordinary calls,
 			// except that their signature cannot be determined by their func obj.
 			// Without this special handling, f.expr(e.Fun) would fail below.
-			if s, ok := unparen(e.Fun).(*ast.SelectorExpr); ok {
+			if s, ok := ast.Unparen(e.Fun).(*ast.SelectorExpr); ok {
 				if obj, ok := f.info.Uses[s.Sel].(*types.Builtin); ok && obj.Pkg().Path() == "unsafe" {
 					sig := f.info.Types[e.Fun].Type.(*types.Signature)
 					f.call(sig, e.Args)
@@ -449,7 +449,7 @@
 			}
 
 			// builtin call
-			if id, ok := unparen(e.Fun).(*ast.Ident); ok {
+			if id, ok := ast.Unparen(e.Fun).(*ast.Ident); ok {
 				if obj, ok := f.info.Uses[id].(*types.Builtin); ok {
 					sig := f.info.Types[id].Type.(*types.Signature)
 					f.builtin(obj, sig, e.Args)
@@ -458,7 +458,7 @@
 			}
 
 			// ordinary call
-			f.call(coreType(f.expr(e.Fun)).(*types.Signature), e.Args)
+			f.call(typeparams.CoreType(f.expr(e.Fun)).(*types.Signature), e.Args)
 		}
 
 	case *ast.StarExpr:
@@ -518,7 +518,7 @@
 	case *ast.SendStmt:
 		ch := f.expr(s.Chan)
 		val := f.expr(s.Value)
-		f.assign(coreType(ch).(*types.Chan).Elem(), val)
+		f.assign(typeparams.CoreType(ch).(*types.Chan).Elem(), val)
 
 	case *ast.IncDecStmt:
 		f.expr(s.X)
@@ -622,9 +622,9 @@
 		var I types.Type
 		switch ass := s.Assign.(type) {
 		case *ast.ExprStmt: // x.(type)
-			I = f.expr(unparen(ass.X).(*ast.TypeAssertExpr).X)
+			I = f.expr(ast.Unparen(ass.X).(*ast.TypeAssertExpr).X)
 		case *ast.AssignStmt: // y := x.(type)
-			I = f.expr(unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
+			I = f.expr(ast.Unparen(ass.Rhs[0]).(*ast.TypeAssertExpr).X)
 		}
 		for _, cc := range s.Body.List {
 			cc := cc.(*ast.CaseClause)
@@ -668,7 +668,7 @@
 				var xelem types.Type
 				// Keys of array, *array, slice, string aren't interesting
 				// since the RHS key type is just an int.
-				switch ux := coreType(x).(type) {
+				switch ux := typeparams.CoreType(x).(type) {
 				case *types.Chan:
 					xelem = ux.Elem()
 				case *types.Map:
@@ -683,13 +683,13 @@
 				var xelem types.Type
 				// Values of type strings aren't interesting because
 				// the RHS value type is just a rune.
-				switch ux := coreType(x).(type) {
+				switch ux := typeparams.CoreType(x).(type) {
 				case *types.Array:
 					xelem = ux.Elem()
 				case *types.Map:
 					xelem = ux.Elem()
 				case *types.Pointer: // *array
-					xelem = coreType(typeparams.Deref(ux)).(*types.Array).Elem()
+					xelem = typeparams.CoreType(typeparams.Deref(ux)).(*types.Array).Elem()
 				case *types.Slice:
 					xelem = ux.Elem()
 				}
@@ -707,12 +707,6 @@
 
 // -- Plundered from golang.org/x/tools/go/ssa -----------------
 
-func unparen(e ast.Expr) ast.Expr { return ast.Unparen(e) }
-
-func isInterface(T types.Type) bool { return types.IsInterface(T) }
-
-func coreType(T types.Type) types.Type { return typeparams.CoreType(T) }
-
 func instance(info *types.Info, expr ast.Expr) bool {
 	var id *ast.Ident
 	switch x := expr.(type) {