cmd/cover: preserve //go: comments

Cover deleted all comments because they can break the simple way that
counters are injected into the rewritten source. But //go: comments have
semantic value, and for instance go test -cover runtime fails during
compilation because of their absence from the annotated source.

We can keep the //go: comments because they are at the beginning of
the line and are not affected by our counter injection.

Fixes #10270.

After this CL, go test -cover runtime works.
A testing strategy that does not involve a golden file would be welcome
but I can't think of one.

Change-Id: I73f7b7a36383a8efed8e33fa2414cd0eac7d015a
Reviewed-on: https://go-review.googlesource.com/8173
Reviewed-by: Robert Griesemer <gri@golang.org>
diff --git a/cmd/cover/cover.go b/cmd/cover/cover.go
index 6826f0b..e6e7b46 100644
--- a/cmd/cover/cover.go
+++ b/cmd/cover/cover.go
@@ -19,6 +19,7 @@
 	"path/filepath"
 	"sort"
 	"strconv"
+	"strings"
 )
 
 const usageMessage = "" +
@@ -329,10 +330,11 @@
 	if err != nil {
 		log.Fatalf("cover: %s: %s", name, err)
 	}
-	parsedFile, err := parser.ParseFile(fset, name, content, 0)
+	parsedFile, err := parser.ParseFile(fset, name, content, parser.ParseComments)
 	if err != nil {
 		log.Fatalf("cover: %s: %s", name, err)
 	}
+	parsedFile.Comments = trimComments(parsedFile, fset)
 
 	file := &File{
 		fset:    fset,
@@ -358,6 +360,26 @@
 	file.addVariables(fd)
 }
 
+// trimComments drops all but the //go: comments, some of which are semantically important.
+// We drop all others because they can appear in places that cause our counters
+// to appear in syntactically incorrect places. //go: appears at the beginning of
+// the line and is syntactically safe.
+func trimComments(file *ast.File, fset *token.FileSet) []*ast.CommentGroup {
+	var comments []*ast.CommentGroup
+	for _, group := range file.Comments {
+		var list []*ast.Comment
+		for _, comment := range group.List {
+			if strings.HasPrefix(comment.Text, "//go:") && fset.Position(comment.Slash).Column == 1 {
+				list = append(list, comment)
+			}
+		}
+		if list != nil {
+			comments = append(comments, &ast.CommentGroup{list})
+		}
+	}
+	return comments
+}
+
 func (f *File) print(w io.Writer) {
 	printer.Fprint(w, f.fset, f.astFile)
 }