Skip to content

Commit

Permalink
sync with go1.19beta1's gofmt
Browse files Browse the repository at this point in the history
The changes since go1.18 roughly include:

* new diff package, which is much faster

* error on empty Go files

* use SkipObjectResolution to avoid unnecessary work
  • Loading branch information
mvdan committed Jun 13, 2022
1 parent 2a5d4bc commit 900c61a
Show file tree
Hide file tree
Showing 3 changed files with 261 additions and 108 deletions.
22 changes: 13 additions & 9 deletions format/simplify.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,22 +53,26 @@ func (s simplifier) Visit(node ast.Node) ast.Visitor {
// 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 another
// file belonging to the same package. However, this is extremely unlikely
// and so far (April 2016, after years of supporting this rewrite feature)
// 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 && s.Obj != nil {
// the array/slice object is a single, resolved identifier
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" && fun.Obj == nil {
// the function called is "len" and it is not locally defined; and
// because we don't have dot imports, it must be the predefined len()
if arg, _ := call.Args[0].(*ast.Ident); arg != nil && arg.Obj == s.Obj {
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
}
Expand Down
57 changes: 12 additions & 45 deletions gofmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import (
"strings"
"sync"

// TODO: we can soon use os/exec thanks to
// https://go.dev/issue/43724
"golang.org/x/sync/semaphore"
exec "golang.org/x/sys/execabs"

Expand Down Expand Up @@ -100,7 +102,7 @@ func usage() {
}

func initParserMode() {
parserMode = parser.ParseComments
parserMode = parser.ParseComments | parser.SkipObjectResolution
if *allErrors {
parserMode |= parser.AllErrors
}
Expand Down Expand Up @@ -348,12 +350,9 @@ func processFile(filename string, info fs.FileInfo, in io.Reader, r *reporter, e
}
}
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 @@ -412,7 +411,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 @@ -589,43 +593,6 @@ func fileWeight(path string, info fs.FileInfo) int64 {
return info.Size()
}

func diffWithReplaceTempFile(b1, b2 []byte, filename string) ([]byte, error) {
data, err := diff.Diff("gofumpt", b1, b2)
if len(data) > 0 {
return replaceTempFilename(data, filename)
}
return data, err
}

// replaceTempFilename replaces temporary filenames in diff with actual one.
//
// --- /tmp/gofumpt316145376 2017-02-03 19:13:00.280468375 -0500
// +++ /tmp/gofumpt617882815 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
}

const chmodSupported = runtime.GOOS != "windows"

// backupFile writes data to a new file named filename<number> with permissions perm,
Expand Down
Loading

0 comments on commit 900c61a

Please sign in to comment.