[tools] gopls/internal/golang: MoveType - extract type to new file

17 views
Skip to first unread message

Madeline Kalil (Gerrit)

unread,
Nov 25, 2025, 3:49:14 PM11/25/25
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Madeline Kalil has uploaded the change for review

Commit message

gopls/internal/golang: MoveType - extract type to new file

Implements computing the edits to a new file after
adding the type declaration, imports, and build constraint comments.
Also deletes the type declaration from its current location.

Adds several checks for currently illegal type moves, which may be
relaxed later in the implementation.
Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b

Change diff

diff --git a/gopls/internal/golang/codeaction.go b/gopls/internal/golang/codeaction.go
index d09fc47..a38a4e0 100644
--- a/gopls/internal/golang/codeaction.go
+++ b/gopls/internal/golang/codeaction.go
@@ -255,7 +255,7 @@
{kind: settings.RefactorExtractVariableAll, fn: refactorExtractVariableAll, needPkg: true},
{kind: settings.RefactorInlineCall, fn: refactorInlineCall, needPkg: true},
{kind: settings.RefactorInlineVariable, fn: refactorInlineVariable, needPkg: true},
- // {kind: settings.RefactorMoveType, fn: refactorMoveType, needPkg: true},
+ {kind: settings.RefactorMoveType, fn: refactorMoveType, needPkg: true},
{kind: settings.RefactorRewriteChangeQuote, fn: refactorRewriteChangeQuote},
{kind: settings.RefactorRewriteFillStruct, fn: refactorRewriteFillStruct, needPkg: true},
{kind: settings.RefactorRewriteFillSwitch, fn: refactorRewriteFillSwitch, needPkg: true},
@@ -1136,7 +1136,7 @@

func refactorMoveType(ctx context.Context, req *codeActionsRequest) error {
curSel, _ := req.pgf.Cursor.FindByPos(req.start, req.end)
- if _, _, _, typeName, ok := selectionContainsType(curSel); ok {
+ if _, _, _, typeName, ok := selectionContainsTypeDecl(curSel); ok {
cmd := command.NewMoveTypeCommand(fmt.Sprintf("Move type %s", typeName), command.MoveTypeArgs{Location: req.loc})
req.addCommandAction(cmd, false)
}
diff --git a/gopls/internal/golang/extracttofile.go b/gopls/internal/golang/extracttofile.go
index 6aef663..a07c8f5 100644
--- a/gopls/internal/golang/extracttofile.go
+++ b/gopls/internal/golang/extracttofile.go
@@ -135,54 +135,11 @@
return nil, err
}

- var importDeletes []protocol.TextEdit
- // For unparenthesised declarations like `import "fmt"` we remove
- // the whole declaration because simply removing importSpec leaves
- // `import \n`, which does not compile.
- // For parenthesised declarations like `import ("fmt"\n "log")`
- // we only remove the ImportSpec, because removing the whole declaration
- // might remove other ImportsSpecs we don't want to touch.
- unparenthesizedImports := unparenthesizedImports(pgf)
- for _, importSpec := range deletes {
- if decl := unparenthesizedImports[importSpec]; decl != nil {
- importDeletes = append(importDeletes, removeNode(pgf, decl))
- } else {
- importDeletes = append(importDeletes, removeNode(pgf, importSpec))
- }
- }
+ importDeletes := importDeletes(pgf, deletes)

- var buf bytes.Buffer
- if c := CopyrightComment(pgf.File); c != nil {
- text, err := pgf.NodeText(c)
- if err != nil {
- return nil, err
- }
- buf.Write(text)
- // One empty line between copyright header and following.
- buf.WriteString("\n\n")
- }
-
- if c := buildConstraintComment(pgf.File); c != nil {
- text, err := pgf.NodeText(c)
- if err != nil {
- return nil, err
- }
- buf.Write(text)
- // One empty line between build constraint and following.
- buf.WriteString("\n\n")
- }
-
- fmt.Fprintf(&buf, "package %s\n", pgf.File.Name.Name)
- if len(adds) > 0 {
- buf.WriteString("import (")
- for _, importSpec := range adds {
- if importSpec.Name != nil {
- fmt.Fprintf(&buf, "%s %s\n", importSpec.Name.Name, importSpec.Path.Value)
- } else {
- fmt.Fprintf(&buf, "%s\n", importSpec.Path.Value)
- }
- }
- buf.WriteString(")\n")
+ buf, err := newFileContentFromImports(pgf, adds, pgf.File.Name.Name)
+ if err != nil {
+ return nil, err
}

newFile, err := chooseNewFile(ctx, snapshot, pgf.URI.DirPath(), firstSymbol)
@@ -208,6 +165,67 @@
})}, nil
}

