diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-01-17 12:40:45 +0100 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-01-17 12:40:45 +0100 |
commit | 3e45412327a2654a77944249962b3652e6142299 (patch) | |
tree | bc3bf69452afa055423cbe0c5cfa8ca357df6ccf /src/cmd/gofmt | |
parent | c533680039762cacbc37db8dc7eed074c3e497be (diff) | |
download | golang-upstream/2011.01.12.tar.gz |
Imported Upstream version 2011.01.12upstream/2011.01.12
Diffstat (limited to 'src/cmd/gofmt')
-rw-r--r-- | src/cmd/gofmt/Makefile | 6 | ||||
-rw-r--r-- | src/cmd/gofmt/doc.go | 4 | ||||
-rw-r--r-- | src/cmd/gofmt/gofmt.go | 25 | ||||
-rw-r--r-- | src/cmd/gofmt/rewrite.go | 61 | ||||
-rw-r--r-- | src/cmd/gofmt/simplify.go | 67 | ||||
-rwxr-xr-x | src/cmd/gofmt/test.sh | 5 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/composites.golden | 104 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/composites.input | 104 | ||||
-rwxr-xr-x | src/cmd/gofmt/testdata/test.sh | 65 |
9 files changed, 408 insertions, 33 deletions
diff --git a/src/cmd/gofmt/Makefile b/src/cmd/gofmt/Makefile index 2294fd1db..5f2f454e8 100644 --- a/src/cmd/gofmt/Makefile +++ b/src/cmd/gofmt/Makefile @@ -2,12 +2,13 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -include ../../Make.$(GOARCH) +include ../../Make.inc TARG=gofmt GOFILES=\ gofmt.go\ rewrite.go\ + simplify.go\ include ../../Make.cmd @@ -15,5 +16,4 @@ test: $(TARG) ./test.sh smoketest: $(TARG) - ./test.sh "$(GOROOT)"/src/pkg/go/parser/parser.go - + (cd testdata; ./test.sh) diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go index 2e4c40c21..2d2c9ae61 100644 --- a/src/cmd/gofmt/doc.go +++ b/src/cmd/gofmt/doc.go @@ -20,6 +20,8 @@ The flags are: unless -w is also set. -r rule apply the rewrite rule to the source before reformatting. + -s + try to simplify code (after applying the rewrite rule, if any). -w if set, overwrite each input file with its output. -spaces @@ -33,6 +35,8 @@ Debugging flags: -trace print parse trace. + -ast + print AST (before rewrites). -comments=true print comments; if false, all comments are elided from the output. diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index ffec0325f..d7b70c461 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -12,6 +12,7 @@ import ( "go/parser" "go/printer" "go/scanner" + "go/token" "io/ioutil" "os" pathutil "path" @@ -24,11 +25,12 @@ var ( list = flag.Bool("l", false, "list files whose formatting differs from gofmt's") write = flag.Bool("w", false, "write result to (source) file instead of stdout") rewriteRule = flag.String("r", "", "rewrite rule (e.g., 'α[β:len(α)] -> α[β:]')") + simplifyAST = flag.Bool("s", false, "simplify code") // debugging support comments = flag.Bool("comments", true, "print comments") - debug = flag.Bool("debug", false, "print debugging information") trace = flag.Bool("trace", false, "print parse trace") + printAST = flag.Bool("ast", false, "print AST (before rewrites)") // layout control tabWidth = flag.Int("tabwidth", 8, "tab width") @@ -38,6 +40,7 @@ var ( var ( + fset = token.NewFileSet() exitCode = 0 rewrite func(*ast.File) *ast.File parserMode uint @@ -92,22 +95,26 @@ func processFile(f *os.File) os.Error { return err } - var scope *ast.Scope - if *debug { - scope = ast.NewScope(nil) - } - file, err := parser.ParseFile(f.Name(), src, scope, parserMode) + file, err := parser.ParseFile(fset, f.Name(), src, parserMode) if err != nil { return err } + if *printAST { + ast.Print(file) + } + if rewrite != nil { file = rewrite(file) } + if *simplifyAST { + simplify(file) + } + var res bytes.Buffer - _, err = (&printer.Config{printerMode, *tabWidth, nil}).Fprint(&res, file) + _, err = (&printer.Config{printerMode, *tabWidth, nil}).Fprint(&res, fset, file) if err != nil { return err } @@ -133,10 +140,10 @@ func processFile(f *os.File) os.Error { } -func processFileByName(filename string) (err os.Error) { +func processFileByName(filename string) os.Error { file, err := os.Open(filename, os.O_RDONLY, 0) if err != nil { - return + return err } defer file.Close() return processFile(file) diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go index a89146ca0..8ea5334e9 100644 --- a/src/cmd/gofmt/rewrite.go +++ b/src/cmd/gofmt/rewrite.go @@ -37,7 +37,7 @@ func initRewrite() { // but there are problems with preserving formatting and also // with what a wildcard for a statement looks like. func parseExpr(s string, what string) ast.Expr { - x, err := parser.ParseExpr("input", s, nil) + x, err := parser.ParseExpr(fset, "input", s) if err != nil { fmt.Fprintf(os.Stderr, "parsing %s %s: %s\n", what, s, err) os.Exit(2) @@ -66,13 +66,19 @@ func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File { } -var positionType = reflect.Typeof(token.Position{}) -var identType = reflect.Typeof((*ast.Ident)(nil)) - - -func isWildcard(s string) bool { - rune, size := utf8.DecodeRuneInString(s) - return size == len(s) && unicode.IsLower(rune) +// setValue is a wrapper for x.SetValue(y); it protects +// the caller from panics if x cannot be changed to y. +func setValue(x, y reflect.Value) { + defer func() { + if x := recover(); x != nil { + if s, ok := x.(string); ok && strings.HasPrefix(s, "type mismatch") { + // x cannot be set to y - ignore this rewrite + return + } + panic(x) + } + }() + x.SetValue(y) } @@ -86,21 +92,31 @@ func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value case *reflect.SliceValue: for i := 0; i < v.Len(); i++ { e := v.Elem(i) - e.SetValue(f(e)) + setValue(e, f(e)) } case *reflect.StructValue: for i := 0; i < v.NumField(); i++ { e := v.Field(i) - e.SetValue(f(e)) + setValue(e, f(e)) } case *reflect.InterfaceValue: e := v.Elem() - v.SetValue(f(e)) + setValue(v, f(e)) } return val } +var positionType = reflect.Typeof(token.NoPos) +var identType = reflect.Typeof((*ast.Ident)(nil)) + + +func isWildcard(s string) bool { + rune, size := utf8.DecodeRuneInString(s) + return size == len(s) && unicode.IsLower(rune) +} + + // match returns true if pattern matches val, // recording wildcard submatches in m. // If m == nil, match checks whether pattern == val. @@ -109,17 +125,20 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool { // times in the pattern, it must match the same expression // each time. if m != nil && pattern.Type() == identType { - name := pattern.Interface().(*ast.Ident).Name() + name := pattern.Interface().(*ast.Ident).Name if isWildcard(name) { - if old, ok := m[name]; ok { - return match(nil, old, val) + // wildcards only match expressions + if _, ok := val.Interface().(ast.Expr); ok { + if old, ok := m[name]; ok { + return match(nil, old, val) + } + m[name] = val + return true } - m[name] = val - return true } } - // Otherwise, the expressions must match recursively. + // Otherwise, pattern and val must match recursively. if pattern == nil || val == nil { return pattern == nil && val == nil } @@ -139,7 +158,7 @@ func match(m map[string]reflect.Value, pattern, val reflect.Value) bool { // of recursing down any further via reflection. p := pattern.Interface().(*ast.Ident) v := val.Interface().(*ast.Ident) - return p == nil && v == nil || p != nil && v != nil && p.Name() == v.Name() + return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name } p := reflect.Indirect(pattern) @@ -194,7 +213,7 @@ func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) // Wildcard gets replaced with map value. if m != nil && pattern.Type() == identType { - name := pattern.Interface().(*ast.Ident).Name() + name := pattern.Interface().(*ast.Ident).Name if isWildcard(name) { if old, ok := m[name]; ok { return subst(nil, old, nil) @@ -203,6 +222,10 @@ func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) } if pos != nil && pattern.Type() == positionType { + // use new position only if old position was valid in the first place + if old := pattern.Interface().(token.Pos); !old.IsValid() { + return pattern + } return pos } diff --git a/src/cmd/gofmt/simplify.go b/src/cmd/gofmt/simplify.go new file mode 100644 index 000000000..bcc67c4a6 --- /dev/null +++ b/src/cmd/gofmt/simplify.go @@ -0,0 +1,67 @@ +// Copyright 2010 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 main + +import ( + "go/ast" + "reflect" +) + + +type simplifier struct{} + +func (s *simplifier) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.CompositeLit: + // array, slice, and map composite literals may be simplified + outer := n + var eltType ast.Expr + switch typ := outer.Type.(type) { + case *ast.ArrayType: + eltType = typ.Elt + case *ast.MapType: + eltType = typ.Value + } + + if eltType != nil { + typ := reflect.NewValue(eltType) + for _, x := range outer.Elts { + // look at value of indexed/named elements + if t, ok := x.(*ast.KeyValueExpr); ok { + x = t.Value + } + 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 + if inner, ok := x.(*ast.CompositeLit); ok { + if match(nil, typ, reflect.NewValue(inner.Type)) { + inner.Type = nil + } + } + } + + // node was simplified - stop walk (there are no subnodes to simplify) + return nil + } + + case *ast.RangeStmt: + // 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 + } + } + } + + return s +} + + +func simplify(node ast.Node) { + var s simplifier + ast.Walk(&s, node) +} diff --git a/src/cmd/gofmt/test.sh b/src/cmd/gofmt/test.sh index bed46532b..b5f4de1e2 100755 --- a/src/cmd/gofmt/test.sh +++ b/src/cmd/gofmt/test.sh @@ -3,7 +3,7 @@ # Use of this source code is governed by a BSD-style # license that can be found in the LICENSE file. -. "$GOROOT"/src/Make.$GOARCH +eval $(gomake --no-print-directory -f ../../Make.inc go-env) if [ -z "$O" ]; then echo 'missing $O - maybe no Make.$GOARCH?' 1>&2 exit 1 @@ -41,7 +41,8 @@ apply1() { bug106.go | bug121.go | bug125.go | bug133.go | bug160.go | \ bug163.go | bug166.go | bug169.go | bug217.go | bug222.go | \ bug226.go | bug228.go | bug248.go | bug274.go | bug280.go | \ - bug282.go ) return ;; + bug282.go | bug287.go | bug298.go | bug299.go | bug300.go | \ + bug302.go | bug306.go ) return ;; esac # the following directories are skipped because they contain test # cases for syntax errors and thus won't parse in the first place: diff --git a/src/cmd/gofmt/testdata/composites.golden b/src/cmd/gofmt/testdata/composites.golden new file mode 100644 index 000000000..1fd5847c1 --- /dev/null +++ b/src/cmd/gofmt/testdata/composites.golden @@ -0,0 +1,104 @@ +package P + +type T struct { + x, y int +} + +var _ = [42]T{ + {}, + {1, 2}, + {3, 4}, +} + +var _ = [...]T{ + {}, + {1, 2}, + {3, 4}, +} + +var _ = []T{ + {}, + {1, 2}, + {3, 4}, +} + +var _ = []T{ + {}, + 10: {1, 2}, + 20: {3, 4}, +} + +var _ = []struct { + x, y int +}{ + {}, + 10: {1, 2}, + 20: {3, 4}, +} + +var _ = []interface{}{ + T{}, + 10: T{1, 2}, + 20: T{3, 4}, +} + +var _ = [][]int{ + {}, + {1, 2}, + {3, 4}, +} + +var _ = [][]int{ + ([]int{}), + ([]int{1, 2}), + {3, 4}, +} + +var _ = [][][]int{ + {}, + { + {}, + {0, 1, 2, 3}, + {4, 5}, + }, +} + +var _ = map[string]T{ + "foo": {}, + "bar": {1, 2}, + "bal": {3, 4}, +} + +var _ = map[string]struct { + x, y int +}{ + "foo": {}, + "bar": {1, 2}, + "bal": {3, 4}, +} + +var _ = map[string]interface{}{ + "foo": T{}, + "bar": T{1, 2}, + "bal": T{3, 4}, +} + +var _ = map[string][]int{ + "foo": {}, + "bar": {1, 2}, + "bal": {3, 4}, +} + +var _ = map[string][]int{ + "foo": ([]int{}), + "bar": ([]int{1, 2}), + "bal": {3, 4}, +} + +// from exp/4s/data.go +var pieces4 = []Piece{ + {0, 0, Point{4, 1}, []Point{{0, 0}, {1, 0}, {1, 0}, {1, 0}}, nil, nil}, + {1, 0, Point{1, 4}, []Point{{0, 0}, {0, 1}, {0, 1}, {0, 1}}, nil, nil}, + {2, 0, Point{4, 1}, []Point{{0, 0}, {1, 0}, {1, 0}, {1, 0}}, nil, nil}, + {3, 0, Point{1, 4}, []Point{{0, 0}, {0, 1}, {0, 1}, {0, 1}}, nil, nil}, +} diff --git a/src/cmd/gofmt/testdata/composites.input b/src/cmd/gofmt/testdata/composites.input new file mode 100644 index 000000000..15afd9e5c --- /dev/null +++ b/src/cmd/gofmt/testdata/composites.input @@ -0,0 +1,104 @@ +package P + +type T struct { + x, y int +} + +var _ = [42]T{ + T{}, + T{1, 2}, + T{3, 4}, +} + +var _ = [...]T{ + T{}, + T{1, 2}, + T{3, 4}, +} + +var _ = []T{ + T{}, + T{1, 2}, + T{3, 4}, +} + +var _ = []T{ + T{}, + 10: T{1, 2}, + 20: T{3, 4}, +} + +var _ = []struct { + x, y int +}{ + struct{ x, y int }{}, + 10: struct{ x, y int }{1, 2}, + 20: struct{ x, y int }{3, 4}, +} + +var _ = []interface{}{ + T{}, + 10: T{1, 2}, + 20: T{3, 4}, +} + +var _ = [][]int{ + []int{}, + []int{1, 2}, + []int{3, 4}, +} + +var _ = [][]int{ + ([]int{}), + ([]int{1, 2}), + []int{3, 4}, +} + +var _ = [][][]int{ + [][]int{}, + [][]int{ + []int{}, + []int{0, 1, 2, 3}, + []int{4, 5}, + }, +} + +var _ = map[string]T{ + "foo": T{}, + "bar": T{1, 2}, + "bal": T{3, 4}, +} + +var _ = map[string]struct { + x, y int +}{ + "foo": struct{ x, y int }{}, + "bar": struct{ x, y int }{1, 2}, + "bal": struct{ x, y int }{3, 4}, +} + +var _ = map[string]interface{}{ + "foo": T{}, + "bar": T{1, 2}, + "bal": T{3, 4}, +} + +var _ = map[string][]int{ + "foo": []int{}, + "bar": []int{1, 2}, + "bal": []int{3, 4}, +} + +var _ = map[string][]int{ + "foo": ([]int{}), + "bar": ([]int{1, 2}), + "bal": []int{3, 4}, +} + +// from exp/4s/data.go +var pieces4 = []Piece{ + Piece{0, 0, Point{4, 1}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}}, nil, nil}, + Piece{1, 0, Point{1, 4}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{0, 1}}, nil, nil}, + Piece{2, 0, Point{4, 1}, []Point{Point{0, 0}, Point{1, 0}, Point{1, 0}, Point{1, 0}}, nil, nil}, + Piece{3, 0, Point{1, 4}, []Point{Point{0, 0}, Point{0, 1}, Point{0, 1}, Point{0, 1}}, nil, nil}, +} diff --git a/src/cmd/gofmt/testdata/test.sh b/src/cmd/gofmt/testdata/test.sh new file mode 100755 index 000000000..a1d5d823e --- /dev/null +++ b/src/cmd/gofmt/testdata/test.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# Copyright 2010 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. + +CMD="../gofmt" +TMP=test_tmp.go +COUNT=0 + + +cleanup() { + rm -f $TMP +} + + +error() { + echo $1 + exit 1 +} + + +count() { + #echo $1 + let COUNT=$COUNT+1 + let M=$COUNT%10 + if [ $M == 0 ]; then + echo -n "." + fi +} + + +test() { + count $1 + + # compare against .golden file + cleanup + $CMD -s $1 > $TMP + cmp -s $TMP $2 + if [ $? != 0 ]; then + diff $TMP $2 + error "Error: simplified $1 does not match $2" + fi + + # make sure .golden is idempotent + cleanup + $CMD -s $2 > $TMP + cmp -s $TMP $2 + if [ $? != 0 ]; then + diff $TMP $2 + error "Error: $2 is not idempotent" + fi +} + + +runtests() { + smoketest=../../../pkg/go/parser/parser.go + test $smoketest $smoketest + test composites.input composites.golden + # add more test cases here +} + + +runtests +cleanup +echo "PASSED ($COUNT tests)" |