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: Bryan C. Mills <bcmills@google.com>
diff --git a/src/cmd/go/internal/modfile/rule.go b/src/cmd/go/internal/modfile/rule.go
index e1f2687..1b64216 100644
--- a/src/cmd/go/internal/modfile/rule.go
+++ b/src/cmd/go/internal/modfile/rule.go
@@ -566,6 +566,9 @@
 				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])
diff --git a/src/cmd/go/internal/modfile/rule_test.go b/src/cmd/go/internal/modfile/rule_test.go
index b88ad62..edd2890 100644
--- a/src/cmd/go/internal/modfile/rule_test.go
+++ b/src/cmd/go/internal/modfile/rule_test.go
@@ -8,6 +8,8 @@
 	"bytes"
 	"fmt"
 	"testing"
+
+	"cmd/go/internal/module"
 )
 
 var addRequireTests = []struct {
@@ -59,6 +61,40 @@
 	},
 }
 
+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 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)
+			}
+		})
+	}
+}