+// importDeletes returns the text edits necessary to delete the given imports
+// from the given file.
+func importDeletes(pgf *parsego.File, deletes []*ast.ImportSpec) []protocol.TextEdit {
+ var importDeletes []protocol.TextEdit
+ // For unparenthesised declarations like `import "fmt"` we remove
+ // the whole declaration because simply removing importSpec leaves
+ // `import \n`, which does not compile.
+ // For parenthesised declarations like `import ("fmt"\n "log")`
+ // we only remove the ImportSpec, because removing the whole declaration
+ // might remove other ImportsSpecs we don't want to touch.
+ unparenthesizedImports := unparenthesizedImports(pgf)
+ for _, importSpec := range deletes {
+ if decl := unparenthesizedImports[importSpec]; decl != nil {
+ importDeletes = append(importDeletes, removeNode(pgf, decl))
+ } else {
+ importDeletes = append(importDeletes, removeNode(pgf, importSpec))
+ }
+ }
+ return importDeletes
+}
+
+// newFileContentFromImports returns the unformatted file content resulting from adding the
+// imports in the given pgf to a new file, plus copyright and build constraint
+// comments.
+func newFileContentFromImports(pgf *parsego.File, adds []*ast.ImportSpec, pkgName string) (bytes.Buffer, error) {
+ var buf bytes.Buffer
+ if c := CopyrightComment(pgf.File); c != nil {
+ text, err := pgf.NodeText(c)
+ if err != nil {
+ return bytes.Buffer{}, err
+ }
+ buf.Write(text)
+ // One empty line between copyright header and following.
+ buf.WriteString("\n\n")
+ }
+
+ if c := buildConstraintComment(pgf.File); c != nil {
+ text, err := pgf.NodeText(c)
+ if err != nil {
+ return bytes.Buffer{}, err
+ }
+ buf.Write(text)
+ // One empty line between build constraint and following.
+ buf.WriteString("\n\n")
+ }
+
+ fmt.Fprintf(&buf, "package %s\n", pkgName)
+ if len(adds) > 0 {
+ buf.WriteString("import (")
+ for _, importSpec := range adds {
+ if importSpec.Name != nil {
+ fmt.Fprintf(&buf, "%s %s\n", importSpec.Name.Name, importSpec.Path.Value)
+ } else {
+ fmt.Fprintf(&buf, "%s\n", importSpec.Path.Value)
+ }
+ }
+ buf.WriteString(")\n")
+ }
+ return buf, nil
+}
+
// chooseNewFile chooses a new filename in dir, based on the name of the
// first extracted symbol, and if necessary to disambiguate, a numeric suffix.
func chooseNewFile(ctx context.Context, snapshot *cache.Snapshot, dir string, firstSymbol string) (file.Handle, error) {
diff --git a/gopls/internal/golang/movetype.go b/gopls/internal/golang/movetype.go
index 3ea8ece..379a313 100644
--- a/gopls/internal/golang/movetype.go
+++ b/gopls/internal/golang/movetype.go
@@ -8,10 +8,14 @@
"context"
"fmt"
"go/ast"
+ "go/format"
"go/token"
+ "go/types"
+ "path/filepath"

"golang.org/x/tools/go/ast/inspector"
"golang.org/x/tools/gopls/internal/cache"
+ "golang.org/x/tools/gopls/internal/cache/parsego"
"golang.org/x/tools/gopls/internal/file"
"golang.org/x/tools/gopls/internal/protocol"
"golang.org/x/tools/internal/moreiters"
@@ -19,12 +23,93 @@

// MoveType moves the selected type declaration into a new package and updates all references.
func MoveType(ctx context.Context, fh file.Handle, snapshot *cache.Snapshot, loc protocol.Location, newPkgDir string) ([]protocol.DocumentChange, error) {
- return nil, fmt.Errorf("MoveType: not yet supported")
+ pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
+ if err != nil {
+ return nil, err
+ }
+
+ start, end, err := pgf.RangePos(loc.Range)
+ if err != nil {
+ return nil, err
+ }
+
+ curSel, ok := pgf.Cursor.FindByPos(start, end)
+ if !ok {
+ return nil, err
+ }
+ _, decl, typS, _, _ := selectionContainsTypeDecl(curSel) // we also check this condition before providing the move type codeaction
+
+ if err := canMoveType(pkg.TypesInfo(), typS); err != nil {
+ return nil, err
+ }
+
+ changes, err := addTypeToPackage(ctx, snapshot, pgf, pkg, pkg.TypesInfo(), decl, newPkgDir, filepath.Base(newPkgDir))
+ if err != nil {
+ return nil, err
+ }
+
+ // Get the range to delete the type decl from the current file including any
+ // comments associated with it.
+ declStart, declEnd := decl.Pos(), decl.End()
+ if decl.Doc != nil {
+ declStart = decl.Doc.Pos()
+ }
+ typeDeclRange, err := pgf.PosRange(declStart, declEnd)
+ if err != nil {
+ return nil, fmt.Errorf("invalid range: %v", err)
+ }
+
+ // Delete the old type declaration.
+ changes = append(changes, protocol.DocumentChangeEdit(fh, []protocol.TextEdit{
+ {Range: typeDeclRange, NewText: ""},
+ }))
+
+ return changes, nil
}

-// selectionContainsType returns the Cursor, GenDecl and TypeSpec of the type
+// addTypeToPackage returns the document changes necessary to add the type to a new file in the given package.
+// It only produces edits for the new file.
+func addTypeToPackage(ctx context.Context, snapshot *cache.Snapshot, pgf *parsego.File, pkg *cache.Package, info *types.Info, decl *ast.GenDecl, pkgDir, pkgName string) ([]protocol.DocumentChange, error) {
+ adds, _, err := findImportEdits(pgf.File, info, decl.Pos(), decl.End())
+ if err != nil {
+ return nil, fmt.Errorf("error calculating import edits: %v", err)
+ }
+
+ buf, err := newFileContentFromImports(pgf, adds, pkgName)
+ if err != nil {
+ return nil, fmt.Errorf("error getting new file content: %v", err)
+ }
+
+ // Add the type decl to the new file content.
+ err = format.Node(&buf, pkg.FileSet(), decl)
+ if err != nil {
+ return nil, fmt.Errorf("error adding type decl to new file: %v", err)
+ }
+
+ // Format the file content.
+ newFileContent, err := format.Source(buf.Bytes())
+ if err != nil {
+ return nil, fmt.Errorf("error formatting new file content: %v", err)
+ }
+
+ newFile, err := chooseNewFile(ctx, snapshot, pkgDir, pkgName)
+ if err != nil {
+ return nil, err
+ }
+
+ return []protocol.DocumentChange{
+ // Create the new file.
+ protocol.DocumentChangeCreate(newFile.URI()),
+ // Edit the created file.
+ protocol.DocumentChangeEdit(newFile, []protocol.TextEdit{
+ {Range: protocol.Range{}, NewText: string(newFileContent)},
+ }),
+ }, nil
+}
+
+// selectionContainsTypeDecl returns the Cursor, GenDecl and TypeSpec of the type
// declaration that encloses cursor if one exists. Otherwise it returns false.
-func selectionContainsType(cursor inspector.Cursor) (inspector.Cursor, *ast.GenDecl, *ast.TypeSpec, string, bool) {
+func selectionContainsTypeDecl(cursor inspector.Cursor) (inspector.Cursor, *ast.GenDecl, *ast.TypeSpec, string, bool) {
declCur, ok := moreiters.First(cursor.Enclosing((*ast.GenDecl)(nil)))
if !ok {
return inspector.Cursor{}, &ast.GenDecl{}, &ast.TypeSpec{}, "", false
@@ -44,3 +129,36 @@

return declCur, declNode, declNode.Specs[0].(*ast.TypeSpec), typSpec.Name.Name, true
}
+
+// canMoveType reports if it is valid to move the input type spec to a new package, returning
+// an error if invalid.
+func canMoveType(info *types.Info, typ *ast.TypeSpec) error {
+ if !typ.Name.IsExported() {
+ // TODO(mkalil): have an option to turn unexported type into exported type
+ return fmt.Errorf("cannot move type: type is not exported")
+ }
+ typObj := info.Defs[typ.Name]
+ if typExp, ok := typ.Type.(*ast.StructType); ok {
+ for _, field := range typExp.Fields.List {
+
+ for _, name := range field.Names {
+ // TODO(mkalil): check if this field is accessed. What if they are moving the
+ // type before using it? We can have an option to turn unexported type fields into
+ // exported fields.
+ if !name.IsExported() {
+ return fmt.Errorf("cannot move type: would result in reference to unexported fields")
+ }
+ }
+
+ // TODO(mkalil): How should we handle moving types that have fields which are
+ // types defined at the package level? Should we also move those types to the
+ // new package, recursively? (If we don't, it could cause an import cycle).
+ // Or throw an error?
+ fieldType := info.TypeOf(field.Type)
+ if fieldObj, ok := fieldType.(*types.Named); ok && fieldObj.Obj().Pkg() == typObj.Pkg() {
+ return fmt.Errorf("cannot move type: would result in import cycle")
+ }
+ }
+ }
+ return nil
+}
diff --git a/gopls/internal/server/command.go b/gopls/internal/server/command.go
index 7e62ab5..576a035 100644
--- a/gopls/internal/server/command.go
+++ b/gopls/internal/server/command.go
@@ -1897,7 +1897,7 @@
err := c.run(ctx, commandConfig{
forURI: args.Location.URI,
}, func(ctx context.Context, deps commandDeps) error {
- changes, err := golang.MoveType(ctx, deps.fh, deps.snapshot, args.Location, "newpkg/new.go")
+ changes, err := golang.MoveType(ctx, deps.fh, deps.snapshot, args.Location, "newpkg/new")
if err != nil {
return err
}
diff --git a/gopls/internal/settings/default.go b/gopls/internal/settings/default.go
index f197ad2..612d95d 100644
--- a/gopls/internal/settings/default.go
+++ b/gopls/internal/settings/default.go
@@ -71,7 +71,7 @@
RefactorExtractVariable: true,
RefactorExtractVariableAll: true,
RefactorExtractToNewFile: true,
- RefactorMoveType: true, // off while implementation unfinished
+ RefactorMoveType: true,
// Not GoTest: it must be explicit in CodeActionParams.Context.Only
},
file.Mod: {
diff --git a/gopls/internal/test/marker/testdata/codeaction/movetype.txt b/gopls/internal/test/marker/testdata/codeaction/movetype.txt
new file mode 100644
index 0000000..54be1fa
--- /dev/null
+++ b/gopls/internal/test/marker/testdata/codeaction/movetype.txt
@@ -0,0 +1,35 @@
+This test checks the behavior of the 'move type' code action.
+
+-- flags --
+-ignore_extra_diags
+
+-- go.mod --
+module example.com
+
+-- a/a.go --
+package a
+
+import (
+ "bytes"
+ "context"
+)
+
+type a struct { //@codeaction("a", "refactor.move.moveType", err=re"type is not exported")
+ Buf bytes.Buffer
+ Ctx context.Context
+}
+
+type A struct { //@codeaction("A", "refactor.move.moveType", err=re"unexported fields")
+ buf bytes.Buffer
+ ctx context.Context
+}
+
+type B struct { //@codeaction("B", "refactor.move.moveType", err=re"import cycle")
+ C
+}
+
+type BB struct { //@codeaction("B", "refactor.move.moveType", err=re"import cycle")
+ Field C
+}
+
+type C string

Change information

Files:
  • M gopls/internal/golang/codeaction.go
  • M gopls/internal/golang/extracttofile.go
  • M gopls/internal/golang/movetype.go
  • M gopls/internal/server/command.go
  • M gopls/internal/settings/default.go
  • A gopls/internal/test/marker/testdata/codeaction/movetype.txt
Change size: L
Delta: 6 files changed, 225 insertions(+), 54 deletions(-)
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newchange
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
Gerrit-Change-Number: 723982
Gerrit-PatchSet: 1
Gerrit-Owner: Madeline Kalil <mka...@google.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Madeline Kalil (Gerrit)

unread,
Nov 25, 2025, 5:02:13 PM11/25/25
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Madeline Kalil uploaded new patchset

Madeline Kalil uploaded patch set #3 to this change.
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newpatchset
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
Gerrit-Change-Number: 723982
Gerrit-PatchSet: 3
Gerrit-Owner: Madeline Kalil <mka...@google.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Madeline Kalil (Gerrit)

unread,
Nov 25, 2025, 5:04:15 PM11/25/25
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Madeline Kalil voted Commit-Queue+1

Commit-Queue+1
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
Gerrit-Change-Number: 723982
Gerrit-PatchSet: 3
Gerrit-Owner: Madeline Kalil <mka...@google.com>
Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
Gerrit-Comment-Date: Tue, 25 Nov 2025 22:04:11 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
unsatisfied_requirement
satisfied_requirement
open
diffy

Madeline Kalil (Gerrit)

unread,
Dec 2, 2025, 10:59:52 AM12/2/25
to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

Madeline Kalil uploaded new patchset

Madeline Kalil uploaded patch set #4 to this change.
Following approvals got outdated and were removed:
  • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
  • requirement is not satisfiedCode-Review
  • requirement satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
  • requirement is not satisfiedTryBots-Pass
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: newpatchset
Gerrit-Project: tools
Gerrit-Branch: master
Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
Gerrit-Change-Number: 723982
Gerrit-PatchSet: 4
Gerrit-Owner: Madeline Kalil <mka...@google.com>
Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
unsatisfied_requirement
satisfied_requirement
open
diffy

Madeline Kalil (Gerrit)

unread,
Dec 2, 2025, 12:44:48 PM12/2/25
to goph...@pubsubhelper.golang.org, Go LUCI, golang-co...@googlegroups.com

Madeline Kalil added 1 comment

Patchset-level comments
File-level comment, Patchset 4 (Latest):
Madeline Kalil . unresolved

I was hoping there would be a way to enable the code action in the marker tests but disable producing the code action in real requests until it's fully implemented but could not figure this out.

Open in Gerrit

Related details

Attention set is empty
Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: tools
    Gerrit-Branch: master
    Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
    Gerrit-Change-Number: 723982
    Gerrit-PatchSet: 4
    Gerrit-Owner: Madeline Kalil <mka...@google.com>
    Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
    Gerrit-Comment-Date: Tue, 02 Dec 2025 17:44:44 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    unsatisfied_requirement
    open
    diffy

    Madeline Kalil (Gerrit)

    unread,
    Dec 2, 2025, 3:37:24 PM12/2/25
    to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com

    Madeline Kalil uploaded new patchset

    Madeline Kalil uploaded patch set #5 to this change.
    Open in Gerrit

    Related details

    Attention set is empty
    Submit Requirements:
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    • requirement is not satisfiedTryBots-Pass
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: newpatchset
    Gerrit-Project: tools
    Gerrit-Branch: master
    Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
    Gerrit-Change-Number: 723982
    Gerrit-PatchSet: 5
    unsatisfied_requirement
    open
    diffy

    Madeline Kalil (Gerrit)

    unread,
    Dec 2, 2025, 3:38:30 PM12/2/25
    to goph...@pubsubhelper.golang.org, Go LUCI, golang-co...@googlegroups.com

    Madeline Kalil voted and added 1 comment

    Votes added by Madeline Kalil

    Commit-Queue+1

    1 comment

    Patchset-level comments
    File-level comment, Patchset 4:
    Madeline Kalil . resolved

    I was hoping there would be a way to enable the code action in the marker tests but disable producing the code action in real requests until it's fully implemented but could not figure this out.

    Madeline Kalil

    Nevermind - added a hacky "testing.Testing()" to disable the code action when not inside a test.

    Open in Gerrit

    Related details

    Attention set is empty
    Submit Requirements:
      • requirement is not satisfiedCode-Review
      • requirement satisfiedNo-Unresolved-Comments
      • requirement is not satisfiedReview-Enforcement
      • requirement is not satisfiedTryBots-Pass
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: tools
      Gerrit-Branch: master
      Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
      Gerrit-Change-Number: 723982
      Gerrit-PatchSet: 5
      Gerrit-Owner: Madeline Kalil <mka...@google.com>
      Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
      Gerrit-Comment-Date: Tue, 02 Dec 2025 20:38:25 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: Yes
      Comment-In-Reply-To: Madeline Kalil <mka...@google.com>
      unsatisfied_requirement
      satisfied_requirement
      open
      diffy

      Robert Findley (Gerrit)

      unread,
      Dec 8, 2025, 10:31:38 AM12/8/25
      to Madeline Kalil, goph...@pubsubhelper.golang.org, Go LUCI, golang-co...@googlegroups.com
      Attention needed from Madeline Kalil

      Robert Findley added 16 comments

      Patchset-level comments
      File-level comment, Patchset 5 (Latest):
      Robert Findley . resolved

      Solid start. Many comments but mostly superficial, and the big stuff can be in a follow-up CL.

      File gopls/internal/golang/extracttofile.go
      Line 170, Patchset 5 (Latest):func importDeletes(pgf *parsego.File, deletes []*ast.ImportSpec) []protocol.TextEdit {
      Robert Findley . unresolved

      How about 'importDeleteEdits'

      Line 190, Patchset 5 (Latest):// imports in the given pgf to a new file, plus copyright and build constraint
      Robert Findley . unresolved

      Is this all the imports in the given file? Looks like it's only a fixed slice. Perhaps this comment is stale?

      Line 192, Patchset 5 (Latest):func newFileContentFromImports(pgf *parsego.File, adds []*ast.ImportSpec, pkgName string) (bytes.Buffer, error) {
      Robert Findley . unresolved

      Document this.

      Also, how about rearranging the arguments a bit:

      ```
      // newFileWithImports creates a new file with the given package name and imports
      // as well as copyright comment and build constraints matching fromPGF.
      //
      // It returns a buffer containing the file content, or an error.
      func newFileWithImports(pkgName string, imports []*ast.ImportSpec, fromPGF *parsego.File) (bytes.Buffer, error)
      ```

      Line 192, Patchset 5 (Latest):func newFileContentFromImports(pgf *parsego.File, adds []*ast.ImportSpec, pkgName string) (bytes.Buffer, error) {
      Robert Findley . unresolved

      How about 'newFileWithImports'?

      Line 200, Patchset 5 (Latest): // One empty line between copyright header and following.
      Robert Findley . unresolved

      "after the copyright header."

      Line 224, Patchset 5 (Latest): buf.WriteString(")\n")
      Robert Findley . unresolved

      Nit: should we add a trailing newline? What should the state of the buffer be, exactly (let's also document this in the doc comment).

      File gopls/internal/golang/movetype.go
      Line 40, Patchset 5 (Latest): _, decl, typS, _, _ := selectionContainsTypeDecl(curSel) // we also check this condition before providing the move type codeaction
      Robert Findley . unresolved

      Shouldn't we still check 'ok' here? If !ok, none of the other results are meaningful.

      Line 51, Patchset 5 (Latest): // Get the range to delete the type from the current file. If the type spec
      Robert Findley . unresolved

      The asymmetry of this bothers me: if we're able to handle moving an individual spec, not just an entire decl, then why don't we check selectionContainsTypeSpec, instead of looking at whole decls.

      Or maybe I'm missing something.

      Line 64, Patchset 5 (Latest): // TODO(mkalil): comments inside struct types are lost, i.e:
      // type A struct { // this comment gets lost.
      // B int
      // }
      Robert Findley . unresolved

      Looks like a case for CommentMap and CommentedNode!

      Line 92, Patchset 5 (Latest):func addTypeToPackage(ctx context.Context, snapshot *cache.Snapshot, pgf *parsego.File, pkg *cache.Package, info *types.Info, spec *ast.TypeSpec, pkgDir, pkgName string) ([]protocol.DocumentChange, error) {
      Robert Findley . unresolved

      If you pass in a CommentMap, you can create a CommentedNode for formatting, and preserve inline comments.

      Line 148, Patchset 5 (Latest): typSpec, ok := declNode.Specs[0].(*ast.TypeSpec)

      if !ok {
      return inspector.Cursor{}, &ast.GenDecl{}, &ast.TypeSpec{}, "", false
      }
      Robert Findley . unresolved

      This looks wrong--shouldn't we find the best matching spec?

      (I know we discussed what to do here, but this has the following unintuitive behavior, IIUC)

      ```
      type (
      A int
      B string // <- cursor here
      )
      ```

      With the cursor on B, the move will actually move A!

      Line 158, Patchset 5 (Latest):func canMoveType(info *types.Info, typ *ast.TypeSpec) error {
      Robert Findley . unresolved

      What about methods on the type? We need to move them as well.

      ...and then we should allow moving the type if the unexported fields are only accessed from its own methods.

      Perhaps add a big TODO explaining the plan.

      Line 168, Patchset 5 (Latest): // TODO(mkalil): check if this field is accessed. What if they are moving the

      // type before using it? We can have an option to turn unexported type fields into
      Robert Findley . resolved

      +1, nice call out

      Add a test, and then check the provided info: should be straightforward.

      (For another CL).

      Line 182, Patchset 5 (Latest): return fmt.Errorf("cannot move type: would result in import cycle")
      Robert Findley . unresolved

      s/would/could 😊

      We don't know without actually doing the analysis!

      File gopls/internal/server/code_action.go
      Line 87, Patchset 5 (Latest): // RefactorMoveType is enabled for testing but disabled otherwise.
      // This is temporary while the implementation of move type refactoring is unfinished.
      if !testing.Testing() && kind == settings.RefactorMoveType {
      return false
      }
      Robert Findley . unresolved

      Prefer doing this with an internal gopls setting. WDYT?

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Madeline Kalil
      Submit Requirements:
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        • requirement satisfiedTryBots-Pass
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: tools
        Gerrit-Branch: master
        Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
        Gerrit-Change-Number: 723982
        Gerrit-PatchSet: 5
        Gerrit-Owner: Madeline Kalil <mka...@google.com>
        Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
        Gerrit-Reviewer: Robert Findley <rfin...@google.com>
        Gerrit-Attention: Madeline Kalil <mka...@google.com>
        Gerrit-Comment-Date: Mon, 08 Dec 2025 15:31:34 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        unsatisfied_requirement
        satisfied_requirement
        open
        diffy

        Madeline Kalil (Gerrit)

        unread,
        Dec 8, 2025, 2:38:11 PM12/8/25
        to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
        Attention needed from Alan Donovan

        Madeline Kalil uploaded new patchset

        Madeline Kalil uploaded patch set #6 to this change.
        Following approvals got outdated and were removed:
        • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alan Donovan
        Submit Requirements:
          • requirement is not satisfiedCode-Review
          • requirement is not satisfiedNo-Unresolved-Comments
          • requirement is not satisfiedReview-Enforcement
          • requirement is not satisfiedTryBots-Pass
          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
          Gerrit-MessageType: newpatchset
          Gerrit-Project: tools
          Gerrit-Branch: master
          Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
          Gerrit-Change-Number: 723982
          Gerrit-PatchSet: 6
          Gerrit-Owner: Madeline Kalil <mka...@google.com>
          Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
          Gerrit-Reviewer: Robert Findley <rfin...@google.com>
          Gerrit-CC: Alan Donovan <adon...@google.com>
          Gerrit-Attention: Alan Donovan <adon...@google.com>
          unsatisfied_requirement
          open
          diffy

          Madeline Kalil (Gerrit)

          unread,
          Dec 8, 2025, 2:38:52 PM12/8/25
          to goph...@pubsubhelper.golang.org, Alan Donovan, Robert Findley, Go LUCI, golang-co...@googlegroups.com
          Attention needed from Alan Donovan and Robert Findley

          Madeline Kalil voted and added 14 comments

          Votes added by Madeline Kalil

          Commit-Queue+1

          14 comments

          File gopls/internal/golang/extracttofile.go
          Line 170, Patchset 5:func importDeletes(pgf *parsego.File, deletes []*ast.ImportSpec) []protocol.TextEdit {
          Robert Findley . resolved

          How about 'importDeleteEdits'

          Madeline Kalil

          Done

          Line 190, Patchset 5:// imports in the given pgf to a new file, plus copyright and build constraint
          Robert Findley . resolved

          Is this all the imports in the given file? Looks like it's only a fixed slice. Perhaps this comment is stale?

          Madeline Kalil

          Updated comment

          Line 192, Patchset 5:func newFileContentFromImports(pgf *parsego.File, adds []*ast.ImportSpec, pkgName string) (bytes.Buffer, error) {
          Robert Findley . resolved

          Document this.

          Also, how about rearranging the arguments a bit:

          ```
          // newFileWithImports creates a new file with the given package name and imports
          // as well as copyright comment and build constraints matching fromPGF.
          //
          // It returns a buffer containing the file content, or an error.
          func newFileWithImports(pkgName string, imports []*ast.ImportSpec, fromPGF *parsego.File) (bytes.Buffer, error)
          ```

          Madeline Kalil

          Done

          Line 192, Patchset 5:func newFileContentFromImports(pgf *parsego.File, adds []*ast.ImportSpec, pkgName string) (bytes.Buffer, error) {
          Robert Findley . resolved

          How about 'newFileWithImports'?

          Madeline Kalil

          Done

          Line 200, Patchset 5: // One empty line between copyright header and following.
          Robert Findley . resolved

          "after the copyright header."

          Madeline Kalil

          Done

          Line 224, Patchset 5: buf.WriteString(")\n")
          Robert Findley . resolved

          Nit: should we add a trailing newline? What should the state of the buffer be, exactly (let's also document this in the doc comment).

          Madeline Kalil

          This was preexisting, so I'm not sure of the original reasoning, but when I comment this line out and run all the tests, a few fail. I'll update the doc comment to specify that we add a new line after the import block.

          File gopls/internal/golang/movetype.go
          Line 40, Patchset 5: _, decl, typS, _, _ := selectionContainsTypeDecl(curSel) // we also check this condition before providing the move type codeaction
          Robert Findley . resolved

          Shouldn't we still check 'ok' here? If !ok, none of the other results are meaningful.

          Madeline Kalil

          We should be able to assume ok is true here, because we check selectionContainsTypeDecl before providing the codeaction that calls this command.

          Line 51, Patchset 5: // Get the range to delete the type from the current file. If the type spec
          Robert Findley . resolved

          The asymmetry of this bothers me: if we're able to handle moving an individual spec, not just an entire decl, then why don't we check selectionContainsTypeSpec, instead of looking at whole decls.

          Or maybe I'm missing something.

          Madeline Kalil

          Yeah, realizing this logic is messed up. Fixing and renaming. (But we still need the GenDecl node since if the type spec in question is the only one in the type decl, we should remove the entire decl)

          Line 64, Patchset 5: // TODO(mkalil): comments inside struct types are lost, i.e:

          // type A struct { // this comment gets lost.
          // B int
          // }
          Robert Findley . resolved

          Looks like a case for CommentMap and CommentedNode!

          Madeline Kalil

          Done

          Line 92, Patchset 5:func addTypeToPackage(ctx context.Context, snapshot *cache.Snapshot, pgf *parsego.File, pkg *cache.Package, info *types.Info, spec *ast.TypeSpec, pkgDir, pkgName string) ([]protocol.DocumentChange, error) {
          Robert Findley . resolved

          If you pass in a CommentMap, you can create a CommentedNode for formatting, and preserve inline comments.

          Madeline Kalil

          Fixed this particular comment preservation issue, but now comments above type specs are formatted strangely. I left a TODO

          Line 148, Patchset 5: typSpec, ok := declNode.Specs[0].(*ast.TypeSpec)

          if !ok {
          return inspector.Cursor{}, &ast.GenDecl{}, &ast.TypeSpec{}, "", false
          }
          Robert Findley . resolved

          This looks wrong--shouldn't we find the best matching spec?

          (I know we discussed what to do here, but this has the following unintuitive behavior, IIUC)

          ```
          type (
          A int
          B string // <- cursor here
          )
          ```

          With the cursor on B, the move will actually move A!

          Madeline Kalil

          Fixed and added a test case

          Line 158, Patchset 5:func canMoveType(info *types.Info, typ *ast.TypeSpec) error {
          Robert Findley . resolved

          What about methods on the type? We need to move them as well.

          ...and then we should allow moving the type if the unexported fields are only accessed from its own methods.

          Perhaps add a big TODO explaining the plan.

          Madeline Kalil

          Done

          Line 182, Patchset 5: return fmt.Errorf("cannot move type: would result in import cycle")
          Robert Findley . resolved

          s/would/could 😊

          We don't know without actually doing the analysis!

          Madeline Kalil

          True, thanks!

          File gopls/internal/server/code_action.go
          Line 87, Patchset 5: // RefactorMoveType is enabled for testing but disabled otherwise.

          // This is temporary while the implementation of move type refactoring is unfinished.
          if !testing.Testing() && kind == settings.RefactorMoveType {
          return false
          }
          Robert Findley . resolved

          Prefer doing this with an internal gopls setting. WDYT?

          Madeline Kalil

          Sure, testing.Testing does feel weird

          Open in Gerrit

          Related details

          Attention is currently required from:
          • Alan Donovan
          • Robert Findley
          Submit Requirements:
            • requirement is not satisfiedCode-Review
            • requirement satisfiedNo-Unresolved-Comments
            • requirement is not satisfiedReview-Enforcement
            • requirement is not satisfiedTryBots-Pass
            Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
            Gerrit-MessageType: comment
            Gerrit-Project: tools
            Gerrit-Branch: master
            Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
            Gerrit-Change-Number: 723982
            Gerrit-PatchSet: 6
            Gerrit-Owner: Madeline Kalil <mka...@google.com>
            Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
            Gerrit-Reviewer: Robert Findley <rfin...@google.com>
            Gerrit-CC: Alan Donovan <adon...@google.com>
            Gerrit-Attention: Alan Donovan <adon...@google.com>
            Gerrit-Attention: Robert Findley <rfin...@google.com>
            Gerrit-Comment-Date: Mon, 08 Dec 2025 19:38:48 +0000
            Gerrit-HasComments: Yes
            Gerrit-Has-Labels: Yes
            Comment-In-Reply-To: Robert Findley <rfin...@google.com>
            unsatisfied_requirement
            satisfied_requirement
            open
            diffy

            Alan Donovan (Gerrit)

            unread,
            Dec 16, 2025, 4:07:45 PM12/16/25
            to Madeline Kalil, goph...@pubsubhelper.golang.org, Robert Findley, Go LUCI, golang-co...@googlegroups.com
            Attention needed from Madeline Kalil

            Alan Donovan added 28 comments

            Commit Message
            Line 7, Patchset 6 (Latest):gopls/internal/golang: MoveType - extract type to new file
            Alan Donovan . unresolved

            Does this always move the type to a new package? I was confused because the extract logic appears to move it to a new file in the same package (based on the package declaration), but the command handler seems to create the file name of a new directory.

            File gopls/internal/golang/extracttofile.go
            Line 137, Patchset 6 (Latest):
            importDeletes := importDeletesEdits(pgf, deletes)
            Alan Donovan . unresolved

            The combination of importDeletesEdits and format.Source is now provided by driverutil.FormatSourceRemoveImports. This should make things easier in findImportEdits and eliminate this step.

            (I should probably move it out of driverutil, but it's a legitimate dependency of the golang package.)

            Line 145, Patchset 6 (Latest): newFile, err := chooseNewFile(ctx, snapshot, pgf.URI.DirPath(), firstSymbol)
            if err != nil {
            return nil, fmt.Errorf("%s: %w", errorPrefix, err)
            }
            Alan Donovan . unresolved

            Move this down so the buf-related stuff stays together.

            Line 170, Patchset 6 (Latest):func importDeletesEdits(pgf *parsego.File, deletes []*ast.ImportSpec) []protocol.TextEdit {
            Alan Donovan . unresolved

            See above; I think this is no longer needed.

            Line 194, Patchset 6 (Latest):func newFileWithImports(pkgName string, imports []*ast.ImportSpec, fromPGF *parsego.File) (bytes.Buffer, error) {
            Alan Donovan . unresolved

            *bytes.Buffer

            (Values of this type, and indeed most opaque types T with *T methods, should not be copied.)

            But why not have the caller provide the buffer? That's usually more efficient since there is often stuff to be written to it before and after.

            Line 216, Patchset 6 (Latest): fmt.Fprintf(&buf, "package %s\n", pkgName)
            Alan Donovan . unresolved

            fromPGF.File.Name.Name

            (then the pkgName parameter is unneeded)

            Line 216, Patchset 6 (Latest): fmt.Fprintf(&buf, "package %s\n", pkgName)
            Alan Donovan . unresolved

            \n\n

            Line 219, Patchset 6 (Latest): for _, importSpec := range imports {
            Alan Donovan . unresolved

            spec (for brevity)

            Line 220, Patchset 6 (Latest): if importSpec.Name != nil {

            fmt.Fprintf(&buf, "%s %s\n", importSpec.Name.Name, importSpec.Path.Value)
            } else {

            fmt.Fprintf(&buf, "%s\n", importSpec.Path.Value)
            }
            Alan Donovan . unresolved

            Factor the Path parts.

            Line 226, Patchset 6 (Latest): buf.WriteString(")\n")
            Alan Donovan . unresolved

            \n\n

            File gopls/internal/golang/movetype.go
            Line 37, Patchset 6 (Latest): curSel, ok := pgf.Cursor.FindByPos(start, end)
            Alan Donovan . unresolved

            astutil.Select may be more appropriate here; it handles whitespace more permissively.

            Line 41, Patchset 6 (Latest): _, decl, typS, _, _ := selectionContainsTypeSpec(curSel) // we also check this condition before providing the move type codeaction
            Alan Donovan . unresolved

            Is decl just the parent of spec? If so, return a cursor and let the caller call Parent.

            Line 41, Patchset 6 (Latest): _, decl, typS, _, _ := selectionContainsTypeSpec(curSel) // we also check this condition before providing the move type codeaction
            Alan Donovan . unresolved

            spec

            Line 47, Patchset 6 (Latest): changes, err := addTypeToPackage(ctx, snapshot, pgf, pkg, pkg.TypesInfo(), typS, decl, newDir, filepath.Base(newDir))
            Alan Donovan . unresolved

            This is redundant w.r.t pkg.

            Line 47, Patchset 6 (Latest): changes, err := addTypeToPackage(ctx, snapshot, pgf, pkg, pkg.TypesInfo(), typS, decl, newDir, filepath.Base(newDir))
            Alan Donovan . unresolved

            Ditto w.r.t. newDir.

            Line 73, Patchset 6 (Latest): }
            Alan Donovan . unresolved
            This is a recurring pattern. The common parts could be factored as:
            ```
            var n Node = spec
            if len(decl.Specs) == 1 {
            n = decl // singleton: delete entire decl
            }
            start, end = n.Pos(), n.End()
            if doc := astutil.DocComment(n); doc != nil {
            start = doc.Pos() // include doc comment
            }
            if c := astutil.PostComment(n); c != nil {
            end = c.End() // include inline comment
            }
            ```
            However, only DocComment exists; you'll need to define PostComment. We could use it in dozens of places.
            Line 74, Patchset 6 (Latest): typeDeclRange, err := pgf.PosRange(typStart, typEnd)
            Alan Donovan . unresolved

            rng

            Line 76, Patchset 6 (Latest): return nil, fmt.Errorf("invalid range: %v", err)
            Alan Donovan . unresolved

            err is fine.

            Line 100, Patchset 6 (Latest): // Use CommentedNode to avoid losing comments inside struct types
            // that are neither doc comments nor line comments.

            // type A struct { // this comment gets lost.
            // B int
            // }
            // TODO(mkalil): Now these comments are preserved but
            // comments above a type spec are formatted in an undesirable way.
            // (See @multitypespecB/d/newpkg/b.go in movetype.txt)
            var declComments []*ast.CommentGroup
            if len(decl.Specs) == 1 {
            for _, comment := range pgf.File.Comments {
            if decl.Pos() <= comment.Pos() && comment.End() <= decl.End() {
            declComments = append(declComments, comment)

            }
            }
            }

            // Add the type decl to the new file content.
            newDecl := &ast.GenDecl{
            Tok: token.TYPE,
            Specs: []ast.Spec{spec},
            }
            commentedNode := &printer.CommentedNode{
            Node: newDecl,
            Comments: declComments,
            }
            err = format.Node(&buf, pkg.FileSet(), commentedNode)
            if err != nil {

            return nil, fmt.Errorf("error adding type decl to new file: %v", err)
            }
            Alan Donovan . unresolved

            It might be simpler here to copy `pgf.NodeText(spec or decl)` directly into buf. Then you don't to think about comments or format.Source.

            Line 155, Patchset 6 (Latest): specCur, ok := moreiters.First(cursor.Enclosing((*ast.TypeSpec)(nil)))
            Alan Donovan . unresolved

            ```
            spec, curSpec := cursorutil.FirstEnclosing[*ast.TypeSpec](cur)
            if spec == nil { ... }
            ```

            then delete L170,

            Line 157, Patchset 6 (Latest): return inspector.Cursor{}, &ast.GenDecl{}, &ast.TypeSpec{}, "", false
            Alan Donovan . unresolved

            nil, nil

            (throughout)

            Line 165, Patchset 6 (Latest): declNode := declCur.Node().(*ast.GenDecl)
            Alan Donovan . unresolved

            decl

            Line 170, Patchset 6 (Latest): typSpec, _ := specCur.Node().(*ast.TypeSpec) // can't fail
            Alan Donovan . unresolved

            delete (since it can't fail)

            Line 171, Patchset 6 (Latest): return declCur, declNode, typSpec, typSpec.Name.Name, true
            Alan Donovan . unresolved

            This seems redundant w.r.t. type typSpec result.

            Line 176, Patchset 6 (Latest):// TODO(mkalil): For now, we only allow moving a type if the type itself and all its
            Alan Donovan . unresolved

            We also need to check that:

            • all references from the moved declarations are either internal to it, or to imported packages, but not to other declarations in the old package.
            • any internal packages that would be imported by the new package are visible to it (see packagepath.CanImport).
            Line 187, Patchset 6 (Latest): if typExp, ok := typ.Type.(*ast.StructType); ok {

            for _, field := range typExp.Fields.List {

            for _, name := range field.Names {
            Alan Donovan . unresolved

            This kind of check, which relates to symbol references, is best done using the type checker's data structures.

            Line 199, Patchset 6 (Latest): // TODO(mkalil): How should we handle moving types that have fields which are

            // types defined at the package level? Should we also move those types to the
            // new package, recursively? (If we don't, it could cause an import cycle).
            // Or throw an error?
            Alan Donovan . unresolved

            We should probably attempt to move them recursively (applying the same validity checks). If the user didn't want that, they can always undo, but there's no good way to ask the user "move type T2 too?" (yet).

            File gopls/internal/server/code_action.go
            Line 86, Patchset 6 (Latest): // RefactorMoveType is enabled for testing but disabled otherwise.

            // This is temporary while the implementation of move type refactoring is unfinished.
            if kind == settings.RefactorMoveType && !snapshot.Options().MoveType {
            return false
            }
            Alan Donovan . unresolved

            I wonder why this option is needed. How many steps are there to finishing the feature? Is it too much for this CL, or a small stack that can be merged in the same day or two?

            Open in Gerrit

            Related details

            Attention is currently required from:
            • Madeline Kalil
            Submit Requirements:
              • requirement is not satisfiedCode-Review
              • requirement is not satisfiedNo-Unresolved-Comments
              • requirement is not satisfiedReview-Enforcement
              • requirement satisfiedTryBots-Pass
              Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
              Gerrit-MessageType: comment
              Gerrit-Project: tools
              Gerrit-Branch: master
              Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
              Gerrit-Change-Number: 723982
              Gerrit-PatchSet: 6
              Gerrit-Owner: Madeline Kalil <mka...@google.com>
              Gerrit-Reviewer: Alan Donovan <adon...@google.com>
              Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
              Gerrit-CC: Robert Findley <rfin...@golang.org>
              Gerrit-Attention: Madeline Kalil <mka...@google.com>
              Gerrit-Comment-Date: Tue, 16 Dec 2025 21:07:42 +0000
              Gerrit-HasComments: Yes
              Gerrit-Has-Labels: No
              unsatisfied_requirement
              satisfied_requirement
              open
              diffy

              Madeline Kalil (Gerrit)

              unread,
              Dec 17, 2025, 2:43:28 PM12/17/25
              to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
              Attention needed from Madeline Kalil

              Madeline Kalil uploaded new patchset

              Madeline Kalil uploaded patch set #7 to this change.
              Following approvals got outdated and were removed:
              • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI

              Related details

              Attention is currently required from:
              • Madeline Kalil
              Submit Requirements:
                • requirement is not satisfiedCode-Review
                • requirement is not satisfiedNo-Unresolved-Comments
                • requirement is not satisfiedReview-Enforcement
                • requirement is not satisfiedTryBots-Pass
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: newpatchset
                Gerrit-Project: tools
                Gerrit-Branch: master
                Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                Gerrit-Change-Number: 723982
                Gerrit-PatchSet: 7
                unsatisfied_requirement
                open
                diffy

                Madeline Kalil (Gerrit)

                unread,
                Dec 17, 2025, 2:43:41 PM12/17/25
                to goph...@pubsubhelper.golang.org, Robert Findley, Alan Donovan, Go LUCI, golang-co...@googlegroups.com
                Attention needed from Alan Donovan

                Madeline Kalil voted and added 27 comments

                Votes added by Madeline Kalil

                Commit-Queue+1

                27 comments

                Commit Message
                Line 7, Patchset 6:gopls/internal/golang: MoveType - extract type to new file
                Alan Donovan . unresolved

                Does this always move the type to a new package? I was confused because the extract logic appears to move it to a new file in the same package (based on the package declaration), but the command handler seems to create the file name of a new directory.

                Madeline Kalil

                The logic is inconsistent right now because I'm not sure of the way we'll take input from users once we have dialogue support (file chooser vs input package name, etc.). For now, the input is a URI and then it will add the type to a new file in the same package as the URI. The feature only makes sense moving to a new package but we should still allow people to move types within a package so there's no check for if the URI specified is part of a different package than where the type is currently declared.

                File gopls/internal/golang/extracttofile.go
                Line 137, Patchset 6:
                importDeletes := importDeletesEdits(pgf, deletes)
                Alan Donovan . resolved

                The combination of importDeletesEdits and format.Source is now provided by driverutil.FormatSourceRemoveImports. This should make things easier in findImportEdits and eliminate this step.

                (I should probably move it out of driverutil, but it's a legitimate dependency of the golang package.)

                Madeline Kalil

                From discussion: switching to using driverutil.FormatSourceRemoveImports would be a behavior change because previously, extracttofile would not format the file source, it would just delete the imports and the extracted block.
                We should consider why we are computing unused imports to delete for a lot of gopls tools -- we get this formatting behavior on save so it might not be necessary to always compute the changes.

                Line 145, Patchset 6: newFile, err := chooseNewFile(ctx, snapshot, pgf.URI.DirPath(), firstSymbol)

                if err != nil {
                return nil, fmt.Errorf("%s: %w", errorPrefix, err)
                }
                Alan Donovan . resolved

                Move this down so the buf-related stuff stays together.

                Madeline Kalil

                Done

                Line 170, Patchset 6:func importDeletesEdits(pgf *parsego.File, deletes []*ast.ImportSpec) []protocol.TextEdit {
                Alan Donovan . resolved

                See above; I think this is no longer needed.

                Madeline Kalil

                Acknowledged

                Line 194, Patchset 6:func newFileWithImports(pkgName string, imports []*ast.ImportSpec, fromPGF *parsego.File) (bytes.Buffer, error) {
                Alan Donovan . resolved

                *bytes.Buffer

                (Values of this type, and indeed most opaque types T with *T methods, should not be copied.)

                But why not have the caller provide the buffer? That's usually more efficient since there is often stuff to be written to it before and after.

                Madeline Kalil

                Switched to caller providing buffer

                Line 216, Patchset 6: fmt.Fprintf(&buf, "package %s\n", pkgName)
                Alan Donovan . resolved

                fromPGF.File.Name.Name

                (then the pkgName parameter is unneeded)

                Madeline Kalil

                The call to newFileWithImports uses a different package name so I can't do this

                Line 216, Patchset 6: fmt.Fprintf(&buf, "package %s\n", pkgName)
                Alan Donovan . resolved

                \n\n

                Madeline Kalil

                Done

                Line 219, Patchset 6: for _, importSpec := range imports {
                Alan Donovan . resolved

                spec (for brevity)

                Madeline Kalil

                Done

                Line 220, Patchset 6: if importSpec.Name != nil {

                fmt.Fprintf(&buf, "%s %s\n", importSpec.Name.Name, importSpec.Path.Value)
                } else {
                fmt.Fprintf(&buf, "%s\n", importSpec.Path.Value)
                }
                Alan Donovan . resolved

                Factor the Path parts.

                Madeline Kalil

                Done

                Line 226, Patchset 6: buf.WriteString(")\n")
                Alan Donovan . resolved

                \n\n

                Madeline Kalil

                Done

                File gopls/internal/golang/movetype.go
                Line 37, Patchset 6: curSel, ok := pgf.Cursor.FindByPos(start, end)
                Alan Donovan . resolved

                astutil.Select may be more appropriate here; it handles whitespace more permissively.

                Madeline Kalil

                I think Select won't work for my use case until cl/730320 is in; added a TODO

                Line 41, Patchset 6: _, decl, typS, _, _ := selectionContainsTypeSpec(curSel) // we also check this condition before providing the move type codeaction
                Alan Donovan . resolved

                spec

                Madeline Kalil

                Done

                Line 41, Patchset 6: _, decl, typS, _, _ := selectionContainsTypeSpec(curSel) // we also check this condition before providing the move type codeaction
                Alan Donovan . unresolved

                Is decl just the parent of spec? If so, return a cursor and let the caller call Parent.

                Madeline Kalil

                I feel like there was some reason why I didn't assume that GenDecl is always the direct parent of the TypeSpec but now I can't remember why. Is this always true?

                Line 47, Patchset 6: changes, err := addTypeToPackage(ctx, snapshot, pgf, pkg, pkg.TypesInfo(), typS, decl, newDir, filepath.Base(newDir))
                Alan Donovan . resolved

                Ditto w.r.t. newDir.

                Madeline Kalil

                Sorry, I should've caught this

                Line 47, Patchset 6: changes, err := addTypeToPackage(ctx, snapshot, pgf, pkg, pkg.TypesInfo(), typS, decl, newDir, filepath.Base(newDir))
                Alan Donovan . resolved

                This is redundant w.r.t pkg.

                Madeline Kalil

                Done

                Line 74, Patchset 6: typeDeclRange, err := pgf.PosRange(typStart, typEnd)
                Alan Donovan . resolved

                rng

                Madeline Kalil

                Done

                Line 76, Patchset 6: return nil, fmt.Errorf("invalid range: %v", err)
                Alan Donovan . resolved

                err is fine.

                Madeline Kalil

                Done

                Line 100, Patchset 6: // Use CommentedNode to avoid losing comments inside struct types

                // that are neither doc comments nor line comments.
                // type A struct { // this comment gets lost.
                // B int
                // }
                // TODO(mkalil): Now these comments are preserved but
                // comments above a type spec are formatted in an undesirable way.
                // (See @multitypespecB/d/newpkg/b.go in movetype.txt)
                var declComments []*ast.CommentGroup
                if len(decl.Specs) == 1 {
                for _, comment := range pgf.File.Comments {
                if decl.Pos() <= comment.Pos() && comment.End() <= decl.End() {
                declComments = append(declComments, comment)
                }
                }
                }

                // Add the type decl to the new file content.
                newDecl := &ast.GenDecl{
                Tok: token.TYPE,
                Specs: []ast.Spec{spec},
                }
                commentedNode := &printer.CommentedNode{
                Node: newDecl,
                Comments: declComments,
                }
                err = format.Node(&buf, pkg.FileSet(), commentedNode)
                if err != nil {
                return nil, fmt.Errorf("error adding type decl to new file: %v", err)
                }
                Alan Donovan . unresolved

                It might be simpler here to copy `pgf.NodeText(spec or decl)` directly into buf. Then you don't to think about comments or format.Source.

                Madeline Kalil

                I'm finding that NodeText doesn't include comment:
                type A string // this comment gets lost

                Line 155, Patchset 6: specCur, ok := moreiters.First(cursor.Enclosing((*ast.TypeSpec)(nil)))
                Alan Donovan . resolved

                ```
                spec, curSpec := cursorutil.FirstEnclosing[*ast.TypeSpec](cur)
                if spec == nil { ... }
                ```

                then delete L170,

                Madeline Kalil

                Done

                Line 157, Patchset 6: return inspector.Cursor{}, &ast.GenDecl{}, &ast.TypeSpec{}, "", false
                Alan Donovan . resolved

                nil, nil

                (throughout)

                Madeline Kalil

                Done

                Line 165, Patchset 6: declNode := declCur.Node().(*ast.GenDecl)
                Alan Donovan . resolved

                decl

                Madeline Kalil

                Done

                Line 170, Patchset 6: typSpec, _ := specCur.Node().(*ast.TypeSpec) // can't fail
                Alan Donovan . resolved

                delete (since it can't fail)

                Madeline Kalil

                Done

                Line 171, Patchset 6: return declCur, declNode, typSpec, typSpec.Name.Name, true
                Alan Donovan . resolved

                This seems redundant w.r.t. type typSpec result.

                Madeline Kalil

                Done

                Line 176, Patchset 6:// TODO(mkalil): For now, we only allow moving a type if the type itself and all its
                Alan Donovan . resolved

                We also need to check that:

                • all references from the moved declarations are either internal to it, or to imported packages, but not to other declarations in the old package.
                • any internal packages that would be imported by the new package are visible to it (see packagepath.CanImport).
                Madeline Kalil

                Thanks, added a TODO

                Line 187, Patchset 6: if typExp, ok := typ.Type.(*ast.StructType); ok {

                for _, field := range typExp.Fields.List {

                for _, name := range field.Names {
                Alan Donovan . resolved

                This kind of check, which relates to symbol references, is best done using the type checker's data structures.

                Madeline Kalil

                Done

                Line 199, Patchset 6: // TODO(mkalil): How should we handle moving types that have fields which are

                // types defined at the package level? Should we also move those types to the
                // new package, recursively? (If we don't, it could cause an import cycle).
                // Or throw an error?
                Alan Donovan . resolved

                We should probably attempt to move them recursively (applying the same validity checks). If the user didn't want that, they can always undo, but there's no good way to ask the user "move type T2 too?" (yet).

                Madeline Kalil

                Makes sense to me, updated the TODO

                File gopls/internal/server/code_action.go
                Line 86, Patchset 6: // RefactorMoveType is enabled for testing but disabled otherwise.

                // This is temporary while the implementation of move type refactoring is unfinished.
                if kind == settings.RefactorMoveType && !snapshot.Options().MoveType {
                return false
                }
                Alan Donovan . resolved

                I wonder why this option is needed. How many steps are there to finishing the feature? Is it too much for this CL, or a small stack that can be merged in the same day or two?

                Madeline Kalil

                Even with all the CLs in the current chain it won't be finished (and probably won't get finished over the holiday period) so I'd rather have the setting so I can merge in the partial implementation if that's okay. Will remove it when it's done.

                Open in Gerrit

                Related details

                Attention is currently required from:
                • Alan Donovan
                Submit Requirements:
                • requirement is not satisfiedCode-Review
                • requirement is not satisfiedNo-Unresolved-Comments
                • requirement is not satisfiedReview-Enforcement
                • requirement is not satisfiedTryBots-Pass
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: comment
                Gerrit-Project: tools
                Gerrit-Branch: master
                Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                Gerrit-Change-Number: 723982
                Gerrit-PatchSet: 7
                Gerrit-Owner: Madeline Kalil <mka...@google.com>
                Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                Gerrit-CC: Robert Findley <rfin...@golang.org>
                Gerrit-Attention: Alan Donovan <adon...@google.com>
                Gerrit-Comment-Date: Wed, 17 Dec 2025 19:43:38 +0000
                Gerrit-HasComments: Yes
                Gerrit-Has-Labels: Yes
                Comment-In-Reply-To: Alan Donovan <adon...@google.com>
                unsatisfied_requirement
                open
                diffy

                Madeline Kalil (Gerrit)

                unread,
                Jan 6, 2026, 1:48:04 PMJan 6
                to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
                Attention needed from Alan Donovan

                Madeline Kalil uploaded new patchset

                Madeline Kalil uploaded patch set #8 to this change.
                Following approvals got outdated and were removed:
                • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
                Open in Gerrit

                Related details

                Attention is currently required from:
                • Alan Donovan
                Submit Requirements:
                • requirement is not satisfiedCode-Review
                • requirement is not satisfiedNo-Unresolved-Comments
                • requirement is not satisfiedReview-Enforcement
                • requirement is not satisfiedTryBots-Pass
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: newpatchset
                Gerrit-Project: tools
                Gerrit-Branch: master
                Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                Gerrit-Change-Number: 723982
                Gerrit-PatchSet: 8
                unsatisfied_requirement
                open
                diffy

                Madeline Kalil (Gerrit)

                unread,
                Jan 6, 2026, 3:10:53 PMJan 6
                to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
                Attention needed from Alan Donovan

                Madeline Kalil uploaded new patchset

                Madeline Kalil uploaded patch set #9 to this change.
                Open in Gerrit

                Related details

                Attention is currently required from:
                • Alan Donovan
                Submit Requirements:
                • requirement is not satisfiedCode-Review
                • requirement is not satisfiedNo-Unresolved-Comments
                • requirement is not satisfiedReview-Enforcement
                • requirement is not satisfiedTryBots-Pass
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: newpatchset
                Gerrit-Project: tools
                Gerrit-Branch: master
                Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                Gerrit-Change-Number: 723982
                Gerrit-PatchSet: 9
                unsatisfied_requirement
                open
                diffy

                Madeline Kalil (Gerrit)

                unread,
                Jan 6, 2026, 3:11:49 PMJan 6
                to goph...@pubsubhelper.golang.org, Go LUCI, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                Attention needed from Alan Donovan

                Madeline Kalil voted and added 1 comment

                Votes added by Madeline Kalil

                Commit-Queue+1

                1 comment

                File gopls/internal/golang/movetype.go
                Line 73, Patchset 6: }
                Alan Donovan . resolved
                This is a recurring pattern. The common parts could be factored as:

                ```
                var n Node = spec
                if len(decl.Specs) == 1 {
                n = decl // singleton: delete entire decl
                }
                start, end = n.Pos(), n.End()
                if doc := astutil.DocComment(n); doc != nil {
                start = doc.Pos() // include doc comment
                }
                if c := astutil.PostComment(n); c != nil {
                end = c.End() // include inline comment
                }
                ```
                However, only DocComment exists; you'll need to define PostComment. We could use it in dozens of places.
                Madeline Kalil

                Left a TODO to find the appropriate places to replace with PostComment

                Open in Gerrit

                Related details

                Attention is currently required from:
                • Alan Donovan
                Submit Requirements:
                • requirement is not satisfiedCode-Review
                • requirement is not satisfiedNo-Unresolved-Comments
                • requirement is not satisfiedReview-Enforcement
                • requirement is not satisfiedTryBots-Pass
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: comment
                Gerrit-Project: tools
                Gerrit-Branch: master
                Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                Gerrit-Change-Number: 723982
                Gerrit-PatchSet: 9
                Gerrit-Owner: Madeline Kalil <mka...@google.com>
                Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                Gerrit-CC: Robert Findley <rfin...@golang.org>
                Gerrit-Attention: Alan Donovan <adon...@google.com>
                Gerrit-Comment-Date: Tue, 06 Jan 2026 20:11:46 +0000
                unsatisfied_requirement
                open
                diffy

                Madeline Kalil (Gerrit)

                unread,
                Jan 22, 2026, 2:05:50 PMJan 22
                to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
                Attention needed from Alan Donovan

                Madeline Kalil uploaded new patchset

                Madeline Kalil uploaded patch set #11 to this change.
                Open in Gerrit

                Related details

                Attention is currently required from:
                • Alan Donovan
                Submit Requirements:
                • requirement is not satisfiedCode-Review
                • requirement is not satisfiedNo-Unresolved-Comments
                • requirement is not satisfiedReview-Enforcement
                • requirement is not satisfiedTryBots-Pass
                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                Gerrit-MessageType: newpatchset
                Gerrit-Project: tools
                Gerrit-Branch: master
                Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                Gerrit-Change-Number: 723982
                Gerrit-PatchSet: 11
                unsatisfied_requirement
                open
                diffy

                Madeline Kalil (Gerrit)

                unread,
                Jan 22, 2026, 2:07:52 PMJan 22
                to goph...@pubsubhelper.golang.org, Go LUCI, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                Attention needed from Alan Donovan

                Madeline Kalil voted and added 3 comments

                Votes added by Madeline Kalil

                Commit-Queue+1

                3 comments

                Commit Message
                Line 7, Patchset 6:gopls/internal/golang: MoveType - extract type to new file
                Alan Donovan . resolved

                Does this always move the type to a new package? I was confused because the extract logic appears to move it to a new file in the same package (based on the package declaration), but the command handler seems to create the file name of a new directory.

                Madeline Kalil

                The logic is inconsistent right now because I'm not sure of the way we'll take input from users once we have dialogue support (file chooser vs input package name, etc.). For now, the input is a URI and then it will add the type to a new file in the same package as the URI. The feature only makes sense moving to a new package but we should still allow people to move types within a package so there's no check for if the URI specified is part of a different package than where the type is currently declared.

                Madeline Kalil

                I'd like to break the implementation of type move refactoring into 3 or so CLs if that's okay. This one produces the initial set of necessary edits to 1) move the type to a new file 2) remove the type declaration from its current location.

                The exact behavior of determining which file in which package the type will be moved to will definitely change.

                File gopls/internal/golang/movetype.go
                Line 41, Patchset 6: _, decl, typS, _, _ := selectionContainsTypeSpec(curSel) // we also check this condition before providing the move type codeaction
                Alan Donovan . resolved

                Is decl just the parent of spec? If so, return a cursor and let the caller call Parent.

                Madeline Kalil

                I feel like there was some reason why I didn't assume that GenDecl is always the direct parent of the TypeSpec but now I can't remember why. Is this always true?

                Madeline Kalil

                Nevermind, fixed. selectionContainsTypeSpec now just returns the cursor corresponding to the type spec.

                Line 100, Patchset 6: // Use CommentedNode to avoid losing comments inside struct types
                // that are neither doc comments nor line comments.
                // type A struct { // this comment gets lost.
                // B int
                // }
                // TODO(mkalil): Now these comments are preserved but
                // comments above a type spec are formatted in an undesirable way.
                // (See @multitypespecB/d/newpkg/b.go in movetype.txt)
                var declComments []*ast.CommentGroup
                if len(decl.Specs) == 1 {
                for _, comment := range pgf.File.Comments {
                if decl.Pos() <= comment.Pos() && comment.End() <= decl.End() {
                declComments = append(declComments, comment)
                }
                }
                }

                // Add the type decl to the new file content.
                newDecl := &ast.GenDecl{
                Tok: token.TYPE,
                Specs: []ast.Spec{spec},
                }
                commentedNode := &printer.CommentedNode{
                Node: newDecl,
                Comments: declComments,
                }
                err = format.Node(&buf, pkg.FileSet(), commentedNode)
                if err != nil {
                return nil, fmt.Errorf("error adding type decl to new file: %v", err)
                }
                Alan Donovan . resolved

                It might be simpler here to copy `pgf.NodeText(spec or decl)` directly into buf. Then you don't to think about comments or format.Source.

                Madeline Kalil

                I'm finding that NodeText doesn't include comment:
                type A string // this comment gets lost

                Madeline Kalil

                Leaving the handling of comments as-is for now.

                Open in Gerrit

                Related details

                Attention is currently required from:
                • Alan Donovan
                Submit Requirements:
                  • requirement is not satisfiedCode-Review
                  • requirement satisfiedNo-Unresolved-Comments
                  • requirement is not satisfiedReview-Enforcement
                  • requirement is not satisfiedTryBots-Pass
                  Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                  Gerrit-MessageType: comment
                  Gerrit-Project: tools
                  Gerrit-Branch: master
                  Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                  Gerrit-Change-Number: 723982
                  Gerrit-PatchSet: 11
                  Gerrit-Owner: Madeline Kalil <mka...@google.com>
                  Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                  Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                  Gerrit-CC: Robert Findley <rfin...@golang.org>
                  Gerrit-Attention: Alan Donovan <adon...@google.com>
                  Gerrit-Comment-Date: Thu, 22 Jan 2026 19:07:47 +0000
                  Gerrit-HasComments: Yes
                  Gerrit-Has-Labels: Yes
                  Comment-In-Reply-To: Madeline Kalil <mka...@google.com>
                  Comment-In-Reply-To: Alan Donovan <adon...@google.com>
                  unsatisfied_requirement
                  satisfied_requirement
                  open
                  diffy

                  Alan Donovan (Gerrit)

                  unread,
                  Jan 22, 2026, 2:39:21 PMJan 22
                  to Madeline Kalil, goph...@pubsubhelper.golang.org, Go LUCI, Robert Findley, golang-co...@googlegroups.com
                  Attention needed from Madeline Kalil

                  Alan Donovan added 9 comments

                  Patchset-level comments
                  File-level comment, Patchset 11 (Latest):
                  Alan Donovan . resolved

                  Thanks, this is converging. Sorry my review took so long.

                  File gopls/internal/golang/extracttofile.go
                  Line 138, Patchset 11 (Latest): importDeletes := importDeletesEdits(pgf, deletes)
                  Alan Donovan . unresolved

                  Inline this call? The golang package is very large so I'd like to avoid adding too many additional helper functions with tempting names to the package namespace when they are really bespoke parts of algorithms such as ExtractToNewFile.

                  Line 160, Patchset 11 (Latest): protocol.DocumentChangeEdit(fh, append(importDeletes, protocol.TextEdit{Range: replaceRange, NewText: ""})),
                  Alan Donovan . unresolved

                  delete

                  File gopls/internal/golang/movetype.go
                  Line 44, Patchset 11 (Latest): specCur, _ := selectionContainsTypeSpec(curSel) // we also check this condition before providing the move type codeaction
                  Alan Donovan . unresolved

                  This is true, but it is good practice for a server not to assume that its clients won't fling any old junk at it, so we should check the validity of the result again.

                  Line 47, Patchset 11 (Latest): if err := canMoveType(pkg.TypesInfo(), spec); err != nil {
                  Alan Donovan . unresolved

                  specCur

                  (see below)

                  Line 77, Patchset 11 (Latest): {Range: rng, NewText: ""},
                  Alan Donovan . unresolved

                  delete

                  Line 182, Patchset 11 (Latest): return fmt.Errorf("cannot move type: type is not exported")
                  Alan Donovan . unresolved
                  Line 186, Patchset 11 (Latest): if structType, ok := typ.(*types.Struct); ok {
                  Alan Donovan . unresolved
                  This really wants to be recursive check over the entire TypeSpec. You should be able to use something like this:
                  ```
                  for n := range curTypeSpec.Preorder() {
                  switch n := n.(type) {
                  case *ast.Ident:
                  // if it's a Use of an Object (e.g. type name or constant),
                  // check the object is exported (or also moving)
                  case *ast.StructType:
                  // check each field name is exported, like you do here,
                  // (or all its uses are also moving).
                  }
                  }
                  ```
                  The "also moving" checks are fine to leave as a TODO.
                  File gopls/internal/server/code_action.go
                  Line 86, Patchset 11 (Latest): // RefactorMoveType is enabled for testing but disabled otherwise.

                  // This is temporary while the implementation of move type refactoring is unfinished.
                  if kind == settings.RefactorMoveType && !snapshot.Options().MoveType {
                  return false
                  }
                  Alan Donovan . unresolved

                  I don't think this option is necessary; let's aim to get it to readiness in this CL. If we need to fix some bugs before the 22 release, that's fine.

                  Open in Gerrit

                  Related details

                  Attention is currently required from:
                  • Madeline Kalil
                  Submit Requirements:
                    • requirement is not satisfiedCode-Review
                    • requirement is not satisfiedNo-Unresolved-Comments
                    • requirement is not satisfiedReview-Enforcement
                    • requirement satisfiedTryBots-Pass
                    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                    Gerrit-MessageType: comment
                    Gerrit-Project: tools
                    Gerrit-Branch: master
                    Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                    Gerrit-Change-Number: 723982
                    Gerrit-PatchSet: 11
                    Gerrit-Owner: Madeline Kalil <mka...@google.com>
                    Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                    Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                    Gerrit-CC: Robert Findley <rfin...@golang.org>
                    Gerrit-Attention: Madeline Kalil <mka...@google.com>
                    Gerrit-Comment-Date: Thu, 22 Jan 2026 19:39:14 +0000
                    Gerrit-HasComments: Yes
                    Gerrit-Has-Labels: No
                    unsatisfied_requirement
                    satisfied_requirement
                    open
                    diffy

                    Madeline Kalil (Gerrit)

                    unread,
                    Jan 22, 2026, 5:06:32 PMJan 22
                    to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
                    Attention needed from Madeline Kalil

                    Madeline Kalil uploaded new patchset

                    Madeline Kalil uploaded patch set #12 to this change.
                    Following approvals got outdated and were removed:
                    • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI

                    Related details

                    Attention is currently required from:
                    • Madeline Kalil
                    Submit Requirements:
                      • requirement is not satisfiedCode-Review
                      • requirement is not satisfiedNo-Unresolved-Comments
                      • requirement is not satisfiedReview-Enforcement
                      • requirement is not satisfiedTryBots-Pass
                      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                      Gerrit-MessageType: newpatchset
                      Gerrit-Project: tools
                      Gerrit-Branch: master
                      Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                      Gerrit-Change-Number: 723982
                      Gerrit-PatchSet: 12
                      unsatisfied_requirement
                      open
                      diffy

                      Madeline Kalil (Gerrit)

                      unread,
                      Jan 22, 2026, 5:06:39 PMJan 22
                      to goph...@pubsubhelper.golang.org, Go LUCI, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                      Attention needed from Alan Donovan

                      Madeline Kalil voted and added 8 comments

                      Votes added by Madeline Kalil

                      Commit-Queue+1

                      8 comments

                      File gopls/internal/golang/extracttofile.go
                      Line 138, Patchset 11: importDeletes := importDeletesEdits(pgf, deletes)
                      Alan Donovan . resolved

                      Inline this call? The golang package is very large so I'd like to avoid adding too many additional helper functions with tempting names to the package namespace when they are really bespoke parts of algorithms such as ExtractToNewFile.

                      Madeline Kalil

                      I think I originally extracted this to a separate function so that move type could also use it, but that is no longer happening. Done.

                      Line 160, Patchset 11: protocol.DocumentChangeEdit(fh, append(importDeletes, protocol.TextEdit{Range: replaceRange, NewText: ""})),
                      Alan Donovan . resolved

                      delete

                      Madeline Kalil

                      Done

                      File gopls/internal/golang/movetype.go
                      Line 44, Patchset 11: specCur, _ := selectionContainsTypeSpec(curSel) // we also check this condition before providing the move type codeaction
                      Alan Donovan . resolved

                      This is true, but it is good practice for a server not to assume that its clients won't fling any old junk at it, so we should check the validity of the result again.

                      Madeline Kalil

                      Done

                      Line 47, Patchset 11: if err := canMoveType(pkg.TypesInfo(), spec); err != nil {
                      Alan Donovan . resolved

                      specCur

                      (see below)

                      Madeline Kalil

                      Done

                      Line 77, Patchset 11: {Range: rng, NewText: ""},
                      Alan Donovan . resolved

                      delete

                      Madeline Kalil

                      Done

                      Line 182, Patchset 11: return fmt.Errorf("cannot move type: type is not exported")
                      Alan Donovan . resolved

                      "%s", typSpec.Name.Name

                      Madeline Kalil

                      Done

                      Line 186, Patchset 11: if structType, ok := typ.(*types.Struct); ok {
                      Alan Donovan . unresolved
                      This really wants to be recursive check over the entire TypeSpec. You should be able to use something like this:
                      ```
                      for n := range curTypeSpec.Preorder() {
                      switch n := n.(type) {
                      case *ast.Ident:
                      // if it's a Use of an Object (e.g. type name or constant),
                      // check the object is exported (or also moving)
                      case *ast.StructType:
                      // check each field name is exported, like you do here,
                      // (or all its uses are also moving).
                      }
                      }
                      ```
                      The "also moving" checks are fine to leave as a TODO.
                      Madeline Kalil

                      Sorry, can you give an example of what you mean by "also moving"?

                      File gopls/internal/server/code_action.go
                      Line 86, Patchset 11: // RefactorMoveType is enabled for testing but disabled otherwise.

                      // This is temporary while the implementation of move type refactoring is unfinished.
                      if kind == settings.RefactorMoveType && !snapshot.Options().MoveType {
                      return false
                      }
                      Alan Donovan . unresolved

                      I don't think this option is necessary; let's aim to get it to readiness in this CL. If we need to fix some bugs before the 22 release, that's fine.

                      Madeline Kalil

                      Do you think the whole move type implementation should be done in this CL? I was going to merge and then chain the rest of the implementation

                      Open in Gerrit

                      Related details

                      Attention is currently required from:
                      • Alan Donovan
                      Submit Requirements:
                      • requirement is not satisfiedCode-Review
                      • requirement is not satisfiedNo-Unresolved-Comments
                      • requirement is not satisfiedReview-Enforcement
                      • requirement is not satisfiedTryBots-Pass
                      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                      Gerrit-MessageType: comment
                      Gerrit-Project: tools
                      Gerrit-Branch: master
                      Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                      Gerrit-Change-Number: 723982
                      Gerrit-PatchSet: 12
                      Gerrit-Owner: Madeline Kalil <mka...@google.com>
                      Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                      Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                      Gerrit-CC: Robert Findley <rfin...@golang.org>
                      Gerrit-Attention: Alan Donovan <adon...@google.com>
                      Gerrit-Comment-Date: Thu, 22 Jan 2026 22:06:36 +0000
                      Gerrit-HasComments: Yes
                      Gerrit-Has-Labels: Yes
                      Comment-In-Reply-To: Alan Donovan <adon...@google.com>
                      unsatisfied_requirement
                      open
                      diffy

                      Alan Donovan (Gerrit)

                      unread,
                      Jan 22, 2026, 8:23:34 PMJan 22
                      to Madeline Kalil, goph...@pubsubhelper.golang.org, Go LUCI, Robert Findley, golang-co...@googlegroups.com
                      Attention needed from Madeline Kalil

                      Alan Donovan added 2 comments

                      File gopls/internal/golang/movetype.go
                      Line 186, Patchset 11: if structType, ok := typ.(*types.Struct); ok {
                      Alan Donovan . unresolved
                      This really wants to be recursive check over the entire TypeSpec. You should be able to use something like this:
                      ```
                      for n := range curTypeSpec.Preorder() {
                      switch n := n.(type) {
                      case *ast.Ident:
                      // if it's a Use of an Object (e.g. type name or constant),
                      // check the object is exported (or also moving)
                      case *ast.StructType:
                      // check each field name is exported, like you do here,
                      // (or all its uses are also moving).
                      }
                      }
                      ```
                      The "also moving" checks are fine to leave as a TODO.
                      Madeline Kalil

                      Sorry, can you give an example of what you mean by "also moving"?

                      Alan Donovan

                      Sorry, I was vaguely thinking that we were moving a set of declarations at once, as in https://go.dev/gopls/features/web#splitpkg, in which case it's ok not to export b in `type A struct {b}; type b int` if we're moving both A and b, but now I remember that we're only moving a single type and its methods. So my "also moving" comments don't apply (though it will be something to think about when we get around to implementing the transformation part of the split-package feature).

                      File gopls/internal/server/code_action.go
                      Line 86, Patchset 11: // RefactorMoveType is enabled for testing but disabled otherwise.
                      // This is temporary while the implementation of move type refactoring is unfinished.
                      if kind == settings.RefactorMoveType && !snapshot.Options().MoveType {
                      return false
                      }
                      Alan Donovan . unresolved

                      I don't think this option is necessary; let's aim to get it to readiness in this CL. If we need to fix some bugs before the 22 release, that's fine.

                      Madeline Kalil

                      Do you think the whole move type implementation should be done in this CL? I was going to merge and then chain the rest of the implementation

                      Alan Donovan

                      If you want to split it across several CLs that you plan to do in sequence, that's fine. We have plenty of time before the release.

                      Open in Gerrit

                      Related details

                      Attention is currently required from:
                      • Madeline Kalil
                      Submit Requirements:
                        • requirement is not satisfiedCode-Review
                        • requirement is not satisfiedNo-Unresolved-Comments
                        • requirement is not satisfiedReview-Enforcement
                        • requirement satisfiedTryBots-Pass
                        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                        Gerrit-MessageType: comment
                        Gerrit-Project: tools
                        Gerrit-Branch: master
                        Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                        Gerrit-Change-Number: 723982
                        Gerrit-PatchSet: 12
                        Gerrit-Owner: Madeline Kalil <mka...@google.com>
                        Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                        Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                        Gerrit-CC: Robert Findley <rfin...@golang.org>
                        Gerrit-Attention: Madeline Kalil <mka...@google.com>
                        Gerrit-Comment-Date: Fri, 23 Jan 2026 01:23:31 +0000
                        Gerrit-HasComments: Yes
                        Gerrit-Has-Labels: No
                        unsatisfied_requirement
                        satisfied_requirement
                        open
                        diffy

                        Madeline Kalil (Gerrit)

                        unread,
                        Jan 23, 2026, 10:14:51 AMJan 23
                        to goph...@pubsubhelper.golang.org, Go LUCI, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                        Attention needed from Alan Donovan

                        Madeline Kalil added 2 comments

                        File gopls/internal/golang/movetype.go
                        Line 186, Patchset 11: if structType, ok := typ.(*types.Struct); ok {
                        Alan Donovan . resolved
                        This really wants to be recursive check over the entire TypeSpec. You should be able to use something like this:
                        ```
                        for n := range curTypeSpec.Preorder() {
                        switch n := n.(type) {
                        case *ast.Ident:
                        // if it's a Use of an Object (e.g. type name or constant),
                        // check the object is exported (or also moving)
                        case *ast.StructType:
                        // check each field name is exported, like you do here,
                        // (or all its uses are also moving).
                        }
                        }
                        ```
                        The "also moving" checks are fine to leave as a TODO.
                        Madeline Kalil

                        Sorry, can you give an example of what you mean by "also moving"?

                        Alan Donovan

                        Sorry, I was vaguely thinking that we were moving a set of declarations at once, as in https://go.dev/gopls/features/web#splitpkg, in which case it's ok not to export b in `type A struct {b}; type b int` if we're moving both A and b, but now I remember that we're only moving a single type and its methods. So my "also moving" comments don't apply (though it will be something to think about when we get around to implementing the transformation part of the split-package feature).

                        Madeline Kalil

                        Ah got it. Maybe this feature could eventually be extended to also move multiple types

                        File gopls/internal/server/code_action.go
                        Line 86, Patchset 11: // RefactorMoveType is enabled for testing but disabled otherwise.
                        // This is temporary while the implementation of move type refactoring is unfinished.
                        if kind == settings.RefactorMoveType && !snapshot.Options().MoveType {
                        return false
                        }
                        Alan Donovan . resolved

                        I don't think this option is necessary; let's aim to get it to readiness in this CL. If we need to fix some bugs before the 22 release, that's fine.

                        Madeline Kalil

                        Do you think the whole move type implementation should be done in this CL? I was going to merge and then chain the rest of the implementation

                        Alan Donovan

                        If you want to split it across several CLs that you plan to do in sequence, that's fine. We have plenty of time before the release.

                        Madeline Kalil

                        Okay, that would be my preference, so I'll leave this setting. Thanks!

                        Open in Gerrit

                        Related details

                        Attention is currently required from:
                        • Alan Donovan
                        Submit Requirements:
                          • requirement is not satisfiedCode-Review
                          • requirement satisfiedNo-Unresolved-Comments
                          • requirement is not satisfiedReview-Enforcement
                          • requirement satisfiedTryBots-Pass
                          Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                          Gerrit-MessageType: comment
                          Gerrit-Project: tools
                          Gerrit-Branch: master
                          Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                          Gerrit-Change-Number: 723982
                          Gerrit-PatchSet: 12
                          Gerrit-Owner: Madeline Kalil <mka...@google.com>
                          Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                          Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                          Gerrit-CC: Robert Findley <rfin...@golang.org>
                          Gerrit-Attention: Alan Donovan <adon...@google.com>
                          Gerrit-Comment-Date: Fri, 23 Jan 2026 15:14:48 +0000
                          unsatisfied_requirement
                          satisfied_requirement
                          open
                          diffy

                          Madeline Kalil (Gerrit)

                          unread,
                          Feb 2, 2026, 1:40:53 PMFeb 2
                          to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
                          Attention needed from Alan Donovan and Hongxiang Jiang

                          Madeline Kalil uploaded new patchset

                          Madeline Kalil uploaded patch set #13 to this change.
                          Following approvals got outdated and were removed:
                          • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
                          Open in Gerrit

                          Related details

                          Attention is currently required from:
                          • Alan Donovan
                          • Hongxiang Jiang
                          Submit Requirements:
                            • requirement is not satisfiedCode-Review
                            • requirement satisfiedNo-Unresolved-Comments
                            • requirement is not satisfiedReview-Enforcement
                            • requirement is not satisfiedTryBots-Pass
                            Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                            Gerrit-MessageType: newpatchset
                            Gerrit-Project: tools
                            Gerrit-Branch: master
                            Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                            Gerrit-Change-Number: 723982
                            Gerrit-PatchSet: 13
                            Gerrit-Owner: Madeline Kalil <mka...@google.com>
                            Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                            Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                            Gerrit-CC: Hongxiang Jiang <hxj...@golang.org>
                            Gerrit-CC: Robert Findley <rfin...@golang.org>
                            Gerrit-Attention: Hongxiang Jiang <hxj...@golang.org>
                            Gerrit-Attention: Alan Donovan <adon...@google.com>
                            unsatisfied_requirement
                            satisfied_requirement
                            open
                            diffy

                            Hongxiang Jiang (Gerrit)

                            unread,
                            Feb 10, 2026, 3:54:30 AMFeb 10
                            to Madeline Kalil, goph...@pubsubhelper.golang.org, Go LUCI, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                            Attention needed from Alan Donovan and Madeline Kalil

                            Hongxiang Jiang added 6 comments

                            File gopls/internal/golang/movetype.go
                            Line 154, Patchset 13 (Latest):// selectionContainsTypeSpec returns the cursor of the type declaration that
                            Hongxiang Jiang . unresolved
                            ```
                            func foo() {
                            type fooa struct {
                            Buf bytes.Buffer
                            Ctx context.Context
                            }
                            }
                            ```

                            Is this type `fooa` movable? The implementation for this function did not make sure the GenDecl is the file level decl, is this intended?

                            IMO, type that defined within a function is designed and only used within this function or the scope this type is declared.

                            If this is something we want to support, could you add a comment?
                            If not, let's mention only the file level type can be moved.

                            Line 185, Patchset 13 (Latest): if !ok {
                            return fmt.Errorf("input is not a type spec")
                            }
                            Hongxiang Jiang . unresolved

                            this is not possible right? but I don't really have a strong opinion here.

                            because the caller have the verification:

                            ```
                            spec := specCur.Node().(*ast.TypeSpec)
                            decl := specCur.Parent().Node().(*ast.GenDecl)
                            if err := canMoveType(pkg.TypesInfo(), specCur); err != nil {
                            return nil, err
                            }
                            ```
                            Line 189, Patchset 13 (Latest): return fmt.Errorf("cannot move type %s: type is not exported", typSpec.Name.Name)
                            Hongxiang Jiang . unresolved

                            we can ask the caller to do it, so we don't have to prepend `cannot move type %s` in all errors.

                            `fmt.Errorf("cannot move type %s: %w", spec.Name.Name, err)`

                            Line 214, Patchset 13 (Latest): if named, ok := field.Type().(*types.Named); ok && named.Obj().Pkg() == typNameObj.Pkg() {
                            Hongxiang Jiang . unresolved

                            this import cycle is the cycle between the "from" package and the "to" package right? We can ask Alan about how to detect this kind of circle dependency. But it will be a fun problem to solve!

                            Problem statement: if the type we are moving depend on package ABC.., after the move, we may remove a sub set of "ABC.." from package "from", but will add "ABC..." to the package "to".


                            there is also possibility that is more complicated:

                            ```
                            package "from" -> "foo" -> "to"

                            -- from/from.go --
                            package from

                            import "foo"

                            var _ foo.Foo
                            var _ From

                            type From struct {
                            foo foo.Foo
                            }

                            -- foo/foo.go --
                            package foo

                            import "to"

                            type Foo int

                            var _ to.To

                            -- to/to.go --
                            package to

                            type To int
                            ```

                            After the type `From` from package "from" to package "to" the dependency looks like:

                            ```
                            from ----
                            | . |
                            v v
                            to <--> foo
                            ```
                            ```
                            -- from.go --
                            package from

                            import "to"
                            import "foo"

                            var _ foo.Foo
                            var _ to.From

                            -- foo.go --
                            package foo

                            import "to"

                            type Foo int

                            var _ to.To

                            -- to.go --
                            package to

                            import "foo"

                            type To int

                            type From struct {
                            foo foo.Foo
                            }
                            ```
                            File gopls/internal/test/marker/testdata/codeaction/movetype.txt
                            Line 76, Patchset 13 (Latest):
                            Hongxiang Jiang . unresolved

                            delete

                            Line 96, Patchset 13 (Latest):
                            Hongxiang Jiang . unresolved

                            delete

                            Open in Gerrit

                            Related details

                            Attention is currently required from:
                            • Alan Donovan
                            • Madeline Kalil
                            Submit Requirements:
                              • requirement is not satisfiedCode-Review
                              • requirement is not satisfiedNo-Unresolved-Comments
                              • requirement is not satisfiedReview-Enforcement
                              • requirement is not satisfiedTryBots-Pass
                              Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                              Gerrit-MessageType: comment
                              Gerrit-Project: tools
                              Gerrit-Branch: master
                              Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                              Gerrit-Change-Number: 723982
                              Gerrit-PatchSet: 13
                              Gerrit-Owner: Madeline Kalil <mka...@google.com>
                              Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                              Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                              Gerrit-CC: Hongxiang Jiang <hxj...@golang.org>
                              Gerrit-CC: Robert Findley <rfin...@golang.org>
                              Gerrit-Attention: Madeline Kalil <mka...@google.com>
                              Gerrit-Attention: Alan Donovan <adon...@google.com>
                              Gerrit-Comment-Date: Tue, 10 Feb 2026 08:54:22 +0000
                              Gerrit-HasComments: Yes
                              Gerrit-Has-Labels: No
                              unsatisfied_requirement
                              open
                              diffy

                              Madeline Kalil (Gerrit)

                              unread,
                              Feb 10, 2026, 11:06:58 AMFeb 10
                              to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
                              Attention needed from Alan Donovan and Madeline Kalil

                              Madeline Kalil uploaded new patchset

                              Madeline Kalil uploaded patch set #14 to this change.
                              Open in Gerrit

                              Related details

                              Attention is currently required from:
                              • Alan Donovan
                              • Madeline Kalil
                              Submit Requirements:
                              • requirement is not satisfiedCode-Review
                              • requirement is not satisfiedNo-Unresolved-Comments
                              • requirement is not satisfiedReview-Enforcement
                              • requirement is not satisfiedTryBots-Pass
                              Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                              Gerrit-MessageType: newpatchset
                              Gerrit-Project: tools
                              Gerrit-Branch: master
                              Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                              Gerrit-Change-Number: 723982
                              Gerrit-PatchSet: 14
                              unsatisfied_requirement
                              open
                              diffy

                              Madeline Kalil (Gerrit)

                              unread,
                              Feb 10, 2026, 11:07:09 AMFeb 10
                              to goph...@pubsubhelper.golang.org, Hongxiang Jiang, Go LUCI, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                              Attention needed from Alan Donovan and Hongxiang Jiang

                              Madeline Kalil voted and added 6 comments

                              Votes added by Madeline Kalil

                              Commit-Queue+1

                              6 comments

                              File gopls/internal/golang/movetype.go
                              Line 154, Patchset 13:// selectionContainsTypeSpec returns the cursor of the type declaration that
                              Hongxiang Jiang . resolved
                              ```
                              func foo() {
                              type fooa struct {
                              Buf bytes.Buffer
                              Ctx context.Context
                              }
                              }
                              ```

                              Is this type `fooa` movable? The implementation for this function did not make sure the GenDecl is the file level decl, is this intended?

                              IMO, type that defined within a function is designed and only used within this function or the scope this type is declared.

                              If this is something we want to support, could you add a comment?
                              If not, let's mention only the file level type can be moved.

                              Madeline Kalil

                              Thanks for bringing up this case, I will make a note to add a test case for this in cl/741700. I agree that a type defined within a function is normally only used within the function scope, but I see no reason why we shouldn't allow moving these types, and it's already supported with my current implementation. (This particular example, we don't support because fooa is not exported, but if the type is Fooa then it works)


                              return fmt.Errorf("input is not a type spec")
                              }
                              Hongxiang Jiang . resolved

                              this is not possible right? but I don't really have a strong opinion here.

                              because the caller have the verification:

                              ```
                              spec := specCur.Node().(*ast.TypeSpec)
                              decl := specCur.Parent().Node().(*ast.GenDecl)
                              if err := canMoveType(pkg.TypesInfo(), specCur); err != nil {
                              return nil, err
                              }
                              ```
                              Madeline Kalil

                              Yeah good point, I'll just add a comment on this function about the invariant.

                              Line 189, Patchset 13: return fmt.Errorf("cannot move type %s: type is not exported", typSpec.Name.Name)
                              Hongxiang Jiang . resolved

                              we can ask the caller to do it, so we don't have to prepend `cannot move type %s` in all errors.

                              `fmt.Errorf("cannot move type %s: %w", spec.Name.Name, err)`

                              Madeline Kalil

                              Done

                              Line 214, Patchset 13: if named, ok := field.Type().(*types.Named); ok && named.Obj().Pkg() == typNameObj.Pkg() {
                              Madeline Kalil

                              Yes, this is the cycle I'm referring to.
                              Now that we have dialogue support, if we can calculate how to resolve the cycle by moving additional type declarations to the new package, we can ask the user how they would like us to proceed.

                              File gopls/internal/test/marker/testdata/codeaction/movetype.txt
                              Line 76, Patchset 13:
                              Hongxiang Jiang . resolved

                              delete

                              Madeline Kalil

                              Done

                              Line 96, Patchset 13:
                              Hongxiang Jiang . resolved

                              delete

                              Madeline Kalil

                              Done

                              Open in Gerrit

                              Related details

                              Attention is currently required from:
                              • Alan Donovan
                              • Hongxiang Jiang
                              Submit Requirements:
                              • requirement is not satisfiedCode-Review
                              • requirement is not satisfiedNo-Unresolved-Comments
                              • requirement is not satisfiedReview-Enforcement
                              • requirement is not satisfiedTryBots-Pass
                              Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                              Gerrit-MessageType: comment
                              Gerrit-Project: tools
                              Gerrit-Branch: master
                              Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                              Gerrit-Change-Number: 723982
                              Gerrit-PatchSet: 14
                              Gerrit-Owner: Madeline Kalil <mka...@google.com>
                              Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                              Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                              Gerrit-CC: Hongxiang Jiang <hxj...@golang.org>
                              Gerrit-CC: Robert Findley <rfin...@golang.org>
                              Gerrit-Attention: Hongxiang Jiang <hxj...@golang.org>
                              Gerrit-Attention: Alan Donovan <adon...@google.com>
                              Gerrit-Comment-Date: Tue, 10 Feb 2026 16:07:05 +0000
                              Gerrit-HasComments: Yes
                              Gerrit-Has-Labels: Yes
                              Comment-In-Reply-To: Hongxiang Jiang <hxj...@golang.org>
                              unsatisfied_requirement
                              open
                              diffy

                              Madeline Kalil (Gerrit)

                              unread,
                              Feb 10, 2026, 11:25:55 AMFeb 10
                              to goph...@pubsubhelper.golang.org, Go LUCI, Hongxiang Jiang, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                              Attention needed from Alan Donovan and Hongxiang Jiang

                              Madeline Kalil voted and added 1 comment

                              Votes added by Madeline Kalil

                              Commit-Queue+1

                              1 comment

                              File gopls/internal/golang/movetype.go
                              Line 154, Patchset 13:// selectionContainsTypeSpec returns the cursor of the type declaration that
                              Hongxiang Jiang . resolved
                              ```
                              func foo() {
                              type fooa struct {
                              Buf bytes.Buffer
                              Ctx context.Context
                              }
                              }
                              ```

                              Is this type `fooa` movable? The implementation for this function did not make sure the GenDecl is the file level decl, is this intended?

                              IMO, type that defined within a function is designed and only used within this function or the scope this type is declared.

                              If this is something we want to support, could you add a comment?
                              If not, let's mention only the file level type can be moved.

                              Madeline Kalil

                              Thanks for bringing up this case, I will make a note to add a test case for this in cl/741700. I agree that a type defined within a function is normally only used within the function scope, but I see no reason why we shouldn't allow moving these types, and it's already supported with my current implementation. (This particular example, we don't support because fooa is not exported, but if the type is Fooa then it works)

                              Madeline Kalil

                              Nevermind, this case isn't fully working in cl/741700, I need to make some updates.

                              Open in Gerrit

                              Related details

                              Attention is currently required from:
                              • Alan Donovan
                              • Hongxiang Jiang
                              Submit Requirements:
                                • requirement is not satisfiedCode-Review
                                • requirement is not satisfiedNo-Unresolved-Comments
                                • requirement is not satisfiedReview-Enforcement
                                • requirement satisfiedTryBots-Pass
                                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                                Gerrit-MessageType: comment
                                Gerrit-Project: tools
                                Gerrit-Branch: master
                                Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                                Gerrit-Change-Number: 723982
                                Gerrit-PatchSet: 14
                                Gerrit-Owner: Madeline Kalil <mka...@google.com>
                                Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                                Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                                Gerrit-CC: Hongxiang Jiang <hxj...@golang.org>
                                Gerrit-CC: Robert Findley <rfin...@golang.org>
                                Gerrit-Attention: Hongxiang Jiang <hxj...@golang.org>
                                Gerrit-Attention: Alan Donovan <adon...@google.com>
                                Gerrit-Comment-Date: Tue, 10 Feb 2026 16:25:52 +0000
                                Gerrit-HasComments: Yes
                                Gerrit-Has-Labels: Yes
                                Comment-In-Reply-To: Madeline Kalil <mka...@google.com>
                                Comment-In-Reply-To: Hongxiang Jiang <hxj...@golang.org>
                                unsatisfied_requirement
                                satisfied_requirement
                                open
                                diffy

                                Hongxiang Jiang (Gerrit)

                                unread,
                                Feb 10, 2026, 12:22:14 PMFeb 10
                                to Madeline Kalil, goph...@pubsubhelper.golang.org, Go LUCI, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                                Attention needed from Alan Donovan and Madeline Kalil

                                Hongxiang Jiang added 1 comment

                                File gopls/internal/golang/movetype.go
                                Hongxiang Jiang

                                I see. SG. Sorry I must misread your comment. Acked.

                                Just to make sure, the condition below

                                ```


                                if named, ok := field.Type().(*types.Named); ok && named.Obj().Pkg() == typNameObj.Pkg()

                                ```

                                is preventing the case below, but not the case I mentioned above right? Because the case above is more complicated which involves 3 packages and this one is only two.

                                ```
                                -- from.go --
                                package from

                                var _ Foo

                                type Bar string

                                type Foo struct {
                                bar Bar
                                }

                                -- to.go --
                                package to

                                ```

                                after move, package from depends on to and package to depends on from.

                                ```
                                -- from.go --
                                package from

                                import "to"

                                var _ to.Foo

                                type Bar string

                                -- to.go --
                                package to

                                import "from"

                                type Foo struct {
                                bar from.Bar
                                }
                                ```
                                Open in Gerrit

                                Related details

                                Attention is currently required from:
                                • Alan Donovan
                                • Madeline Kalil
                                Submit Requirements:
                                • requirement is not satisfiedCode-Review
                                • requirement is not satisfiedNo-Unresolved-Comments
                                • requirement is not satisfiedReview-Enforcement
                                • requirement satisfiedTryBots-Pass
                                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                                Gerrit-MessageType: comment
                                Gerrit-Project: tools
                                Gerrit-Branch: master
                                Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                                Gerrit-Change-Number: 723982
                                Gerrit-PatchSet: 14
                                Gerrit-Owner: Madeline Kalil <mka...@google.com>
                                Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                                Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                                Gerrit-CC: Hongxiang Jiang <hxj...@golang.org>
                                Gerrit-CC: Robert Findley <rfin...@golang.org>
                                Gerrit-Attention: Madeline Kalil <mka...@google.com>
                                Gerrit-Attention: Alan Donovan <adon...@google.com>
                                Gerrit-Comment-Date: Tue, 10 Feb 2026 17:22:07 +0000
                                Gerrit-HasComments: Yes
                                Gerrit-Has-Labels: No
                                unsatisfied_requirement
                                satisfied_requirement
                                open
                                diffy

                                Madeline Kalil (Gerrit)

                                unread,
                                Feb 10, 2026, 12:30:02 PMFeb 10
                                to goph...@pubsubhelper.golang.org, Go LUCI, Hongxiang Jiang, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                                Attention needed from Alan Donovan and Hongxiang Jiang

                                Madeline Kalil added 1 comment

                                File gopls/internal/golang/movetype.go
                                Madeline Kalil

                                Oh no sorry my bad, I didn't fully process your example. I was only considering the case of the cycle between two packages; thank you for pointing out the more complicated case. Let me think about how we can handle this one

                                Open in Gerrit

                                Related details

                                Attention is currently required from:
                                • Alan Donovan
                                • Hongxiang Jiang
                                Submit Requirements:
                                • requirement is not satisfiedCode-Review
                                • requirement is not satisfiedNo-Unresolved-Comments
                                • requirement is not satisfiedReview-Enforcement
                                • requirement satisfiedTryBots-Pass
                                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                                Gerrit-MessageType: comment
                                Gerrit-Project: tools
                                Gerrit-Branch: master
                                Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                                Gerrit-Change-Number: 723982
                                Gerrit-PatchSet: 14
                                Gerrit-Owner: Madeline Kalil <mka...@google.com>
                                Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                                Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                                Gerrit-CC: Hongxiang Jiang <hxj...@golang.org>
                                Gerrit-CC: Robert Findley <rfin...@golang.org>
                                Gerrit-Attention: Hongxiang Jiang <hxj...@golang.org>
                                Gerrit-Attention: Alan Donovan <adon...@google.com>
                                Gerrit-Comment-Date: Tue, 10 Feb 2026 17:29:57 +0000
                                unsatisfied_requirement
                                satisfied_requirement
                                open
                                diffy

                                Madeline Kalil (Gerrit)

                                unread,
                                Feb 10, 2026, 12:43:45 PMFeb 10
                                to goph...@pubsubhelper.golang.org, Hongxiang Jiang, Go LUCI, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                                File gopls/internal/golang/movetype.go
                                Madeline Kalil

                                Right now we always move the new type to a new file at {curDir}/newpkg/{typeName}.go, but we will need to handle this case when we implement moving a type to an existing package/file.

                                Open in Gerrit

                                Related details

                                Attention is currently required from:
                                • Alan Donovan
                                • Hongxiang Jiang
                                Submit Requirements:
                                • requirement is not satisfiedCode-Review
                                • requirement is not satisfiedNo-Unresolved-Comments
                                • requirement is not satisfiedReview-Enforcement
                                • requirement satisfiedTryBots-Pass
                                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                                Gerrit-MessageType: comment
                                Gerrit-Project: tools
                                Gerrit-Branch: master
                                Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                                Gerrit-Change-Number: 723982
                                Gerrit-PatchSet: 14
                                Gerrit-Owner: Madeline Kalil <mka...@google.com>
                                Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                                Gerrit-Reviewer: Hongxiang Jiang <hxj...@golang.org>
                                Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                                Gerrit-CC: Robert Findley <rfin...@golang.org>
                                Gerrit-Attention: Hongxiang Jiang <hxj...@golang.org>
                                Gerrit-Attention: Alan Donovan <adon...@google.com>
                                Gerrit-Comment-Date: Tue, 10 Feb 2026 17:43:42 +0000
                                unsatisfied_requirement
                                satisfied_requirement
                                open
                                diffy

                                Hongxiang Jiang (Gerrit)

                                unread,
                                Feb 11, 2026, 12:43:49 AMFeb 11
                                to Madeline Kalil, goph...@pubsubhelper.golang.org, Go LUCI, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                                Attention needed from Alan Donovan and Madeline Kalil

                                Hongxiang Jiang added 1 comment

                                File gopls/internal/golang/movetype.go
                                Hongxiang Jiang

                                Oh, I see. If we are only moving new package, the example I mentioned above does not exist at all.

                                Let's add a TODO, this is not a easy problem to solve we probably can't solve this in this CL. We can have a meeting with Alan and Matloob together.

                                Open in Gerrit

                                Related details

                                Attention is currently required from:
                                • Alan Donovan
                                • Madeline Kalil
                                Submit Requirements:
                                • requirement is not satisfiedCode-Review
                                • requirement is not satisfiedNo-Unresolved-Comments
                                • requirement is not satisfiedReview-Enforcement
                                • requirement satisfiedTryBots-Pass
                                Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                                Gerrit-MessageType: comment
                                Gerrit-Project: tools
                                Gerrit-Branch: master
                                Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                                Gerrit-Change-Number: 723982
                                Gerrit-PatchSet: 14
                                Gerrit-Owner: Madeline Kalil <mka...@google.com>
                                Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                                Gerrit-Reviewer: Hongxiang Jiang <hxj...@golang.org>
                                Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                                Gerrit-CC: Robert Findley <rfin...@golang.org>
                                Gerrit-Attention: Madeline Kalil <mka...@google.com>
                                Gerrit-Attention: Alan Donovan <adon...@google.com>
                                Gerrit-Comment-Date: Wed, 11 Feb 2026 05:43:42 +0000
                                unsatisfied_requirement
                                satisfied_requirement
                                open
                                diffy

                                Hongxiang Jiang (Gerrit)

                                unread,
                                9:43 AM (7 hours ago) 9:43 AM
                                to Madeline Kalil, goph...@pubsubhelper.golang.org, Go LUCI, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                                Attention needed from Alan Donovan and Madeline Kalil

                                Hongxiang Jiang added 13 comments

                                Patchset-level comments
                                File-level comment, Patchset 14 (Latest):
                                Hongxiang Jiang . resolved

                                Sorry for the delay, this is not a very big change but require quite some thinking to understand this.

                                Alan is very busy so I will try to review them based on the knowledge I have.

                                File gopls/internal/golang/movetype.go
                                Line 174, Patchset 14 (Latest):// fields are exported.
                                Hongxiang Jiang . unresolved

                                Update the doc comment:

                                -> Consider other types other than struct type. Field only exist in struct type.

                                The code below uses Preorder to figure out all the types.Object which is a very good solution. It covers not only struct type but also other kind of type and recursively as well.

                                like `type Foo func(string, net.Error)`

                                -> We detected not only the field name but also the field type.

                                Line 175, Patchset 14 (Latest):// There are many ways that we could loosen this condition with further checks, such as:
                                Hongxiang Jiang . unresolved

                                Allow moving unexpected type if the type is not referenced from the same package at all. (Maybe not very common though)

                                Line 178, Patchset 14 (Latest):// - Verifying potential import cycles.
                                Hongxiang Jiang . unresolved

                                Same here, `// -`

                                Line 179, Patchset 14 (Latest):// TODO(mkalil): Additional checks:
                                Hongxiang Jiang . unresolved

                                Try have a new line in between, if there is no new line, the content will be conncted together like `import cicles. TODO(mkalil)...`

                                ```
                                //
                                // TODO(...
                                ```

                                Line 180, Patchset 14 (Latest):// - All references from the moved declarations are either internal to it, or to
                                Hongxiang Jiang . unresolved

                                You can have some indentation to make the hover content format it like markdown.

                                ```
                                // - All ...
                                // imported ...
                                ```

                                Line 187, Patchset 14 (Latest): return fmt.Errorf("cannot move type %s: type is not exported", typSpec.Name.Name)
                                Hongxiang Jiang . unresolved

                                delete: covered in the called

                                Line 190, Patchset 14 (Latest): for n := range curTypeSpec.Preorder() {
                                Hongxiang Jiang . unresolved

                                the iterator yield cursor, so maybe `c`

                                Line 194, Patchset 14 (Latest): if _, isPkgName := obj.(*types.PkgName); !isPkgName && !obj.Exported() && obj.Pkg().Scope() != types.Universe {
                                Hongxiang Jiang . unresolved

                                minor suggestion

                                ```
                                if _, isPkgName := obj.(*types.PkgName); isPkgName {
                                continue
                                }
                                if !obj.Exported() && obj.Pkg().Scope() != types.Universe {
                                return fmt.Errorf("would result in reference to unexported object %s", obj.Name())
                                }

                                ...
                                ```

                                Line 197, Patchset 14 (Latest): if typesinternal.IsPackageLevel(obj) && obj.Pkg() == typNameObj.Pkg() {
                                Hongxiang Jiang . unresolved

                                I'm not very sure what this means, the case below is not allowed because the limitation.

                                ```
                                type Bar string

                                type Foo struct {
                                Bar Bar
                                }
                                ```

                                But the below is allowed?

                                ```
                                func _() {
                                type Bar string
                                  type Foo struct {
                                Bar Bar
                                }
                                }
                                ```
                                Line 210, Patchset 14 (Latest): return fmt.Errorf("would result in reference to unexported fields")
                                Hongxiang Jiang . unresolved

                                %s" field name?

                                Line 214, Patchset 13: if named, ok := field.Type().(*types.Named); ok && named.Obj().Pkg() == typNameObj.Pkg() {
                                Hongxiang Jiang . resolved
                                Hongxiang Jiang

                                Acknowledged

                                Line 215, Patchset 14 (Latest): if named, ok := field.Type().(*types.Named); ok && named.Obj().Pkg() == typNameObj.Pkg() {
                                Hongxiang Jiang . unresolved

                                I did not fully understand the circle dependency here. This check is saying, the struct type's fields should not have type defined in the same package.

                                I think use the ident detection is more appropriate, the code below will be caught by this check correct?

                                ```
                                type Bar string

                                type Foo struct {
                                Bar Bar
                                }
                                ```

                                and but? the type of the field is func, but the code here did not dig deeper into the func...

                                ```
                                type Bar string

                                type Foo struct {
                                Bar func(Bar) error
                                }
                                ```
                                Gerrit-Comment-Date: Fri, 27 Feb 2026 14:43:29 +0000
                                unsatisfied_requirement
                                satisfied_requirement
                                open
                                diffy

                                Hongxiang Jiang (Gerrit)

                                unread,
                                11:30 AM (5 hours ago) 11:30 AM
                                to Madeline Kalil, goph...@pubsubhelper.golang.org, Go LUCI, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                                Attention needed from Alan Donovan and Madeline Kalil

                                Hongxiang Jiang added 9 comments

                                File gopls/internal/golang/extracttofile.go
                                Line 156, Patchset 14 (Latest): if err != nil {
                                Hongxiang Jiang . unresolved

                                if err := ..; err != nil {

                                Line 188, Patchset 14 (Latest):func newFileWithImports(pkgName string, imports []*ast.ImportSpec, fromPGF *parsego.File, buf *bytes.Buffer) error {
                                Hongxiang Jiang . unresolved

                                if the function name is new file, then we should return a buffer instead of writing it to a buffer.

                                I assumed this function must be called first before the caller want to do anything, so let's return a buffer.

                                the doc comment also indicate it it returning a buffer.

                                File gopls/internal/golang/movetype.go
                                Line 55, Patchset 14 (Latest): changes, err := addTypeToPackage(ctx, snapshot, pgf, pkg, spec, decl, newDir)
                                Hongxiang Jiang . unresolved

                                Instead of passing the decl to the function, maybe we can pass only the comment to it to make this more clear. I was a little bit confused when I saw the decl is also passed into it.

                                ```
                                // move the decl's comment along with the type spec only if
                                // it is the only type declared.

                                var declComments []*ast.CommentGroup
                                if len(decl.Specs) == 1 {
                                  .... 
                                }
                                ```

                                so we can keep the comment handling logic in the same file, including the old file comment deletion and the new file comment addition.

                                Line 88, Patchset 14 (Latest):// It only produces edits for the new file.
                                Hongxiang Jiang . unresolved

                                the first sentence is clear enough.

                                Line 99, Patchset 14 (Latest): if err != nil {
                                Hongxiang Jiang . unresolved

                                if err := ...; err != nil {}

                                Line 100, Patchset 6: // Use CommentedNode to avoid losing comments inside struct types
                                Hongxiang Jiang . unresolved

                                Maybe it has been discussed, may I ask why not simply copy the entire content from the source file and move it here?

                                Or it is because using CommentedNode is more general solution for all kinds of cases? I think we used to prefer copy the content whenever possible.

                                Line 102, Patchset 6: // type A struct { // this comment gets lost.
                                Hongxiang Jiang . unresolved
                                ```

                                // type A struct { // this comment gets lost.
                                // B int
                                // }
                                ```

                                In sert a space, go doc will then intepret it as code.

                                Line 107, Patchset 6: // (See @multitypespecB/d/newpkg/b.go in movetype.txt)
                                Hongxiang Jiang . resolved

                                TIL. I thought this is not legal. 😂

                                File gopls/internal/server/command.go
                                Line 1904, Patchset 14 (Latest): }
                                Hongxiang Jiang . unresolved

                                TODO: open the file that this type is moving to.

                                See AddTest command as example, test example is available

                                gopls/internal/test/integration/misc/addtest_test.go

                                Gerrit-Comment-Date: Fri, 27 Feb 2026 16:29:56 +0000
                                Gerrit-HasComments: Yes
                                Gerrit-Has-Labels: No
                                unsatisfied_requirement
                                satisfied_requirement
                                open
                                diffy

                                Madeline Kalil (Gerrit)

                                unread,
                                12:54 PM (4 hours ago) 12:54 PM
                                to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
                                Attention needed from Alan Donovan and Madeline Kalil

                                Madeline Kalil uploaded new patchset

                                Madeline Kalil uploaded patch set #15 to this change.
                                Following approvals got outdated and were removed:
                                • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI

                                Related details

                                Attention is currently required from:
                                • Alan Donovan
                                • Madeline Kalil
                                Submit Requirements:
                                  • requirement is not satisfiedCode-Review
                                  • requirement is not satisfiedNo-Unresolved-Comments
                                  • requirement is not satisfiedReview-Enforcement
                                  • requirement is not satisfiedTryBots-Pass
                                  Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                                  Gerrit-MessageType: newpatchset
                                  Gerrit-Project: tools
                                  Gerrit-Branch: master
                                  Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                                  Gerrit-Change-Number: 723982
                                  Gerrit-PatchSet: 15
                                  unsatisfied_requirement
                                  open
                                  diffy

                                  Madeline Kalil (Gerrit)

                                  unread,
                                  12:54 PM (4 hours ago) 12:54 PM
                                  to goph...@pubsubhelper.golang.org, Hongxiang Jiang, Go LUCI, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                                  Attention needed from Alan Donovan and Hongxiang Jiang

                                  Madeline Kalil voted and added 11 comments

                                  Votes added by Madeline Kalil

                                  Commit-Queue+1

                                  11 comments

                                  File gopls/internal/golang/movetype.go
                                  Line 174, Patchset 14:// fields are exported.
                                  Hongxiang Jiang . resolved

                                  Update the doc comment:

                                  -> Consider other types other than struct type. Field only exist in struct type.

                                  The code below uses Preorder to figure out all the types.Object which is a very good solution. It covers not only struct type but also other kind of type and recursively as well.

                                  like `type Foo func(string, net.Error)`

                                  -> We detected not only the field name but also the field type.

                                  Madeline Kalil

                                  Done

                                  Line 175, Patchset 14:// There are many ways that we could loosen this condition with further checks, such as:
                                  Hongxiang Jiang . resolved

                                  Allow moving unexpected type if the type is not referenced from the same package at all. (Maybe not very common though)

                                  Madeline Kalil

                                  Done (modified second bullet)

                                  Line 178, Patchset 14:// - Verifying potential import cycles.
                                  Hongxiang Jiang . resolved

                                  Same here, `// -`

                                  Madeline Kalil

                                  Done

                                  Line 179, Patchset 14:// TODO(mkalil): Additional checks:
                                  Hongxiang Jiang . resolved

                                  Try have a new line in between, if there is no new line, the content will be conncted together like `import cicles. TODO(mkalil)...`

                                  ```
                                  //
                                  // TODO(...
                                  ```

                                  Madeline Kalil

                                  Looks much better now, thanks!

                                  Line 180, Patchset 14:// - All references from the moved declarations are either internal to it, or to
                                  Hongxiang Jiang . resolved

                                  You can have some indentation to make the hover content format it like markdown.

                                  ```
                                  // - All ...
                                  // imported ...
                                  ```

                                  Madeline Kalil

                                  Done

                                  Line 187, Patchset 14: return fmt.Errorf("cannot move type %s: type is not exported", typSpec.Name.Name)
                                  Hongxiang Jiang . resolved

                                  delete: covered in the called

                                  Madeline Kalil

                                  Done

                                  Line 190, Patchset 14: for n := range curTypeSpec.Preorder() {
                                  Hongxiang Jiang . resolved

                                  the iterator yield cursor, so maybe `c`

                                  Madeline Kalil

                                  Done

                                  Line 194, Patchset 14: if _, isPkgName := obj.(*types.PkgName); !isPkgName && !obj.Exported() && obj.Pkg().Scope() != types.Universe {
                                  Hongxiang Jiang . resolved

                                  minor suggestion

                                  ```
                                  if _, isPkgName := obj.(*types.PkgName); isPkgName {
                                  continue
                                  }
                                  if !obj.Exported() && obj.Pkg().Scope() != types.Universe {
                                  return fmt.Errorf("would result in reference to unexported object %s", obj.Name())
                                  }

                                  ...
                                  ```

                                  Madeline Kalil

                                  Done

                                  Line 197, Patchset 14: if typesinternal.IsPackageLevel(obj) && obj.Pkg() == typNameObj.Pkg() {
                                  Hongxiang Jiang . resolved

                                  I'm not very sure what this means, the case below is not allowed because the limitation.

                                  ```
                                  type Bar string

                                  type Foo struct {
                                  Bar Bar
                                  }
                                  ```

                                  But the below is allowed?

                                  ```
                                  func _() {
                                  type Bar string
                                    type Foo struct {
                                  Bar Bar
                                  }
                                  }
                                  ```
                                  Madeline Kalil

                                  Thanks for pointing this out; the second case should not be allowed. It is not sufficient to check IsExported because this just checks whether the type name is capitalized. We should check if the type is package level.

                                  Line 210, Patchset 14: return fmt.Errorf("would result in reference to unexported fields")
                                  Hongxiang Jiang . resolved

                                  %s" field name?

                                  Madeline Kalil

                                  Done

                                  Line 215, Patchset 14: if named, ok := field.Type().(*types.Named); ok && named.Obj().Pkg() == typNameObj.Pkg() {
                                  Hongxiang Jiang . unresolved

                                  I did not fully understand the circle dependency here. This check is saying, the struct type's fields should not have type defined in the same package.

                                  I think use the ident detection is more appropriate, the code below will be caught by this check correct?

                                  ```
                                  type Bar string

                                  type Foo struct {
                                  Bar Bar
                                  }
                                  ```

                                  and but? the type of the field is func, but the code here did not dig deeper into the func...

                                  ```
                                  type Bar string

                                  type Foo struct {
                                  Bar func(Bar) error
                                  }
                                  ```
                                  Madeline Kalil

                                  Yes, moving the type Foo would result in an import cycle: Foo's destination package needs to import the package with "Bar" in it, and the package with "Bar" in it needs to import Foo's destination package.
                                  We will inspect the Bar in func(Bar) when it inspects *ast.Idents in the Preorder traversal. Are you saying that you think we should remove the *ast.StructType case and just inspect Idents?

                                  Open in Gerrit

                                  Related details

                                  Attention is currently required from:
                                  • Alan Donovan
                                  • Hongxiang Jiang
                                  Submit Requirements:
                                  • requirement is not satisfiedCode-Review
                                  • requirement is not satisfiedNo-Unresolved-Comments
                                  • requirement is not satisfiedReview-Enforcement
                                  • requirement is not satisfiedTryBots-Pass
                                  Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                                  Gerrit-MessageType: comment
                                  Gerrit-Project: tools
                                  Gerrit-Branch: master
                                  Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                                  Gerrit-Change-Number: 723982
                                  Gerrit-PatchSet: 15
                                  Gerrit-Owner: Madeline Kalil <mka...@google.com>
                                  Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                                  Gerrit-Reviewer: Hongxiang Jiang <hxj...@golang.org>
                                  Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                                  Gerrit-CC: Robert Findley <rfin...@golang.org>
                                  Gerrit-Attention: Hongxiang Jiang <hxj...@golang.org>
                                  Gerrit-Attention: Alan Donovan <adon...@google.com>
                                  Gerrit-Comment-Date: Fri, 27 Feb 2026 17:54:17 +0000
                                  Gerrit-HasComments: Yes
                                  Gerrit-Has-Labels: Yes
                                  Comment-In-Reply-To: Hongxiang Jiang <hxj...@golang.org>
                                  unsatisfied_requirement
                                  open
                                  diffy

                                  Madeline Kalil (Gerrit)

                                  unread,
                                  2:32 PM (2 hours ago) 2:32 PM
                                  to goph...@pubsubhelper.golang.org, golang-co...@googlegroups.com
                                  Attention needed from Alan Donovan and Hongxiang Jiang

                                  Madeline Kalil uploaded new patchset

                                  Madeline Kalil uploaded patch set #16 to this change.
                                  Following approvals got outdated and were removed:
                                  • TryBots-Pass: LUCI-TryBot-Result+1 by Go LUCI
                                  Open in Gerrit

                                  Related details

                                  Attention is currently required from:
                                  • Alan Donovan
                                  • Hongxiang Jiang
                                  Submit Requirements:
                                  • requirement is not satisfiedCode-Review
                                  • requirement is not satisfiedNo-Unresolved-Comments
                                  • requirement is not satisfiedReview-Enforcement
                                  • requirement is not satisfiedTryBots-Pass
                                  Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                                  Gerrit-MessageType: newpatchset
                                  Gerrit-Project: tools
                                  Gerrit-Branch: master
                                  Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                                  Gerrit-Change-Number: 723982
                                  Gerrit-PatchSet: 16
                                  unsatisfied_requirement
                                  open
                                  diffy

                                  Madeline Kalil (Gerrit)

                                  unread,
                                  2:32 PM (2 hours ago) 2:32 PM
                                  to goph...@pubsubhelper.golang.org, Go LUCI, Hongxiang Jiang, Robert Findley, Alan Donovan, golang-co...@googlegroups.com
                                  Attention needed from Alan Donovan and Hongxiang Jiang

                                  Madeline Kalil voted and added 7 comments

                                  Votes added by Madeline Kalil

                                  Commit-Queue+1

                                  7 comments

                                  File gopls/internal/golang/extracttofile.go
                                  Line 156, Patchset 14: if err != nil {
                                  Hongxiang Jiang . resolved

                                  if err := ..; err != nil {

                                  Madeline Kalil

                                  Done

                                  Line 188, Patchset 14:func newFileWithImports(pkgName string, imports []*ast.ImportSpec, fromPGF *parsego.File, buf *bytes.Buffer) error {
                                  Hongxiang Jiang . unresolved

                                  if the function name is new file, then we should return a buffer instead of writing it to a buffer.

                                  I assumed this function must be called first before the caller want to do anything, so let's return a buffer.

                                  the doc comment also indicate it it returning a buffer.

                                  Madeline Kalil

                                  Thanks, I agree this was confusing. I've changed the function to writeNewFileWithImports because I think we should still write to the buffer in this case.

                                  File gopls/internal/golang/movetype.go
                                  Line 88, Patchset 14:// It only produces edits for the new file.
                                  Hongxiang Jiang . resolved

                                  the first sentence is clear enough.

                                  Madeline Kalil

                                  Done

                                  Line 99, Patchset 14: if err != nil {
                                  Hongxiang Jiang . resolved

                                  if err := ...; err != nil {}

                                  Madeline Kalil

                                  Done

                                  Line 100, Patchset 6: // Use CommentedNode to avoid losing comments inside struct types
                                  Hongxiang Jiang . unresolved

                                  Maybe it has been discussed, may I ask why not simply copy the entire content from the source file and move it here?

                                  Or it is because using CommentedNode is more general solution for all kinds of cases? I think we used to prefer copy the content whenever possible.

                                  Madeline Kalil
                                  Copying the content directly won't always work, for example if we have to move a type from within a type decl like:
                                  ```
                                  type (
                                  A int <- move type A
                                  B string
                                  )
                                  ```
                                  But I think if we are in the "normal" case of moving a single type not within a type decl, we could copy the content directly. I will implement this
                                  Line 102, Patchset 6: // type A struct { // this comment gets lost.
                                  Hongxiang Jiang . resolved
                                  ```
                                  // type A struct { // this comment gets lost.
                                  // B int
                                  // }
                                  ```

                                  In sert a space, go doc will then intepret it as code.

                                  Madeline Kalil

                                  Done

                                  File gopls/internal/server/command.go
                                  Hongxiang Jiang . unresolved

                                  TODO: open the file that this type is moving to.

                                  See AddTest command as example, test example is available

                                  gopls/internal/test/integration/misc/addtest_test.go

                                  Madeline Kalil

                                  Good idea, thanks

                                  Open in Gerrit

                                  Related details

                                  Attention is currently required from:
                                  • Alan Donovan
                                  • Hongxiang Jiang
                                  Submit Requirements:
                                  • requirement is not satisfiedCode-Review
                                  • requirement is not satisfiedNo-Unresolved-Comments
                                  • requirement is not satisfiedReview-Enforcement
                                  • requirement is not satisfiedTryBots-Pass
                                  Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
                                  Gerrit-MessageType: comment
                                  Gerrit-Project: tools
                                  Gerrit-Branch: master
                                  Gerrit-Change-Id: I063fcf6b15b03ae4c89297b73942e2c89b02c51b
                                  Gerrit-Change-Number: 723982
                                  Gerrit-PatchSet: 16
                                  Gerrit-Owner: Madeline Kalil <mka...@google.com>
                                  Gerrit-Reviewer: Alan Donovan <adon...@google.com>
                                  Gerrit-Reviewer: Hongxiang Jiang <hxj...@golang.org>
                                  Gerrit-Reviewer: Madeline Kalil <mka...@google.com>
                                  Gerrit-CC: Robert Findley <rfin...@golang.org>
                                  Gerrit-Attention: Hongxiang Jiang <hxj...@golang.org>
                                  Gerrit-Attention: Alan Donovan <adon...@google.com>
                                  Gerrit-Comment-Date: Fri, 27 Feb 2026 19:32:41 +0000
                                  unsatisfied_requirement
                                  open
                                  diffy
                                  Reply all
                                  Reply to author
                                  Forward
                                  0 new messages