| // Copyright 2013 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package astutil |
| |
| import ( |
| "bytes" |
| "go/ast" |
| "go/format" |
| "go/parser" |
| "go/token" |
| "reflect" |
| "strconv" |
| "testing" |
| ) |
| |
| var fset = token.NewFileSet() |
| |
| func parse(t *testing.T, name, in string) *ast.File { |
| file, err := parser.ParseFile(fset, name, in, parser.ParseComments) |
| if err != nil { |
| t.Fatalf("%s parse: %v", name, err) |
| } |
| return file |
| } |
| |
| func print(t *testing.T, name string, f *ast.File) string { |
| var buf bytes.Buffer |
| if err := format.Node(&buf, fset, f); err != nil { |
| t.Fatalf("%s gofmt: %v", name, err) |
| } |
| return buf.String() |
| } |
| |
| type test struct { |
| name string |
| renamedPkg string |
| pkg string |
| in string |
| out string |
| unchanged bool // Expect added/deleted return value to be false. |
| } |
| |
| var addTests = []test{ |
| { |
| name: "leave os alone", |
| pkg: "os", |
| in: `package main |
| |
| import ( |
| "os" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "os" |
| ) |
| `, |
| unchanged: true, |
| }, |
| { |
| name: "import.1", |
| pkg: "os", |
| in: `package main |
| `, |
| out: `package main |
| |
| import "os" |
| `, |
| }, |
| { |
| name: "import.2", |
| pkg: "os", |
| in: `package main |
| |
| // Comment |
| import "C" |
| `, |
| out: `package main |
| |
| // Comment |
| import "C" |
| import "os" |
| `, |
| }, |
| { |
| name: "import.3", |
| pkg: "os", |
| in: `package main |
| |
| // Comment |
| import "C" |
| |
| import ( |
| "io" |
| "utf8" |
| ) |
| `, |
| out: `package main |
| |
| // Comment |
| import "C" |
| |
| import ( |
| "io" |
| "os" |
| "utf8" |
| ) |
| `, |
| }, |
| { |
| name: "import.17", |
| pkg: "x/y/z", |
| in: `package main |
| |
| // Comment |
| import "C" |
| |
| import ( |
| "a" |
| "b" |
| |
| "x/w" |
| |
| "d/f" |
| ) |
| `, |
| out: `package main |
| |
| // Comment |
| import "C" |
| |
| import ( |
| "a" |
| "b" |
| |
| "x/w" |
| "x/y/z" |
| |
| "d/f" |
| ) |
| `, |
| }, |
| { |
| name: "issue #19190", |
| pkg: "x.org/y/z", |
| in: `package main |
| |
| // Comment |
| import "C" |
| |
| import ( |
| "bytes" |
| "os" |
| |
| "d.com/f" |
| ) |
| `, |
| out: `package main |
| |
| // Comment |
| import "C" |
| |
| import ( |
| "bytes" |
| "os" |
| |
| "d.com/f" |
| "x.org/y/z" |
| ) |
| `, |
| }, |
| { |
| name: "issue #19190 with existing grouped import packages", |
| pkg: "x.org/y/z", |
| in: `package main |
| |
| // Comment |
| import "C" |
| |
| import ( |
| "bytes" |
| "os" |
| |
| "c.com/f" |
| "d.com/f" |
| |
| "y.com/a" |
| "y.com/b" |
| "y.com/c" |
| ) |
| `, |
| out: `package main |
| |
| // Comment |
| import "C" |
| |
| import ( |
| "bytes" |
| "os" |
| |
| "c.com/f" |
| "d.com/f" |
| "x.org/y/z" |
| |
| "y.com/a" |
| "y.com/b" |
| "y.com/c" |
| ) |
| `, |
| }, |
| { |
| name: "issue #19190 - match score is still respected", |
| pkg: "y.org/c", |
| in: `package main |
| |
| import ( |
| "x.org/a" |
| |
| "y.org/b" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "x.org/a" |
| |
| "y.org/b" |
| "y.org/c" |
| ) |
| `, |
| }, |
| { |
| name: "import into singular group", |
| pkg: "bytes", |
| in: `package main |
| |
| import "os" |
| |
| `, |
| out: `package main |
| |
| import ( |
| "bytes" |
| "os" |
| ) |
| `, |
| }, |
| { |
| name: "import into singular group with comment", |
| pkg: "bytes", |
| in: `package main |
| |
| import /* why */ /* comment here? */ "os" |
| |
| `, |
| out: `package main |
| |
| import /* why */ /* comment here? */ ( |
| "bytes" |
| "os" |
| ) |
| `, |
| }, |
| { |
| name: "import into group with leading comment", |
| pkg: "strings", |
| in: `package main |
| |
| import ( |
| // comment before bytes |
| "bytes" |
| "os" |
| ) |
| |
| `, |
| out: `package main |
| |
| import ( |
| // comment before bytes |
| "bytes" |
| "os" |
| "strings" |
| ) |
| `, |
| }, |
| { |
| name: "", |
| renamedPkg: "fmtpkg", |
| pkg: "fmt", |
| in: `package main |
| |
| import "os" |
| |
| `, |
| out: `package main |
| |
| import ( |
| fmtpkg "fmt" |
| "os" |
| ) |
| `, |
| }, |
| { |
| name: "struct comment", |
| pkg: "time", |
| in: `package main |
| |
| // This is a comment before a struct. |
| type T struct { |
| t time.Time |
| } |
| `, |
| out: `package main |
| |
| import "time" |
| |
| // This is a comment before a struct. |
| type T struct { |
| t time.Time |
| } |
| `, |
| }, |
| { |
| name: "issue 8729 import C", |
| pkg: "time", |
| in: `package main |
| |
| import "C" |
| |
| // comment |
| type T time.Time |
| `, |
| out: `package main |
| |
| import "C" |
| import "time" |
| |
| // comment |
| type T time.Time |
| `, |
| }, |
| { |
| name: "issue 8729 empty import", |
| pkg: "time", |
| in: `package main |
| |
| import () |
| |
| // comment |
| type T time.Time |
| `, |
| out: `package main |
| |
| import "time" |
| |
| // comment |
| type T time.Time |
| `, |
| }, |
| { |
| name: "issue 8729 comment on package line", |
| pkg: "time", |
| in: `package main // comment |
| |
| type T time.Time |
| `, |
| out: `package main // comment |
| |
| import "time" |
| |
| type T time.Time |
| `, |
| }, |
| { |
| name: "issue 8729 comment after package", |
| pkg: "time", |
| in: `package main |
| // comment |
| |
| type T time.Time |
| `, |
| out: `package main |
| |
| import "time" |
| |
| // comment |
| |
| type T time.Time |
| `, |
| }, |
| { |
| name: "issue 8729 comment before and on package line", |
| pkg: "time", |
| in: `// comment before |
| package main // comment on |
| |
| type T time.Time |
| `, |
| out: `// comment before |
| package main // comment on |
| |
| import "time" |
| |
| type T time.Time |
| `, |
| }, |
| |
| // Issue 9961: Match prefixes using path segments rather than bytes |
| { |
| name: "issue 9961", |
| pkg: "regexp", |
| in: `package main |
| |
| import ( |
| "flag" |
| "testing" |
| |
| "rsc.io/p" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "flag" |
| "regexp" |
| "testing" |
| |
| "rsc.io/p" |
| ) |
| `, |
| }, |
| // Issue 10337: Preserve comment position |
| { |
| name: "issue 10337", |
| pkg: "fmt", |
| in: `package main |
| |
| import ( |
| "bytes" // a |
| "log" // c |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "bytes" // a |
| "fmt" |
| "log" // c |
| ) |
| `, |
| }, |
| { |
| name: "issue 10337 new import at the start", |
| pkg: "bytes", |
| in: `package main |
| |
| import ( |
| "fmt" // b |
| "log" // c |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "bytes" |
| "fmt" // b |
| "log" // c |
| ) |
| `, |
| }, |
| { |
| name: "issue 10337 new import at the end", |
| pkg: "log", |
| in: `package main |
| |
| import ( |
| "bytes" // a |
| "fmt" // b |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "bytes" // a |
| "fmt" // b |
| "log" |
| ) |
| `, |
| }, |
| // Issue 14075: Merge import declarations |
| { |
| name: "issue 14075", |
| pkg: "bufio", |
| in: `package main |
| |
| import "bytes" |
| import "fmt" |
| `, |
| out: `package main |
| |
| import ( |
| "bufio" |
| "bytes" |
| "fmt" |
| ) |
| `, |
| }, |
| { |
| name: "issue 14075 update position", |
| pkg: "bufio", |
| in: `package main |
| |
| import "bytes" |
| import ( |
| "fmt" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "bufio" |
| "bytes" |
| "fmt" |
| ) |
| `, |
| }, |
| { |
| name: `issue 14075 ignore import "C"`, |
| pkg: "bufio", |
| in: `package main |
| |
| // Comment |
| import "C" |
| |
| import "bytes" |
| import "fmt" |
| `, |
| out: `package main |
| |
| // Comment |
| import "C" |
| |
| import ( |
| "bufio" |
| "bytes" |
| "fmt" |
| ) |
| `, |
| }, |
| { |
| name: `issue 14075 ignore adjacent import "C"`, |
| pkg: "bufio", |
| in: `package main |
| |
| // Comment |
| import "C" |
| import "fmt" |
| `, |
| out: `package main |
| |
| // Comment |
| import "C" |
| import ( |
| "bufio" |
| "fmt" |
| ) |
| `, |
| }, |
| { |
| name: `issue 14075 ignore adjacent import "C" (without factored import)`, |
| pkg: "bufio", |
| in: `package main |
| |
| // Comment |
| import "C" |
| import "fmt" |
| `, |
| out: `package main |
| |
| // Comment |
| import "C" |
| import ( |
| "bufio" |
| "fmt" |
| ) |
| `, |
| }, |
| { |
| name: `issue 14075 ignore single import "C"`, |
| pkg: "bufio", |
| in: `package main |
| |
| // Comment |
| import "C" |
| `, |
| out: `package main |
| |
| // Comment |
| import "C" |
| import "bufio" |
| `, |
| }, |
| { |
| name: `issue 17212 several single-import lines with shared prefix ending in a slash`, |
| pkg: "net/http", |
| in: `package main |
| |
| import "bufio" |
| import "net/url" |
| `, |
| out: `package main |
| |
| import ( |
| "bufio" |
| "net/http" |
| "net/url" |
| ) |
| `, |
| }, |
| { |
| name: `issue 17212 block imports lines with shared prefix ending in a slash`, |
| pkg: "net/http", |
| in: `package main |
| |
| import ( |
| "bufio" |
| "net/url" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "bufio" |
| "net/http" |
| "net/url" |
| ) |
| `, |
| }, |
| { |
| name: `issue 17213 many single-import lines`, |
| pkg: "fmt", |
| in: `package main |
| |
| import "bufio" |
| import "bytes" |
| import "errors" |
| `, |
| out: `package main |
| |
| import ( |
| "bufio" |
| "bytes" |
| "errors" |
| "fmt" |
| ) |
| `, |
| }, |
| |
| // Issue 28605: Add specified import, even if that import path is imported under another name |
| { |
| name: "issue 28605 add unnamed path", |
| renamedPkg: "", |
| pkg: "path", |
| in: `package main |
| |
| import ( |
| . "path" |
| _ "path" |
| pathpkg "path" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "path" |
| . "path" |
| _ "path" |
| pathpkg "path" |
| ) |
| `, |
| }, |
| { |
| name: "issue 28605 add pathpkg-renamed path", |
| renamedPkg: "pathpkg", |
| pkg: "path", |
| in: `package main |
| |
| import ( |
| "path" |
| . "path" |
| _ "path" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "path" |
| . "path" |
| _ "path" |
| pathpkg "path" |
| ) |
| `, |
| }, |
| { |
| name: "issue 28605 add blank identifier path", |
| renamedPkg: "_", |
| pkg: "path", |
| in: `package main |
| |
| import ( |
| "path" |
| . "path" |
| pathpkg "path" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "path" |
| . "path" |
| _ "path" |
| pathpkg "path" |
| ) |
| `, |
| }, |
| { |
| name: "issue 28605 add dot import path", |
| renamedPkg: ".", |
| pkg: "path", |
| in: `package main |
| |
| import ( |
| "path" |
| _ "path" |
| pathpkg "path" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "path" |
| . "path" |
| _ "path" |
| pathpkg "path" |
| ) |
| `, |
| }, |
| |
| { |
| name: "duplicate import declarations, add existing one", |
| renamedPkg: "f", |
| pkg: "fmt", |
| in: `package main |
| |
| import "fmt" |
| import "fmt" |
| import f "fmt" |
| import f "fmt" |
| `, |
| out: `package main |
| |
| import "fmt" |
| import "fmt" |
| import f "fmt" |
| import f "fmt" |
| `, |
| unchanged: true, |
| }, |
| } |
| |
| func TestAddImport(t *testing.T) { |
| for _, test := range addTests { |
| file := parse(t, test.name, test.in) |
| var before bytes.Buffer |
| ast.Fprint(&before, fset, file, nil) |
| added := AddNamedImport(fset, file, test.renamedPkg, test.pkg) |
| if got := print(t, test.name, file); got != test.out { |
| t.Errorf("first run: %s:\ngot: %s\nwant: %s", test.name, got, test.out) |
| var after bytes.Buffer |
| ast.Fprint(&after, fset, file, nil) |
| t.Logf("AST before:\n%s\nAST after:\n%s\n", before.String(), after.String()) |
| } |
| if got, want := added, !test.unchanged; got != want { |
| t.Errorf("first run: %s: added = %v, want %v", test.name, got, want) |
| } |
| |
| // AddNamedImport should be idempotent. Verify that by calling it again, |
| // expecting no change to the AST, and the returned added value to always be false. |
| added = AddNamedImport(fset, file, test.renamedPkg, test.pkg) |
| if got := print(t, test.name, file); got != test.out { |
| t.Errorf("second run: %s:\ngot: %s\nwant: %s", test.name, got, test.out) |
| } |
| if got, want := added, false; got != want { |
| t.Errorf("second run: %s: added = %v, want %v", test.name, got, want) |
| } |
| } |
| } |
| |
| func TestDoubleAddImport(t *testing.T) { |
| file := parse(t, "doubleimport", "package main\n") |
| AddImport(fset, file, "os") |
| AddImport(fset, file, "bytes") |
| want := `package main |
| |
| import ( |
| "bytes" |
| "os" |
| ) |
| ` |
| if got := print(t, "doubleimport", file); got != want { |
| t.Errorf("got: %s\nwant: %s", got, want) |
| } |
| } |
| |
| func TestDoubleAddNamedImport(t *testing.T) { |
| file := parse(t, "doublenamedimport", "package main\n") |
| AddNamedImport(fset, file, "o", "os") |
| AddNamedImport(fset, file, "i", "io") |
| want := `package main |
| |
| import ( |
| i "io" |
| o "os" |
| ) |
| ` |
| if got := print(t, "doublenamedimport", file); got != want { |
| t.Errorf("got: %s\nwant: %s", got, want) |
| } |
| } |
| |
| // Part of issue 8729. |
| func TestDoubleAddImportWithDeclComment(t *testing.T) { |
| file := parse(t, "doubleimport", `package main |
| |
| import ( |
| ) |
| |
| // comment |
| type I int |
| `) |
| // The AddImport order here matters. |
| AddImport(fset, file, "golang.org/x/tools/go/ast/astutil") |
| AddImport(fset, file, "os") |
| want := `package main |
| |
| import ( |
| "golang.org/x/tools/go/ast/astutil" |
| "os" |
| ) |
| |
| // comment |
| type I int |
| ` |
| if got := print(t, "doubleimport_with_decl_comment", file); got != want { |
| t.Errorf("got: %s\nwant: %s", got, want) |
| } |
| } |
| |
| var deleteTests = []test{ |
| { |
| name: "import.4", |
| pkg: "os", |
| in: `package main |
| |
| import ( |
| "os" |
| ) |
| `, |
| out: `package main |
| `, |
| }, |
| { |
| name: "import.5", |
| pkg: "os", |
| in: `package main |
| |
| // Comment |
| import "C" |
| import "os" |
| `, |
| out: `package main |
| |
| // Comment |
| import "C" |
| `, |
| }, |
| { |
| name: "import.6", |
| pkg: "os", |
| in: `package main |
| |
| // Comment |
| import "C" |
| |
| import ( |
| "io" |
| "os" |
| "utf8" |
| ) |
| `, |
| out: `package main |
| |
| // Comment |
| import "C" |
| |
| import ( |
| "io" |
| "utf8" |
| ) |
| `, |
| }, |
| { |
| name: "import.7", |
| pkg: "io", |
| in: `package main |
| |
| import ( |
| "io" // a |
| "os" // b |
| "utf8" // c |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| // a |
| "os" // b |
| "utf8" // c |
| ) |
| `, |
| }, |
| { |
| name: "import.8", |
| pkg: "os", |
| in: `package main |
| |
| import ( |
| "io" // a |
| "os" // b |
| "utf8" // c |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "io" // a |
| // b |
| "utf8" // c |
| ) |
| `, |
| }, |
| { |
| name: "import.9", |
| pkg: "utf8", |
| in: `package main |
| |
| import ( |
| "io" // a |
| "os" // b |
| "utf8" // c |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "io" // a |
| "os" // b |
| // c |
| ) |
| `, |
| }, |
| { |
| name: "import.10", |
| pkg: "io", |
| in: `package main |
| |
| import ( |
| "io" |
| "os" |
| "utf8" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "os" |
| "utf8" |
| ) |
| `, |
| }, |
| { |
| name: "import.11", |
| pkg: "os", |
| in: `package main |
| |
| import ( |
| "io" |
| "os" |
| "utf8" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "io" |
| "utf8" |
| ) |
| `, |
| }, |
| { |
| name: "import.12", |
| pkg: "utf8", |
| in: `package main |
| |
| import ( |
| "io" |
| "os" |
| "utf8" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "io" |
| "os" |
| ) |
| `, |
| }, |
| { |
| name: "handle.raw.quote.imports", |
| pkg: "os", |
| in: "package main\n\nimport `os`", |
| out: `package main |
| `, |
| }, |
| { |
| name: "import.13", |
| pkg: "io", |
| in: `package main |
| |
| import ( |
| "fmt" |
| |
| "io" |
| "os" |
| "utf8" |
| |
| "go/format" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "fmt" |
| |
| "os" |
| "utf8" |
| |
| "go/format" |
| ) |
| `, |
| }, |
| { |
| name: "import.14", |
| pkg: "io", |
| in: `package main |
| |
| import ( |
| "fmt" // a |
| |
| "io" // b |
| "os" // c |
| "utf8" // d |
| |
| "go/format" // e |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "fmt" // a |
| |
| // b |
| "os" // c |
| "utf8" // d |
| |
| "go/format" // e |
| ) |
| `, |
| }, |
| { |
| name: "import.15", |
| pkg: "double", |
| in: `package main |
| |
| import ( |
| "double" |
| "double" |
| ) |
| `, |
| out: `package main |
| `, |
| }, |
| { |
| name: "import.16", |
| pkg: "bubble", |
| in: `package main |
| |
| import ( |
| "toil" |
| "bubble" |
| "bubble" |
| "trouble" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "toil" |
| "trouble" |
| ) |
| `, |
| }, |
| { |
| name: "import.17", |
| pkg: "quad", |
| in: `package main |
| |
| import ( |
| "quad" |
| "quad" |
| ) |
| |
| import ( |
| "quad" |
| "quad" |
| ) |
| `, |
| out: `package main |
| `, |
| }, |
| { |
| name: "import.18", |
| renamedPkg: "x", |
| pkg: "fmt", |
| in: `package main |
| |
| import ( |
| "fmt" |
| x "fmt" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "fmt" |
| ) |
| `, |
| }, |
| { |
| name: "import.18", |
| renamedPkg: "x", |
| pkg: "fmt", |
| in: `package main |
| |
| import x "fmt" |
| import y "fmt" |
| `, |
| out: `package main |
| |
| import y "fmt" |
| `, |
| }, |
| // Issue #15432, #18051 |
| { |
| name: "import.19", |
| pkg: "fmt", |
| in: `package main |
| |
| import ( |
| "fmt" |
| |
| // Some comment. |
| "io" |
| )`, |
| out: `package main |
| |
| import ( |
| // Some comment. |
| "io" |
| ) |
| `, |
| }, |
| { |
| name: "import.20", |
| pkg: "fmt", |
| in: `package main |
| |
| import ( |
| "fmt" |
| |
| // Some |
| // comment. |
| "io" |
| )`, |
| out: `package main |
| |
| import ( |
| // Some |
| // comment. |
| "io" |
| ) |
| `, |
| }, |
| { |
| name: "import.21", |
| pkg: "fmt", |
| in: `package main |
| |
| import ( |
| "fmt" |
| |
| /* |
| Some |
| comment. |
| */ |
| "io" |
| )`, |
| out: `package main |
| |
| import ( |
| /* |
| Some |
| comment. |
| */ |
| "io" |
| ) |
| `, |
| }, |
| { |
| name: "import.22", |
| pkg: "fmt", |
| in: `package main |
| |
| import ( |
| /* Some */ |
| // comment. |
| "io" |
| "fmt" |
| )`, |
| out: `package main |
| |
| import ( |
| /* Some */ |
| // comment. |
| "io" |
| ) |
| `, |
| }, |
| { |
| name: "import.23", |
| pkg: "fmt", |
| in: `package main |
| |
| import ( |
| // comment 1 |
| "fmt" |
| // comment 2 |
| "io" |
| )`, |
| out: `package main |
| |
| import ( |
| // comment 2 |
| "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" |
| ) |
| `, |
| }, |
| { |
| name: "import.35", |
| pkg: "fmt", |
| in: `package main |
| |
| // comment 1 |
| import ( |
| "fmt" |
| // comment 2 |
| "io" |
| )`, |
| out: `package main |
| |
| // comment 1 |
| import ( |
| // comment 2 |
| "io" |
| ) |
| `, |
| }, |
| { |
| name: "import.36", |
| pkg: "fmt", |
| in: `package main |
| |
| /* comment 1 */ |
| import ( |
| "fmt" |
| /* comment 2 */ |
| "io" |
| )`, |
| out: `package main |
| |
| /* comment 1 */ |
| import ( |
| /* comment 2 */ |
| "io" |
| ) |
| `, |
| }, |
| |
| // Issue 20229: MergeLine panic on weird input |
| { |
| name: "import.37", |
| pkg: "io", |
| in: `package main |
| import("_" |
| "io")`, |
| out: `package main |
| |
| import ( |
| "_" |
| ) |
| `, |
| }, |
| |
| // Issue 28605: Delete specified import, even if that import path is imported under another name |
| { |
| name: "import.38", |
| renamedPkg: "", |
| pkg: "path", |
| in: `package main |
| |
| import ( |
| "path" |
| . "path" |
| _ "path" |
| pathpkg "path" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| . "path" |
| _ "path" |
| pathpkg "path" |
| ) |
| `, |
| }, |
| { |
| name: "import.39", |
| renamedPkg: "pathpkg", |
| pkg: "path", |
| in: `package main |
| |
| import ( |
| "path" |
| . "path" |
| _ "path" |
| pathpkg "path" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "path" |
| . "path" |
| _ "path" |
| ) |
| `, |
| }, |
| { |
| name: "import.40", |
| renamedPkg: "_", |
| pkg: "path", |
| in: `package main |
| |
| import ( |
| "path" |
| . "path" |
| _ "path" |
| pathpkg "path" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "path" |
| . "path" |
| pathpkg "path" |
| ) |
| `, |
| }, |
| { |
| name: "import.41", |
| renamedPkg: ".", |
| pkg: "path", |
| in: `package main |
| |
| import ( |
| "path" |
| . "path" |
| _ "path" |
| pathpkg "path" |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "path" |
| _ "path" |
| pathpkg "path" |
| ) |
| `, |
| }, |
| |
| // Duplicate import declarations, all matching ones are deleted. |
| { |
| name: "import.42", |
| renamedPkg: "f", |
| pkg: "fmt", |
| in: `package main |
| |
| import "fmt" |
| import "fmt" |
| import f "fmt" |
| import f "fmt" |
| `, |
| out: `package main |
| |
| import "fmt" |
| import "fmt" |
| `, |
| }, |
| { |
| name: "import.43", |
| renamedPkg: "x", |
| pkg: "fmt", |
| in: `package main |
| |
| import "fmt" |
| import "fmt" |
| import f "fmt" |
| import f "fmt" |
| `, |
| out: `package main |
| |
| import "fmt" |
| import "fmt" |
| import f "fmt" |
| import f "fmt" |
| `, |
| unchanged: true, |
| }, |
| // this test panics without PositionFor in DeleteNamedImport |
| { |
| name: "import.44", |
| pkg: "foo.com/other/v3", |
| renamedPkg: "", |
| in: `package main |
| //line mah.go:600 |
| |
| import ( |
| "foo.com/a.thing" |
| "foo.com/surprise" |
| "foo.com/v1" |
| "foo.com/other/v2" |
| "foo.com/other/v3" |
| ) |
| `, |
| out: `package main |
| |
| //line mah.go:600 |
| |
| import ( |
| "foo.com/a.thing" |
| "foo.com/other/v2" |
| "foo.com/surprise" |
| "foo.com/v1" |
| ) |
| `, |
| }, |
| } |
| |
| func TestDeleteImport(t *testing.T) { |
| for _, test := range deleteTests { |
| file := parse(t, test.name, test.in) |
| var before bytes.Buffer |
| ast.Fprint(&before, fset, file, nil) |
| deleted := DeleteNamedImport(fset, file, test.renamedPkg, test.pkg) |
| if got := print(t, test.name, file); got != test.out { |
| t.Errorf("first run: %s:\ngot: %s\nwant: %s", test.name, got, test.out) |
| var after bytes.Buffer |
| ast.Fprint(&after, fset, file, nil) |
| t.Logf("AST before:\n%s\nAST after:\n%s\n", before.String(), after.String()) |
| } |
| if got, want := deleted, !test.unchanged; got != want { |
| t.Errorf("first run: %s: deleted = %v, want %v", test.name, got, want) |
| } |
| |
| // DeleteNamedImport should be idempotent. Verify that by calling it again, |
| // expecting no change to the AST, and the returned deleted value to always be false. |
| deleted = DeleteNamedImport(fset, file, test.renamedPkg, test.pkg) |
| if got := print(t, test.name, file); got != test.out { |
| t.Errorf("second run: %s:\ngot: %s\nwant: %s", test.name, got, test.out) |
| } |
| if got, want := deleted, false; got != want { |
| t.Errorf("second run: %s: deleted = %v, want %v", test.name, got, want) |
| } |
| } |
| } |
| |
| func TestDeleteImportAfterAddImport(t *testing.T) { |
| file := parse(t, "test", `package main |
| |
| import "os" |
| `) |
| if got, want := AddImport(fset, file, "fmt"), true; got != want { |
| t.Errorf("AddImport: got: %v, want: %v", got, want) |
| } |
| if got, want := DeleteImport(fset, file, "fmt"), true; got != want { |
| t.Errorf("DeleteImport: got: %v, want: %v", got, want) |
| } |
| } |
| |
| type rewriteTest struct { |
| name string |
| srcPkg string |
| dstPkg string |
| in string |
| out string |
| } |
| |
| var rewriteTests = []rewriteTest{ |
| { |
| name: "import.13", |
| srcPkg: "utf8", |
| dstPkg: "encoding/utf8", |
| in: `package main |
| |
| import ( |
| "io" |
| "os" |
| "utf8" // thanks ken |
| ) |
| `, |
| out: `package main |
| |
| import ( |
| "encoding/utf8" // thanks ken |
| "io" |
| "os" |
| ) |
| `, |
| }, |
| { |
| name: "import.14", |
| srcPkg: "asn1", |
| dstPkg: "encoding/asn1", |
| in: `package main |
| |
| import ( |
| "asn1" |
| "crypto" |
| "crypto/rsa" |
| _ "crypto/sha1" |
| "crypto/x509" |
| "crypto/x509/pkix" |
| "time" |
| ) |
| |
| var x = 1 |
| `, |
| out: `package main |
| |
| import ( |
| "crypto" |
| "crypto/rsa" |
| _ "crypto/sha1" |
| "crypto/x509" |
| "crypto/x509/pkix" |
| "encoding/asn1" |
| "time" |
| ) |
| |
| var x = 1 |
| `, |
| }, |
| { |
| name: "import.15", |
| srcPkg: "url", |
| dstPkg: "net/url", |
| in: `package main |
| |
| import ( |
| "bufio" |
| "net" |
| "path" |
| "url" |
| ) |
| |
| var x = 1 // comment on x, not on url |
| `, |
| out: `package main |
| |
| import ( |
| "bufio" |
| "net" |
| "net/url" |
| "path" |
| ) |
| |
| var x = 1 // comment on x, not on url |
| `, |
| }, |
| { |
| name: "import.16", |
| srcPkg: "http", |
| dstPkg: "net/http", |
| in: `package main |
| |
| import ( |
| "flag" |
| "http" |
| "log" |
| "text/template" |
| ) |
| |
| var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18 |
| `, |
| out: `package main |
| |
| import ( |
| "flag" |
| "log" |
| "net/http" |
| "text/template" |
| ) |
| |
| var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18 |
| `, |
| }, |
| } |
| |
| func TestRewriteImport(t *testing.T) { |
| for _, test := range rewriteTests { |
| file := parse(t, test.name, test.in) |
| RewriteImport(fset, file, test.srcPkg, test.dstPkg) |
| if got := print(t, test.name, file); got != test.out { |
| t.Errorf("%s:\ngot: %s\nwant: %s", test.name, got, test.out) |
| } |
| } |
| } |
| |
| var importsTests = []struct { |
| name string |
| in string |
| want [][]string |
| }{ |
| { |
| name: "no packages", |
| in: `package foo |
| `, |
| want: nil, |
| }, |
| { |
| name: "one group", |
| in: `package foo |
| |
| import ( |
| "fmt" |
| "testing" |
| ) |
| `, |
| want: [][]string{{"fmt", "testing"}}, |
| }, |
| { |
| name: "four groups", |
| in: `package foo |
| |
| import "C" |
| import ( |
| "fmt" |
| "testing" |
| |
| "appengine" |
| |
| "myproject/mylib1" |
| "myproject/mylib2" |
| ) |
| `, |
| want: [][]string{ |
| {"C"}, |
| {"fmt", "testing"}, |
| {"appengine"}, |
| {"myproject/mylib1", "myproject/mylib2"}, |
| }, |
| }, |
| { |
| name: "multiple factored groups", |
| in: `package foo |
| |
| import ( |
| "fmt" |
| "testing" |
| |
| "appengine" |
| ) |
| import ( |
| "reflect" |
| |
| "bytes" |
| ) |
| `, |
| want: [][]string{ |
| {"fmt", "testing"}, |
| {"appengine"}, |
| {"reflect"}, |
| {"bytes"}, |
| }, |
| }, |
| } |
| |
| func unquote(s string) string { |
| res, err := strconv.Unquote(s) |
| if err != nil { |
| return "could_not_unquote" |
| } |
| return res |
| } |
| |
| func TestImports(t *testing.T) { |
| fset := token.NewFileSet() |
| for _, test := range importsTests { |
| f, err := parser.ParseFile(fset, "test.go", test.in, 0) |
| if err != nil { |
| t.Errorf("%s: %v", test.name, err) |
| continue |
| } |
| var got [][]string |
| for _, group := range Imports(fset, f) { |
| var b []string |
| for _, spec := range group { |
| b = append(b, unquote(spec.Path.Value)) |
| } |
| got = append(got, b) |
| } |
| if !reflect.DeepEqual(got, test.want) { |
| t.Errorf("Imports(%s)=%v, want %v", test.name, got, test.want) |
| } |
| } |
| } |
| |
| var usesImportTests = []struct { |
| name string |
| path string |
| in string |
| want bool |
| }{ |
| { |
| name: "no packages", |
| path: "io", |
| in: `package foo |
| `, |
| want: false, |
| }, |
| { |
| name: "import.1", |
| path: "io", |
| in: `package foo |
| |
| import "io" |
| |
| var _ io.Writer |
| `, |
| want: true, |
| }, |
| { |
| name: "import.2", |
| path: "io", |
| in: `package foo |
| |
| import "io" |
| `, |
| want: false, |
| }, |
| { |
| name: "import.3", |
| path: "io", |
| in: `package foo |
| |
| import "io" |
| |
| var io = 42 |
| `, |
| want: false, |
| }, |
| { |
| name: "import.4", |
| path: "io", |
| in: `package foo |
| |
| import i "io" |
| |
| var _ i.Writer |
| `, |
| want: true, |
| }, |
| { |
| name: "import.5", |
| path: "io", |
| in: `package foo |
| |
| import i "io" |
| `, |
| want: false, |
| }, |
| { |
| name: "import.6", |
| path: "io", |
| in: `package foo |
| |
| import i "io" |
| |
| var i = 42 |
| var io = 42 |
| `, |
| want: false, |
| }, |
| { |
| name: "import.7", |
| path: "encoding/json", |
| in: `package foo |
| |
| import "encoding/json" |
| |
| var _ json.Encoder |
| `, |
| want: true, |
| }, |
| { |
| name: "import.8", |
| path: "encoding/json", |
| in: `package foo |
| |
| import "encoding/json" |
| `, |
| want: false, |
| }, |
| { |
| name: "import.9", |
| path: "encoding/json", |
| in: `package foo |
| |
| import "encoding/json" |
| |
| var json = 42 |
| `, |
| want: false, |
| }, |
| { |
| name: "import.10", |
| path: "encoding/json", |
| in: `package foo |
| |
| import j "encoding/json" |
| |
| var _ j.Encoder |
| `, |
| want: true, |
| }, |
| { |
| name: "import.11", |
| path: "encoding/json", |
| in: `package foo |
| |
| import j "encoding/json" |
| `, |
| want: false, |
| }, |
| { |
| name: "import.12", |
| path: "encoding/json", |
| in: `package foo |
| |
| import j "encoding/json" |
| |
| var j = 42 |
| var json = 42 |
| `, |
| want: false, |
| }, |
| { |
| name: "import.13", |
| path: "io", |
| in: `package foo |
| |
| import _ "io" |
| `, |
| want: true, |
| }, |
| { |
| name: "import.14", |
| path: "io", |
| in: `package foo |
| |
| import . "io" |
| `, |
| want: true, |
| }, |
| } |
| |
| func TestUsesImport(t *testing.T) { |
| fset := token.NewFileSet() |
| for _, test := range usesImportTests { |
| f, err := parser.ParseFile(fset, "test.go", test.in, 0) |
| if err != nil { |
| t.Errorf("%s: %v", test.name, err) |
| continue |
| } |
| got := UsesImport(f, test.path) |
| if got != test.want { |
| t.Errorf("UsesImport(%s)=%v, want %v", test.name, got, test.want) |
| } |
| } |
| } |