When rewriting a go.mod file, we currently sort all of the require
lines in a block. The way the parser works is that it considers
preceding blank lines to be empty comment lines, and preceding empty
comment lines are "owned" by their adjoining line. So when we go to sort
them, the empty lines follow around each sorted entry, which doesn't
make a whole lot of sense, since usually vertical space is inserted to
show sections, and if things get moved around by sorting, those sections
are no longer meaningful. This all results in one especially troublesome
edge case: blank lines between a block opening ("require (") and the
first block line ("golang.org/x/sys ...") are not treated the same way
and are rewritten out of existence.
Here's an example of the behavior this fixes.
Starting input file:
require (
golang.zx2c4.com/wireguard master
golang.org/x/crypto latest
golang.org/x/net latest
golang.org/x/sys latest
golang.org/x/text latest
github.com/lxn/walk latest
github.com/lxn/win latest
)
Now we run this through `GOPROXY=direct go get -d`:
require (
github.com/lxn/walk v0.0.0-
20190619151032-
86d8802c197a
github.com/lxn/win v0.0.0-
20190716185335-
d1d36f0e4f48
golang.org/x/crypto v0.0.0-
20190820162420-
60c769a6c586
golang.org/x/net v0.0.0-
20190813141303-
74dc4d7220e7
golang.org/x/sys v0.0.0-
20190813064441-
fde4db37ae7a
golang.org/x/text v0.3.2
golang.zx2c4.com/wireguard v0.0.
20190806-0.
20190822065259-
3cedc22d7b49
)
Notice how the blank lines before lxn/walk and x/crypto were preserved.
Finally, we have this be rewritten yet again with a call to `go build`:
require (
github.com/lxn/walk v0.0.0-
20190619151032-
86d8802c197a
github.com/lxn/win v0.0.0-
20190716185335-
d1d36f0e4f48
golang.org/x/crypto v0.0.0-
20190820162420-
60c769a6c586
golang.org/x/net v0.0.0-
20190813141303-
74dc4d7220e7
golang.org/x/sys v0.0.0-
20190813064441-
fde4db37ae7a
golang.org/x/text v0.3.2
golang.zx2c4.com/wireguard v0.0.
20190806-0.
20190822065259-
3cedc22d7b49
)
In this final resting point, the first blank line has been removed.
The discrepancy between those two last stages are especially bothersome,
because it makes for lots of dirty git commits and file contents
bouncing back and forth.
This commit fixes the problem as mentioned above, getting rid of those
preceding blank lines. The output in all cases looks as it should, like
this:
require (
github.com/lxn/walk v0.0.0-
20190619151032-
86d8802c197a
github.com/lxn/win v0.0.0-
20190716185335-
d1d36f0e4f48
golang.org/x/crypto v0.0.0-
20190820162420-
60c769a6c586
golang.org/x/net v0.0.0-
20190813141303-
74dc4d7220e7
golang.org/x/sys v0.0.0-
20190813064441-
fde4db37ae7a
golang.org/x/text v0.3.2
golang.zx2c4.com/wireguard v0.0.
20190806-0.
20190822065259-
3cedc22d7b49
)
Fixes #33779
Change-Id: I11c894440bd35f343ee62db3e06a50fa871f2599
Reviewed-on: https://go-review.googlesource.com/c/go/+/199917
Run-TryBot: Jason A. Donenfeld <Jason@zx2c4.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Bryan C. Mills <bcmills@google.com>
var newLines []*Line
for _, line := range stmt.Line {
if p, err := parseString(&line.Token[0]); err == nil && need[p] != "" {
+ if len(line.Comments.Before) == 1 && len(line.Comments.Before[0].Token) == 0 {
+ line.Comments.Before = line.Comments.Before[:0]
+ }
line.Token[1] = need[p]
delete(need, p)
setIndirect(line, indirect[p])
"bytes"
"fmt"
"testing"
+
+ "cmd/go/internal/module"
)
var addRequireTests = []struct {
},
}
+var setRequireTests = []struct {
+ in string
+ mods []struct {
+ path string
+ vers string
+ }
+ out string
+}{
+ {
+ `module m
+ require (
+ x.y/b v1.2.3
+
+ x.y/a v1.2.3
+ )
+ `,
+ []struct {
+ path string
+ vers string
+ }{
+ {"x.y/a", "v1.2.3"},
+ {"x.y/b", "v1.2.3"},
+ {"x.y/c", "v1.2.3"},
+ },
+ `module m
+ require (
+ x.y/a v1.2.3
+ x.y/b v1.2.3
+ x.y/c v1.2.3
+ )
+ `,
+ },
+}
+
func TestAddRequire(t *testing.T) {
for i, tt := range addRequireTests {
t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
})
}
}
+
+func TestSetRequire(t *testing.T) {
+ for i, tt := range setRequireTests {
+ t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
+ f, err := Parse("in", []byte(tt.in), nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ g, err := Parse("out", []byte(tt.out), nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ golden, err := g.Format()
+ if err != nil {
+ t.Fatal(err)
+ }
+ var mods []*Require
+ for _, mod := range tt.mods {
+ mods = append(mods, &Require{
+ Mod: module.Version{
+ Path: mod.path,
+ Version: mod.vers,
+ },
+ })
+ }
+
+ f.SetRequire(mods)
+ out, err := f.Format()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(out, golden) {
+ t.Errorf("have:\n%s\nwant:\n%s", out, golden)
+ }
+ })
+ }
+}