Commit d6899463 authored by Ian Lance Taylor's avatar Ian Lance Taylor

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 <r@golang.org>
Reviewed-by: default avatarRob Pike <r@golang.org>
parent 77caea5b
......@@ -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
}
......@@ -45,6 +45,11 @@ var (
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)
}
......
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