summaryrefslogtreecommitdiff
path: root/src/cmd/gofmt
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/gofmt')
-rw-r--r--src/cmd/gofmt/doc.go2
-rw-r--r--src/cmd/gofmt/gofmt.go4
-rw-r--r--src/cmd/gofmt/gofmt_test.go33
-rw-r--r--src/cmd/gofmt/long_test.go2
-rw-r--r--src/cmd/gofmt/rewrite.go5
-rw-r--r--src/cmd/gofmt/simplify.go54
-rw-r--r--src/cmd/gofmt/testdata/crlf.golden12
-rw-r--r--src/cmd/gofmt/testdata/crlf.input12
-rw-r--r--src/cmd/gofmt/testdata/rewrite5.golden15
-rw-r--r--src/cmd/gofmt/testdata/rewrite5.input15
-rw-r--r--src/cmd/gofmt/testdata/slices1.golden58
-rw-r--r--src/cmd/gofmt/testdata/slices1.input58
-rw-r--r--src/cmd/gofmt/testdata/slices2.golden61
-rw-r--r--src/cmd/gofmt/testdata/slices2.input61
-rw-r--r--src/cmd/gofmt/testdata/typeswitch.golden60
-rw-r--r--src/cmd/gofmt/testdata/typeswitch.input60
16 files changed, 495 insertions, 17 deletions
diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go
index 65842a3b1..fffc7f06e 100644
--- a/src/cmd/gofmt/doc.go
+++ b/src/cmd/gofmt/doc.go
@@ -72,6 +72,6 @@ To convert the package tree from explicit slice upper bounds to implicit ones:
gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src/pkg
*/
-package documentation
+package main
// BUG(rsc): The implementation of -r is a bit slow.
diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go
index 0bc385b5b..861ff9390 100644
--- a/src/cmd/gofmt/gofmt.go
+++ b/src/cmd/gofmt/gofmt.go
@@ -29,7 +29,7 @@ var (
rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')")
simplifyAST = flag.Bool("s", false, "simplify code")
doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")
- allErrors = flag.Bool("e", false, "print all (including spurious) errors")
+ allErrors = flag.Bool("e", false, "report all errors (not just the first 10 on different lines)")
// layout control
comments = flag.Bool("comments", true, "print comments")
@@ -65,7 +65,7 @@ func initParserMode() {
parserMode |= parser.ParseComments
}
if *allErrors {
- parserMode |= parser.SpuriousErrors
+ parserMode |= parser.AllErrors
}
}
diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go
index 4b2805009..202d0a50c 100644
--- a/src/cmd/gofmt/gofmt_test.go
+++ b/src/cmd/gofmt/gofmt_test.go
@@ -56,31 +56,37 @@ func runTest(t *testing.T, in, out, flags string) {
return
}
- if got := buf.Bytes(); bytes.Compare(got, expected) != 0 {
+ if got := buf.Bytes(); !bytes.Equal(got, expected) {
t.Errorf("(gofmt %s) != %s (see %s.gofmt)", in, out, in)
d, err := diff(expected, got)
if err == nil {
t.Errorf("%s", d)
}
- ioutil.WriteFile(in+".gofmt", got, 0666)
+ if err := ioutil.WriteFile(in+".gofmt", got, 0666); err != nil {
+ t.Error(err)
+ }
}
}
-// TODO(gri) Add more test cases!
var tests = []struct {
in, flags string
}{
{"gofmt.go", ""},
{"gofmt_test.go", ""},
{"testdata/composites.input", "-s"},
+ {"testdata/slices1.input", "-s"},
+ {"testdata/slices2.input", "-s"},
{"testdata/old.input", ""},
{"testdata/rewrite1.input", "-r=Foo->Bar"},
{"testdata/rewrite2.input", "-r=int->bool"},
{"testdata/rewrite3.input", "-r=x->x"},
{"testdata/rewrite4.input", "-r=(x)->x"},
+ {"testdata/rewrite5.input", "-r=x+x->2*x"},
{"testdata/stdin*.input", "-stdin"},
{"testdata/comments.input", ""},
{"testdata/import.input", ""},
+ {"testdata/crlf.input", ""}, // test case for issue 3961; see also TestCRLF
+ {"testdata/typeswitch.input", ""}, // test case for issue 4470
}
func TestRewrite(t *testing.T) {
@@ -103,3 +109,24 @@ func TestRewrite(t *testing.T) {
}
}
}
+
+func TestCRLF(t *testing.T) {
+ const input = "testdata/crlf.input" // must contain CR/LF's
+ const golden = "testdata/crlf.golden" // must not contain any CR's
+
+ data, err := ioutil.ReadFile(input)
+ if err != nil {
+ t.Error(err)
+ }
+ if bytes.Index(data, []byte("\r\n")) < 0 {
+ t.Errorf("%s contains no CR/LF's", input)
+ }
+
+ data, err = ioutil.ReadFile(golden)
+ if err != nil {
+ t.Error(err)
+ }
+ if bytes.Index(data, []byte("\r")) >= 0 {
+ t.Errorf("%s contains CR's", golden)
+ }
+}
diff --git a/src/cmd/gofmt/long_test.go b/src/cmd/gofmt/long_test.go
index edbce606a..862e9d987 100644
--- a/src/cmd/gofmt/long_test.go
+++ b/src/cmd/gofmt/long_test.go
@@ -84,7 +84,7 @@ func testFile(t *testing.T, b1, b2 *bytes.Buffer, filename string) {
}
// the first and 2nd result should be identical
- if bytes.Compare(b1.Bytes(), b2.Bytes()) != 0 {
+ if !bytes.Equal(b1.Bytes(), b2.Bytes()) {
t.Errorf("gofmt %s not idempotent", filename)
}
}
diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go
index 3c7861f0d..dfabb6198 100644
--- a/src/cmd/gofmt/rewrite.go
+++ b/src/cmd/gofmt/rewrite.go
@@ -55,6 +55,7 @@ func dump(msg string, val reflect.Value) {
// rewriteFile applies the rewrite rule 'pattern -> replace' to an entire file.
func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File {
+ cmap := ast.NewCommentMap(fileSet, p, p.Comments)
m := make(map[string]reflect.Value)
pat := reflect.ValueOf(pattern)
repl := reflect.ValueOf(replace)
@@ -73,7 +74,9 @@ func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File {
}
return val
}
- return apply(f, reflect.ValueOf(p)).Interface().(*ast.File)
+ r := apply(f, reflect.ValueOf(p)).Interface().(*ast.File)
+ r.Comments = cmap.Filter(r).Comments() // recreate comments list
+ return r
}
// setValue is a wrapper for x.SetValue(y); it protects
diff --git a/src/cmd/gofmt/simplify.go b/src/cmd/gofmt/simplify.go
index 470c00625..e9a67a73a 100644
--- a/src/cmd/gofmt/simplify.go
+++ b/src/cmd/gofmt/simplify.go
@@ -10,7 +10,9 @@ import (
"reflect"
)
-type simplifier struct{}
+type simplifier struct {
+ hasDotImport bool // package file contains: import . "some/import/path"
+}
func (s *simplifier) Visit(node ast.Node) ast.Visitor {
switch n := node.(type) {
@@ -34,7 +36,7 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor {
x = t.Value
px = &t.Value
}
- simplify(x)
+ ast.Walk(s, x) // simplify x
// if the element is a composite literal and its literal type
// matches the outer literal's element type exactly, the inner
// literal type may be omitted
@@ -62,20 +64,54 @@ func (s *simplifier) Visit(node ast.Node) ast.Visitor {
return nil
}
+ case *ast.SliceExpr:
+ // a slice expression of the form: s[a:len(s)]
+ // can be simplified to: s[a:]
+ // if s is "simple enough" (for now we only accept identifiers)
+ if s.hasDotImport {
+ // if dot imports are present, we cannot be certain that an
+ // unresolved "len" identifier refers to the predefined len()
+ break
+ }
+ if s, _ := n.X.(*ast.Ident); s != nil && s.Obj != nil {
+ // the array/slice object is a single, resolved identifier
+ if call, _ := n.High.(*ast.CallExpr); call != nil && len(call.Args) == 1 && !call.Ellipsis.IsValid() {
+ // the high expression is a function call with a single argument
+ if fun, _ := call.Fun.(*ast.Ident); fun != nil && fun.Name == "len" && fun.Obj == nil {
+ // the function called is "len" and it is not locally defined; and
+ // because we don't have dot imports, it must be the predefined len()
+ if arg, _ := call.Args[0].(*ast.Ident); arg != nil && arg.Obj == s.Obj {
+ // the len argument is the array/slice object
+ n.High = nil
+ }
+ }
+ }
+ }
+ // Note: We could also simplify slice expressions of the form s[0:b] to s[:b]
+ // but we leave them as is since sometimes we want to be very explicit
+ // about the lower bound.
+
case *ast.RangeStmt:
- // range of the form: for x, _ = range v {...}
+ // a range of the form: for x, _ = range v {...}
// can be simplified to: for x = range v {...}
- if n.Value != nil {
- if ident, ok := n.Value.(*ast.Ident); ok && ident.Name == "_" {
- n.Value = nil
- }
+ if ident, _ := n.Value.(*ast.Ident); ident != nil && ident.Name == "_" {
+ n.Value = nil
}
}
return s
}
-func simplify(node ast.Node) {
+func simplify(f *ast.File) {
var s simplifier
- ast.Walk(&s, node)
+
+ // determine if f contains dot imports
+ for _, imp := range f.Imports {
+ if imp.Name != nil && imp.Name.Name == "." {
+ s.hasDotImport = true
+ break
+ }
+ }
+
+ ast.Walk(&s, f)
}
diff --git a/src/cmd/gofmt/testdata/crlf.golden b/src/cmd/gofmt/testdata/crlf.golden
new file mode 100644
index 000000000..57679f770
--- /dev/null
+++ b/src/cmd/gofmt/testdata/crlf.golden
@@ -0,0 +1,12 @@
+/*
+ Source containing CR/LF line endings.
+ The gofmt'ed output must only have LF
+ line endings.
+*/
+package main
+
+func main() {
+ // line comment
+ println("hello, world!") // another line comment
+ println()
+}
diff --git a/src/cmd/gofmt/testdata/crlf.input b/src/cmd/gofmt/testdata/crlf.input
new file mode 100644
index 000000000..61a1aa0b4
--- /dev/null
+++ b/src/cmd/gofmt/testdata/crlf.input
@@ -0,0 +1,12 @@
+/*
+ Source containing CR/LF line endings.
+ The gofmt'ed output must only have LF
+ line endings.
+*/
+package main
+
+func main() {
+ // line comment
+ println("hello, world!") // another line comment
+ println()
+}
diff --git a/src/cmd/gofmt/testdata/rewrite5.golden b/src/cmd/gofmt/testdata/rewrite5.golden
new file mode 100644
index 000000000..5a448a63d
--- /dev/null
+++ b/src/cmd/gofmt/testdata/rewrite5.golden
@@ -0,0 +1,15 @@
+// Copyright 2011 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.
+
+// Rewriting of expressions containing nodes with associated comments to
+// expressions without those nodes must also eliminate the associated
+// comments.
+
+package p
+
+func f(x int) int {
+ _ = 2 * x // this comment remains in the rewrite
+ _ = 2 * x
+ return 2 * x
+}
diff --git a/src/cmd/gofmt/testdata/rewrite5.input b/src/cmd/gofmt/testdata/rewrite5.input
new file mode 100644
index 000000000..0d759e69b
--- /dev/null
+++ b/src/cmd/gofmt/testdata/rewrite5.input
@@ -0,0 +1,15 @@
+// Copyright 2011 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.
+
+// Rewriting of expressions containing nodes with associated comments to
+// expressions without those nodes must also eliminate the associated
+// comments.
+
+package p
+
+func f(x int) int {
+ _ = x + x // this comment remains in the rewrite
+ _ = x /* this comment must not be in the rewrite */ + x
+ return x /* this comment must not be in the rewrite */ + x
+}
diff --git a/src/cmd/gofmt/testdata/slices1.golden b/src/cmd/gofmt/testdata/slices1.golden
new file mode 100644
index 000000000..61e074f68
--- /dev/null
+++ b/src/cmd/gofmt/testdata/slices1.golden
@@ -0,0 +1,58 @@
+// Test cases for slice expression simplification.
+package p
+
+var (
+ a [10]byte
+ b [20]float32
+ s []int
+ t struct {
+ s []byte
+ }
+
+ _ = a[0:]
+ _ = a[1:10]
+ _ = a[2:]
+ _ = a[3:(len(a))]
+ _ = a[len(a) : len(a)-1]
+ _ = a[0:len(b)]
+
+ _ = a[:]
+ _ = a[:10]
+ _ = a[:]
+ _ = a[:(len(a))]
+ _ = a[:len(a)-1]
+ _ = a[:len(b)]
+
+ _ = s[0:]
+ _ = s[1:10]
+ _ = s[2:]
+ _ = s[3:(len(s))]
+ _ = s[len(a) : len(s)-1]
+ _ = s[0:len(b)]
+
+ _ = s[:]
+ _ = s[:10]
+ _ = s[:]
+ _ = s[:(len(s))]
+ _ = s[:len(s)-1]
+ _ = s[:len(b)]
+
+ _ = t.s[0:]
+ _ = t.s[1:10]
+ _ = t.s[2:len(t.s)]
+ _ = t.s[3:(len(t.s))]
+ _ = t.s[len(a) : len(t.s)-1]
+ _ = t.s[0:len(b)]
+
+ _ = t.s[:]
+ _ = t.s[:10]
+ _ = t.s[:len(t.s)]
+ _ = t.s[:(len(t.s))]
+ _ = t.s[:len(t.s)-1]
+ _ = t.s[:len(b)]
+)
+
+func _() {
+ s := s[0:]
+ _ = s
+}
diff --git a/src/cmd/gofmt/testdata/slices1.input b/src/cmd/gofmt/testdata/slices1.input
new file mode 100644
index 000000000..4d2cbfff4
--- /dev/null
+++ b/src/cmd/gofmt/testdata/slices1.input
@@ -0,0 +1,58 @@
+// Test cases for slice expression simplification.
+package p
+
+var (
+ a [10]byte
+ b [20]float32
+ s []int
+ t struct {
+ s []byte
+ }
+
+ _ = a[0:]
+ _ = a[1:10]
+ _ = a[2:len(a)]
+ _ = a[3:(len(a))]
+ _ = a[len(a) : len(a)-1]
+ _ = a[0:len(b)]
+
+ _ = a[:]
+ _ = a[:10]
+ _ = a[:len(a)]
+ _ = a[:(len(a))]
+ _ = a[:len(a)-1]
+ _ = a[:len(b)]
+
+ _ = s[0:]
+ _ = s[1:10]
+ _ = s[2:len(s)]
+ _ = s[3:(len(s))]
+ _ = s[len(a) : len(s)-1]
+ _ = s[0:len(b)]
+
+ _ = s[:]
+ _ = s[:10]
+ _ = s[:len(s)]
+ _ = s[:(len(s))]
+ _ = s[:len(s)-1]
+ _ = s[:len(b)]
+
+ _ = t.s[0:]
+ _ = t.s[1:10]
+ _ = t.s[2:len(t.s)]
+ _ = t.s[3:(len(t.s))]
+ _ = t.s[len(a) : len(t.s)-1]
+ _ = t.s[0:len(b)]
+
+ _ = t.s[:]
+ _ = t.s[:10]
+ _ = t.s[:len(t.s)]
+ _ = t.s[:(len(t.s))]
+ _ = t.s[:len(t.s)-1]
+ _ = t.s[:len(b)]
+)
+
+func _() {
+ s := s[0:len(s)]
+ _ = s
+}
diff --git a/src/cmd/gofmt/testdata/slices2.golden b/src/cmd/gofmt/testdata/slices2.golden
new file mode 100644
index 000000000..433788e1e
--- /dev/null
+++ b/src/cmd/gofmt/testdata/slices2.golden
@@ -0,0 +1,61 @@
+// Test cases for slice expression simplification.
+// Because of a dot import, these slices must remain untouched.
+package p
+
+import . "math"
+
+var (
+ a [10]byte
+ b [20]float32
+ s []int
+ t struct {
+ s []byte
+ }
+
+ _ = a[0:]
+ _ = a[1:10]
+ _ = a[2:len(a)]
+ _ = a[3:(len(a))]
+ _ = a[len(a) : len(a)-1]
+ _ = a[0:len(b)]
+
+ _ = a[:]
+ _ = a[:10]
+ _ = a[:len(a)]
+ _ = a[:(len(a))]
+ _ = a[:len(a)-1]
+ _ = a[:len(b)]
+
+ _ = s[0:]
+ _ = s[1:10]
+ _ = s[2:len(s)]
+ _ = s[3:(len(s))]
+ _ = s[len(a) : len(s)-1]
+ _ = s[0:len(b)]
+
+ _ = s[:]
+ _ = s[:10]
+ _ = s[:len(s)]
+ _ = s[:(len(s))]
+ _ = s[:len(s)-1]
+ _ = s[:len(b)]
+
+ _ = t.s[0:]
+ _ = t.s[1:10]
+ _ = t.s[2:len(t.s)]
+ _ = t.s[3:(len(t.s))]
+ _ = t.s[len(a) : len(t.s)-1]
+ _ = t.s[0:len(b)]
+
+ _ = t.s[:]
+ _ = t.s[:10]
+ _ = t.s[:len(t.s)]
+ _ = t.s[:(len(t.s))]
+ _ = t.s[:len(t.s)-1]
+ _ = t.s[:len(b)]
+)
+
+func _() {
+ s := s[0:len(s)]
+ _ = s
+}
diff --git a/src/cmd/gofmt/testdata/slices2.input b/src/cmd/gofmt/testdata/slices2.input
new file mode 100644
index 000000000..433788e1e
--- /dev/null
+++ b/src/cmd/gofmt/testdata/slices2.input
@@ -0,0 +1,61 @@
+// Test cases for slice expression simplification.
+// Because of a dot import, these slices must remain untouched.
+package p
+
+import . "math"
+
+var (
+ a [10]byte
+ b [20]float32
+ s []int
+ t struct {
+ s []byte
+ }
+
+ _ = a[0:]
+ _ = a[1:10]
+ _ = a[2:len(a)]
+ _ = a[3:(len(a))]
+ _ = a[len(a) : len(a)-1]
+ _ = a[0:len(b)]
+
+ _ = a[:]
+ _ = a[:10]
+ _ = a[:len(a)]
+ _ = a[:(len(a))]
+ _ = a[:len(a)-1]
+ _ = a[:len(b)]
+
+ _ = s[0:]
+ _ = s[1:10]
+ _ = s[2:len(s)]
+ _ = s[3:(len(s))]
+ _ = s[len(a) : len(s)-1]
+ _ = s[0:len(b)]
+
+ _ = s[:]
+ _ = s[:10]
+ _ = s[:len(s)]
+ _ = s[:(len(s))]
+ _ = s[:len(s)-1]
+ _ = s[:len(b)]
+
+ _ = t.s[0:]
+ _ = t.s[1:10]
+ _ = t.s[2:len(t.s)]
+ _ = t.s[3:(len(t.s))]
+ _ = t.s[len(a) : len(t.s)-1]
+ _ = t.s[0:len(b)]
+
+ _ = t.s[:]
+ _ = t.s[:10]
+ _ = t.s[:len(t.s)]
+ _ = t.s[:(len(t.s))]
+ _ = t.s[:len(t.s)-1]
+ _ = t.s[:len(b)]
+)
+
+func _() {
+ s := s[0:len(s)]
+ _ = s
+}
diff --git a/src/cmd/gofmt/testdata/typeswitch.golden b/src/cmd/gofmt/testdata/typeswitch.golden
new file mode 100644
index 000000000..87e916181
--- /dev/null
+++ b/src/cmd/gofmt/testdata/typeswitch.golden
@@ -0,0 +1,60 @@
+/*
+ Parenthesized type switch expressions originally
+ accepted by gofmt must continue to be rewritten
+ into the correct unparenthesized form.
+
+ Only type-switches that didn't declare a variable
+ in the the type switch type assertion and which
+ contained only "expression-like" (named) types in their
+ cases were permitted to have their type assertion parenthesized
+ by go/parser (due to a weak predicate in the parser). All others
+ were rejected always, either with a syntax error in the
+ type switch header or in the case.
+
+ See also issue 4470.
+*/
+package p
+
+func f() {
+ var x interface{}
+ switch x.(type) { // should remain the same
+ }
+ switch x.(type) { // should become: switch x.(type) {
+ }
+
+ switch x.(type) { // should remain the same
+ case int:
+ }
+ switch x.(type) { // should become: switch x.(type) {
+ case int:
+ }
+
+ switch x.(type) { // should remain the same
+ case []int:
+ }
+
+ // Parenthesized (x.(type)) in type switches containing cases
+ // with unnamed (literal) types were never permitted by gofmt;
+ // thus there won't be any code in the wild using this style if
+ // the code was gofmt-ed.
+ /*
+ switch (x.(type)) {
+ case []int:
+ }
+ */
+
+ switch t := x.(type) { // should remain the same
+ default:
+ _ = t
+ }
+
+ // Parenthesized (x.(type)) in type switches declaring a variable
+ // were never permitted by gofmt; thus there won't be any code in
+ // the wild using this style if the code was gofmt-ed.
+ /*
+ switch t := (x.(type)) {
+ default:
+ _ = t
+ }
+ */
+}
diff --git a/src/cmd/gofmt/testdata/typeswitch.input b/src/cmd/gofmt/testdata/typeswitch.input
new file mode 100644
index 000000000..f90f28949
--- /dev/null
+++ b/src/cmd/gofmt/testdata/typeswitch.input
@@ -0,0 +1,60 @@
+/*
+ Parenthesized type switch expressions originally
+ accepted by gofmt must continue to be rewritten
+ into the correct unparenthesized form.
+
+ Only type-switches that didn't declare a variable
+ in the the type switch type assertion and which
+ contained only "expression-like" (named) types in their
+ cases were permitted to have their type assertion parenthesized
+ by go/parser (due to a weak predicate in the parser). All others
+ were rejected always, either with a syntax error in the
+ type switch header or in the case.
+
+ See also issue 4470.
+*/
+package p
+
+func f() {
+ var x interface{}
+ switch x.(type) { // should remain the same
+ }
+ switch (x.(type)) { // should become: switch x.(type) {
+ }
+
+ switch x.(type) { // should remain the same
+ case int:
+ }
+ switch (x.(type)) { // should become: switch x.(type) {
+ case int:
+ }
+
+ switch x.(type) { // should remain the same
+ case []int:
+ }
+
+ // Parenthesized (x.(type)) in type switches containing cases
+ // with unnamed (literal) types were never permitted by gofmt;
+ // thus there won't be any code in the wild using this style if
+ // the code was gofmt-ed.
+ /*
+ switch (x.(type)) {
+ case []int:
+ }
+ */
+
+ switch t := x.(type) { // should remain the same
+ default:
+ _ = t
+ }
+
+ // Parenthesized (x.(type)) in type switches declaring a variable
+ // were never permitted by gofmt; thus there won't be any code in
+ // the wild using this style if the code was gofmt-ed.
+ /*
+ switch t := (x.(type)) {
+ default:
+ _ = t
+ }
+ */
+}