Skip to content

x/tools/gopls: new CodeAction: "Replace := with var declaration" #74838

@skewb1k

Description

@skewb1k

Description

Proposal to add a code action that refactors short variable declarations (:=) into explicit var declarations with separate assignment.

Example:

// Before
f := os.DirFS("/")

// After
var f fs.FS
f = os.DirFS("/")

Motivation

In some cases, a variable cannot be initialized immediately because its value depends on branching logic. For example:

var str string
switch fix.FixType {
case imports.AddImport:
	str = fmt.Sprintf("Add import: %s %q", fix.StmtInfo.Name, fix.StmtInfo.ImportPath)
case imports.DeleteImport:
	str = fmt.Sprintf("Delete import: %s %q", fix.StmtInfo.Name, fix.StmtInfo.ImportPath)
case imports.SetImportName:
	str = fmt.Sprintf("Rename import: %s %q", fix.StmtInfo.Name, fix.StmtInfo.ImportPath)
}

If the variable was initially declared using a short declaration, its type is inferred and not visible in the source. Refactoring such a variable to a var declaration requires hovering over it or navigating to its definition to determine the type. A code action that performs this conversion automatically would streamline this process.

Concerns and Open Questions

There are a few edge cases where behavior is less clear or might need refinement:

  1. Multiple assignments
// Extract each individually
//
// var n int
// var err error
// n, err = fmt.Fprintln(os.Stdout)
n, err := fmt.Fprintln(os.Stdout)
  1. Shadowed variables
n1, err := fmt.Fprintln(os.Stdout)

// Keep shadowing
//
// var n2 int
// n2, err = fmt.Fprintln(os.Stdout)
n2, err := fmt.Fprintln(os.Stdout)
  1. Missing imports
// Should add import for fs.FS
//
// var f fs.FS
// f = os.DirFS("/")
f := os.DirFS("/")
  1. Unexported types

In some cases, the inferred type may be an unexported type from another package. Since such types cannot be named outside their defining package, there's no valid 'var' declaration that can reference them. In these situations, the code action should be suppressed, as there is no meaningful or compilable transformation.

I'd be happy to receive feedback, discuss expected behavior in more detail, and work on the implementation if this feature is considered useful and aligned with gopls's direction.

Metadata

Metadata

Assignees

No one assigned

    Labels

    FeatureRequestIssues asking for a new feature that does not need a proposal.ToolsThis label describes issues relating to any tools in the x/tools repository.goplsIssues related to the Go language server, gopls.help wanted

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions