Commit 4c7a8d63 authored by Jason A. Donenfeld's avatar Jason A. Donenfeld Committed by Bryan C. Mills

cmd/go/internal/modfile: remove preceding empty lines when setting require

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: default avatarBryan C. Mills <bcmills@google.com>
parent 829fae3b
......@@ -566,6 +566,9 @@ func (f *File) SetRequire(req []*Require) {
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])
......
......@@ -8,6 +8,8 @@ import (
"bytes"
"fmt"
"testing"
"cmd/go/internal/module"
)
var addRequireTests = []struct {
......@@ -59,6 +61,40 @@ 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) {
......@@ -88,3 +124,40 @@ func TestAddRequire(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)
}
})
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment