Skip to content

Commit d689946

Browse files
cmd/cover: avoid repeating positions
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 <[email protected]> Reviewed-by: Rob Pike <[email protected]>
1 parent 77caea5 commit d689946

File tree

2 files changed

+80
-8
lines changed

2 files changed

+80
-8
lines changed

src/cmd/cover/cover.go

+20
Original file line numberDiff line numberDiff line change
@@ -646,9 +646,21 @@ func (f *File) addVariables(w io.Writer) {
646646
// - 32-bit starting line number
647647
// - 32-bit ending line number
648648
// - (16 bit ending column number << 16) | (16-bit starting column number).
649+
var lastStart, lastEnd token.Position
649650
for i, block := range f.blocks {
650651
start := f.fset.Position(block.startByte)
651652
end := f.fset.Position(block.endByte)
653+
654+
// It is possible for positions to repeat when there is a
655+
// line directive that does not specify column information
656+
// and the input has not been passed through gofmt.
657+
// See issue #27350 and TestHtmlUnformatted.
658+
if samePos(start, lastStart) && samePos(end, lastEnd) {
659+
end.Column++
660+
}
661+
lastStart = start
662+
lastEnd = end
663+
652664
fmt.Fprintf(w, "\t\t%d, %d, %#x, // [%d]\n", start.Line, end.Line, (end.Column&0xFFFF)<<16|(start.Column&0xFFFF), i)
653665
}
654666

@@ -697,3 +709,11 @@ func isValidIdentifier(ident string) bool {
697709
}
698710
return true
699711
}
712+
713+
// samePos returns whether two positions have the same file/line/column.
714+
// We don't use p1 == p2 because token.Position also has an Offset field,
715+
// and when the input uses //line directives two Positions can have different
716+
// Offset values while having the same file/line/dolumn.
717+
func samePos(p1, p2 token.Position) bool {
718+
return p1.Filename == p2.Filename && p1.Line == p2.Line && p1.Column == p2.Column
719+
}

src/cmd/cover/cover_test.go

+60-8
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,16 @@ var (
4040
htmlGolden = filepath.Join(testdata, "html", "html.golden")
4141

4242
// Temporary files.
43-
tmpTestMain string
44-
coverInput string
45-
coverOutput string
46-
htmlProfile string
47-
htmlHTML string
43+
tmpTestMain string
44+
coverInput string
45+
coverOutput string
46+
htmlProfile string
47+
htmlHTML string
48+
htmlUDir string
49+
htmlU string
50+
htmlUTest string
51+
htmlUProfile string
52+
htmlUHTML string
4853
)
4954

5055
var (
@@ -85,6 +90,11 @@ func TestMain(m *testing.M) {
8590
coverOutput = filepath.Join(dir, "test_cover.go")
8691
htmlProfile = filepath.Join(dir, "html.cov")
8792
htmlHTML = filepath.Join(dir, "html.html")
93+
htmlUDir = filepath.Join(dir, "htmlunformatted")
94+
htmlU = filepath.Join(htmlUDir, "htmlunformatted.go")
95+
htmlUTest = filepath.Join(htmlUDir, "htmlunformatted_test.go")
96+
htmlUProfile = filepath.Join(htmlUDir, "htmlunformatted.cov")
97+
htmlUHTML = filepath.Join(htmlUDir, "htmlunformatted.html")
8898

8999
status := m.Run()
90100

@@ -427,12 +437,54 @@ func TestCoverHTML(t *testing.T) {
427437
}
428438
}
429439

440+
// Test HTML processing with a source file not run through gofmt.
441+
// Issue #27350.
442+
func TestHtmlUnformatted(t *testing.T) {
443+
t.Parallel()
444+
testenv.MustHaveGoRun(t)
445+
buildCover(t)
446+
447+
if err := os.Mkdir(htmlUDir, 0777); err != nil {
448+
t.Fatal(err)
449+
}
450+
451+
const htmlUContents = `
452+
package htmlunformatted
453+
454+
var g int
455+
456+
func F() {
457+
//line x.go:1
458+
{ { F(); goto lab } }
459+
lab:
460+
}`
461+
462+
const htmlUTestContents = `package htmlunformatted`
463+
464+
if err := ioutil.WriteFile(htmlU, []byte(htmlUContents), 0444); err != nil {
465+
t.Fatal(err)
466+
}
467+
if err := ioutil.WriteFile(htmlUTest, []byte(htmlUTestContents), 0444); err != nil {
468+
t.Fatal(err)
469+
}
470+
471+
// go test -covermode=count -coverprofile TMPDIR/htmlunformatted.cov
472+
cmd := exec.Command(testenv.GoToolPath(t), "test", toolexecArg, "-covermode=count", "-coverprofile", htmlUProfile)
473+
cmd.Dir = htmlUDir
474+
run(cmd, t)
475+
476+
// testcover -html TMPDIR/htmlunformatted.cov -o unformatted.html
477+
cmd = exec.Command(testcover, "-html", htmlUProfile, "-o", htmlUHTML)
478+
run(cmd, t)
479+
}
480+
430481
func run(c *exec.Cmd, t *testing.T) {
431482
t.Helper()
432483
t.Log("running", c.Args)
433-
c.Stdout = os.Stdout
434-
c.Stderr = os.Stderr
435-
err := c.Run()
484+
out, err := c.CombinedOutput()
485+
if len(out) > 0 {
486+
t.Logf("%s", out)
487+
}
436488
if err != nil {
437489
t.Fatal(err)
438490
}

0 commit comments

Comments
 (0)