diff options
Diffstat (limited to 'src/cmd/gofmt')
-rw-r--r-- | src/cmd/gofmt/doc.go | 2 | ||||
-rw-r--r-- | src/cmd/gofmt/gofmt.go | 4 | ||||
-rw-r--r-- | src/cmd/gofmt/gofmt_test.go | 33 | ||||
-rw-r--r-- | src/cmd/gofmt/long_test.go | 2 | ||||
-rw-r--r-- | src/cmd/gofmt/rewrite.go | 5 | ||||
-rw-r--r-- | src/cmd/gofmt/simplify.go | 54 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/crlf.golden | 12 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/crlf.input | 12 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/rewrite5.golden | 15 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/rewrite5.input | 15 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/slices1.golden | 58 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/slices1.input | 58 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/slices2.golden | 61 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/slices2.input | 61 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/typeswitch.golden | 60 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/typeswitch.input | 60 |
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 + } + */ +} |