From f4e1751b91d59c081cd28d574a6b2f646189c0c6 Mon Sep 17 00:00:00 2001 From: "David R. Jenni" Date: Mon, 25 Apr 2016 13:36:54 +0200 Subject: [PATCH] go/ast/astutil: handle documentation of imports in DeleteNamedImport. If the parens of an ast.GenDecl are dropped, move the documentation of the ImportSpec above the import statement, otherwise the the code is invalid. Fixes golang/go#15432. Change-Id: I715750b8f528380b96a6bc8b5f216043937976c2 Reviewed-on: https://go-review.googlesource.com/22415 Run-TryBot: Russ Cox Reviewed-by: Brad Fitzpatrick --- go/ast/astutil/imports.go | 46 +++++- go/ast/astutil/imports_test.go | 264 +++++++++++++++++++++++++++++++++ 2 files changed, 309 insertions(+), 1 deletion(-) 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" `, }, }