Beispiel #1
0
func (m *mover) checkValid() error {
	const prefix = "invalid move destination"

	match, err := regexp.MatchString("^[_\\pL][_\\pL\\p{Nd}]*$", path.Base(m.to))
	if err != nil {
		panic("regexp.MatchString failed")
	}
	if !match {
		return fmt.Errorf("%s: %s; gomvpkg does not support move destinations "+
			"whose base names are not valid go identifiers", prefix, m.to)
	}

	if buildutil.FileExists(m.ctxt, m.toDir) {
		return fmt.Errorf("%s: %s conflicts with file %s", prefix, m.to, m.toDir)
	}
	if buildutil.IsDir(m.ctxt, m.toDir) {
		return fmt.Errorf("%s: %s conflicts with directory %s", prefix, m.to, m.toDir)
	}

	for _, toSubPkg := range m.destinations {
		if _, err := m.ctxt.Import(toSubPkg, "", build.FindOnly); err == nil {
			return fmt.Errorf("%s: %s; package or subpackage %s already exists",
				prefix, m.to, toSubPkg)
		}
	}

	return nil
}
Beispiel #2
0
func getwdPackages(ctxt *build.Context) string {
	wd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	if !buildutil.FileExists(&build.Default, wd) {
		flag.Usage()
		os.Exit(1)
	}
	importPath, err := getImportPath(ctxt, wd)
	if err != nil {
		panic(err)
	}
	return importPath
}
Beispiel #3
0
// parseOffsetFlag interprets the "-offset" flag value as a renaming specification.
func parseOffsetFlag(ctxt *build.Context, offsetFlag string) (*spec, error) {
	var spec spec
	// Validate -offset, e.g. file.go:#123
	parts := strings.Split(offsetFlag, ":#")
	if len(parts) != 2 {
		return nil, fmt.Errorf("-offset %q: invalid offset specification", offsetFlag)
	}

	spec.filename = parts[0]
	if !buildutil.FileExists(ctxt, spec.filename) {
		return nil, fmt.Errorf("no such file: %s", spec.filename)
	}

	bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename)
	if err != nil {
		return nil, err
	}
	spec.pkg = bp.ImportPath

	for _, r := range parts[1] {
		if !isDigit(r) {
			return nil, fmt.Errorf("-offset %q: non-numeric offset", offsetFlag)
		}
	}
	spec.offset, err = strconv.Atoi(parts[1])
	if err != nil {
		return nil, fmt.Errorf("-offset %q: non-numeric offset", offsetFlag)
	}

	// Parse the file and check there's an identifier at that offset.
	fset := token.NewFileSet()
	f, err := buildutil.ParseFile(fset, ctxt, nil, wd, spec.filename, parser.ParseComments)
	if err != nil {
		return nil, fmt.Errorf("-offset %q: cannot parse file: %s", offsetFlag, err)
	}

	id := identAtOffset(fset, f, spec.offset)
	if id == nil {
		return nil, fmt.Errorf("-offset %q: no identifier at this position", offsetFlag)
	}

	spec.fromName = id.Name

	return &spec, nil
}
Beispiel #4
0
// parseFromFlag interprets the "-from" flag value as a renaming specification.
// See Usage in rename.go for valid formats.
func parseFromFlag(ctxt *build.Context, fromFlag string) (*spec, error) {
	var spec spec
	var main string // sans "::x" suffix
	switch parts := strings.Split(fromFlag, "::"); len(parts) {
	case 1:
		main = parts[0]
	case 2:
		main = parts[0]
		spec.searchFor = parts[1]
		if parts[1] == "" {
			// error
		}
	default:
		return nil, fmt.Errorf("-from %q: invalid identifier specification (see -help for formats)", fromFlag)
	}

	if strings.HasSuffix(main, ".go") {
		// main is "filename.go"
		if spec.searchFor == "" {
			return nil, fmt.Errorf("-from: filename %q must have a ::name suffix", main)
		}
		spec.filename = main
		if !buildutil.FileExists(ctxt, spec.filename) {
			return nil, fmt.Errorf("no such file: %s", spec.filename)
		}

		bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename)
		if err != nil {
			return nil, err
		}
		spec.pkg = bp.ImportPath

	} else {
		// main is one of:
		//  "importpath"
		//  "importpath".member
		//  (*"importpath".type).fieldormethod           (parens and star optional)
		if err := parseObjectSpec(&spec, main); err != nil {
			return nil, err
		}
	}

	if spec.searchFor != "" {
		spec.fromName = spec.searchFor
	}

	cwd, err := os.Getwd()
	if err != nil {
		return nil, err
	}

	// Sanitize the package.
	bp, err := ctxt.Import(spec.pkg, cwd, build.FindOnly)
	if err != nil {
		return nil, fmt.Errorf("can't find package %q", spec.pkg)
	}
	spec.pkg = bp.ImportPath

	if !isValidIdentifier(spec.fromName) {
		return nil, fmt.Errorf("-from: invalid identifier %q", spec.fromName)
	}

	if Verbose {
		log.Printf("-from spec: %+v", spec)
	}

	return &spec, nil
}
Beispiel #5
0
func TestMoves(t *testing.T) {
	tests := []struct {
		ctxt     *build.Context
		from, to string
		want     map[string]string
	}{
		// Simple example.
		{
			ctxt: fakeContext(map[string][]string{
				"foo": {`package foo; type T int`},
				"main": {`package main

import "foo"

var _ foo.T
`},
			}),
			from: "foo", to: "bar",
			want: map[string]string{
				"/go/src/main/0.go": `package main

import "bar"

var _ bar.T
`,
				"/go/src/bar/0.go": `package bar

type T int
`,
			},
		},

		// Example with subpackage.
		{
			ctxt: fakeContext(map[string][]string{
				"foo":     {`package foo; type T int`},
				"foo/sub": {`package sub; type T int`},
				"main": {`package main

import "foo"
import "foo/sub"

var _ foo.T
var _ sub.T
`},
			}),
			from: "foo", to: "bar",
			want: map[string]string{
				"/go/src/main/0.go": `package main

import "bar"
import "bar/sub"

var _ bar.T
var _ sub.T
`,
				"/go/src/bar/0.go": `package bar

type T int
`,
				"/go/src/bar/sub/0.go": `package sub; type T int`,
			},
		},

		// References into subpackages
		{
			ctxt: fakeContext(map[string][]string{
				"foo":   {`package foo; import "foo/a"; var _ a.T`},
				"foo/a": {`package a; type T int`},
				"foo/b": {`package b; import "foo/a"; var _ a.T`},
			}),
			from: "foo", to: "bar",
			want: map[string]string{
				"/go/src/bar/0.go": `package bar

import "bar/a"

var _ a.T
`,
				"/go/src/bar/a/0.go": `package a; type T int`,
				"/go/src/bar/b/0.go": `package b

import "bar/a"

var _ a.T
`,
			},
		},

		// External test packages
		{
			ctxt: buildutil.FakeContext(map[string]map[string]string{
				"foo": {
					"0.go":      `package foo; type T int`,
					"0_test.go": `package foo_test; import "foo"; var _ foo.T`,
				},
				"baz": {
					"0_test.go": `package baz_test; import "foo"; var _ foo.T`,
				},
			}),
			from: "foo", to: "bar",
			want: map[string]string{
				"/go/src/bar/0.go": `package bar

type T int
`,
				"/go/src/bar/0_test.go": `package bar_test

import "bar"

var _ bar.T
`,
				"/go/src/baz/0_test.go": `package baz_test

import "bar"

var _ bar.T
`,
			},
		},
	}

	for _, test := range tests {
		ctxt := test.ctxt

		got := make(map[string]string)
		// Populate got with starting file set. rewriteFile and moveDirectory
		// will mutate got to produce resulting file set.
		buildutil.ForEachPackage(ctxt, func(importPath string, err error) {
			if err != nil {
				return
			}
			path := filepath.Join("/go/src", importPath, "0.go")
			if !buildutil.FileExists(ctxt, path) {
				return
			}
			f, err := ctxt.OpenFile(path)
			if err != nil {
				t.Errorf("unexpected error opening file: %s", err)
				return
			}
			bytes, err := ioutil.ReadAll(f)
			f.Close()
			if err != nil {
				t.Errorf("unexpected error reading file: %s", err)
				return
			}
			got[path] = string(bytes)
		})
		rewriteFile = func(fset *token.FileSet, f *ast.File, orig string) error {
			var out bytes.Buffer
			if err := format.Node(&out, fset, f); err != nil {
				return err
			}
			got[orig] = out.String()
			return nil
		}
		moveDirectory = func(from, to string) error {
			for path, contents := range got {
				if strings.HasPrefix(path, from) {
					newPath := strings.Replace(path, from, to, 1)
					delete(got, path)
					got[newPath] = contents
				}
			}
			return nil
		}

		err := Move(ctxt, test.from, test.to, "")
		prefix := fmt.Sprintf("-from %q -to %q", test.from, test.to)
		if err != nil {
			t.Errorf("%s: unexpected error: %s", prefix, err)
			continue
		}

		for file, wantContent := range test.want {
			k := filepath.FromSlash(file)
			gotContent, ok := got[k]
			delete(got, k)
			if !ok {
				// TODO(matloob): some testcases might have files that won't be
				// rewritten
				t.Errorf("%s: file %s not rewritten", prefix, file)
				continue
			}
			if gotContent != wantContent {
				t.Errorf("%s: rewritten file %s does not match expectation; got <<<%s>>>\n"+
					"want <<<%s>>>", prefix, file, gotContent, wantContent)
			}
		}
		// got should now be empty
		for file := range got {
			t.Errorf("%s: unexpected rewrite of file %s", prefix, file)
		}
	}
}
Beispiel #6
0
func TestMoves(t *testing.T) {
	if runtime.GOOS == "windows" {
		t.Skip("broken on Windows; see golang.org/issue/16384")
	}
	tests := []struct {
		ctxt         *build.Context
		from, to     string
		want         map[string]string
		wantWarnings []string
	}{
		// Simple example.
		{
			ctxt: fakeContext(map[string][]string{
				"foo": {`package foo; type T int`},
				"main": {`package main

import "foo"

var _ foo.T
`},
			}),
			from: "foo", to: "bar",
			want: map[string]string{
				"/go/src/main/0.go": `package main

import "bar"

var _ bar.T
`,
				"/go/src/bar/0.go": `package bar

type T int
`,
			},
		},

		// Example with subpackage.
		{
			ctxt: fakeContext(map[string][]string{
				"foo":     {`package foo; type T int`},
				"foo/sub": {`package sub; type T int`},
				"main": {`package main

import "foo"
import "foo/sub"

var _ foo.T
var _ sub.T
`},
			}),
			from: "foo", to: "bar",
			want: map[string]string{
				"/go/src/main/0.go": `package main

import "bar"
import "bar/sub"

var _ bar.T
var _ sub.T
`,
				"/go/src/bar/0.go": `package bar

type T int
`,
				"/go/src/bar/sub/0.go": `package sub; type T int`,
			},
		},

		// References into subpackages
		{
			ctxt: fakeContext(map[string][]string{
				"foo":   {`package foo; import "foo/a"; var _ a.T`},
				"foo/a": {`package a; type T int`},
				"foo/b": {`package b; import "foo/a"; var _ a.T`},
			}),
			from: "foo", to: "bar",
			want: map[string]string{
				"/go/src/bar/0.go": `package bar

import "bar/a"

var _ a.T
`,
				"/go/src/bar/a/0.go": `package a; type T int`,
				"/go/src/bar/b/0.go": `package b

import "bar/a"

var _ a.T
`,
			},
		},

		// External test packages
		{
			ctxt: buildutil.FakeContext(map[string]map[string]string{
				"foo": {
					"0.go":      `package foo; type T int`,
					"0_test.go": `package foo_test; import "foo"; var _ foo.T`,
				},
				"baz": {
					"0_test.go": `package baz_test; import "foo"; var _ foo.T`,
				},
			}),
			from: "foo", to: "bar",
			want: map[string]string{
				"/go/src/bar/0.go": `package bar

type T int
`,
				"/go/src/bar/0_test.go": `package bar_test

import "bar"

var _ bar.T
`,
				"/go/src/baz/0_test.go": `package baz_test

import "bar"

var _ bar.T
`,
			},
		},
		// package import comments
		{
			ctxt: fakeContext(map[string][]string{"foo": {`package foo // import "baz"`}}),
			from: "foo", to: "bar",
			want: map[string]string{"/go/src/bar/0.go": `package bar // import "bar"
`},
		},
		{
			ctxt: fakeContext(map[string][]string{"foo": {`package foo /* import "baz" */`}}),
			from: "foo", to: "bar",
			want: map[string]string{"/go/src/bar/0.go": `package bar /* import "bar" */
`},
		},
		{
			ctxt: fakeContext(map[string][]string{"foo": {`package foo       // import "baz"`}}),
			from: "foo", to: "bar",
			want: map[string]string{"/go/src/bar/0.go": `package bar // import "bar"
`},
		},
		{
			ctxt: fakeContext(map[string][]string{"foo": {`package foo
// import " this is not an import comment`}}),
			from: "foo", to: "bar",
			want: map[string]string{"/go/src/bar/0.go": `package bar

// import " this is not an import comment
`},
		},
		{
			ctxt: fakeContext(map[string][]string{"foo": {`package foo
/* import " this is not an import comment */`}}),
			from: "foo", to: "bar",
			want: map[string]string{"/go/src/bar/0.go": `package bar

/* import " this is not an import comment */
`},
		},
		// Import name conflict generates a warning, not an error.
		{
			ctxt: fakeContext(map[string][]string{
				"x": {},
				"a": {`package a; type A int`},
				"b": {`package b; type B int`},
				"conflict": {`package conflict

import "a"
import "b"
var _ a.A
var _ b.B
`},
				"ok": {`package ok
import "b"
var _ b.B
`},
			}),
			from: "b", to: "x/a",
			want: map[string]string{
				"/go/src/a/0.go": `package a; type A int`,
				"/go/src/ok/0.go": `package ok

import "x/a"

var _ a.B
`,
				"/go/src/conflict/0.go": `package conflict

import "a"
import "x/a"

var _ a.A
var _ b.B
`,
				"/go/src/x/a/0.go": `package a

type B int
`,
			},
			wantWarnings: []string{
				`/go/src/conflict/0.go:4:8: renaming this imported package name "b" to "a"`,
				`/go/src/conflict/0.go:3:8: 	conflicts with imported package name in same block`,
				`/go/src/conflict/0.go:3:8: skipping update of this file`,
			},
		},
		// Rename with same base name.
		{
			ctxt: fakeContext(map[string][]string{
				"x": {},
				"y": {},
				"x/foo": {`package foo

type T int
`},
				"main": {`package main; import "x/foo"; var _ foo.T`},
			}),
			from: "x/foo", to: "y/foo",
			want: map[string]string{
				"/go/src/y/foo/0.go": `package foo

type T int
`,
				"/go/src/main/0.go": `package main

import "y/foo"

var _ foo.T
`,
			},
		},
	}

	for _, test := range tests {
		ctxt := test.ctxt

		got := make(map[string]string)
		// Populate got with starting file set. rewriteFile and moveDirectory
		// will mutate got to produce resulting file set.
		buildutil.ForEachPackage(ctxt, func(importPath string, err error) {
			if err != nil {
				return
			}
			path := filepath.Join("/go/src", importPath, "0.go")
			if !buildutil.FileExists(ctxt, path) {
				return
			}
			f, err := ctxt.OpenFile(path)
			if err != nil {
				t.Errorf("unexpected error opening file: %s", err)
				return
			}
			bytes, err := ioutil.ReadAll(f)
			f.Close()
			if err != nil {
				t.Errorf("unexpected error reading file: %s", err)
				return
			}
			got[path] = string(bytes)
		})
		var warnings []string
		reportError = func(posn token.Position, message string) {
			warning := fmt.Sprintf("%s:%d:%d: %s",
				filepath.ToSlash(posn.Filename), // for MS Windows
				posn.Line,
				posn.Column,
				message)
			warnings = append(warnings, warning)

		}
		writeFile = func(filename string, content []byte) error {
			got[filename] = string(content)
			return nil
		}
		moveDirectory = func(from, to string) error {
			for path, contents := range got {
				if strings.HasPrefix(path, from) {
					newPath := strings.Replace(path, from, to, 1)
					delete(got, path)
					got[newPath] = contents
				}
			}
			return nil
		}

		err := Move(ctxt, test.from, test.to, "")
		prefix := fmt.Sprintf("-from %q -to %q", test.from, test.to)
		if err != nil {
			t.Errorf("%s: unexpected error: %s", prefix, err)
			continue
		}

		if !reflect.DeepEqual(warnings, test.wantWarnings) {
			t.Errorf("%s: unexpected warnings:\n%s\nwant:\n%s",
				prefix,
				strings.Join(warnings, "\n"),
				strings.Join(test.wantWarnings, "\n"))
		}

		for file, wantContent := range test.want {
			k := filepath.FromSlash(file)
			gotContent, ok := got[k]
			delete(got, k)
			if !ok {
				// TODO(matloob): some testcases might have files that won't be
				// rewritten
				t.Errorf("%s: file %s not rewritten", prefix, file)
				continue
			}
			if gotContent != wantContent {
				t.Errorf("%s: rewritten file %s does not match expectation; got <<<%s>>>\n"+
					"want <<<%s>>>", prefix, file, gotContent, wantContent)
			}
		}
		// got should now be empty
		for file := range got {
			t.Errorf("%s: unexpected rewrite of file %s", prefix, file)
		}
	}
}