Skip to content

Commit

Permalink
feat(gofmt): update to gofmt from Go 1.21.1
Browse files Browse the repository at this point in the history
New version does not use external diff tool.

Related to golangci/golangci-lint#3408.
  • Loading branch information
trollixx committed Oct 4, 2023
1 parent f2edd75 commit 19fe22b
Show file tree
Hide file tree
Showing 19 changed files with 399 additions and 1,313 deletions.
11 changes: 7 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ module github.com/golangci/gofmt
go 1.18

require (
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
golang.org/x/tools v0.1.12
golang.org/x/sync v0.3.0
golang.org/x/tools v0.13.0
)

require (
golang.org/x/mod v0.12.0 // indirect
golang.org/x/sys v0.12.0 // indirect
)
16 changes: 8 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
8 changes: 5 additions & 3 deletions gofmt/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ 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
Expand All @@ -37,10 +39,10 @@ The flags are:
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
Expand All @@ -57,7 +59,7 @@ 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
# Examples
To check files for unnecessary parentheses:
Expand All @@ -71,7 +73,7 @@ To convert the package tree from explicit slice upper bounds to implicit ones:
gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src
The simplify command
# The simplify command
When invoked with -s gofmt will make the following source transformations where possible.
Expand Down
203 changes: 120 additions & 83 deletions gofmt/gofmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,30 @@ import (
"go/token"
"io"
"io/fs"
"math/rand"
"os"
"path/filepath"
"runtime"
"runtime/pprof"
"strconv"
"strings"

"github.com/golangci/gofmt/gofmt/internal/diff"
"golang.org/x/sync/semaphore"

"github.com/golangci/gofmt/gofmt/internal/diff"
)

var (
// main operation modes
list = flag.Bool("gofmt.l", false, "list files whose formatting differs from gofmt's")
write = flag.Bool("gofmt.w", false, "write result to (source) file instead of stdout")
rewriteRule = flag.String("gofmt.r", "", "rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')")
simplifyAST = flag.Bool("gofmt.s", false, "simplify code")
doDiff = flag.Bool("gofmt.d", false, "display diffs instead of rewriting files")
allErrors = flag.Bool("gofmt.e", false, "report all errors (not just the first 10 on different lines)")
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("gofmt.cpuprofile", "", "write cpu profile to this file")
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to this file")
)

// Keep these in sync with go/format/format.go.
Expand Down Expand Up @@ -76,6 +79,11 @@ func initParserMode() {
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 {
Expand Down Expand Up @@ -228,12 +236,9 @@ func processFile(filename string, info fs.FileInfo, in io.Reader, r *reporter) e
}

fileSet := token.NewFileSet()
fragmentOk := false
if info == nil {
// If we are formatting stdin, we accept a program fragment in lieu of a
// complete source file.
fragmentOk = true
}
// 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
Expand Down Expand Up @@ -267,31 +272,16 @@ func processFile(filename string, info fs.FileInfo, in io.Reader, r *reporter) e
if info == nil {
panic("-w should not have been allowed with stdin")
}
// make a temporary backup before overwriting original

perm := info.Mode().Perm()
bakname, err := backupFile(filename+".", src, perm)
if err != nil {
return err
}
fdSem <- true
err = os.WriteFile(filename, res, perm)
<-fdSem
if err != nil {
os.Rename(bakname, filename)
return err
}
err = os.Remove(bakname)
if err != nil {
if err := writeFile(filename, src, res, perm, info.Size()); err != nil {
return err
}
}
if *doDiff {
data, err := diffWithReplaceTempFile(src, res, filename)
if err != nil {
return fmt.Errorf("computing diff: %s", err)
}
fmt.Fprintf(r, "diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename))
r.Write(data)
newName := filepath.ToSlash(filename)
oldName := newName + ".orig"
r.Write(diff.Diff(oldName, src, newName, res))
}
}

Expand Down Expand Up @@ -350,7 +340,12 @@ func readFile(filename string, info fs.FileInfo, in io.Reader) ([]byte, error) {
// stop to avoid corrupting it.)
src := make([]byte, size+1)
n, err := io.ReadFull(in, src)
if err != nil && err != io.ErrUnexpectedEOF {
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 {
Expand Down Expand Up @@ -463,69 +458,111 @@ func fileWeight(path string, info fs.FileInfo) int64 {
return info.Size()
}

func diffWithReplaceTempFile(b1, b2 []byte, filename string) ([]byte, error) {
data, err := diff.Diff("gofmt", b1, b2)
if len(data) > 0 {
return replaceTempFilename(data, filename)
// 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
}
return data, err
}

// replaceTempFilename replaces temporary filenames in diff with actual one.
//
// --- /tmp/gofmt316145376 2017-02-03 19:13:00.280468375 -0500
// +++ /tmp/gofmt617882815 2017-02-03 19:13:00.280468375 -0500
// ...
// ->
// --- path/to/file.go.orig 2017-02-03 19:13:00.280468375 -0500
// +++ path/to/file.go 2017-02-03 19:13:00.280468375 -0500
// ...
func replaceTempFilename(diff []byte, filename string) ([]byte, error) {
bs := bytes.SplitN(diff, []byte{'\n'}, 3)
if len(bs) < 3 {
return nil, fmt.Errorf("got unexpected diff for %s", filename)
}
// Preserve timestamps.
var t0, t1 []byte
if i := bytes.LastIndexByte(bs[0], '\t'); i != -1 {
t0 = bs[0][i:]
}
if i := bytes.LastIndexByte(bs[1], '\t'); i != -1 {
t1 = bs[1][i:]
}
// Always print filepath with slash separator.
f := filepath.ToSlash(filename)
bs[0] = []byte(fmt.Sprintf("--- %s%s", f+".orig", t0))
bs[1] = []byte(fmt.Sprintf("+++ %s%s", f, t1))
return bytes.Join(bs, []byte{'\n'}), nil
}
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)
}

const chmodSupported = runtime.GOOS != "windows"
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
// 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 }()

// create backup file
f, err := os.CreateTemp(filepath.Dir(filename), filepath.Base(filename))
if err != nil {
return "", err
nextRandom := func() string {
return strconv.Itoa(rand.Int())
}
bakname := f.Name()
if chmodSupported {
err = f.Chmod(perm)
if err != nil {
f.Close()
os.Remove(bakname)
return bakname, err

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)
_, err := f.Write(data)
if err1 := f.Close(); err == nil {
err = err1
}
Expand Down
Loading

0 comments on commit 19fe22b

Please sign in to comment.