diff options
Diffstat (limited to 'src/cmd/gofmt')
68 files changed, 4121 insertions, 0 deletions
diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go new file mode 100644 index 0000000..8ac9c6a --- /dev/null +++ b/src/cmd/gofmt/doc.go @@ -0,0 +1,106 @@ +// 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. +It uses tabs for indentation and blanks for alignment. +Alignment assumes that an editor is using a fixed-width font. + +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. If an error occurred during overwriting, + the original file is restored from an automatic backup. + +Debugging support: + + -cpuprofile filename + Write cpu profile to the specified file. + +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. + +When gofmt reads from standard input, it accepts either a full Go program +or a program fragment. A program fragment must be a syntactically +valid declaration list, statement list, or expression. When formatting +such a fragment, gofmt preserves leading indentation as well as leading +and trailing spaces, so that individual sections of a Go program can be +formatted by piping them through gofmt. + +# 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 + +# The simplify command + +When invoked with -s gofmt will make the following source transformations where possible. + + An array, slice, or map composite literal of the form: + []T{T{}, T{}} + will be simplified to: + []T{{}, {}} + + A slice expression of the form: + s[a:len(s)] + will be simplified to: + s[a:] + + A range of the form: + for x, _ = range v {...} + will be simplified to: + for x = range v {...} + + A range of the form: + for _ = range v {...} + will be simplified to: + for range v {...} + +This may result in changes that are incompatible with earlier versions of Go. +*/ +package main + +// BUG(rsc): The implementation of -r is a bit slow. +// BUG(gri): If -w fails, the restored original file may not have some of the +// original file attributes. diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go new file mode 100644 index 0000000..f4fb6bf --- /dev/null +++ b/src/cmd/gofmt/gofmt.go @@ -0,0 +1,570 @@ +// 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" + "context" + "flag" + "fmt" + "go/ast" + "go/parser" + "go/printer" + "go/scanner" + "go/token" + "internal/diff" + "io" + "io/fs" + "math/rand" + "os" + "path/filepath" + "runtime" + "runtime/pprof" + "strconv" + "strings" + + "golang.org/x/sync/semaphore" +) + +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., '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, "report all errors (not just the first 10 on different lines)") + + // debugging + cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file") +) + +// Keep these in sync with go/format/format.go. +const ( + tabWidth = 8 + printerMode = printer.UseSpaces | printer.TabIndent | printerNormalizeNumbers + + // printerNormalizeNumbers means to canonicalize number literal prefixes + // and exponents while printing. See https://golang.org/doc/go1.13#gofmt. + // + // This value is defined in go/printer specifically for go/format and cmd/gofmt. + printerNormalizeNumbers = 1 << 30 +) + +// fdSem guards the number of concurrently-open file descriptors. +// +// For now, this is arbitrarily set to 200, based on the observation that many +// platforms default to a kernel limit of 256. Ideally, perhaps we should derive +// it from rlimit on platforms that support that system call. +// +// File descriptors opened from outside of this package are not tracked, +// so this limit may be approximate. +var fdSem = make(chan bool, 200) + +var ( + rewrite func(*token.FileSet, *ast.File) *ast.File + parserMode parser.Mode +) + +func usage() { + fmt.Fprintf(os.Stderr, "usage: gofmt [flags] [path ...]\n") + flag.PrintDefaults() +} + +func initParserMode() { + parserMode = parser.ParseComments + if *allErrors { + parserMode |= parser.AllErrors + } + // It's only -r that makes use of go/ast's object resolution, + // so avoid the unnecessary work if the flag isn't used. + if *rewriteRule == "" { + parserMode |= parser.SkipObjectResolution + } +} + +func isGoFile(f fs.DirEntry) bool { + // ignore non-Go files + name := f.Name() + return !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") && !f.IsDir() +} + +// A sequencer performs concurrent tasks that may write output, but emits that +// output in a deterministic order. +type sequencer struct { + maxWeight int64 + sem *semaphore.Weighted // weighted by input bytes (an approximate proxy for memory overhead) + prev <-chan *reporterState // 1-buffered +} + +// newSequencer returns a sequencer that allows concurrent tasks up to maxWeight +// and writes tasks' output to out and err. +func newSequencer(maxWeight int64, out, err io.Writer) *sequencer { + sem := semaphore.NewWeighted(maxWeight) + prev := make(chan *reporterState, 1) + prev <- &reporterState{out: out, err: err} + return &sequencer{ + maxWeight: maxWeight, + sem: sem, + prev: prev, + } +} + +// exclusive is a weight that can be passed to a sequencer to cause +// a task to be executed without any other concurrent tasks. +const exclusive = -1 + +// Add blocks until the sequencer has enough weight to spare, then adds f as a +// task to be executed concurrently. +// +// If the weight is either negative or larger than the sequencer's maximum +// weight, Add blocks until all other tasks have completed, then the task +// executes exclusively (blocking all other calls to Add until it completes). +// +// f may run concurrently in a goroutine, but its output to the passed-in +// reporter will be sequential relative to the other tasks in the sequencer. +// +// If f invokes a method on the reporter, execution of that method may block +// until the previous task has finished. (To maximize concurrency, f should +// avoid invoking the reporter until it has finished any parallelizable work.) +// +// If f returns a non-nil error, that error will be reported after f's output +// (if any) and will cause a nonzero final exit code. +func (s *sequencer) Add(weight int64, f func(*reporter) error) { + if weight < 0 || weight > s.maxWeight { + weight = s.maxWeight + } + if err := s.sem.Acquire(context.TODO(), weight); err != nil { + // Change the task from "execute f" to "report err". + weight = 0 + f = func(*reporter) error { return err } + } + + r := &reporter{prev: s.prev} + next := make(chan *reporterState, 1) + s.prev = next + + // Start f in parallel: it can run until it invokes a method on r, at which + // point it will block until the previous task releases the output state. + go func() { + if err := f(r); err != nil { + r.Report(err) + } + next <- r.getState() // Release the next task. + s.sem.Release(weight) + }() +} + +// AddReport prints an error to s after the output of any previously-added +// tasks, causing the final exit code to be nonzero. +func (s *sequencer) AddReport(err error) { + s.Add(0, func(*reporter) error { return err }) +} + +// GetExitCode waits for all previously-added tasks to complete, then returns an +// exit code for the sequence suitable for passing to os.Exit. +func (s *sequencer) GetExitCode() int { + c := make(chan int, 1) + s.Add(0, func(r *reporter) error { + c <- r.ExitCode() + return nil + }) + return <-c +} + +// A reporter reports output, warnings, and errors. +type reporter struct { + prev <-chan *reporterState + state *reporterState +} + +// reporterState carries the state of a reporter instance. +// +// Only one reporter at a time may have access to a reporterState. +type reporterState struct { + out, err io.Writer + exitCode int +} + +// getState blocks until any prior reporters are finished with the reporter +// state, then returns the state for manipulation. +func (r *reporter) getState() *reporterState { + if r.state == nil { + r.state = <-r.prev + } + return r.state +} + +// Warnf emits a warning message to the reporter's error stream, +// without changing its exit code. +func (r *reporter) Warnf(format string, args ...any) { + fmt.Fprintf(r.getState().err, format, args...) +} + +// Write emits a slice to the reporter's output stream. +// +// Any error is returned to the caller, and does not otherwise affect the +// reporter's exit code. +func (r *reporter) Write(p []byte) (int, error) { + return r.getState().out.Write(p) +} + +// Report emits a non-nil error to the reporter's error stream, +// changing its exit code to a nonzero value. +func (r *reporter) Report(err error) { + if err == nil { + panic("Report with nil error") + } + st := r.getState() + scanner.PrintError(st.err, err) + st.exitCode = 2 +} + +func (r *reporter) ExitCode() int { + return r.getState().exitCode +} + +// If info == nil, we are formatting stdin instead of a file. +// If in == nil, the source is the contents of the file with the given filename. +func processFile(filename string, info fs.FileInfo, in io.Reader, r *reporter) error { + src, err := readFile(filename, info, in) + if err != nil { + return err + } + + fileSet := token.NewFileSet() + // If we are formatting stdin, we accept a program fragment in lieu of a + // complete source file. + fragmentOk := info == nil + file, sourceAdj, indentAdj, err := parse(fileSet, filename, src, fragmentOk) + if err != nil { + return err + } + + if rewrite != nil { + if sourceAdj == nil { + file = rewrite(fileSet, file) + } else { + r.Warnf("warning: rewrite ignored for incomplete programs\n") + } + } + + ast.SortImports(fileSet, file) + + if *simplifyAST { + simplify(file) + } + + res, err := format(fileSet, file, sourceAdj, indentAdj, src, printer.Config{Mode: printerMode, Tabwidth: tabWidth}) + if err != nil { + return err + } + + if !bytes.Equal(src, res) { + // formatting has changed + if *list { + fmt.Fprintln(r, filename) + } + if *write { + if info == nil { + panic("-w should not have been allowed with stdin") + } + + perm := info.Mode().Perm() + if err := writeFile(filename, src, res, perm, info.Size()); err != nil { + return err + } + } + if *doDiff { + newName := filepath.ToSlash(filename) + oldName := newName + ".orig" + r.Write(diff.Diff(oldName, src, newName, res)) + } + } + + if !*list && !*write && !*doDiff { + _, err = r.Write(res) + } + + return err +} + +// readFile reads the contents of filename, described by info. +// If in is non-nil, readFile reads directly from it. +// Otherwise, readFile opens and reads the file itself, +// with the number of concurrently-open files limited by fdSem. +func readFile(filename string, info fs.FileInfo, in io.Reader) ([]byte, error) { + if in == nil { + fdSem <- true + var err error + f, err := os.Open(filename) + if err != nil { + return nil, err + } + in = f + defer func() { + f.Close() + <-fdSem + }() + } + + // Compute the file's size and read its contents with minimal allocations. + // + // If we have the FileInfo from filepath.WalkDir, use it to make + // a buffer of the right size and avoid ReadAll's reallocations. + // + // If the size is unknown (or bogus, or overflows an int), fall back to + // a size-independent ReadAll. + size := -1 + if info != nil && info.Mode().IsRegular() && int64(int(info.Size())) == info.Size() { + size = int(info.Size()) + } + if size+1 <= 0 { + // The file is not known to be regular, so we don't have a reliable size for it. + var err error + src, err := io.ReadAll(in) + if err != nil { + return nil, err + } + return src, nil + } + + // We try to read size+1 bytes so that we can detect modifications: if we + // read more than size bytes, then the file was modified concurrently. + // (If that happens, we could, say, append to src to finish the read, or + // proceed with a truncated buffer — but the fact that it changed at all + // indicates a possible race with someone editing the file, so we prefer to + // stop to avoid corrupting it.) + src := make([]byte, size+1) + n, err := io.ReadFull(in, src) + switch err { + case nil, io.EOF, io.ErrUnexpectedEOF: + // io.ReadFull returns io.EOF (for an empty file) or io.ErrUnexpectedEOF + // (for a non-empty file) if the file was changed unexpectedly. Continue + // with comparing file sizes in those cases. + default: + return nil, err + } + if n < size { + return nil, fmt.Errorf("error: size of %s changed during reading (from %d to %d bytes)", filename, size, n) + } else if n > size { + return nil, fmt.Errorf("error: size of %s changed during reading (from %d to >=%d bytes)", filename, size, len(src)) + } + return src[:n], nil +} + +func main() { + // Arbitrarily limit in-flight work to 2MiB times the number of threads. + // + // The actual overhead for the parse tree and output will depend on the + // specifics of the file, but this at least keeps the footprint of the process + // roughly proportional to GOMAXPROCS. + maxWeight := (2 << 20) * int64(runtime.GOMAXPROCS(0)) + s := newSequencer(maxWeight, os.Stdout, os.Stderr) + + // call gofmtMain in a separate function + // so that it can use defer and have them + // run before the exit. + gofmtMain(s) + os.Exit(s.GetExitCode()) +} + +func gofmtMain(s *sequencer) { + flag.Usage = usage + flag.Parse() + + if *cpuprofile != "" { + fdSem <- true + f, err := os.Create(*cpuprofile) + if err != nil { + s.AddReport(fmt.Errorf("creating cpu profile: %s", err)) + return + } + defer func() { + f.Close() + <-fdSem + }() + pprof.StartCPUProfile(f) + defer pprof.StopCPUProfile() + } + + initParserMode() + initRewrite() + + args := flag.Args() + if len(args) == 0 { + if *write { + s.AddReport(fmt.Errorf("error: cannot use -w with standard input")) + return + } + s.Add(0, func(r *reporter) error { + return processFile("<standard input>", nil, os.Stdin, r) + }) + return + } + + for _, arg := range args { + switch info, err := os.Stat(arg); { + case err != nil: + s.AddReport(err) + case !info.IsDir(): + // Non-directory arguments are always formatted. + arg := arg + s.Add(fileWeight(arg, info), func(r *reporter) error { + return processFile(arg, info, nil, r) + }) + default: + // Directories are walked, ignoring non-Go files. + err := filepath.WalkDir(arg, func(path string, f fs.DirEntry, err error) error { + if err != nil || !isGoFile(f) { + return err + } + info, err := f.Info() + if err != nil { + s.AddReport(err) + return nil + } + s.Add(fileWeight(path, info), func(r *reporter) error { + return processFile(path, info, nil, r) + }) + return nil + }) + if err != nil { + s.AddReport(err) + } + } + } +} + +func fileWeight(path string, info fs.FileInfo) int64 { + if info == nil { + return exclusive + } + if info.Mode().Type() == fs.ModeSymlink { + var err error + info, err = os.Stat(path) + if err != nil { + return exclusive + } + } + if !info.Mode().IsRegular() { + // For non-regular files, FileInfo.Size is system-dependent and thus not a + // reliable indicator of weight. + return exclusive + } + return info.Size() +} + +// writeFile updates a file with the new formatted data. +func writeFile(filename string, orig, formatted []byte, perm fs.FileMode, size int64) error { + // Make a temporary backup file before rewriting the original file. + bakname, err := backupFile(filename, orig, perm) + if err != nil { + return err + } + + fdSem <- true + defer func() { <-fdSem }() + + fout, err := os.OpenFile(filename, os.O_WRONLY, perm) + if err != nil { + // We couldn't even open the file, so it should + // not have changed. + os.Remove(bakname) + return err + } + defer fout.Close() // for error paths + + restoreFail := func(err error) { + fmt.Fprintf(os.Stderr, "gofmt: %s: error restoring file to original: %v; backup in %s\n", filename, err, bakname) + } + + n, err := fout.Write(formatted) + if err == nil && int64(n) < size { + err = fout.Truncate(int64(n)) + } + + if err != nil { + // Rewriting the file failed. + + if n == 0 { + // Original file unchanged. + os.Remove(bakname) + return err + } + + // Try to restore the original contents. + + no, erro := fout.WriteAt(orig, 0) + if erro != nil { + // That failed too. + restoreFail(erro) + return err + } + + if no < n { + // Original file is shorter. Truncate. + if erro = fout.Truncate(int64(no)); erro != nil { + restoreFail(erro) + return err + } + } + + if erro := fout.Close(); erro != nil { + restoreFail(erro) + return err + } + + // Original contents restored. + os.Remove(bakname) + return err + } + + if err := fout.Close(); err != nil { + restoreFail(err) + return err + } + + // File updated. + os.Remove(bakname) + return nil +} + +// backupFile writes data to a new file named filename<number> with permissions perm, +// with <number> randomly chosen such that the file name is unique. backupFile returns +// the chosen file name. +func backupFile(filename string, data []byte, perm fs.FileMode) (string, error) { + fdSem <- true + defer func() { <-fdSem }() + + nextRandom := func() string { + return strconv.Itoa(rand.Int()) + } + + dir, base := filepath.Split(filename) + var ( + bakname string + f *os.File + ) + for { + bakname = filepath.Join(dir, base+"."+nextRandom()) + var err error + f, err = os.OpenFile(bakname, os.O_RDWR|os.O_CREATE|os.O_EXCL, perm) + if err == nil { + break + } + if err != nil && !os.IsExist(err) { + return "", err + } + } + + // write data to backup file + _, err := f.Write(data) + if err1 := f.Close(); err == nil { + err = err1 + } + + return bakname, err +} diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go new file mode 100644 index 0000000..6b80673 --- /dev/null +++ b/src/cmd/gofmt/gofmt_test.go @@ -0,0 +1,195 @@ +// 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" + "flag" + "internal/diff" + "os" + "path/filepath" + "strings" + "testing" + "text/scanner" +) + +var update = flag.Bool("update", false, "update .golden files") + +// gofmtFlags looks for a comment of the form +// +// //gofmt flags +// +// within the first maxLines lines of the given file, +// and returns the flags string, if any. Otherwise it +// returns the empty string. +func gofmtFlags(filename string, maxLines int) string { + f, err := os.Open(filename) + if err != nil { + return "" // ignore errors - they will be found later + } + defer f.Close() + + // initialize scanner + var s scanner.Scanner + s.Init(f) + s.Error = func(*scanner.Scanner, string) {} // ignore errors + s.Mode = scanner.GoTokens &^ scanner.SkipComments // want comments + + // look for //gofmt comment + for s.Line <= maxLines { + switch s.Scan() { + case scanner.Comment: + const prefix = "//gofmt " + if t := s.TokenText(); strings.HasPrefix(t, prefix) { + return strings.TrimSpace(t[len(prefix):]) + } + case scanner.EOF: + return "" + } + } + + return "" +} + +func runTest(t *testing.T, in, out string) { + // process flags + *simplifyAST = false + *rewriteRule = "" + info, err := os.Lstat(in) + if err != nil { + t.Error(err) + return + } + for _, flag := range strings.Split(gofmtFlags(in, 20), " ") { + 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 + case "-stdin": + // fake flag - pretend input is from stdin + info = nil + default: + t.Errorf("unrecognized flag name: %s", name) + } + } + + initParserMode() + initRewrite() + + const maxWeight = 2 << 20 + var buf, errBuf bytes.Buffer + s := newSequencer(maxWeight, &buf, &errBuf) + s.Add(fileWeight(in, info), func(r *reporter) error { + return processFile(in, info, nil, r) + }) + if errBuf.Len() > 0 { + t.Logf("%q", errBuf.Bytes()) + } + if s.GetExitCode() != 0 { + t.Fail() + } + + expected, err := os.ReadFile(out) + if err != nil { + t.Error(err) + return + } + + if got := buf.Bytes(); !bytes.Equal(got, expected) { + if *update { + if in != out { + if err := os.WriteFile(out, got, 0666); err != nil { + t.Error(err) + } + return + } + // in == out: don't accidentally destroy input + t.Errorf("WARNING: -update did not rewrite input file %s", in) + } + + t.Errorf("(gofmt %s) != %s (see %s.gofmt)\n%s", in, out, in, + diff.Diff("expected", expected, "got", got)) + if err := os.WriteFile(in+".gofmt", got, 0666); err != nil { + t.Error(err) + } + } +} + +// TestRewrite processes testdata/*.input files and compares them to the +// corresponding testdata/*.golden files. The gofmt flags used to process +// a file must be provided via a comment of the form +// +// //gofmt flags +// +// in the processed file within the first 20 lines, if any. +func TestRewrite(t *testing.T) { + // determine input files + match, err := filepath.Glob("testdata/*.input") + if err != nil { + t.Fatal(err) + } + + // add larger examples + match = append(match, "gofmt.go", "gofmt_test.go") + + for _, in := range match { + name := filepath.Base(in) + t.Run(name, func(t *testing.T) { + out := in // for files where input and output are identical + if strings.HasSuffix(in, ".input") { + out = in[:len(in)-len(".input")] + ".golden" + } + runTest(t, in, out) + if in != out && !t.Failed() { + // Check idempotence. + runTest(t, out, out) + } + }) + } +} + +// Test case for issue 3961. +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 := os.ReadFile(input) + if err != nil { + t.Error(err) + } + if !bytes.Contains(data, []byte("\r\n")) { + t.Errorf("%s contains no CR/LF's", input) + } + + data, err = os.ReadFile(golden) + if err != nil { + t.Error(err) + } + if bytes.Contains(data, []byte("\r")) { + t.Errorf("%s contains CR's", golden) + } +} + +func TestBackupFile(t *testing.T) { + dir, err := os.MkdirTemp("", "gofmt_test") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + name, err := backupFile(filepath.Join(dir, "foo.go"), []byte(" package main"), 0644) + if err != nil { + t.Fatal(err) + } + t.Logf("Created: %s", name) +} diff --git a/src/cmd/gofmt/gofmt_unix_test.go b/src/cmd/gofmt/gofmt_unix_test.go new file mode 100644 index 0000000..fec5143 --- /dev/null +++ b/src/cmd/gofmt/gofmt_unix_test.go @@ -0,0 +1,67 @@ +// Copyright 2023 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. + +//go:build unix + +package main + +import ( + "os" + "path/filepath" + "strings" + "testing" + "time" +) + +func TestPermissions(t *testing.T) { + if os.Getuid() == 0 { + t.Skip("skipping permission test when running as root") + } + + dir := t.TempDir() + fn := filepath.Join(dir, "perm.go") + + // Create a file that needs formatting without write permission. + if err := os.WriteFile(filepath.Join(fn), []byte(" package main"), 0o400); err != nil { + t.Fatal(err) + } + + // Set mtime of the file in the past. + past := time.Now().Add(-time.Hour) + if err := os.Chtimes(fn, past, past); err != nil { + t.Fatal(err) + } + + info, err := os.Stat(fn) + if err != nil { + t.Fatal(err) + } + + defer func() { *write = false }() + *write = true + + initParserMode() + initRewrite() + + const maxWeight = 2 << 20 + var buf, errBuf strings.Builder + s := newSequencer(maxWeight, &buf, &errBuf) + s.Add(fileWeight(fn, info), func(r *reporter) error { + return processFile(fn, info, nil, r) + }) + if s.GetExitCode() == 0 { + t.Fatal("rewrite of read-only file succeeded unexpectedly") + } + if errBuf.Len() > 0 { + t.Log(errBuf) + } + + info, err = os.Stat(fn) + if err != nil { + t.Fatal(err) + } + if !info.ModTime().Equal(past) { + t.Errorf("after rewrite mod time is %v, want %v", info.ModTime(), past) + } +} diff --git a/src/cmd/gofmt/internal.go b/src/cmd/gofmt/internal.go new file mode 100644 index 0000000..058158a --- /dev/null +++ b/src/cmd/gofmt/internal.go @@ -0,0 +1,176 @@ +// Copyright 2015 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. + +// TODO(gri): This file and the file src/go/format/internal.go are +// the same (but for this comment and the package name). Do not modify +// one without the other. Determine if we can factor out functionality +// in a public API. See also #11844 for context. + +package main + +import ( + "bytes" + "go/ast" + "go/parser" + "go/printer" + "go/token" + "strings" +) + +// parse parses src, which was read from the named file, +// as a Go source file, declaration, or statement list. +func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) ( + file *ast.File, + sourceAdj func(src []byte, indent int) []byte, + indentAdj int, + err error, +) { + // Try as whole source file. + file, err = parser.ParseFile(fset, filename, src, parserMode) + // If there's no error, return. If the error is that the source file didn't begin with a + // package line and source fragments are ok, fall through to + // try as a source fragment. Stop and return on any other error. + if err == nil || !fragmentOk || !strings.Contains(err.Error(), "expected 'package'") { + return + } + + // If this is a declaration list, make it a source file + // by inserting a package clause. + // Insert using a ';', not a newline, so that the line numbers + // in psrc match the ones in src. + psrc := append([]byte("package p;"), src...) + file, err = parser.ParseFile(fset, filename, psrc, parserMode) + if err == nil { + sourceAdj = func(src []byte, indent int) []byte { + // Remove the package clause. + // Gofmt has turned the ';' into a '\n'. + src = src[indent+len("package p\n"):] + return bytes.TrimSpace(src) + } + return + } + // If the error is that the source file didn't begin with a + // declaration, fall through to try as a statement list. + // Stop and return on any other error. + if !strings.Contains(err.Error(), "expected declaration") { + return + } + + // If this is a statement list, make it a source file + // by inserting a package clause and turning the list + // into a function body. This handles expressions too. + // Insert using a ';', not a newline, so that the line numbers + // in fsrc match the ones in src. Add an extra '\n' before the '}' + // to make sure comments are flushed before the '}'. + fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '\n', '}') + file, err = parser.ParseFile(fset, filename, fsrc, parserMode) + if err == nil { + sourceAdj = func(src []byte, indent int) []byte { + // Cap adjusted indent to zero. + if indent < 0 { + indent = 0 + } + // Remove the wrapping. + // Gofmt has turned the "; " into a "\n\n". + // There will be two non-blank lines with indent, hence 2*indent. + src = src[2*indent+len("package p\n\nfunc _() {"):] + // Remove only the "}\n" suffix: remaining whitespaces will be trimmed anyway + src = src[:len(src)-len("}\n")] + return bytes.TrimSpace(src) + } + // Gofmt has also indented the function body one level. + // Adjust that with indentAdj. + indentAdj = -1 + } + + // Succeeded, or out of options. + return +} + +// format formats the given package file originally obtained from src +// and adjusts the result based on the original source via sourceAdj +// and indentAdj. +func format( + fset *token.FileSet, + file *ast.File, + sourceAdj func(src []byte, indent int) []byte, + indentAdj int, + src []byte, + cfg printer.Config, +) ([]byte, error) { + if sourceAdj == nil { + // Complete source file. + var buf bytes.Buffer + err := cfg.Fprint(&buf, fset, file) + if err != nil { + return nil, err + } + return buf.Bytes(), nil + } + + // Partial source file. + // Determine and prepend leading space. + i, j := 0, 0 + for j < len(src) && isSpace(src[j]) { + if src[j] == '\n' { + i = j + 1 // byte offset of last line in leading space + } + j++ + } + var res []byte + res = append(res, src[:i]...) + + // Determine and prepend indentation of first code line. + // Spaces are ignored unless there are no tabs, + // in which case spaces count as one tab. + indent := 0 + hasSpace := false + for _, b := range src[i:j] { + switch b { + case ' ': + hasSpace = true + case '\t': + indent++ + } + } + if indent == 0 && hasSpace { + indent = 1 + } + for i := 0; i < indent; i++ { + res = append(res, '\t') + } + + // Format the source. + // Write it without any leading and trailing space. + cfg.Indent = indent + indentAdj + var buf bytes.Buffer + err := cfg.Fprint(&buf, fset, file) + if err != nil { + return nil, err + } + out := sourceAdj(buf.Bytes(), cfg.Indent) + + // If the adjusted output is empty, the source + // was empty but (possibly) for white space. + // The result is the incoming source. + if len(out) == 0 { + return src, nil + } + + // Otherwise, append output to leading space. + res = append(res, out...) + + // Determine and append trailing space. + i = len(src) + for i > 0 && isSpace(src[i-1]) { + i-- + } + return append(res, src[i:]...), nil +} + +// isSpace reports whether the byte is a space character. +// isSpace defines a space as being among the following bytes: ' ', '\t', '\n' and '\r'. +func isSpace(b byte) bool { + return b == ' ' || b == '\t' || b == '\n' || b == '\r' +} diff --git a/src/cmd/gofmt/long_test.go b/src/cmd/gofmt/long_test.go new file mode 100644 index 0000000..8db348a --- /dev/null +++ b/src/cmd/gofmt/long_test.go @@ -0,0 +1,185 @@ +// 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. + +// This test applies gofmt to all Go files under -root. +// To test specific files provide a list of comma-separated +// filenames via the -files flag: go test -files=gofmt.go . + +package main + +import ( + "bytes" + "flag" + "fmt" + "go/ast" + "go/printer" + "go/token" + "internal/testenv" + "io" + "io/fs" + "os" + "path/filepath" + "runtime" + "strings" + "testing" +) + +var ( + root = flag.String("root", runtime.GOROOT(), "test root directory") + files = flag.String("files", "", "comma-separated list of files to test") + ngo = flag.Int("n", runtime.NumCPU(), "number of goroutines used") + verbose = flag.Bool("verbose", false, "verbose mode") + nfiles int // number of files processed +) + +func gofmt(fset *token.FileSet, filename string, src *bytes.Buffer) error { + f, _, _, err := parse(fset, filename, src.Bytes(), false) + if err != nil { + return err + } + ast.SortImports(fset, f) + src.Reset() + return (&printer.Config{Mode: printerMode, Tabwidth: tabWidth}).Fprint(src, fset, f) +} + +func testFile(t *testing.T, b1, b2 *bytes.Buffer, filename string) { + // open file + f, err := os.Open(filename) + if err != nil { + t.Error(err) + return + } + + // read file + b1.Reset() + _, err = io.Copy(b1, f) + f.Close() + if err != nil { + t.Error(err) + return + } + + // exclude files w/ syntax errors (typically test cases) + fset := token.NewFileSet() + if _, _, _, err = parse(fset, filename, b1.Bytes(), false); err != nil { + if *verbose { + fmt.Fprintf(os.Stderr, "ignoring %s\n", err) + } + return + } + + // gofmt file + if err = gofmt(fset, filename, b1); err != nil { + t.Errorf("1st gofmt failed: %v", err) + return + } + + // make a copy of the result + b2.Reset() + b2.Write(b1.Bytes()) + + // gofmt result again + if err = gofmt(fset, filename, b2); err != nil { + t.Errorf("2nd gofmt failed: %v", err) + return + } + + // the first and 2nd result should be identical + if !bytes.Equal(b1.Bytes(), b2.Bytes()) { + // A known instance of gofmt not being idempotent + // (see Issue #24472) + if strings.HasSuffix(filename, "issue22662.go") { + t.Log("known gofmt idempotency bug (Issue #24472)") + return + } + t.Errorf("gofmt %s not idempotent", filename) + } +} + +func testFiles(t *testing.T, filenames <-chan string, done chan<- int) { + b1 := new(bytes.Buffer) + b2 := new(bytes.Buffer) + for filename := range filenames { + testFile(t, b1, b2, filename) + } + done <- 0 +} + +func genFilenames(t *testing.T, filenames chan<- string) { + defer close(filenames) + + handleFile := func(filename string, d fs.DirEntry, err error) error { + if err != nil { + t.Error(err) + return nil + } + // don't descend into testdata directories + if isGoFile(d) && !strings.Contains(filepath.ToSlash(filename), "/testdata/") { + filenames <- filename + nfiles++ + } + return nil + } + + // test Go files provided via -files, if any + if *files != "" { + for _, filename := range strings.Split(*files, ",") { + fi, err := os.Stat(filename) + handleFile(filename, &statDirEntry{fi}, err) + } + return // ignore files under -root + } + + // otherwise, test all Go files under *root + goroot := *root + if goroot == "" { + goroot = testenv.GOROOT(t) + } + filepath.WalkDir(goroot, handleFile) +} + +func TestAll(t *testing.T) { + if testing.Short() { + return + } + + if *ngo < 1 { + *ngo = 1 // make sure test is run + } + if *verbose { + fmt.Printf("running test using %d goroutines\n", *ngo) + } + + // generate filenames + filenames := make(chan string, 32) + go genFilenames(t, filenames) + + // launch test goroutines + done := make(chan int) + for i := 0; i < *ngo; i++ { + go testFiles(t, filenames, done) + } + + // wait for all test goroutines to complete + for i := 0; i < *ngo; i++ { + <-done + } + + if *verbose { + fmt.Printf("processed %d files\n", nfiles) + } +} + +type statDirEntry struct { + info fs.FileInfo +} + +func (d *statDirEntry) Name() string { return d.info.Name() } +func (d *statDirEntry) IsDir() bool { return d.info.IsDir() } +func (d *statDirEntry) Type() fs.FileMode { return d.info.Mode().Type() } +func (d *statDirEntry) Info() (fs.FileInfo, error) { return d.info, nil } + +func (d *statDirEntry) String() string { + return fs.FormatDirEntry(d) +} diff --git a/src/cmd/gofmt/rewrite.go b/src/cmd/gofmt/rewrite.go new file mode 100644 index 0000000..0b7e211 --- /dev/null +++ b/src/cmd/gofmt/rewrite.go @@ -0,0 +1,311 @@ +// 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" + "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(fset *token.FileSet, p *ast.File) *ast.File { + return rewriteFile(fset, 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, what string) ast.Expr { + x, err := parser.ParseExpr(s) + if err != nil { + fmt.Fprintf(os.Stderr, "parsing %s %s at %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(fileSet, val.Interface()) + fmt.Println() +} +*/ + +// rewriteFile applies the rewrite rule 'pattern -> replace' to an entire file. +func rewriteFile(fileSet *token.FileSet, 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) + + var rewriteVal func(val reflect.Value) reflect.Value + rewriteVal = func(val reflect.Value) reflect.Value { + // don't bother if val is invalid to start with + if !val.IsValid() { + return reflect.Value{} + } + val = apply(rewriteVal, val) + for k := range m { + delete(m, k) + } + if match(m, pat, val) { + val = subst(m, repl, reflect.ValueOf(val.Interface().(ast.Node).Pos())) + } + return val + } + + r := apply(rewriteVal, reflect.ValueOf(p)).Interface().(*ast.File) + r.Comments = cmap.Filter(r).Comments() // recreate comments list + return r +} + +// set is a wrapper for x.Set(y); it protects the caller from panics if x cannot be changed to y. +func set(x, y reflect.Value) { + // don't bother if x cannot be set or y is invalid + if !x.CanSet() || !y.IsValid() { + return + } + defer func() { + if x := recover(); x != nil { + if s, ok := x.(string); ok && + (strings.Contains(s, "type mismatch") || strings.Contains(s, "not assignable")) { + // 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) + callExprType = reflect.TypeOf((*ast.CallExpr)(nil)) + 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) + set(e, f(e)) + } + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + e := v.Field(i) + set(e, f(e)) + } + case reflect.Interface: + e := v.Elem() + set(v, f(e)) + } + return val +} + +func isWildcard(s string) bool { + rune, size := utf8.DecodeRuneInString(s) + return size == len(s) && unicode.IsLower(rune) +} + +// match reports whether 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 valid (non-nil) expressions. + if _, ok := val.Interface().(ast.Expr); ok && !val.IsNil() { + 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 always match + return true + case callExprType: + // For calls, the Ellipsis fields (token.Pos) must + // match since that is how f(x) and f(x...) are different. + // Check them here but fall through for the remaining fields. + p := pattern.Interface().(*ast.CallExpr) + v := val.Interface().(*ast.CallExpr) + if p.Ellipsis.IsValid() != v.Ellipsis.IsValid() { + return false + } + } + + 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: + 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: + if p.IsNil() { + // Do not turn nil slices into empty slices. go/ast + // guarantees that certain lists will be nil if not + // populated. + return reflect.Zero(p.Type()) + } + 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.Pointer: + 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 0000000..eb55daa --- /dev/null +++ b/src/cmd/gofmt/simplify.go @@ -0,0 +1,169 @@ +// 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" + "go/token" + "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 keyType, eltType ast.Expr + switch typ := outer.Type.(type) { + case *ast.ArrayType: + eltType = typ.Elt + case *ast.MapType: + keyType = typ.Key + eltType = typ.Value + } + + if eltType != nil { + var ktyp reflect.Value + if keyType != nil { + ktyp = reflect.ValueOf(keyType) + } + typ := reflect.ValueOf(eltType) + for i, x := range outer.Elts { + px := &outer.Elts[i] + // look at value of indexed/named elements + if t, ok := x.(*ast.KeyValueExpr); ok { + if keyType != nil { + s.simplifyLiteral(ktyp, keyType, t.Key, &t.Key) + } + x = t.Value + px = &t.Value + } + s.simplifyLiteral(typ, eltType, x, px) + } + // node was simplified - stop walk (there are no subnodes to simplify) + 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) + // + // Note: This may not be correct because len may have been redeclared in + // the same package. However, this is extremely unlikely and so far + // (April 2022, after years of supporting this rewrite feature) + // has never come up, so let's keep it working as is (see also #15153). + // + // Also note that this code used to use go/ast's object tracking, + // which was removed in exchange for go/parser.Mode.SkipObjectResolution. + // False positives are extremely unlikely as described above, + // and go/ast's object tracking is incomplete in any case. + if n.Max != nil { + // - 3-index slices always require the 2nd and 3rd index + break + } + if s, _ := n.X.(*ast.Ident); s != nil { + // the array/slice object is a single 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" { + // the function called is "len" + if arg, _ := call.Args[0].(*ast.Ident); arg != nil && arg.Name == s.Name { + // 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. + // An example where the 0 helps: + // x, y, z := b[0:2], b[2:4], b[4:6] + // An example where it does not: + // x, y := b[:n], b[n:] + + case *ast.RangeStmt: + // - a range of the form: for x, _ = range v {...} + // can be simplified to: for x = range v {...} + // - a range of the form: for _ = range v {...} + // can be simplified to: for range v {...} + if isBlank(n.Value) { + n.Value = nil + } + if isBlank(n.Key) && n.Value == nil { + n.Key = nil + } + } + + return s +} + +func (s simplifier) simplifyLiteral(typ reflect.Value, astType, x ast.Expr, px *ast.Expr) { + 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 + if inner, ok := x.(*ast.CompositeLit); ok { + if match(nil, typ, reflect.ValueOf(inner.Type)) { + inner.Type = nil + } + } + // if the outer literal's element type is a pointer type *T + // and the element is & of a composite literal of type T, + // the inner &T may be omitted. + if ptr, ok := astType.(*ast.StarExpr); ok { + if addr, ok := x.(*ast.UnaryExpr); ok && addr.Op == token.AND { + if inner, ok := addr.X.(*ast.CompositeLit); ok { + if match(nil, reflect.ValueOf(ptr.X), reflect.ValueOf(inner.Type)) { + inner.Type = nil // drop T + *px = inner // drop & + } + } + } + } +} + +func isBlank(x ast.Expr) bool { + ident, ok := x.(*ast.Ident) + return ok && ident.Name == "_" +} + +func simplify(f *ast.File) { + // remove empty declarations such as "const ()", etc + removeEmptyDeclGroups(f) + + var s simplifier + ast.Walk(s, f) +} + +func removeEmptyDeclGroups(f *ast.File) { + i := 0 + for _, d := range f.Decls { + if g, ok := d.(*ast.GenDecl); !ok || !isEmpty(f, g) { + f.Decls[i] = d + i++ + } + } + f.Decls = f.Decls[:i] +} + +func isEmpty(f *ast.File, g *ast.GenDecl) bool { + if g.Doc != nil || g.Specs != nil { + return false + } + + for _, c := range f.Comments { + // if there is a comment in the declaration, it is not considered empty + if g.Pos() <= c.Pos() && c.End() <= g.End() { + return false + } + } + + return true +} diff --git a/src/cmd/gofmt/testdata/comments.golden b/src/cmd/gofmt/testdata/comments.golden new file mode 100644 index 0000000..ad6bcaf --- /dev/null +++ b/src/cmd/gofmt/testdata/comments.golden @@ -0,0 +1,9 @@ +package main + +func main() {} + +// comment here + +func f() {} + +//line foo.go:1 diff --git a/src/cmd/gofmt/testdata/comments.input b/src/cmd/gofmt/testdata/comments.input new file mode 100644 index 0000000..ad6bcaf --- /dev/null +++ b/src/cmd/gofmt/testdata/comments.input @@ -0,0 +1,9 @@ +package main + +func main() {} + +// comment here + +func f() {} + +//line foo.go:1 diff --git a/src/cmd/gofmt/testdata/composites.golden b/src/cmd/gofmt/testdata/composites.golden new file mode 100644 index 0000000..a06a69d --- /dev/null +++ b/src/cmd/gofmt/testdata/composites.golden @@ -0,0 +1,218 @@ +//gofmt -s + +package P + +type T struct { + x, y int +} + +type T2 struct { + w, z 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}, +} + +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}, +} + +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}, +} + +var _ = map[T]T2{ + {1, 2}: {3, 4}, + {5, 6}: {7, 8}, +} + +var _ = map[*T]*T2{ + {1, 2}: {3, 4}, + {5, 6}: {7, 8}, +} diff --git a/src/cmd/gofmt/testdata/composites.input b/src/cmd/gofmt/testdata/composites.input new file mode 100644 index 0000000..9d28ac7 --- /dev/null +++ b/src/cmd/gofmt/testdata/composites.input @@ -0,0 +1,218 @@ +//gofmt -s + +package P + +type T struct { + x, y int +} + +type T2 struct { + w, z 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}, +} + +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}, +} + +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}, +} + +var _ = map[T]T2{ + T{1, 2}: T2{3, 4}, + T{5, 6}: T2{7, 8}, +} + +var _ = map[*T]*T2{ + &T{1, 2}: &T2{3, 4}, + &T{5, 6}: &T2{7, 8}, +} diff --git a/src/cmd/gofmt/testdata/crlf.golden b/src/cmd/gofmt/testdata/crlf.golden new file mode 100644 index 0000000..65de9cf --- /dev/null +++ b/src/cmd/gofmt/testdata/crlf.golden @@ -0,0 +1,13 @@ +/* +Source containing CR/LF line endings. +The gofmt'ed output must only have LF +line endings. +Test case for issue 3961. +*/ +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 0000000..3cd4934 --- /dev/null +++ b/src/cmd/gofmt/testdata/crlf.input @@ -0,0 +1,13 @@ +/*
+Source containing CR/LF line endings.
+The gofmt'ed output must only have LF
+line endings.
+Test case for issue 3961.
+*/
+package main
+
+func main() {
+ // line comment
+ println("hello, world!") // another line comment
+ println()
+}
diff --git a/src/cmd/gofmt/testdata/emptydecl.golden b/src/cmd/gofmt/testdata/emptydecl.golden new file mode 100644 index 0000000..33d6435 --- /dev/null +++ b/src/cmd/gofmt/testdata/emptydecl.golden @@ -0,0 +1,14 @@ +//gofmt -s + +// Test case for issue 7631. + +package main + +// Keep this declaration +var () + +const ( +// Keep this declaration +) + +func main() {} diff --git a/src/cmd/gofmt/testdata/emptydecl.input b/src/cmd/gofmt/testdata/emptydecl.input new file mode 100644 index 0000000..4948a61 --- /dev/null +++ b/src/cmd/gofmt/testdata/emptydecl.input @@ -0,0 +1,16 @@ +//gofmt -s + +// Test case for issue 7631. + +package main + +// Keep this declaration +var () + +const ( +// Keep this declaration +) + +type () + +func main() {}
\ No newline at end of file diff --git a/src/cmd/gofmt/testdata/go2numbers.golden b/src/cmd/gofmt/testdata/go2numbers.golden new file mode 100644 index 0000000..0184aaa --- /dev/null +++ b/src/cmd/gofmt/testdata/go2numbers.golden @@ -0,0 +1,186 @@ +package p + +const ( + // 0-octals + _ = 0 + _ = 0123 + _ = 0123456 + + _ = 0_123 + _ = 0123_456 + + // decimals + _ = 1 + _ = 1234 + _ = 1234567 + + _ = 1_234 + _ = 1_234_567 + + // hexadecimals + _ = 0x0 + _ = 0x1234 + _ = 0xcafef00d + + _ = 0x0 + _ = 0x1234 + _ = 0xCAFEf00d + + _ = 0x_0 + _ = 0x_1234 + _ = 0x_CAFE_f00d + + // octals + _ = 0o0 + _ = 0o1234 + _ = 0o01234567 + + _ = 0o0 + _ = 0o1234 + _ = 0o01234567 + + _ = 0o_0 + _ = 0o_1234 + _ = 0o0123_4567 + + _ = 0o_0 + _ = 0o_1234 + _ = 0o0123_4567 + + // binaries + _ = 0b0 + _ = 0b1011 + _ = 0b00101101 + + _ = 0b0 + _ = 0b1011 + _ = 0b00101101 + + _ = 0b_0 + _ = 0b10_11 + _ = 0b_0010_1101 + + // decimal floats + _ = 0. + _ = 123. + _ = 0123. + + _ = .0 + _ = .123 + _ = .0123 + + _ = 0e0 + _ = 123e+0 + _ = 0123e-1 + + _ = 0e-0 + _ = 123e+0 + _ = 0123e123 + + _ = 0.e+1 + _ = 123.e-10 + _ = 0123.e123 + + _ = .0e-1 + _ = .123e+10 + _ = .0123e123 + + _ = 0.0 + _ = 123.123 + _ = 0123.0123 + + _ = 0.0e1 + _ = 123.123e-10 + _ = 0123.0123e+456 + + _ = 1_2_3. + _ = 0_123. + + _ = 0_0e0 + _ = 1_2_3e0 + _ = 0_123e0 + + _ = 0e-0_0 + _ = 1_2_3e+0 + _ = 0123e1_2_3 + + _ = 0.e+1 + _ = 123.e-1_0 + _ = 01_23.e123 + + _ = .0e-1 + _ = .123e+10 + _ = .0123e123 + + _ = 1_2_3.123 + _ = 0123.01_23 + + // hexadecimal floats + _ = 0x0.p+0 + _ = 0xdeadcafe.p-10 + _ = 0x1234.p123 + + _ = 0x.1p-0 + _ = 0x.deadcafep2 + _ = 0x.1234p+10 + + _ = 0x0p0 + _ = 0xdeadcafep+1 + _ = 0x1234p-10 + + _ = 0x0.0p0 + _ = 0xdead.cafep+1 + _ = 0x12.34p-10 + + _ = 0xdead_cafep+1 + _ = 0x_1234p-10 + + _ = 0x_dead_cafe.p-10 + _ = 0x12_34.p1_2_3 + _ = 0x1_2_3_4.p-1_2_3 + + // imaginaries + _ = 0i + _ = 0i + _ = 8i + _ = 0i + _ = 123i + _ = 123i + _ = 56789i + _ = 1234i + _ = 1234567i + + _ = 0i + _ = 0i + _ = 8i + _ = 0i + _ = 123i + _ = 123i + _ = 56_789i + _ = 1_234i + _ = 1_234_567i + + _ = 0.i + _ = 123.i + _ = 0123.i + _ = 000123.i + + _ = 0e0i + _ = 123e0i + _ = 0123e0i + _ = 000123e0i + + _ = 0.e+1i + _ = 123.e-1_0i + _ = 01_23.e123i + _ = 00_01_23.e123i + + _ = 0b1010i + _ = 0b1010i + _ = 0o660i + _ = 0o660i + _ = 0xabcDEFi + _ = 0xabcDEFi + _ = 0xabcDEFp0i + _ = 0xabcDEFp0i +) diff --git a/src/cmd/gofmt/testdata/go2numbers.input b/src/cmd/gofmt/testdata/go2numbers.input new file mode 100644 index 0000000..f3e7828 --- /dev/null +++ b/src/cmd/gofmt/testdata/go2numbers.input @@ -0,0 +1,186 @@ +package p + +const ( + // 0-octals + _ = 0 + _ = 0123 + _ = 0123456 + + _ = 0_123 + _ = 0123_456 + + // decimals + _ = 1 + _ = 1234 + _ = 1234567 + + _ = 1_234 + _ = 1_234_567 + + // hexadecimals + _ = 0x0 + _ = 0x1234 + _ = 0xcafef00d + + _ = 0X0 + _ = 0X1234 + _ = 0XCAFEf00d + + _ = 0X_0 + _ = 0X_1234 + _ = 0X_CAFE_f00d + + // octals + _ = 0o0 + _ = 0o1234 + _ = 0o01234567 + + _ = 0O0 + _ = 0O1234 + _ = 0O01234567 + + _ = 0o_0 + _ = 0o_1234 + _ = 0o0123_4567 + + _ = 0O_0 + _ = 0O_1234 + _ = 0O0123_4567 + + // binaries + _ = 0b0 + _ = 0b1011 + _ = 0b00101101 + + _ = 0B0 + _ = 0B1011 + _ = 0B00101101 + + _ = 0b_0 + _ = 0b10_11 + _ = 0b_0010_1101 + + // decimal floats + _ = 0. + _ = 123. + _ = 0123. + + _ = .0 + _ = .123 + _ = .0123 + + _ = 0e0 + _ = 123e+0 + _ = 0123E-1 + + _ = 0e-0 + _ = 123E+0 + _ = 0123E123 + + _ = 0.e+1 + _ = 123.E-10 + _ = 0123.e123 + + _ = .0e-1 + _ = .123E+10 + _ = .0123E123 + + _ = 0.0 + _ = 123.123 + _ = 0123.0123 + + _ = 0.0e1 + _ = 123.123E-10 + _ = 0123.0123e+456 + + _ = 1_2_3. + _ = 0_123. + + _ = 0_0e0 + _ = 1_2_3e0 + _ = 0_123e0 + + _ = 0e-0_0 + _ = 1_2_3E+0 + _ = 0123E1_2_3 + + _ = 0.e+1 + _ = 123.E-1_0 + _ = 01_23.e123 + + _ = .0e-1 + _ = .123E+10 + _ = .0123E123 + + _ = 1_2_3.123 + _ = 0123.01_23 + + // hexadecimal floats + _ = 0x0.p+0 + _ = 0Xdeadcafe.p-10 + _ = 0x1234.P123 + + _ = 0x.1p-0 + _ = 0X.deadcafep2 + _ = 0x.1234P+10 + + _ = 0x0p0 + _ = 0Xdeadcafep+1 + _ = 0x1234P-10 + + _ = 0x0.0p0 + _ = 0Xdead.cafep+1 + _ = 0x12.34P-10 + + _ = 0Xdead_cafep+1 + _ = 0x_1234P-10 + + _ = 0X_dead_cafe.p-10 + _ = 0x12_34.P1_2_3 + _ = 0X1_2_3_4.P-1_2_3 + + // imaginaries + _ = 0i + _ = 00i + _ = 08i + _ = 0000000000i + _ = 0123i + _ = 0000000123i + _ = 0000056789i + _ = 1234i + _ = 1234567i + + _ = 0i + _ = 0_0i + _ = 0_8i + _ = 0_000_000_000i + _ = 0_123i + _ = 0_000_000_123i + _ = 0_000_056_789i + _ = 1_234i + _ = 1_234_567i + + _ = 0.i + _ = 123.i + _ = 0123.i + _ = 000123.i + + _ = 0e0i + _ = 123e0i + _ = 0123E0i + _ = 000123E0i + + _ = 0.e+1i + _ = 123.E-1_0i + _ = 01_23.e123i + _ = 00_01_23.e123i + + _ = 0b1010i + _ = 0B1010i + _ = 0o660i + _ = 0O660i + _ = 0xabcDEFi + _ = 0XabcDEFi + _ = 0xabcDEFP0i + _ = 0XabcDEFp0i +) diff --git a/src/cmd/gofmt/testdata/import.golden b/src/cmd/gofmt/testdata/import.golden new file mode 100644 index 0000000..1125b70 --- /dev/null +++ b/src/cmd/gofmt/testdata/import.golden @@ -0,0 +1,194 @@ +// package comment +package main + +import ( + "errors" + "fmt" + "io" + "log" + "math" +) + +import ( + "fmt" + + "math" + + "log" + + "errors" + + "io" +) + +// We reset the line numbering to test that +// the formatting works independent of line directives +//line :19 + +import ( + "errors" + "fmt" + "io" + "log" + "math" + + "fmt" + + "math" + + "log" + + "errors" + + "io" +) + +import ( + // a block with comments + "errors" + "fmt" // for Printf + "io" // for Reader + "log" // for Fatal + "math" +) + +import ( + "fmt" // for Printf + + "math" + + "log" // for Fatal + + "errors" + + "io" // for Reader +) + +import ( + // for Printf + "fmt" + + "math" + + // for Fatal + "log" + + "errors" + + // for Reader + "io" +) + +import ( + "errors" + "fmt" // for Printf + "io" // for Reader + "log" // for Fatal + "math" + + "fmt" // for Printf + + "math" + + "log" // for Fatal + + "errors" + + "io" // for Reader +) + +import ( + "fmt" // for Printf + + "errors" + "io" // for Reader + "log" // for Fatal + "math" + + "errors" + "fmt" // for Printf + "io" // for Reader + "log" // for Fatal + "math" +) + +// Test deduping and extended sorting +import ( + a "A" // aA + b "A" // bA1 + b "A" // bA2 + "B" // B + . "B" // .B + _ "B" // _b + "C" + a "D" // aD +) + +import ( + "dedup_by_group" + + "dedup_by_group" +) + +import ( + "fmt" // for Printf + /* comment */ io1 "io" + /* comment */ io2 "io" + /* comment */ "log" +) + +import ( + "fmt" + /* comment */ io1 "io" + /* comment */ io2 "io" // hello + "math" /* right side */ + // end +) + +import ( + "errors" // for New + "fmt" + /* comment */ io1 "io" /* before */ // after + io2 "io" // another + // end +) + +import ( + "errors" // for New + /* left */ "fmt" /* right */ + "log" // for Fatal + /* left */ "math" /* right */ +) + +import /* why */ /* comment here? */ ( + /* comment */ "fmt" + "math" +) + +// Reset it again +//line :100 + +// Dedup with different import styles +import ( + "path" + . "path" + _ "path" + pathpkg "path" +) + +/* comment */ +import ( + "fmt" + "math" // for Abs + // This is a new run + "errors" + "fmt" +) + +// End an import declaration in the same line +// as the last import. See golang.org/issue/33538. +// Note: Must be the last (or 2nd last) line of the file. +import ( + "fmt" + "math" +) diff --git a/src/cmd/gofmt/testdata/import.input b/src/cmd/gofmt/testdata/import.input new file mode 100644 index 0000000..040b872 --- /dev/null +++ b/src/cmd/gofmt/testdata/import.input @@ -0,0 +1,199 @@ +// package comment +package main + +import ( + "fmt" + "math" + "log" + "errors" + "io" +) + +import ( + "fmt" + + "math" + + "log" + + "errors" + + "io" +) + +// We reset the line numbering to test that +// the formatting works independent of line directives +//line :19 + +import ( + "fmt" + "math" + "log" + "errors" + "io" + + "fmt" + + "math" + + "log" + + "errors" + + "io" +) + +import ( + // a block with comments + "fmt" // for Printf + "math" + "log" // for Fatal + "errors" + "io" // for Reader +) + +import ( + "fmt" // for Printf + + "math" + + "log" // for Fatal + + "errors" + + "io" // for Reader +) + +import ( + // for Printf + "fmt" + + "math" + + // for Fatal + "log" + + "errors" + + // for Reader + "io" +) + +import ( + "fmt" // for Printf + "math" + "log" // for Fatal + "errors" + "io" // for Reader + + "fmt" // for Printf + + "math" + + "log" // for Fatal + + "errors" + + "io" // for Reader +) + +import ( + "fmt" // for Printf + + "math" + "log" // for Fatal + "errors" + "io" // for Reader + + "fmt" // for Printf + "math" + "log" // for Fatal + "errors" + "io" // for Reader +) + +// Test deduping and extended sorting +import ( + "B" // B + a "A" // aA + b "A" // bA2 + b "A" // bA1 + . "B" // .B + . "B" + "C" + "C" + "C" + a "D" // aD + "B" + _ "B" // _b +) + +import ( + "dedup_by_group" + "dedup_by_group" + + "dedup_by_group" +) + +import ( + /* comment */ io1 "io" + "fmt" // for Printf + /* comment */ "log" + /* comment */ io2 "io" +) + +import ( + /* comment */ io2 "io" // hello + /* comment */ io1 "io" + "math" /* right side */ + "fmt" + // end +) + +import ( + /* comment */ io1 "io" /* before */ // after + "fmt" + "errors" // for New + io2 "io" // another + // end +) + +import ( + /* left */ "fmt" /* right */ + "errors" // for New + /* left */ "math" /* right */ + "log" // for Fatal +) + +import /* why */ /* comment here? */ ( + /* comment */ "fmt" + "math" +) + +// Reset it again +//line :100 + +// Dedup with different import styles +import ( + "path" + . "path" + _ "path" + "path" + pathpkg "path" +) + +/* comment */ +import ( + "math" // for Abs + "fmt" + // This is a new run + "errors" + "fmt" + "errors" +) + +// End an import declaration in the same line +// as the last import. See golang.org/issue/33538. +// Note: Must be the last (or 2nd last) line of the file. +import("fmt" +"math")
\ No newline at end of file diff --git a/src/cmd/gofmt/testdata/issue28082.golden b/src/cmd/gofmt/testdata/issue28082.golden new file mode 100644 index 0000000..5837fd5 --- /dev/null +++ b/src/cmd/gofmt/testdata/issue28082.golden @@ -0,0 +1,13 @@ +// Copyright 2019 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 + +// testcase for issue #28082 + +func foo() {} + +func main() {} + +func bar() {} diff --git a/src/cmd/gofmt/testdata/issue28082.input b/src/cmd/gofmt/testdata/issue28082.input new file mode 100644 index 0000000..ab7d218 --- /dev/null +++ b/src/cmd/gofmt/testdata/issue28082.input @@ -0,0 +1,13 @@ +// Copyright 2019 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 + +// testcase for issue #28082 + +func foo( ) {} + +func main( ) {} + +func bar() {} diff --git a/src/cmd/gofmt/testdata/ranges.golden b/src/cmd/gofmt/testdata/ranges.golden new file mode 100644 index 0000000..506b3a0 --- /dev/null +++ b/src/cmd/gofmt/testdata/ranges.golden @@ -0,0 +1,30 @@ +//gofmt -s + +// Test cases for range simplification. +package p + +func _() { + for a, b = range x { + } + for a = range x { + } + for _, b = range x { + } + for range x { + } + + for a = range x { + } + for range x { + } + + for a, b := range x { + } + for a := range x { + } + for _, b := range x { + } + + for a := range x { + } +} diff --git a/src/cmd/gofmt/testdata/ranges.input b/src/cmd/gofmt/testdata/ranges.input new file mode 100644 index 0000000..df5f833 --- /dev/null +++ b/src/cmd/gofmt/testdata/ranges.input @@ -0,0 +1,20 @@ +//gofmt -s + +// Test cases for range simplification. +package p + +func _() { + for a, b = range x {} + for a, _ = range x {} + for _, b = range x {} + for _, _ = range x {} + + for a = range x {} + for _ = range x {} + + for a, b := range x {} + for a, _ := range x {} + for _, b := range x {} + + for a := range x {} +} diff --git a/src/cmd/gofmt/testdata/rewrite1.golden b/src/cmd/gofmt/testdata/rewrite1.golden new file mode 100644 index 0000000..3ee5373 --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite1.golden @@ -0,0 +1,14 @@ +//gofmt -r=Foo->Bar + +// 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 0000000..a84c8f7 --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite1.input @@ -0,0 +1,14 @@ +//gofmt -r=Foo->Bar + +// 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/rewrite10.golden b/src/cmd/gofmt/testdata/rewrite10.golden new file mode 100644 index 0000000..1dd781f --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite10.golden @@ -0,0 +1,19 @@ +//gofmt -r=a->a + +// Copyright 2019 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. + +// Issue 33103, 33104, and 33105. + +package pkg + +func fn() { + _ = func() { + switch { + default: + } + } + _ = func() string {} + _ = func() { var ptr *string; println(ptr) } +} diff --git a/src/cmd/gofmt/testdata/rewrite10.input b/src/cmd/gofmt/testdata/rewrite10.input new file mode 100644 index 0000000..1dd781f --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite10.input @@ -0,0 +1,19 @@ +//gofmt -r=a->a + +// Copyright 2019 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. + +// Issue 33103, 33104, and 33105. + +package pkg + +func fn() { + _ = func() { + switch { + default: + } + } + _ = func() string {} + _ = func() { var ptr *string; println(ptr) } +} diff --git a/src/cmd/gofmt/testdata/rewrite2.golden b/src/cmd/gofmt/testdata/rewrite2.golden new file mode 100644 index 0000000..f980e03 --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite2.golden @@ -0,0 +1,12 @@ +//gofmt -r=int->bool + +// 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 0000000..489be4e --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite2.input @@ -0,0 +1,12 @@ +//gofmt -r=int->bool + +// 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 {} diff --git a/src/cmd/gofmt/testdata/rewrite3.golden b/src/cmd/gofmt/testdata/rewrite3.golden new file mode 100644 index 0000000..261a220 --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite3.golden @@ -0,0 +1,14 @@ +//gofmt -r=x->x + +// 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 + +// Field tags are *ast.BasicLit nodes that are nil when the tag is +// absent. These nil nodes must not be mistaken for expressions, +// the rewriter should not try to dereference them. Was issue 2410. +type Foo struct { + Field int +} diff --git a/src/cmd/gofmt/testdata/rewrite3.input b/src/cmd/gofmt/testdata/rewrite3.input new file mode 100644 index 0000000..261a220 --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite3.input @@ -0,0 +1,14 @@ +//gofmt -r=x->x + +// 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 + +// Field tags are *ast.BasicLit nodes that are nil when the tag is +// absent. These nil nodes must not be mistaken for expressions, +// the rewriter should not try to dereference them. Was issue 2410. +type Foo struct { + Field int +} diff --git a/src/cmd/gofmt/testdata/rewrite4.golden b/src/cmd/gofmt/testdata/rewrite4.golden new file mode 100644 index 0000000..b05547b --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite4.golden @@ -0,0 +1,76 @@ +//gofmt -r=(x)->x + +// Copyright 2012 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 parenthesized expressions (x) -> x +// must not drop parentheses if that would lead to +// wrong association of the operands. +// Was issue 1847. + +package main + +// From example 1 of issue 1847. +func _() { + var t = (&T{1000}).Id() +} + +// From example 2 of issue 1847. +func _() { + fmt.Println((*xpp).a) +} + +// Some more test cases. +func _() { + _ = (-x).f + _ = (*x).f + _ = (&x).f + _ = (!x).f + _ = -x.f + _ = *x.f + _ = &x.f + _ = !x.f + (-x).f() + (*x).f() + (&x).f() + (!x).f() + _ = -x.f() + _ = *x.f() + _ = &x.f() + _ = !x.f() + + _ = (-x).f + _ = (*x).f + _ = (&x).f + _ = (!x).f + _ = -x.f + _ = *x.f + _ = &x.f + _ = !x.f + (-x).f() + (*x).f() + (&x).f() + (!x).f() + _ = -x.f() + _ = *x.f() + _ = &x.f() + _ = !x.f() + + _ = -x.f + _ = *x.f + _ = &x.f + _ = !x.f + _ = -x.f + _ = *x.f + _ = &x.f + _ = !x.f + _ = -x.f() + _ = *x.f() + _ = &x.f() + _ = !x.f() + _ = -x.f() + _ = *x.f() + _ = &x.f() + _ = !x.f() +} diff --git a/src/cmd/gofmt/testdata/rewrite4.input b/src/cmd/gofmt/testdata/rewrite4.input new file mode 100644 index 0000000..0817099 --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite4.input @@ -0,0 +1,76 @@ +//gofmt -r=(x)->x + +// Copyright 2012 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 parenthesized expressions (x) -> x +// must not drop parentheses if that would lead to +// wrong association of the operands. +// Was issue 1847. + +package main + +// From example 1 of issue 1847. +func _() { + var t = (&T{1000}).Id() +} + +// From example 2 of issue 1847. +func _() { + fmt.Println((*xpp).a) +} + +// Some more test cases. +func _() { + _ = (-x).f + _ = (*x).f + _ = (&x).f + _ = (!x).f + _ = (-x.f) + _ = (*x.f) + _ = (&x.f) + _ = (!x.f) + (-x).f() + (*x).f() + (&x).f() + (!x).f() + _ = (-x.f()) + _ = (*x.f()) + _ = (&x.f()) + _ = (!x.f()) + + _ = ((-x)).f + _ = ((*x)).f + _ = ((&x)).f + _ = ((!x)).f + _ = ((-x.f)) + _ = ((*x.f)) + _ = ((&x.f)) + _ = ((!x.f)) + ((-x)).f() + ((*x)).f() + ((&x)).f() + ((!x)).f() + _ = ((-x.f())) + _ = ((*x.f())) + _ = ((&x.f())) + _ = ((!x.f())) + + _ = -(x).f + _ = *(x).f + _ = &(x).f + _ = !(x).f + _ = -x.f + _ = *x.f + _ = &x.f + _ = !x.f + _ = -(x).f() + _ = *(x).f() + _ = &(x).f() + _ = !(x).f() + _ = -x.f() + _ = *x.f() + _ = &x.f() + _ = !x.f() +} diff --git a/src/cmd/gofmt/testdata/rewrite5.golden b/src/cmd/gofmt/testdata/rewrite5.golden new file mode 100644 index 0000000..9beb34a --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite5.golden @@ -0,0 +1,17 @@ +//gofmt -r=x+x->2*x + +// 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 0000000..d7a6122 --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite5.input @@ -0,0 +1,17 @@ +//gofmt -r=x+x->2*x + +// 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/rewrite6.golden b/src/cmd/gofmt/testdata/rewrite6.golden new file mode 100644 index 0000000..48ec9aa --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite6.golden @@ -0,0 +1,17 @@ +//gofmt -r=fun(x)->Fun(x) + +// Copyright 2013 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 calls must take the ... (ellipsis) +// attribute for the last argument into account. + +package p + +func fun(x []int) {} + +func g(x []int) { + Fun(x) // -r='fun(x)->Fun(x)' should rewrite this to Fun(x) + fun(x...) // -r='fun(x)->Fun(x)' should not rewrite this +} diff --git a/src/cmd/gofmt/testdata/rewrite6.input b/src/cmd/gofmt/testdata/rewrite6.input new file mode 100644 index 0000000..b085a84 --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite6.input @@ -0,0 +1,17 @@ +//gofmt -r=fun(x)->Fun(x) + +// Copyright 2013 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 calls must take the ... (ellipsis) +// attribute for the last argument into account. + +package p + +func fun(x []int) {} + +func g(x []int) { + fun(x) // -r='fun(x)->Fun(x)' should rewrite this to Fun(x) + fun(x...) // -r='fun(x)->Fun(x)' should not rewrite this +} diff --git a/src/cmd/gofmt/testdata/rewrite7.golden b/src/cmd/gofmt/testdata/rewrite7.golden new file mode 100644 index 0000000..8386a0b --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite7.golden @@ -0,0 +1,17 @@ +//gofmt -r=fun(x...)->Fun(x) + +// Copyright 2013 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 calls must take the ... (ellipsis) +// attribute for the last argument into account. + +package p + +func fun(x []int) {} + +func g(x []int) { + fun(x) // -r='fun(x...)->Fun(x)' should not rewrite this + Fun(x) // -r='fun(x...)->Fun(x)' should rewrite this to Fun(x) +} diff --git a/src/cmd/gofmt/testdata/rewrite7.input b/src/cmd/gofmt/testdata/rewrite7.input new file mode 100644 index 0000000..c198470 --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite7.input @@ -0,0 +1,17 @@ +//gofmt -r=fun(x...)->Fun(x) + +// Copyright 2013 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 calls must take the ... (ellipsis) +// attribute for the last argument into account. + +package p + +func fun(x []int) {} + +func g(x []int) { + fun(x) // -r='fun(x...)->Fun(x)' should not rewrite this + fun(x...) // -r='fun(x...)->Fun(x)' should rewrite this to Fun(x) +} diff --git a/src/cmd/gofmt/testdata/rewrite8.golden b/src/cmd/gofmt/testdata/rewrite8.golden new file mode 100644 index 0000000..62f0419 --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite8.golden @@ -0,0 +1,12 @@ +//gofmt -r=interface{}->int + +// Copyright 2013 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. + +// Check that literal type expression rewrites are accepted. +// Was issue 4406. + +package p + +type T int diff --git a/src/cmd/gofmt/testdata/rewrite8.input b/src/cmd/gofmt/testdata/rewrite8.input new file mode 100644 index 0000000..7964c5c --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite8.input @@ -0,0 +1,12 @@ +//gofmt -r=interface{}->int + +// Copyright 2013 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. + +// Check that literal type expression rewrites are accepted. +// Was issue 4406. + +package p + +type T interface{} diff --git a/src/cmd/gofmt/testdata/rewrite9.golden b/src/cmd/gofmt/testdata/rewrite9.golden new file mode 100644 index 0000000..fffbd3d --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite9.golden @@ -0,0 +1,11 @@ +//gofmt -r=a&&b!=2->a + +// Copyright 2017 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. + +// Issue 18987. + +package p + +const _ = x != 1 diff --git a/src/cmd/gofmt/testdata/rewrite9.input b/src/cmd/gofmt/testdata/rewrite9.input new file mode 100644 index 0000000..106ad94 --- /dev/null +++ b/src/cmd/gofmt/testdata/rewrite9.input @@ -0,0 +1,11 @@ +//gofmt -r=a&&b!=2->a + +// Copyright 2017 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. + +// Issue 18987. + +package p + +const _ = x != 1 && x != 2 diff --git a/src/cmd/gofmt/testdata/slices1.golden b/src/cmd/gofmt/testdata/slices1.golden new file mode 100644 index 0000000..04bc16f --- /dev/null +++ b/src/cmd/gofmt/testdata/slices1.golden @@ -0,0 +1,66 @@ +//gofmt -s + +// 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[2:len(a):len(a)] + + _ = a[:] + _ = a[:10] + _ = a[:] + _ = a[:(len(a))] + _ = a[:len(a)-1] + _ = a[:len(b)] + _ = a[:len(a):len(a)] + + _ = s[0:] + _ = s[1:10] + _ = s[2:] + _ = s[3:(len(s))] + _ = s[len(a) : len(s)-1] + _ = s[0:len(b)] + _ = s[2:len(s):len(s)] + + _ = s[:] + _ = s[:10] + _ = s[:] + _ = s[:(len(s))] + _ = s[:len(s)-1] + _ = s[:len(b)] + _ = s[:len(s):len(s)] + + _ = 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[2:len(t.s):len(t.s)] + + _ = 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)] + _ = t.s[:len(t.s):len(t.s)] +) + +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 0000000..1f25c43 --- /dev/null +++ b/src/cmd/gofmt/testdata/slices1.input @@ -0,0 +1,66 @@ +//gofmt -s + +// 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[2:len(a):len(a)] + + _ = a[:] + _ = a[:10] + _ = a[:len(a)] + _ = a[:(len(a))] + _ = a[:len(a)-1] + _ = a[:len(b)] + _ = a[:len(a):len(a)] + + _ = s[0:] + _ = s[1:10] + _ = s[2:len(s)] + _ = s[3:(len(s))] + _ = s[len(a) : len(s)-1] + _ = s[0:len(b)] + _ = s[2:len(s):len(s)] + + _ = s[:] + _ = s[:10] + _ = s[:len(s)] + _ = s[:(len(s))] + _ = s[:len(s)-1] + _ = s[:len(b)] + _ = s[:len(s):len(s)] + + _ = 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[2:len(t.s):len(t.s)] + + _ = 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)] + _ = t.s[:len(t.s):len(t.s)] +) + +func _() { + s := s[0:len(s)] + _ = s +} diff --git a/src/cmd/gofmt/testdata/stdin1.golden b/src/cmd/gofmt/testdata/stdin1.golden new file mode 100644 index 0000000..9e4dcd2 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin1.golden @@ -0,0 +1,5 @@ + //gofmt -stdin + + if x { + y + } diff --git a/src/cmd/gofmt/testdata/stdin1.input b/src/cmd/gofmt/testdata/stdin1.input new file mode 100644 index 0000000..9e4dcd2 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin1.input @@ -0,0 +1,5 @@ + //gofmt -stdin + + if x { + y + } diff --git a/src/cmd/gofmt/testdata/stdin2.golden b/src/cmd/gofmt/testdata/stdin2.golden new file mode 100644 index 0000000..57df355 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin2.golden @@ -0,0 +1,11 @@ +//gofmt -stdin + +var x int + +func f() { + y := z + /* this is a comment */ + // this is a comment too +} + + diff --git a/src/cmd/gofmt/testdata/stdin2.input b/src/cmd/gofmt/testdata/stdin2.input new file mode 100644 index 0000000..69d6bdd --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin2.input @@ -0,0 +1,11 @@ +//gofmt -stdin + +var x int + + +func f() { y := z + /* this is a comment */ + // this is a comment too +} + + diff --git a/src/cmd/gofmt/testdata/stdin3.golden b/src/cmd/gofmt/testdata/stdin3.golden new file mode 100644 index 0000000..d6da0e4 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin3.golden @@ -0,0 +1,7 @@ + //gofmt -stdin + + /* note: no newline at end of file */ + for i := 0; i < 10; i++ { + s += i + } +
\ No newline at end of file diff --git a/src/cmd/gofmt/testdata/stdin3.input b/src/cmd/gofmt/testdata/stdin3.input new file mode 100644 index 0000000..ab46c10 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin3.input @@ -0,0 +1,5 @@ + //gofmt -stdin + + /* note: no newline at end of file */ + for i := 0; i < 10; i++ { s += i } +
\ No newline at end of file diff --git a/src/cmd/gofmt/testdata/stdin4.golden b/src/cmd/gofmt/testdata/stdin4.golden new file mode 100644 index 0000000..0c7acac --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin4.golden @@ -0,0 +1,5 @@ + //gofmt -stdin + + // comment + + i := 0 diff --git a/src/cmd/gofmt/testdata/stdin4.input b/src/cmd/gofmt/testdata/stdin4.input new file mode 100644 index 0000000..1fc73f3 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin4.input @@ -0,0 +1,5 @@ + //gofmt -stdin + + // comment + + i := 0 diff --git a/src/cmd/gofmt/testdata/stdin5.golden b/src/cmd/gofmt/testdata/stdin5.golden new file mode 100644 index 0000000..31ce6b2 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin5.golden @@ -0,0 +1,3 @@ +//gofmt -stdin + +i := 5 // Line comment without newline.
\ No newline at end of file diff --git a/src/cmd/gofmt/testdata/stdin5.input b/src/cmd/gofmt/testdata/stdin5.input new file mode 100644 index 0000000..0a7c97d --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin5.input @@ -0,0 +1,3 @@ +//gofmt -stdin + +i :=5// Line comment without newline.
\ No newline at end of file diff --git a/src/cmd/gofmt/testdata/stdin6.golden b/src/cmd/gofmt/testdata/stdin6.golden new file mode 100644 index 0000000..ffcea80 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin6.golden @@ -0,0 +1,19 @@ + //gofmt -stdin + + if err != nil { + source := strings.NewReader(`line 1. +line 2. +`) + return source + } + + f := func(hat, tail string) { + + fmt.Println(hat+` +foo + + +`+tail, + "more", + "and more") + } diff --git a/src/cmd/gofmt/testdata/stdin6.input b/src/cmd/gofmt/testdata/stdin6.input new file mode 100644 index 0000000..7833002 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin6.input @@ -0,0 +1,21 @@ + //gofmt -stdin + + if err != nil { + source := strings.NewReader(`line 1. +line 2. +`) + return source + } + + f:=func( hat, tail string){ + + + + fmt. Println ( hat+ ` +foo + + +`+ tail , + "more" , + "and more" ) + } diff --git a/src/cmd/gofmt/testdata/stdin7.golden b/src/cmd/gofmt/testdata/stdin7.golden new file mode 100644 index 0000000..bbac713 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin7.golden @@ -0,0 +1,19 @@ + //gofmt -stdin + + if err != nil { + source := strings.NewReader(`line 1. +line 2. +`) + return source + } + + f := func(hat, tail string) { + + fmt.Println(hat+` + foo + + + `+tail, + "more", + "and more") + } diff --git a/src/cmd/gofmt/testdata/stdin7.input b/src/cmd/gofmt/testdata/stdin7.input new file mode 100644 index 0000000..fd772a3 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin7.input @@ -0,0 +1,21 @@ + //gofmt -stdin + + if err != nil { + source := strings.NewReader(`line 1. +line 2. +`) + return source + } + + f:=func( hat, tail string){ + + + + fmt. Println ( hat+ ` + foo + + + `+ tail , + "more" , + "and more" ) + } diff --git a/src/cmd/gofmt/testdata/tabs.golden b/src/cmd/gofmt/testdata/tabs.golden new file mode 100644 index 0000000..287678c --- /dev/null +++ b/src/cmd/gofmt/testdata/tabs.golden @@ -0,0 +1,33 @@ +// Copyright 2022 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 + +package main + +var _ = []struct { + S string + Integer int +}{ + { + S: "Hello World", + Integer: 42, + }, + { + S: "\t", + Integer: 42, + }, + { + S: " ", // an actual <tab> + Integer: 42, + }, + { + S: ` `, // an actual <tab> + Integer: 42, + }, + { + S: "\u0009", + Integer: 42, + }, +} diff --git a/src/cmd/gofmt/testdata/tabs.input b/src/cmd/gofmt/testdata/tabs.input new file mode 100644 index 0000000..635be79 --- /dev/null +++ b/src/cmd/gofmt/testdata/tabs.input @@ -0,0 +1,33 @@ +// Copyright 2022 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 + +package main + +var _ = []struct{ + S string + Integer int +}{ + { + S: "Hello World", + Integer: 42, + }, + { + S: "\t", + Integer: 42, + }, + { + S: " ", // an actual <tab> + Integer: 42, + }, + { + S: ` `, // an actual <tab> + Integer: 42, + }, + { + S: "\u0009", + Integer: 42, + }, +} diff --git a/src/cmd/gofmt/testdata/typealias.golden b/src/cmd/gofmt/testdata/typealias.golden new file mode 100644 index 0000000..bbbbf32 --- /dev/null +++ b/src/cmd/gofmt/testdata/typealias.golden @@ -0,0 +1,24 @@ +package q + +import "p" + +type _ = int +type a = struct{ x int } +type b = p.B + +type ( + _ = chan<- int + aa = interface{} + bb = p.BB +) + +// TODO(gri) We may want to put the '=' into a separate column if +// we have mixed (regular and alias) type declarations in a group. +type ( + _ chan<- int + _ = chan<- int + aa0 interface{} + aaa = interface{} + bb0 p.BB + bbb = p.BB +) diff --git a/src/cmd/gofmt/testdata/typealias.input b/src/cmd/gofmt/testdata/typealias.input new file mode 100644 index 0000000..6e49328 --- /dev/null +++ b/src/cmd/gofmt/testdata/typealias.input @@ -0,0 +1,24 @@ +package q + +import "p" + +type _ = int +type a = struct{ x int } +type b = p.B + +type ( + _ = chan<- int + aa = interface{} + bb = p.BB +) + +// TODO(gri) We may want to put the '=' into a separate column if +// we have mixed (regular and alias) type declarations in a group. +type ( + _ chan<- int + _ = chan<- int + aa0 interface{} + aaa = interface{} + bb0 p.BB + bbb = p.BB +) diff --git a/src/cmd/gofmt/testdata/typeparams.golden b/src/cmd/gofmt/testdata/typeparams.golden new file mode 100644 index 0000000..d57a2ba --- /dev/null +++ b/src/cmd/gofmt/testdata/typeparams.golden @@ -0,0 +1,35 @@ +// Copyright 2020 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 + +package typeparams + +type T[P any] struct{} +type T[P1, P2, P3 any] struct{} + +type T[P C] struct{} +type T[P1, P2, P3 C] struct{} + +type T[P C[P]] struct{} +type T[P1, P2, P3 C[P1, P2, P3]] struct{} + +func f[P any](x P) +func f[P1, P2, P3 any](x1 P1, x2 P2, x3 P3) struct{} + +func f[P interface{}](x P) +func f[P1, P2, P3 interface { + m1(P1) + ~P2 | ~P3 +}](x1 P1, x2 P2, x3 P3) struct{} +func f[P any](T1[P], T2[P]) T3[P] + +func (x T[P]) m() +func (T[P]) m(x T[P]) P + +func _() { + type _ []T[P] + var _ []T[P] + _ = []T[P]{} +} diff --git a/src/cmd/gofmt/testdata/typeparams.input b/src/cmd/gofmt/testdata/typeparams.input new file mode 100644 index 0000000..775cf9e --- /dev/null +++ b/src/cmd/gofmt/testdata/typeparams.input @@ -0,0 +1,32 @@ +// Copyright 2020 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 + +package typeparams + +type T[ P any] struct{} +type T[P1, P2, P3 any] struct{} + +type T[P C] struct{} +type T[P1,P2, P3 C] struct{} + +type T[P C[P]] struct{} +type T[P1, P2, P3 C[P1,P2,P3]] struct{} + +func f[P any](x P) +func f[P1, P2, P3 any](x1 P1, x2 P2, x3 P3) struct{} + +func f[P interface{}](x P) +func f[P1, P2, P3 interface{ m1(P1); ~P2|~P3 }](x1 P1, x2 P2, x3 P3) struct{} +func f[P any](T1[P], T2[P]) T3[P] + +func (x T[P]) m() +func ((T[P])) m(x T[P]) P + +func _() { + type _ []T[P] + var _ []T[P] + _ = []T[P]{} +} diff --git a/src/cmd/gofmt/testdata/typeswitch.golden b/src/cmd/gofmt/testdata/typeswitch.golden new file mode 100644 index 0000000..3cf4dca --- /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 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 0000000..992a772 --- /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 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 + } + */ +} |