diff --git a/go/ast/astutil/imports.go b/go/ast/astutil/imports.go index 070ff35d..7d793ffa 100644 --- a/go/ast/astutil/imports.go +++ b/go/ast/astutil/imports.go @@ -180,6 +180,7 @@ func DeleteImport(fset *token.FileSet, f *ast.File, path string) (deleted bool) // DeleteNamedImport deletes the import with the given name and path from the file f, if present. func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (deleted bool) { var delspecs []*ast.ImportSpec + var delcomments []*ast.CommentGroup // Find the import nodes that import path, if any. for i := 0; i < len(f.Decls); i++ { @@ -216,7 +217,35 @@ func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (del i-- break } else if len(gen.Specs) == 1 { + if impspec.Doc != nil { + delcomments = append(delcomments, impspec.Doc) + } + if impspec.Comment != nil { + delcomments = append(delcomments, impspec.Comment) + } + for _, cg := range f.Comments { + // Found comment on the same line as the import spec. + if cg.End() < impspec.Pos() && fset.Position(cg.End()).Line == fset.Position(impspec.Pos()).Line { + delcomments = append(delcomments, cg) + break + } + } + gen.Lparen = token.NoPos // drop parens + spec := gen.Specs[0].(*ast.ImportSpec) + if spec.Doc != nil { + // Move the documentation above the import statement. + gen.TokPos = spec.Doc.End() + 1 + } + + for _, cg := range f.Comments { + if cg.End() < spec.Pos() && fset.Position(cg.End()).Line == fset.Position(spec.Pos()).Line { + for fset.Position(gen.TokPos).Line != fset.Position(spec.Pos()).Line { + fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line) + } + break + } + } } if j > 0 { lastImpspec := gen.Specs[j-1].(*ast.ImportSpec) @@ -238,7 +267,7 @@ func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (del } } - // Delete them from f.Imports. + // Delete imports from f.Imports. for i := 0; i < len(f.Imports); i++ { imp := f.Imports[i] for j, del := range delspecs { @@ -253,6 +282,21 @@ func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (del } } + // Delete comments from f.Comments. + for i := 0; i < len(f.Comments); i++ { + cg := f.Comments[i] + for j, del := range delcomments { + if cg == del { + copy(f.Comments[i:], f.Comments[i+1:]) + f.Comments = f.Comments[:len(f.Comments)-1] + copy(delcomments[j:], delcomments[j+1:]) + delcomments = delcomments[:len(delcomments)-1] + i-- + break + } + } + } + if len(delspecs) > 0 { panic(fmt.Sprintf("deleted specs from Decls but not Imports: %v", delspecs)) } diff --git a/go/ast/astutil/imports_test.go b/go/ast/astutil/imports_test.go index eb00e47b..c9f80df7 100644 --- a/go/ast/astutil/imports_test.go +++ b/go/ast/astutil/imports_test.go @@ -923,6 +923,270 @@ import y "fmt" out: `package main import y "fmt" +`, + }, + // Issue #15432 + { + name: "import.19", + pkg: "fmt", + in: `package main + +import ( + "fmt" + + // Some comment. + "io" +)`, + out: `package main + +// Some comment. +import "io" +`, + }, + { + name: "import.20", + pkg: "fmt", + in: `package main + +import ( + "fmt" + + // Some + // comment. + "io" +)`, + out: `package main + +// Some +// comment. +import "io" +`, + }, + { + name: "import.21", + pkg: "fmt", + in: `package main + +import ( + "fmt" + + /* + Some + comment. + */ + "io" +)`, + out: `package main + +/* + Some + comment. +*/ +import "io" +`, + }, + { + name: "import.22", + pkg: "fmt", + in: `package main + +import ( + /* Some */ + // comment. + "io" + "fmt" +)`, + out: `package main + +/* Some */ +// comment. +import "io" +`, + }, + { + name: "import.23", + pkg: "fmt", + in: `package main + +import ( + // comment 1 + "fmt" + // comment 2 + "io" +)`, + out: `package main + +// comment 2 +import "io" +`, + }, + { + name: "import.24", + pkg: "fmt", + in: `package main + +import ( + "fmt" // comment 1 + "io" // comment 2 +)`, + out: `package main + +import "io" // comment 2 +`, + }, + { + name: "import.25", + pkg: "fmt", + in: `package main + +import ( + "fmt" + /* comment */ "io" +)`, + out: `package main + +import /* comment */ "io" +`, + }, + { + name: "import.26", + pkg: "fmt", + in: `package main + +import ( + "fmt" + "io" /* comment */ +)`, + out: `package main + +import "io" /* comment */ +`, + }, + { + name: "import.27", + pkg: "fmt", + in: `package main + +import ( + "fmt" /* comment */ + "io" +)`, + out: `package main + +import "io" +`, + }, + { + name: "import.28", + pkg: "fmt", + in: `package main + +import ( + /* comment */ "fmt" + "io" +)`, + out: `package main + +import "io" +`, + }, + { + name: "import.29", + pkg: "fmt", + in: `package main + +// comment 1 +import ( + "fmt" + "io" // comment 2 +)`, + out: `package main + +// comment 1 +import "io" // comment 2 +`, + }, + { + name: "import.30", + pkg: "fmt", + in: `package main + +// comment 1 +import ( + "fmt" // comment 2 + "io" +)`, + out: `package main + +// comment 1 +import "io" +`, + }, + { + name: "import.31", + pkg: "fmt", + in: `package main + +// comment 1 +import ( + "fmt" + /* comment 2 */ "io" +)`, + out: `package main + +// comment 1 +import /* comment 2 */ "io" +`, + }, + { + name: "import.32", + pkg: "fmt", + renamedPkg: "f", + in: `package main + +// comment 1 +import ( + f "fmt" + /* comment 2 */ i "io" +)`, + out: `package main + +// comment 1 +import /* comment 2 */ i "io" +`, + }, + { + name: "import.33", + pkg: "fmt", + renamedPkg: "f", + in: `package main + +// comment 1 +import ( + /* comment 2 */ f "fmt" + i "io" +)`, + out: `package main + +// comment 1 +import i "io" +`, + }, + { + name: "import.34", + pkg: "fmt", + renamedPkg: "f", + in: `package main + +// comment 1 +import ( + f "fmt" /* comment 2 */ + i "io" +)`, + out: `package main + +// comment 1 +import i "io" `, }, }