Elliot Chance
Elliot Chance

Reputation: 5736

Remove unused imports with ast package?

After parsing a file and removing some functions, is it possible to also remove any now-unused imports before writing the new file?

Upvotes: 1

Views: 1163

Answers (1)

jpap
jpap

Reputation: 964

I recently had a need for something similar when writing a code generator tool. Here's an outline of my solution:

  • golang.org/x/tools/imports exports a func Process that will take a Go source file (as []byte), and automatically "fixes" imports: remove unused imports and add unlisted imports that are referenced in the source file. The goimports command is based on this.
  • After editing my AST (e.g. removing some functions), I first print the AST to a Go source file string with go/format. Note that this does not write to a disk file; it produces the string form of the AST in memory.
  • Next I use imports.Process to "fix" the imports of the file. It is important to pass the true file path of the Go source file to Process, even if it does not exist on disk yet. The result of this call is a "fixed" Go source file as a []byte "string"; i.e. any unused imports are removed.
  • Now I perform a "diff" of the imports from the original AST and this "fixed" source file. To perform the diff, it is easier to do so by comparing ASTs, so I first use go/parser on the "fixed" source file using parser.ParseFile. I use the true file path of the Go source file, and an empty token.NewFileSet() when calling ParseFile. Again, the Go source file does not have to exist on disk.
  • The AST diff simply compares the []*ast.ImportSpec slice (field ast.File.Imports) between each of the two file-root AST nodes. (My implementation uses a map to index each []*ast.ImportSpec first, making it easier to cross-check without a double-nested loop.)
  • Finally, now that I have a list of *ast.ImportSpec that have been removed (unused), I use golang.org/x/tools/go/ast/astutil to rewrite the original AST, using the cursor to remove these known *ast.ImportSpec nodes when visited.

An aside: the imports package cited above has an "internal" package that will provide a FixImports function that essentially provides the raw "diff" derived manually above. Unfortunately we can't use it because it is marked as internal.

Upvotes: 3

Related Questions