summaryrefslogtreecommitdiff
path: root/src/cmd/gofmt
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-01-17 12:40:45 +0100
committerOndřej Surý <ondrej@sury.org>2011-01-17 12:40:45 +0100
commit3e45412327a2654a77944249962b3652e6142299 (patch)
treebc3bf69452afa055423cbe0c5cfa8ca357df6ccf /src/cmd/gofmt
parentc533680039762cacbc37db8dc7eed074c3e497be (diff)
downloadgolang-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/Makefile6
-rw-r--r--src/cmd/gofmt/doc.go4
-rw-r--r--src/cmd/gofmt/gofmt.go25
-rw-r--r--src/cmd/gofmt/rewrite.go61
-rw-r--r--src/cmd/gofmt/simplify.go67
-rwxr-xr-xsrc/cmd/gofmt/test.sh5
-rw-r--r--src/cmd/gofmt/testdata/composites.golden104
-rw-r--r--src/cmd/gofmt/testdata/composites.input104
-rwxr-xr-xsrc/cmd/gofmt/testdata/test.sh65
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)"