2013-12-17 19:21:03 -07:00
|
|
|
// 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 imports
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"go/build"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2014-01-26 10:47:31 -07:00
|
|
|
"sync"
|
2013-12-17 19:21:03 -07:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
var only = flag.String("only", "", "If non-empty, the fix test to run")
|
|
|
|
|
|
|
|
var tests = []struct {
|
|
|
|
name string
|
|
|
|
in, out string
|
|
|
|
}{
|
|
|
|
// Adding an import to an existing parenthesized import
|
|
|
|
{
|
|
|
|
name: "factored_imports_add",
|
|
|
|
in: `package foo
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
func bar() {
|
|
|
|
var b bytes.Buffer
|
|
|
|
fmt.Println(b.String())
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
|
|
|
func bar() {
|
|
|
|
var b bytes.Buffer
|
|
|
|
fmt.Println(b.String())
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Adding an import to an existing parenthesized import,
|
|
|
|
// verifying it goes into the first section.
|
|
|
|
{
|
|
|
|
name: "factored_imports_add_first_sec",
|
|
|
|
in: `package foo
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"appengine"
|
|
|
|
)
|
|
|
|
func bar() {
|
|
|
|
var b bytes.Buffer
|
|
|
|
_ = appengine.IsDevServer
|
|
|
|
fmt.Println(b.String())
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"appengine"
|
|
|
|
)
|
|
|
|
|
|
|
|
func bar() {
|
|
|
|
var b bytes.Buffer
|
|
|
|
_ = appengine.IsDevServer
|
|
|
|
fmt.Println(b.String())
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Adding an import to an existing parenthesized import,
|
|
|
|
// verifying it goes into the first section. (test 2)
|
|
|
|
{
|
|
|
|
name: "factored_imports_add_first_sec_2",
|
|
|
|
in: `package foo
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"appengine"
|
|
|
|
)
|
|
|
|
func bar() {
|
|
|
|
_ = math.NaN
|
|
|
|
_ = fmt.Sprintf
|
|
|
|
_ = appengine.IsDevServer
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
|
|
|
|
"appengine"
|
|
|
|
)
|
|
|
|
|
|
|
|
func bar() {
|
|
|
|
_ = math.NaN
|
|
|
|
_ = fmt.Sprintf
|
|
|
|
_ = appengine.IsDevServer
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Adding a new import line, without parens
|
|
|
|
{
|
|
|
|
name: "add_import_section",
|
|
|
|
in: `package foo
|
|
|
|
func bar() {
|
|
|
|
var b bytes.Buffer
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
import "bytes"
|
|
|
|
|
|
|
|
func bar() {
|
|
|
|
var b bytes.Buffer
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Adding two new imports, which should make a parenthesized import decl.
|
|
|
|
{
|
|
|
|
name: "add_import_paren_section",
|
|
|
|
in: `package foo
|
|
|
|
func bar() {
|
|
|
|
_, _ := bytes.Buffer, zip.NewReader
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"archive/zip"
|
|
|
|
"bytes"
|
|
|
|
)
|
|
|
|
|
|
|
|
func bar() {
|
|
|
|
_, _ := bytes.Buffer, zip.NewReader
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Make sure we don't add things twice
|
|
|
|
{
|
|
|
|
name: "no_double_add",
|
|
|
|
in: `package foo
|
|
|
|
func bar() {
|
|
|
|
_, _ := bytes.Buffer, bytes.NewReader
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
import "bytes"
|
|
|
|
|
|
|
|
func bar() {
|
|
|
|
_, _ := bytes.Buffer, bytes.NewReader
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Remove unused imports, 1 of a factored block
|
|
|
|
{
|
|
|
|
name: "remove_unused_1_of_2",
|
|
|
|
in: `package foo
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
|
|
|
func bar() {
|
|
|
|
_, _ := bytes.Buffer, bytes.NewReader
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
import "bytes"
|
|
|
|
|
|
|
|
func bar() {
|
|
|
|
_, _ := bytes.Buffer, bytes.NewReader
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Remove unused imports, 2 of 2
|
|
|
|
{
|
|
|
|
name: "remove_unused_2_of_2",
|
|
|
|
in: `package foo
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
|
|
|
func bar() {
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
func bar() {
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Remove unused imports, 1 of 1
|
|
|
|
{
|
|
|
|
name: "remove_unused_1_of_1",
|
|
|
|
in: `package foo
|
|
|
|
|
|
|
|
import "fmt"
|
|
|
|
|
|
|
|
func bar() {
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
func bar() {
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Don't remove empty imports.
|
|
|
|
{
|
|
|
|
name: "dont_remove_empty_imports",
|
|
|
|
in: `package foo
|
|
|
|
import (
|
|
|
|
_ "image/png"
|
|
|
|
_ "image/jpeg"
|
|
|
|
)
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
import (
|
|
|
|
_ "image/jpeg"
|
|
|
|
_ "image/png"
|
|
|
|
)
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Don't remove dot imports.
|
|
|
|
{
|
|
|
|
name: "dont_remove_dot_imports",
|
|
|
|
in: `package foo
|
|
|
|
import (
|
|
|
|
. "foo"
|
|
|
|
. "bar"
|
|
|
|
)
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
import (
|
|
|
|
. "bar"
|
|
|
|
. "foo"
|
|
|
|
)
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Skip refs the parser can resolve.
|
|
|
|
{
|
|
|
|
name: "skip_resolved_refs",
|
|
|
|
in: `package foo
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
type t struct{ Println func(string) }
|
|
|
|
fmt := t{Println: func(string) {}}
|
|
|
|
fmt.Println("foo")
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
type t struct{ Println func(string) }
|
|
|
|
fmt := t{Println: func(string) {}}
|
|
|
|
fmt.Println("foo")
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Do not add a package we already have a resolution for.
|
|
|
|
{
|
|
|
|
name: "skip_template",
|
|
|
|
in: `package foo
|
|
|
|
|
|
|
|
import "html/template"
|
|
|
|
|
|
|
|
func f() { t = template.New("sometemplate") }
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
import "html/template"
|
|
|
|
|
|
|
|
func f() { t = template.New("sometemplate") }
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Don't touch cgo
|
|
|
|
{
|
|
|
|
name: "cgo",
|
|
|
|
in: `package foo
|
|
|
|
|
|
|
|
/*
|
|
|
|
#include <foo.h>
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
/*
|
|
|
|
#include <foo.h>
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Put some things in their own section
|
|
|
|
{
|
|
|
|
name: "make_sections",
|
|
|
|
in: `package foo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
)
|
|
|
|
|
|
|
|
func foo () {
|
|
|
|
_, _ = os.Args, fmt.Println
|
|
|
|
_, _ = appengine.FooSomething, user.Current
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
|
|
|
|
"appengine"
|
|
|
|
"appengine/user"
|
|
|
|
)
|
|
|
|
|
|
|
|
func foo() {
|
|
|
|
_, _ = os.Args, fmt.Println
|
|
|
|
_, _ = appengine.FooSomething, user.Current
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Delete existing empty import block
|
|
|
|
{
|
|
|
|
name: "delete_empty_import_block",
|
|
|
|
in: `package foo
|
|
|
|
|
|
|
|
import ()
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Use existing empty import block
|
|
|
|
{
|
|
|
|
name: "use_empty_import_block",
|
|
|
|
in: `package foo
|
|
|
|
|
|
|
|
import ()
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
_ = fmt.Println
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
import "fmt"
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
_ = fmt.Println
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Blank line before adding new section.
|
|
|
|
{
|
|
|
|
name: "blank_line_before_new_group",
|
|
|
|
in: `package foo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
)
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
_ = net.Dial
|
|
|
|
_ = fmt.Printf
|
|
|
|
_ = snappy.Foo
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
|
|
|
|
"code.google.com/p/snappy-go/snappy"
|
|
|
|
)
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
_ = net.Dial
|
|
|
|
_ = fmt.Printf
|
|
|
|
_ = snappy.Foo
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Blank line between standard library and third-party stuff.
|
|
|
|
{
|
|
|
|
name: "blank_line_separating_std_and_third_party",
|
|
|
|
in: `package foo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"code.google.com/p/snappy-go/snappy"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
)
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
_ = net.Dial
|
|
|
|
_ = fmt.Printf
|
|
|
|
_ = snappy.Foo
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package foo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
|
|
|
|
"code.google.com/p/snappy-go/snappy"
|
|
|
|
)
|
|
|
|
|
|
|
|
func f() {
|
|
|
|
_ = net.Dial
|
|
|
|
_ = fmt.Printf
|
|
|
|
_ = snappy.Foo
|
|
|
|
}
|
2013-12-18 10:09:37 -07:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// golang.org/issue/6884
|
|
|
|
{
|
|
|
|
name: "issue 6884",
|
|
|
|
in: `package main
|
|
|
|
|
|
|
|
// A comment
|
|
|
|
func main() {
|
|
|
|
fmt.Println("Hello, world")
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package main
|
|
|
|
|
|
|
|
import "fmt"
|
|
|
|
|
|
|
|
// A comment
|
|
|
|
func main() {
|
|
|
|
fmt.Println("Hello, world")
|
|
|
|
}
|
2014-02-07 18:03:34 -07:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// golang.org/issue/7132
|
|
|
|
{
|
|
|
|
name: "issue 7132",
|
|
|
|
in: `package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"gu"
|
|
|
|
"github.com/foo/bar"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
a = bar.a
|
|
|
|
b = gu.a
|
|
|
|
c = fmt.Printf
|
|
|
|
)
|
|
|
|
`,
|
|
|
|
out: `package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"gu"
|
|
|
|
|
|
|
|
"github.com/foo/bar"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
a = bar.a
|
|
|
|
b = gu.a
|
|
|
|
c = fmt.Printf
|
|
|
|
)
|
2014-03-25 07:37:10 -06:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
name: "renamed package",
|
|
|
|
in: `package main
|
|
|
|
|
|
|
|
var _ = str.HasPrefix
|
|
|
|
`,
|
|
|
|
out: `package main
|
|
|
|
|
|
|
|
import str "strings"
|
|
|
|
|
|
|
|
var _ = str.HasPrefix
|
2013-12-17 19:21:03 -07:00
|
|
|
`,
|
|
|
|
},
|
2014-04-28 15:15:33 -06:00
|
|
|
|
|
|
|
{
|
|
|
|
name: "fragment with main",
|
|
|
|
in: `func main(){fmt.Println("Hello, world")}`,
|
|
|
|
out: `package main
|
|
|
|
|
|
|
|
import "fmt"
|
|
|
|
|
|
|
|
func main() { fmt.Println("Hello, world") }
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
{
|
|
|
|
name: "fragment without main",
|
|
|
|
in: `func notmain(){fmt.Println("Hello, world")}`,
|
|
|
|
out: `import "fmt"
|
|
|
|
|
|
|
|
func notmain() { fmt.Println("Hello, world") }`,
|
|
|
|
},
|
go.tools/astutil: fix edge case in DeleteImport causing merging of import sections.
The issue occurs only when deleting an import that has a blank line immediately preceding,
and other imports before that.
Currently, DeleteImport assumes there's a blank line-sized hole left behind
where the import was, and always deletes it. That blank line-sized hole is there in all cases
except the above edge case.
This fix checks for that edge case, and does not remove the blank line-sized hole.
The CL also adds a previously failing test case that catches this scenario. After the change to
DeleteImport, the new test passes (along with all other tests).
Fixes golang/go#7679.
Note that there is no attempt to ensure the result *ast.File and *token.FileSet are perfectly
matching to what you would get if you printed the AST and parsed it back. This is how the
rest of the package and the current tests work (i.e., they only check that printing the AST gives
the correct output).
Changing that is very hard, if not impossible, at least not
without resorting to manipulating AST via printing, text manipulation and parsing.
This is okay for most usages, but it does create potential problems. For example,
astutil.Imports() currently only works correctly on freshly parsed AST. If that AST
is manipulated via astutil funcs, then Imports() may not always generate correct
output. However, thas is a separate issue and should be treated as such.
LGTM=bradfitz
R=golang-codereviews, gobot, adonovan, bradfitz
CC=golang-codereviews
https://golang.org/cl/92250045
2014-05-19 15:04:30 -06:00
|
|
|
|
|
|
|
// Remove first import within in a 2nd/3rd/4th/etc. section.
|
|
|
|
// golang.org/issue/7679
|
|
|
|
{
|
|
|
|
name: "issue 7679",
|
|
|
|
in: `package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/foo/bar"
|
|
|
|
"github.com/foo/qux"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
var _ = fmt.Println
|
|
|
|
//var _ = bar.A
|
|
|
|
var _ = qux.B
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/foo/qux"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
var _ = fmt.Println
|
|
|
|
//var _ = bar.A
|
|
|
|
var _ = qux.B
|
|
|
|
}
|
2014-05-20 15:02:16 -06:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Blank line can be added before all types of import declarations.
|
|
|
|
// golang.org/issue/7866
|
|
|
|
{
|
|
|
|
name: "issue 7866",
|
|
|
|
in: `package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
renamed_bar "github.com/foo/bar"
|
|
|
|
|
|
|
|
. "github.com/foo/baz"
|
|
|
|
"io"
|
|
|
|
|
|
|
|
_ "github.com/foo/qux"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
_, _, _, _, _ = fmt.Errorf, io.Copy, strings.Contains, renamed_bar.A, B
|
|
|
|
}
|
|
|
|
`,
|
|
|
|
out: `package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
renamed_bar "github.com/foo/bar"
|
|
|
|
|
|
|
|
"io"
|
|
|
|
|
|
|
|
. "github.com/foo/baz"
|
|
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
_ "github.com/foo/qux"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
_, _, _, _, _ = fmt.Errorf, io.Copy, strings.Contains, renamed_bar.A, B
|
|
|
|
}
|
2014-07-28 18:15:17 -06:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Non-idempotent comment formatting
|
|
|
|
// golang.org/issue/8035
|
|
|
|
{
|
|
|
|
name: "issue 8035",
|
|
|
|
in: `package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt" // A
|
|
|
|
"go/ast" // B
|
|
|
|
_ "launchpad.net/gocheck" // C
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() { _, _ = fmt.Print, ast.Walk }
|
|
|
|
`,
|
|
|
|
out: `package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt" // A
|
|
|
|
"go/ast" // B
|
|
|
|
|
|
|
|
_ "launchpad.net/gocheck" // C
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() { _, _ = fmt.Print, ast.Walk }
|
2014-08-14 12:51:51 -06:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Failure to delete all duplicate imports
|
|
|
|
// golang.org/issue/8459
|
|
|
|
{
|
|
|
|
name: "issue 8459",
|
|
|
|
in: `package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"log"
|
|
|
|
"math"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() { fmt.Println("pi:", math.Pi) }
|
|
|
|
`,
|
|
|
|
out: `package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"math"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() { fmt.Println("pi:", math.Pi) }
|
2015-03-25 12:52:44 -06:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Too aggressive prefix matching
|
|
|
|
// golang.org/issue/9961
|
|
|
|
{
|
|
|
|
name: "issue 9961",
|
|
|
|
in: `package p
|
|
|
|
|
|
|
|
import (
|
|
|
|
"zip"
|
|
|
|
|
|
|
|
"rsc.io/p"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
_ = fmt.Print
|
|
|
|
_ = zip.Store
|
|
|
|
_ p.P
|
|
|
|
_ = regexp.Compile
|
|
|
|
)
|
|
|
|
`,
|
|
|
|
out: `package p
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
"zip"
|
|
|
|
|
|
|
|
"rsc.io/p"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
_ = fmt.Print
|
|
|
|
_ = zip.Store
|
|
|
|
_ p.P
|
|
|
|
_ = regexp.Compile
|
|
|
|
)
|
2015-08-07 09:16:12 -06:00
|
|
|
`,
|
|
|
|
},
|
|
|
|
|
|
|
|
// Unused named import is mistaken for unnamed import
|
|
|
|
// golang.org/issue/8149
|
|
|
|
{
|
|
|
|
name: "issue 8149",
|
|
|
|
in: `package main
|
|
|
|
|
|
|
|
import foo "fmt"
|
|
|
|
|
|
|
|
func main() { fmt.Println() }
|
|
|
|
`,
|
|
|
|
out: `package main
|
|
|
|
|
|
|
|
import "fmt"
|
|
|
|
|
|
|
|
func main() { fmt.Println() }
|
go.tools/astutil: fix edge case in DeleteImport causing merging of import sections.
The issue occurs only when deleting an import that has a blank line immediately preceding,
and other imports before that.
Currently, DeleteImport assumes there's a blank line-sized hole left behind
where the import was, and always deletes it. That blank line-sized hole is there in all cases
except the above edge case.
This fix checks for that edge case, and does not remove the blank line-sized hole.
The CL also adds a previously failing test case that catches this scenario. After the change to
DeleteImport, the new test passes (along with all other tests).
Fixes golang/go#7679.
Note that there is no attempt to ensure the result *ast.File and *token.FileSet are perfectly
matching to what you would get if you printed the AST and parsed it back. This is how the
rest of the package and the current tests work (i.e., they only check that printing the AST gives
the correct output).
Changing that is very hard, if not impossible, at least not
without resorting to manipulating AST via printing, text manipulation and parsing.
This is okay for most usages, but it does create potential problems. For example,
astutil.Imports() currently only works correctly on freshly parsed AST. If that AST
is manipulated via astutil funcs, then Imports() may not always generate correct
output. However, thas is a separate issue and should be treated as such.
LGTM=bradfitz
R=golang-codereviews, gobot, adonovan, bradfitz
CC=golang-codereviews
https://golang.org/cl/92250045
2014-05-19 15:04:30 -06:00
|
|
|
`,
|
|
|
|
},
|
2013-12-17 19:21:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestFixImports(t *testing.T) {
|
|
|
|
simplePkgs := map[string]string{
|
|
|
|
"appengine": "appengine",
|
|
|
|
"bytes": "bytes",
|
2015-03-25 12:52:44 -06:00
|
|
|
"fmt": "fmt",
|
|
|
|
"math": "math",
|
|
|
|
"os": "os",
|
|
|
|
"p": "rsc.io/p",
|
|
|
|
"regexp": "regexp",
|
2013-12-17 19:21:03 -07:00
|
|
|
"snappy": "code.google.com/p/snappy-go/snappy",
|
2014-03-25 07:37:10 -06:00
|
|
|
"str": "strings",
|
2015-03-25 12:52:44 -06:00
|
|
|
"user": "appengine/user",
|
|
|
|
"zip": "archive/zip",
|
2013-12-17 19:21:03 -07:00
|
|
|
}
|
2014-03-25 07:37:10 -06:00
|
|
|
findImport = func(pkgName string, symbols map[string]bool) (string, bool, error) {
|
|
|
|
return simplePkgs[pkgName], pkgName == "str", nil
|
2013-12-17 19:21:03 -07:00
|
|
|
}
|
|
|
|
|
2014-04-28 15:15:33 -06:00
|
|
|
options := &Options{
|
|
|
|
TabWidth: 8,
|
|
|
|
TabIndent: true,
|
|
|
|
Comments: true,
|
|
|
|
Fragment: true,
|
|
|
|
}
|
|
|
|
|
2013-12-17 19:21:03 -07:00
|
|
|
for _, tt := range tests {
|
|
|
|
if *only != "" && tt.name != *only {
|
|
|
|
continue
|
|
|
|
}
|
2014-04-28 15:15:33 -06:00
|
|
|
buf, err := Process(tt.name+".go", []byte(tt.in), options)
|
2013-12-17 19:21:03 -07:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("error on %q: %v", tt.name, err)
|
|
|
|
continue
|
|
|
|
}
|
2013-12-18 04:44:50 -07:00
|
|
|
if got := string(buf); got != tt.out {
|
2013-12-17 19:21:03 -07:00
|
|
|
t.Errorf("results diff on %q\nGOT:\n%s\nWANT:\n%s\n", tt.name, got, tt.out)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFindImportGoPath(t *testing.T) {
|
|
|
|
goroot, err := ioutil.TempDir("", "goimports-")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(goroot)
|
2014-01-26 10:47:31 -07:00
|
|
|
|
|
|
|
pkgIndexOnce = sync.Once{}
|
|
|
|
|
|
|
|
origStdlib := stdlib
|
|
|
|
defer func() {
|
|
|
|
stdlib = origStdlib
|
|
|
|
}()
|
|
|
|
stdlib = nil
|
|
|
|
|
2013-12-17 19:21:03 -07:00
|
|
|
// Test against imaginary bits/bytes package in std lib
|
2014-10-12 15:44:37 -06:00
|
|
|
bytesDir := filepath.Join(goroot, "src", "pkg", "bits", "bytes")
|
|
|
|
for _, tag := range build.Default.ReleaseTags {
|
|
|
|
// Go 1.4 rearranged the GOROOT tree to remove the "pkg" path component.
|
|
|
|
if tag == "go1.4" {
|
|
|
|
bytesDir = filepath.Join(goroot, "src", "bits", "bytes")
|
|
|
|
}
|
|
|
|
}
|
2013-12-17 19:21:03 -07:00
|
|
|
if err := os.MkdirAll(bytesDir, 0755); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
bytesSrcPath := filepath.Join(bytesDir, "bytes.go")
|
|
|
|
bytesPkgPath := "bits/bytes"
|
|
|
|
bytesSrc := []byte(`package bytes
|
|
|
|
|
|
|
|
type Buffer2 struct {}
|
|
|
|
`)
|
|
|
|
if err := ioutil.WriteFile(bytesSrcPath, bytesSrc, 0775); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
oldGOROOT := build.Default.GOROOT
|
|
|
|
oldGOPATH := build.Default.GOPATH
|
|
|
|
build.Default.GOROOT = goroot
|
|
|
|
build.Default.GOPATH = ""
|
|
|
|
defer func() {
|
|
|
|
build.Default.GOROOT = oldGOROOT
|
|
|
|
build.Default.GOPATH = oldGOPATH
|
|
|
|
}()
|
|
|
|
|
2014-03-25 07:37:10 -06:00
|
|
|
got, rename, err := findImportGoPath("bytes", map[string]bool{"Buffer2": true})
|
2013-12-17 19:21:03 -07:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2014-03-25 07:37:10 -06:00
|
|
|
if got != bytesPkgPath || rename {
|
|
|
|
t.Errorf(`findImportGoPath("bytes", Buffer2 ...)=%q, %t, want "%s", false`, got, rename, bytesPkgPath)
|
2013-12-17 19:21:03 -07:00
|
|
|
}
|
|
|
|
|
2014-03-25 07:37:10 -06:00
|
|
|
got, rename, err = findImportGoPath("bytes", map[string]bool{"Missing": true})
|
2013-12-17 19:21:03 -07:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2014-03-25 07:37:10 -06:00
|
|
|
if got != "" || rename {
|
|
|
|
t.Errorf(`findImportGoPath("bytes", Missing ...)=%q, %t, want "", false`, got, rename)
|
2013-12-17 19:21:03 -07:00
|
|
|
}
|
|
|
|
}
|
2014-01-26 10:47:31 -07:00
|
|
|
|
|
|
|
func TestFindImportStdlib(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
pkg string
|
|
|
|
symbols []string
|
|
|
|
want string
|
|
|
|
}{
|
|
|
|
{"http", []string{"Get"}, "net/http"},
|
|
|
|
{"http", []string{"Get", "Post"}, "net/http"},
|
|
|
|
{"http", []string{"Get", "Foo"}, ""},
|
|
|
|
{"bytes", []string{"Buffer"}, "bytes"},
|
|
|
|
{"ioutil", []string{"Discard"}, "io/ioutil"},
|
|
|
|
}
|
|
|
|
for _, tt := range tests {
|
2014-03-25 07:37:10 -06:00
|
|
|
got, rename, ok := findImportStdlib(tt.pkg, strSet(tt.symbols))
|
2014-01-26 10:47:31 -07:00
|
|
|
if (got != "") != ok {
|
|
|
|
t.Error("findImportStdlib return value inconsistent")
|
|
|
|
}
|
2014-03-25 07:37:10 -06:00
|
|
|
if got != tt.want || rename {
|
|
|
|
t.Errorf("findImportStdlib(%q, %q) = %q, %t; want %q, false", tt.pkg, tt.symbols, got, rename, tt.want)
|
2014-01-26 10:47:31 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func strSet(ss []string) map[string]bool {
|
|
|
|
m := make(map[string]bool)
|
|
|
|
for _, s := range ss {
|
|
|
|
m[s] = true
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|