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 <rsc@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
David R. Jenni 2016-04-25 13:36:54 +02:00 committed by Brad Fitzpatrick
parent 5e567c6dff
commit f4e1751b91
2 changed files with 309 additions and 1 deletions

View File

@ -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. // 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) { func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (deleted bool) {
var delspecs []*ast.ImportSpec var delspecs []*ast.ImportSpec
var delcomments []*ast.CommentGroup
// Find the import nodes that import path, if any. // Find the import nodes that import path, if any.
for i := 0; i < len(f.Decls); i++ { 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-- i--
break break
} else if len(gen.Specs) == 1 { } 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 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 { if j > 0 {
lastImpspec := gen.Specs[j-1].(*ast.ImportSpec) 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++ { for i := 0; i < len(f.Imports); i++ {
imp := f.Imports[i] imp := f.Imports[i]
for j, del := range delspecs { 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 { if len(delspecs) > 0 {
panic(fmt.Sprintf("deleted specs from Decls but not Imports: %v", delspecs)) panic(fmt.Sprintf("deleted specs from Decls but not Imports: %v", delspecs))
} }

View File

@ -923,6 +923,270 @@ import y "fmt"
out: `package main out: `package main
import y "fmt" 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"
`, `,
}, },
} }