]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/cover: avoid repeating positions
authorIan Lance Taylor <iant@golang.org>
Fri, 7 Dec 2018 04:54:35 +0000 (20:54 -0800)
committerIan Lance Taylor <iant@golang.org>
Tue, 18 Dec 2018 20:57:18 +0000 (20:57 +0000)
When using //line directives and unformatted code it is possible for
positions to repeat. Increment the final column position to avoid that.

Fixes #27350

Change-Id: I2faccc31360075e9814d4a024b0f98b117f8ce97
Reviewed-on: https://go-review.googlesource.com/c/153061
Run-TryBot: Rob Pike <r@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
src/cmd/cover/cover.go
src/cmd/cover/cover_test.go

index 425bcbdd2660a64ce7341ea5a5b48491d5e71b70..0348849578bb1ad0f436186b72d36964c11bbd6d 100644 (file)
@@ -646,9 +646,21 @@ func (f *File) addVariables(w io.Writer) {
        // - 32-bit starting line number
        // - 32-bit ending line number
        // - (16 bit ending column number << 16) | (16-bit starting column number).
+       var lastStart, lastEnd token.Position
        for i, block := range f.blocks {
                start := f.fset.Position(block.startByte)
                end := f.fset.Position(block.endByte)
+
+               // It is possible for positions to repeat when there is a
+               // line directive that does not specify column information
+               // and the input has not been passed through gofmt.
+               // See issue #27350 and TestHtmlUnformatted.
+               if samePos(start, lastStart) && samePos(end, lastEnd) {
+                       end.Column++
+               }
+               lastStart = start
+               lastEnd = end
+
                fmt.Fprintf(w, "\t\t%d, %d, %#x, // [%d]\n", start.Line, end.Line, (end.Column&0xFFFF)<<16|(start.Column&0xFFFF), i)
        }
 
@@ -697,3 +709,11 @@ func isValidIdentifier(ident string) bool {
        }
        return true
 }
+
+// samePos returns whether two positions have the same file/line/column.
+// We don't use p1 == p2 because token.Position also has an Offset field,
+// and when the input uses //line directives two Positions can have different
+// Offset values while having the same file/line/dolumn.
+func samePos(p1, p2 token.Position) bool {
+       return p1.Filename == p2.Filename && p1.Line == p2.Line && p1.Column == p2.Column
+}
index 3e5c076d361822925a64785cab596670d7d11194..3de9b0c12d7ea64dd18440b879a6106ff7da5cb8 100644 (file)
@@ -40,11 +40,16 @@ var (
        htmlGolden = filepath.Join(testdata, "html", "html.golden")
 
        // Temporary files.
-       tmpTestMain string
-       coverInput  string
-       coverOutput string
-       htmlProfile string
-       htmlHTML    string
+       tmpTestMain  string
+       coverInput   string
+       coverOutput  string
+       htmlProfile  string
+       htmlHTML     string
+       htmlUDir     string
+       htmlU        string
+       htmlUTest    string
+       htmlUProfile string
+       htmlUHTML    string
 )
 
 var (
@@ -85,6 +90,11 @@ func TestMain(m *testing.M) {
        coverOutput = filepath.Join(dir, "test_cover.go")
        htmlProfile = filepath.Join(dir, "html.cov")
        htmlHTML = filepath.Join(dir, "html.html")
+       htmlUDir = filepath.Join(dir, "htmlunformatted")
+       htmlU = filepath.Join(htmlUDir, "htmlunformatted.go")
+       htmlUTest = filepath.Join(htmlUDir, "htmlunformatted_test.go")
+       htmlUProfile = filepath.Join(htmlUDir, "htmlunformatted.cov")
+       htmlUHTML = filepath.Join(htmlUDir, "htmlunformatted.html")
 
        status := m.Run()
 
@@ -427,12 +437,54 @@ func TestCoverHTML(t *testing.T) {
        }
 }
 
+// Test HTML processing with a source file not run through gofmt.
+// Issue #27350.
+func TestHtmlUnformatted(t *testing.T) {
+       t.Parallel()
+       testenv.MustHaveGoRun(t)
+       buildCover(t)
+
+       if err := os.Mkdir(htmlUDir, 0777); err != nil {
+               t.Fatal(err)
+       }
+
+       const htmlUContents = `
+package htmlunformatted
+
+var g int
+
+func F() {
+//line x.go:1
+       { { F(); goto lab } }
+lab:
+}`
+
+       const htmlUTestContents = `package htmlunformatted`
+
+       if err := ioutil.WriteFile(htmlU, []byte(htmlUContents), 0444); err != nil {
+               t.Fatal(err)
+       }
+       if err := ioutil.WriteFile(htmlUTest, []byte(htmlUTestContents), 0444); err != nil {
+               t.Fatal(err)
+       }
+
+       // go test -covermode=count -coverprofile TMPDIR/htmlunformatted.cov
+       cmd := exec.Command(testenv.GoToolPath(t), "test", toolexecArg, "-covermode=count", "-coverprofile", htmlUProfile)
+       cmd.Dir = htmlUDir
+       run(cmd, t)
+
+       // testcover -html TMPDIR/htmlunformatted.cov -o unformatted.html
+       cmd = exec.Command(testcover, "-html", htmlUProfile, "-o", htmlUHTML)
+       run(cmd, t)
+}
+
 func run(c *exec.Cmd, t *testing.T) {
        t.Helper()
        t.Log("running", c.Args)
-       c.Stdout = os.Stdout
-       c.Stderr = os.Stderr
-       err := c.Run()
+       out, err := c.CombinedOutput()
+       if len(out) > 0 {
+               t.Logf("%s", out)
+       }
        if err != nil {
                t.Fatal(err)
        }