diff options
Diffstat (limited to 'src/cmd/gofmt')
-rw-r--r-- | src/cmd/gofmt/Makefile | 19 | ||||
-rw-r--r-- | src/cmd/gofmt/doc.go | 73 | ||||
-rw-r--r-- | src/cmd/gofmt/gofmt.go | 261 | ||||
-rw-r--r-- | src/cmd/gofmt/gofmt_test.go | 79 | ||||
-rw-r--r-- | src/cmd/gofmt/rewrite.go | 291 | ||||
-rw-r--r-- | src/cmd/gofmt/simplify.go | 65 | ||||
-rwxr-xr-x | src/cmd/gofmt/test.sh | 162 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/composites.golden | 104 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/composites.input | 104 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/rewrite1.golden | 12 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/rewrite1.input | 12 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/rewrite2.golden | 10 | ||||
-rw-r--r-- | src/cmd/gofmt/testdata/rewrite2.input | 10 |
13 files changed, 1202 insertions, 0 deletions
diff --git a/src/cmd/gofmt/Makefile b/src/cmd/gofmt/Makefile new file mode 100644 index 000000000..dc5b060e6 --- /dev/null +++ b/src/cmd/gofmt/Makefile @@ -0,0 +1,19 @@ +# Copyright 2009 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. + +include ../../Make.inc + +TARG=gofmt +GOFILES=\ + gofmt.go\ + rewrite.go\ + simplify.go\ + +include ../../Make.cmd + +test: $(TARG) + ./test.sh + +testshort: + gotest -test.short diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go new file mode 100644 index 000000000..fca42b76b --- /dev/null +++ b/src/cmd/gofmt/doc.go @@ -0,0 +1,73 @@ +// Copyright 2009 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. + +/* +Gofmt formats Go programs. + +Without an explicit path, it processes the standard input. Given a file, +it operates on that file; given a directory, it operates on all .go files in +that directory, recursively. (Files starting with a period are ignored.) +By default, gofmt prints the reformatted sources to standard output. + +Usage: + gofmt [flags] [path ...] + +The flags are: + -d + Do not print reformatted sources to standard output. + If a file's formatting is different than gofmt's, print diffs + to standard output. + -e + Print all (including spurious) errors. + -l + Do not print reformatted sources to standard output. + If a file's formatting is different from gofmt's, print its name + to standard output. + -r rule + Apply the rewrite rule to the source before reformatting. + -s + Try to simplify code (after applying the rewrite rule, if any). + -w + Do not print reformatted sources to standard output. + If a file's formatting is different from gofmt's, overwrite it + with gofmt's version. + +Formatting control flags: + -comments=true + Print comments; if false, all comments are elided from the output. + -spaces + Align with spaces instead of tabs. + -tabindent + Indent with tabs independent of -spaces. + -tabwidth=8 + Tab width in spaces. + + +The rewrite rule specified with the -r flag must be a string of the form: + + pattern -> replacement + +Both pattern and replacement must be valid Go expressions. +In the pattern, single-character lowercase identifiers serve as +wildcards matching arbitrary sub-expressions; those expressions +will be substituted for the same identifiers in the replacement. + + +Examples + +To check files for unnecessary parentheses: + + gofmt -r '(a) -> a' -l *.go + +To remove the parentheses: + + gofmt -r '(a) -> a' -w *.go + +To convert the package tree from explicit slice upper bounds to implicit ones: + + gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src/pkg +*/ +package documentation + +// BUG(rsc): The implementation of -r is a bit slow. diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go new file mode 100644 index 000000000..975ae6ac6 --- /dev/null +++ b/src/cmd/gofmt/gofmt.go @@ -0,0 +1,261 @@ +// Copyright 2009 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 ( + "bytes" + "exec" + "flag" + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/scanner" + "go/token" + "io" + "io/ioutil" + "os" + "path/filepath" + "runtime/pprof" + "strings" +) + +var ( + // main operation modes + 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") + doDiff = flag.Bool("d", false, "display diffs instead of rewriting files") + allErrors = flag.Bool("e", false, "print all (including spurious) errors") + + // layout control + comments = flag.Bool("comments", true, "print comments") + tabWidth = flag.Int("tabwidth", 8, "tab width") + tabIndent = flag.Bool("tabindent", true, "indent with tabs independent of -spaces") + useSpaces = flag.Bool("spaces", true, "align with spaces instead of tabs") + + // debugging + cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file") +) + +var ( + fset = token.NewFileSet() + exitCode = 0 + rewrite func(*ast.File) *ast.File + parserMode uint + printerMode uint +) + +func report(err os.Error) { + scanner.PrintError(os.Stderr, err) + exitCode = 2 +} + +func usage() { + fmt.Fprintf(os.Stderr, "usage: gofmt [flags] [path ...]\n") + flag.PrintDefaults() + os.Exit(2) +} + +func initParserMode() { + parserMode = uint(0) + if *comments { + parserMode |= parser.ParseComments + } + if *allErrors { + parserMode |= parser.SpuriousErrors + } +} + +func initPrinterMode() { + printerMode = uint(0) + if *tabIndent { + printerMode |= printer.TabIndent + } + if *useSpaces { + printerMode |= printer.UseSpaces + } +} + +func isGoFile(f *os.FileInfo) bool { + // ignore non-Go files + return f.IsRegular() && !strings.HasPrefix(f.Name, ".") && strings.HasSuffix(f.Name, ".go") +} + +// If in == nil, the source is the contents of the file with the given filename. +func processFile(filename string, in io.Reader, out io.Writer) os.Error { + if in == nil { + f, err := os.Open(filename) + if err != nil { + return err + } + defer f.Close() + in = f + } + + src, err := ioutil.ReadAll(in) + if err != nil { + return err + } + + file, err := parser.ParseFile(fset, filename, src, parserMode) + if err != nil { + return err + } + + if rewrite != nil { + file = rewrite(file) + } + + if *simplifyAST { + simplify(file) + } + + var buf bytes.Buffer + _, err = (&printer.Config{printerMode, *tabWidth}).Fprint(&buf, fset, file) + if err != nil { + return err + } + res := buf.Bytes() + + if !bytes.Equal(src, res) { + // formatting has changed + if *list { + fmt.Fprintln(out, filename) + } + if *write { + err = ioutil.WriteFile(filename, res, 0) + if err != nil { + return err + } + } + if *doDiff { + data, err := diff(src, res) + if err != nil { + return fmt.Errorf("computing diff: %s", err) + } + fmt.Printf("diff %s gofmt/%s\n", filename, filename) + out.Write(data) + } + } + + if !*list && !*write && !*doDiff { + _, err = out.Write(res) + } + + return err +} + +type fileVisitor chan os.Error + +func (v fileVisitor) VisitDir(path string, f *os.FileInfo) bool { + return true +} + +func (v fileVisitor) VisitFile(path string, f *os.FileInfo) { + if isGoFile(f) { + v <- nil // synchronize error handler + if err := processFile(path, nil, os.Stdout); err != nil { + v <- err + } + } +} + +func walkDir(path string) { + v := make(fileVisitor) + go func() { + filepath.Walk(path, v, v) + close(v) + }() + for err := range v { + if err != nil { + report(err) + } + } +} + +func main() { + // call gofmtMain in a separate function + // so that it can use defer and have them + // run before the exit. + gofmtMain() + os.Exit(exitCode) +} + +func gofmtMain() { + flag.Usage = usage + flag.Parse() + if *tabWidth < 0 { + fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", *tabWidth) + exitCode = 2 + return + } + + if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + fmt.Fprintf(os.Stderr, "creating cpu profile: %s\n", err) + exitCode = 2 + return + } + defer f.Close() + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + + initParserMode() + initPrinterMode() + initRewrite() + + if flag.NArg() == 0 { + if err := processFile("<standard input>", os.Stdin, os.Stdout); err != nil { + report(err) + } + return + } + + for i := 0; i < flag.NArg(); i++ { + path := flag.Arg(i) + switch dir, err := os.Stat(path); { + case err != nil: + report(err) + case dir.IsRegular(): + if err := processFile(path, nil, os.Stdout); err != nil { + report(err) + } + case dir.IsDirectory(): + walkDir(path) + } + } +} + +func diff(b1, b2 []byte) (data []byte, err os.Error) { + f1, err := ioutil.TempFile("", "gofmt") + if err != nil { + return + } + defer os.Remove(f1.Name()) + defer f1.Close() + + f2, err := ioutil.TempFile("", "gofmt") + if err != nil { + return + } + defer os.Remove(f2.Name()) + defer f2.Close() + + f1.Write(b1) + f2.Write(b2) + + data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() + if len(data) > 0 { + // diff exits with a non-zero status when the files don't match. + // Ignore that failure as long as we get output. + err = nil + } + return + +} diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go new file mode 100644 index 000000000..2e35ce9a4 --- /dev/null +++ b/src/cmd/gofmt/gofmt_test.go @@ -0,0 +1,79 @@ +// 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. + +package main + +import ( + "bytes" + "io/ioutil" + "path/filepath" + "strings" + "testing" +) + +func runTest(t *testing.T, dirname, in, out, flags string) { + in = filepath.Join(dirname, in) + out = filepath.Join(dirname, out) + + // process flags + *simplifyAST = false + *rewriteRule = "" + for _, flag := range strings.Split(flags, " ") { + elts := strings.SplitN(flag, "=", 2) + name := elts[0] + value := "" + if len(elts) == 2 { + value = elts[1] + } + switch name { + case "": + // no flags + case "-r": + *rewriteRule = value + case "-s": + *simplifyAST = true + default: + t.Errorf("unrecognized flag name: %s", name) + } + } + + initParserMode() + initPrinterMode() + initRewrite() + + var buf bytes.Buffer + err := processFile(in, nil, &buf) + if err != nil { + t.Error(err) + return + } + + expected, err := ioutil.ReadFile(out) + if err != nil { + t.Error(err) + return + } + + if got := buf.Bytes(); bytes.Compare(got, expected) != 0 { + t.Errorf("(gofmt %s) != %s (see %s.gofmt)", in, out, in) + ioutil.WriteFile(in+".gofmt", got, 0666) + } +} + +// TODO(gri) Add more test cases! +var tests = []struct { + dirname, in, out, flags string +}{ + {".", "gofmt.go", "gofmt.go", ""}, + {".", "gofmt_test.go", "gofmt_test.go", ""}, + {"testdata", "composites.input", "composites.golden", "-s"}, + {"testdata", "rewrite1.input", "rewrite1.golden", "-r=Foo->Bar"}, + {"testdata", "rewrite2.input", "rewrite2.golden", "-r=int->bool"}, +} + +func TestRewrite(t *testing.T) { + for _, test := range tests { + runTest(t, test.dirname, test.in, test.out, test.flags) + } +} diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go new file mode 100644 index 000000000..3d74dea0f --- /dev/null +++ b/src/cmd/gofmt/rewrite.go @@ -0,0 +1,291 @@ +// Copyright 2009 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 ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "os" + "reflect" + "strings" + "unicode" + "utf8" +) + +func initRewrite() { + if *rewriteRule == "" { + rewrite = nil // disable any previous rewrite + return + } + f := strings.Split(*rewriteRule, "->") + if len(f) != 2 { + fmt.Fprintf(os.Stderr, "rewrite rule must be of the form 'pattern -> replacement'\n") + os.Exit(2) + } + pattern := parseExpr(f[0], "pattern") + replace := parseExpr(f[1], "replacement") + rewrite = func(p *ast.File) *ast.File { return rewriteFile(pattern, replace, p) } +} + +// parseExpr parses s as an expression. +// It might make sense to expand this to allow statement patterns, +// 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(fset, "input", s) + if err != nil { + fmt.Fprintf(os.Stderr, "parsing %s %s: %s\n", what, s, err) + os.Exit(2) + } + return x +} + +// Keep this function for debugging. +/* +func dump(msg string, val reflect.Value) { + fmt.Printf("%s:\n", msg) + ast.Print(fset, val.Interface()) + fmt.Println() +} +*/ + +// rewriteFile applies the rewrite rule 'pattern -> replace' to an entire file. +func rewriteFile(pattern, replace ast.Expr, p *ast.File) *ast.File { + m := make(map[string]reflect.Value) + pat := reflect.ValueOf(pattern) + repl := reflect.ValueOf(replace) + var f func(val reflect.Value) reflect.Value // f is recursive + f = func(val reflect.Value) reflect.Value { + // don't bother if val is invalid to start with + if !val.IsValid() { + return reflect.Value{} + } + for k := range m { + m[k] = reflect.Value{}, false + } + val = apply(f, val) + if match(m, pat, val) { + val = subst(m, repl, reflect.ValueOf(val.Interface().(ast.Node).Pos())) + } + return val + } + return apply(f, reflect.ValueOf(p)).Interface().(*ast.File) +} + +// 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) { + // don't bother if y is invalid to start with + if !y.IsValid() { + return + } + 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.Set(y) +} + +// Values/types for special cases. +var ( + objectPtrNil = reflect.ValueOf((*ast.Object)(nil)) + scopePtrNil = reflect.ValueOf((*ast.Scope)(nil)) + + identType = reflect.TypeOf((*ast.Ident)(nil)) + objectPtrType = reflect.TypeOf((*ast.Object)(nil)) + positionType = reflect.TypeOf(token.NoPos) + scopePtrType = reflect.TypeOf((*ast.Scope)(nil)) +) + +// apply replaces each AST field x in val with f(x), returning val. +// To avoid extra conversions, f operates on the reflect.Value form. +func apply(f func(reflect.Value) reflect.Value, val reflect.Value) reflect.Value { + if !val.IsValid() { + return reflect.Value{} + } + + // *ast.Objects introduce cycles and are likely incorrect after + // rewrite; don't follow them but replace with nil instead + if val.Type() == objectPtrType { + return objectPtrNil + } + + // similarly for scopes: they are likely incorrect after a rewrite; + // replace them with nil + if val.Type() == scopePtrType { + return scopePtrNil + } + + switch v := reflect.Indirect(val); v.Kind() { + case reflect.Slice: + for i := 0; i < v.Len(); i++ { + e := v.Index(i) + setValue(e, f(e)) + } + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + e := v.Field(i) + setValue(e, f(e)) + } + case reflect.Interface: + e := v.Elem() + setValue(v, f(e)) + } + return val +} + +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. +func match(m map[string]reflect.Value, pattern, val reflect.Value) bool { + // Wildcard matches any expression. If it appears multiple + // times in the pattern, it must match the same expression + // each time. + if m != nil && pattern.IsValid() && pattern.Type() == identType { + name := pattern.Interface().(*ast.Ident).Name + if isWildcard(name) && val.IsValid() { + // 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 + } + } + } + + // Otherwise, pattern and val must match recursively. + if !pattern.IsValid() || !val.IsValid() { + return !pattern.IsValid() && !val.IsValid() + } + if pattern.Type() != val.Type() { + return false + } + + // Special cases. + switch pattern.Type() { + case identType: + // For identifiers, only the names need to match + // (and none of the other *ast.Object information). + // This is a common case, handle it all here instead + // 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 + case objectPtrType, positionType: + // object pointers and token positions don't need to match + return true + } + + p := reflect.Indirect(pattern) + v := reflect.Indirect(val) + if !p.IsValid() || !v.IsValid() { + return !p.IsValid() && !v.IsValid() + } + + switch p.Kind() { + case reflect.Slice: + if p.Len() != v.Len() { + return false + } + for i := 0; i < p.Len(); i++ { + if !match(m, p.Index(i), v.Index(i)) { + return false + } + } + return true + + case reflect.Struct: + if p.NumField() != v.NumField() { + return false + } + for i := 0; i < p.NumField(); i++ { + if !match(m, p.Field(i), v.Field(i)) { + return false + } + } + return true + + case reflect.Interface: + return match(m, p.Elem(), v.Elem()) + } + + // Handle token integers, etc. + return p.Interface() == v.Interface() +} + +// subst returns a copy of pattern with values from m substituted in place +// of wildcards and pos used as the position of tokens from the pattern. +// if m == nil, subst returns a copy of pattern and doesn't change the line +// number information. +func subst(m map[string]reflect.Value, pattern reflect.Value, pos reflect.Value) reflect.Value { + if !pattern.IsValid() { + return reflect.Value{} + } + + // Wildcard gets replaced with map value. + if m != nil && pattern.Type() == identType { + name := pattern.Interface().(*ast.Ident).Name + if isWildcard(name) { + if old, ok := m[name]; ok { + return subst(nil, old, reflect.Value{}) + } + } + } + + if pos.IsValid() && 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 + } + + // Otherwise copy. + switch p := pattern; p.Kind() { + case reflect.Slice: + v := reflect.MakeSlice(p.Type(), p.Len(), p.Len()) + for i := 0; i < p.Len(); i++ { + v.Index(i).Set(subst(m, p.Index(i), pos)) + } + return v + + case reflect.Struct: + v := reflect.New(p.Type()).Elem() + for i := 0; i < p.NumField(); i++ { + v.Field(i).Set(subst(m, p.Field(i), pos)) + } + return v + + case reflect.Ptr: + v := reflect.New(p.Type()).Elem() + if elem := p.Elem(); elem.IsValid() { + v.Set(subst(m, elem, pos).Addr()) + } + return v + + case reflect.Interface: + v := reflect.New(p.Type()).Elem() + if elem := p.Elem(); elem.IsValid() { + v.Set(subst(m, elem, pos)) + } + return v + } + + return pattern +} diff --git a/src/cmd/gofmt/simplify.go b/src/cmd/gofmt/simplify.go new file mode 100644 index 000000000..d9afc0e7b --- /dev/null +++ b/src/cmd/gofmt/simplify.go @@ -0,0 +1,65 @@ +// 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.ValueOf(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.ValueOf(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 new file mode 100755 index 000000000..063a0727f --- /dev/null +++ b/src/cmd/gofmt/test.sh @@ -0,0 +1,162 @@ +#!/usr/bin/env bash +# Copyright 2009 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. + +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 +fi + +CMD="./gofmt" +TMP1=test_tmp1.go +TMP2=test_tmp2.go +TMP3=test_tmp3.go +COUNT=0 + +count() { + #echo $1 + let COUNT=$COUNT+1 + let M=$COUNT%10 + if [ $M == 0 ]; then + echo -n "." + fi +} + + +error() { + echo $1 + exit 1 +} + + +# apply to one file +apply1() { + # the following files are skipped because they are test cases + # for syntax errors and thus won't parse in the first place: + case `basename "$F"` in + func3.go | const2.go | char_lit1.go | blank1.go | ddd1.go | \ + bug014.go | bug050.go | bug068.go | bug083.go | bug088.go | \ + 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 | bug287.go | bug298.go | bug299.go | bug300.go | \ + bug302.go | bug306.go | bug322.go | bug324.go | bug335.go | \ + bug340.go | bug349.go | bug351.go | bug358.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: + case `dirname "$F"` in + $GOROOT/test/syntax ) return ;; + esac + #echo $1 $2 + "$1" "$2"; count "$F" +} + + +# apply to local files +applydot() { + for F in `find . -name "*.go" | grep -v "._"`; do + apply1 "$1" $F + done +} + + +# apply to all .go files we can find +apply() { + for F in `find "$GOROOT" -name "*.go" | grep -v "._"`; do + apply1 "$1" $F + done +} + + +cleanup() { + rm -f $TMP1 $TMP2 $TMP3 +} + + +silent() { + cleanup + $CMD "$1" > /dev/null 2> $TMP1 + if [ $? != 0 ]; then + cat $TMP1 + error "Error (silent mode test): test.sh $1" + fi +} + + +idempotent() { + cleanup + $CMD "$1" > $TMP1 + if [ $? != 0 ]; then + error "Error (step 1 of idempotency test): test.sh $1" + fi + + $CMD $TMP1 > $TMP2 + if [ $? != 0 ]; then + error "Error (step 2 of idempotency test): test.sh $1" + fi + + $CMD $TMP2 > $TMP3 + if [ $? != 0 ]; then + error "Error (step 3 of idempotency test): test.sh $1" + fi + + cmp -s $TMP2 $TMP3 + if [ $? != 0 ]; then + diff $TMP2 $TMP3 + error "Error (step 4 of idempotency test): test.sh $1" + fi +} + + +valid() { + cleanup + $CMD "$1" > $TMP1 + if [ $? != 0 ]; then + error "Error (step 1 of validity test): test.sh $1" + fi + + $GC -o /dev/null $TMP1 + if [ $? != 0 ]; then + error "Error (step 2 of validity test): test.sh $1" + fi +} + + +runtest() { + #echo "Testing silent mode" + cleanup + "$1" silent "$2" + + #echo "Testing idempotency" + cleanup + "$1" idempotent "$2" +} + + +runtests() { + if [ $# = 0 ]; then + runtest apply + # verify the pretty-printed files can be compiled with $GC again + # do it in local directory only because of the prerequisites required + #echo "Testing validity" + # Disabled for now due to dependency problems + # cleanup + # applydot valid + else + for F in "$@"; do + runtest apply1 "$F" + done + fi +} + + +# run over all .go files +runtests "$@" +cleanup + +# done +echo +echo "PASSED ($COUNT tests)" 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/rewrite1.golden b/src/cmd/gofmt/testdata/rewrite1.golden new file mode 100644 index 000000000..d9beb3705 --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite1.golden @@ -0,0 +1,12 @@ +// 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. + +package main + +type Bar int + +func main() { + var a Bar + println(a) +} diff --git a/src/cmd/gofmt/testdata/rewrite1.input b/src/cmd/gofmt/testdata/rewrite1.input new file mode 100644 index 000000000..bdb894320 --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite1.input @@ -0,0 +1,12 @@ +// 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. + +package main + +type Foo int + +func main() { + var a Foo + println(a) +} diff --git a/src/cmd/gofmt/testdata/rewrite2.golden b/src/cmd/gofmt/testdata/rewrite2.golden new file mode 100644 index 000000000..64c67ffa6 --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite2.golden @@ -0,0 +1,10 @@ +// 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. + +package p + +// Slices have nil Len values in the corresponding ast.ArrayType +// node and reflect.NewValue(slice.Len) is an invalid reflect.Value. +// The rewriter must not crash in that case. Was issue 1696. +func f() []bool {} diff --git a/src/cmd/gofmt/testdata/rewrite2.input b/src/cmd/gofmt/testdata/rewrite2.input new file mode 100644 index 000000000..21171447a --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite2.input @@ -0,0 +1,10 @@ +// 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. + +package p + +// Slices have nil Len values in the corresponding ast.ArrayType +// node and reflect.NewValue(slice.Len) is an invalid reflect.Value. +// The rewriter must not crash in that case. Was issue 1696. +func f() []int {} |