mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile/internal/syntax: implement //line :line:col handling
For line directives which have a line and a column number, an omitted filename means that the filename has not changed (per the issue below). For line directives w/o a column number, an omitted filename means the empty filename (to preserve the existing behavior). For #22662. Change-Id: I32cd9037550485da5445a34bb104706eccce1df1 Reviewed-on: https://go-review.googlesource.com/96476 Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
parent
5c08b9e8bd
commit
6fa6bde924
2 changed files with 68 additions and 47 deletions
|
|
@ -52,7 +52,7 @@ func (p *parser) init(file *PosBase, r io.Reader, errh ErrorHandler, pragh Pragm
|
||||||
if strings.HasPrefix(text, "line ") {
|
if strings.HasPrefix(text, "line ") {
|
||||||
var pos Pos // position immediately following the comment
|
var pos Pos // position immediately following the comment
|
||||||
if msg[1] == '/' {
|
if msg[1] == '/' {
|
||||||
// line comment
|
// line comment (newline is part of the comment)
|
||||||
pos = MakePos(p.file, line+1, colbase)
|
pos = MakePos(p.file, line+1, colbase)
|
||||||
} else {
|
} else {
|
||||||
// regular comment
|
// regular comment
|
||||||
|
|
@ -83,6 +83,9 @@ func (p *parser) init(file *PosBase, r io.Reader, errh ErrorHandler, pragh Pragm
|
||||||
p.indent = nil
|
p.indent = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateBase sets the current position base to a new line base at pos.
|
||||||
|
// The base's filename, line, and column values are extracted from text
|
||||||
|
// which is positioned at (line, col) (only needed for error messages).
|
||||||
func (p *parser) updateBase(pos Pos, line, col uint, text string) {
|
func (p *parser) updateBase(pos Pos, line, col uint, text string) {
|
||||||
i, n, ok := trailingDigits(text)
|
i, n, ok := trailingDigits(text)
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
|
|
@ -116,8 +119,12 @@ func (p *parser) updateBase(pos Pos, line, col uint, text string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we have a column (//line filename:line:col form),
|
||||||
|
// an empty filename means to use the previous filename.
|
||||||
filename := text[:i-1] // lop off :line
|
filename := text[:i-1] // lop off :line
|
||||||
// TODO(gri) handle case where filename doesn't change (see #22662)
|
if filename == "" && ok2 {
|
||||||
|
filename = p.base.Filename()
|
||||||
|
}
|
||||||
|
|
||||||
p.base = NewLineBase(pos, filename, n, n2)
|
p.base = NewLineBase(pos, filename, n, n2)
|
||||||
}
|
}
|
||||||
|
|
@ -133,7 +140,7 @@ func commentText(s string) string {
|
||||||
if s[i-1] == '\r' {
|
if s[i-1] == '\r' {
|
||||||
i--
|
i--
|
||||||
}
|
}
|
||||||
return s[2:i] // lop off // and \r at end, if any
|
return s[2:i] // lop off //, and \r at end, if any
|
||||||
}
|
}
|
||||||
|
|
||||||
func trailingDigits(text string) (uint, uint, bool) {
|
func trailingDigits(text string) (uint, uint, bool) {
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,7 @@ var tooLarge int = PosMax + 1
|
||||||
func TestLineDirectives(t *testing.T) {
|
func TestLineDirectives(t *testing.T) {
|
||||||
// valid line directives lead to a syntax error after them
|
// valid line directives lead to a syntax error after them
|
||||||
const valid = "syntax error: package statement must be first"
|
const valid = "syntax error: package statement must be first"
|
||||||
|
const filename = "directives.go"
|
||||||
|
|
||||||
for _, test := range []struct {
|
for _, test := range []struct {
|
||||||
src, msg string
|
src, msg string
|
||||||
|
|
@ -195,33 +196,33 @@ func TestLineDirectives(t *testing.T) {
|
||||||
line, col uint // 0-based
|
line, col uint // 0-based
|
||||||
}{
|
}{
|
||||||
// ignored //line directives
|
// ignored //line directives
|
||||||
{"//\n", valid, "", 1, 0}, // no directive
|
{"//\n", valid, filename, 1, 0}, // no directive
|
||||||
{"//line\n", valid, "", 1, 0}, // missing colon
|
{"//line\n", valid, filename, 1, 0}, // missing colon
|
||||||
{"//line foo\n", valid, "", 1, 0}, // missing colon
|
{"//line foo\n", valid, filename, 1, 0}, // missing colon
|
||||||
{" //line foo:\n", valid, "", 1, 0}, // not a line start
|
{" //line foo:\n", valid, filename, 1, 0}, // not a line start
|
||||||
{"// line foo:\n", valid, "", 1, 0}, // space between // and line
|
{"// line foo:\n", valid, filename, 1, 0}, // space between // and line
|
||||||
|
|
||||||
// invalid //line directives with one colon
|
// invalid //line directives with one colon
|
||||||
{"//line :\n", "invalid line number: ", "", 0, 8},
|
{"//line :\n", "invalid line number: ", filename, 0, 8},
|
||||||
{"//line :x\n", "invalid line number: x", "", 0, 8},
|
{"//line :x\n", "invalid line number: x", filename, 0, 8},
|
||||||
{"//line foo :\n", "invalid line number: ", "", 0, 12},
|
{"//line foo :\n", "invalid line number: ", filename, 0, 12},
|
||||||
{"//line foo:x\n", "invalid line number: x", "", 0, 11},
|
{"//line foo:x\n", "invalid line number: x", filename, 0, 11},
|
||||||
{"//line foo:0\n", "invalid line number: 0", "", 0, 11},
|
{"//line foo:0\n", "invalid line number: 0", filename, 0, 11},
|
||||||
{"//line foo:1 \n", "invalid line number: 1 ", "", 0, 11},
|
{"//line foo:1 \n", "invalid line number: 1 ", filename, 0, 11},
|
||||||
{"//line foo:-12\n", "invalid line number: -12", "", 0, 11},
|
{"//line foo:-12\n", "invalid line number: -12", filename, 0, 11},
|
||||||
{"//line C:foo:0\n", "invalid line number: 0", "", 0, 13},
|
{"//line C:foo:0\n", "invalid line number: 0", filename, 0, 13},
|
||||||
{fmt.Sprintf("//line foo:%d\n", tooLarge), fmt.Sprintf("invalid line number: %d", tooLarge), "", 0, 11},
|
{fmt.Sprintf("//line foo:%d\n", tooLarge), fmt.Sprintf("invalid line number: %d", tooLarge), filename, 0, 11},
|
||||||
|
|
||||||
// invalid //line directives with two colons
|
// invalid //line directives with two colons
|
||||||
{"//line ::\n", "invalid line number: ", "", 0, 9},
|
{"//line ::\n", "invalid line number: ", filename, 0, 9},
|
||||||
{"//line ::x\n", "invalid line number: x", "", 0, 9},
|
{"//line ::x\n", "invalid line number: x", filename, 0, 9},
|
||||||
{"//line foo::123abc\n", "invalid line number: 123abc", "", 0, 12},
|
{"//line foo::123abc\n", "invalid line number: 123abc", filename, 0, 12},
|
||||||
{"//line foo::0\n", "invalid line number: 0", "", 0, 12},
|
{"//line foo::0\n", "invalid line number: 0", filename, 0, 12},
|
||||||
{"//line foo:0:1\n", "invalid line number: 0", "", 0, 11},
|
{"//line foo:0:1\n", "invalid line number: 0", filename, 0, 11},
|
||||||
|
|
||||||
{"//line :123:0\n", "invalid column number: 0", "", 0, 12},
|
{"//line :123:0\n", "invalid column number: 0", filename, 0, 12},
|
||||||
{"//line foo:123:0\n", "invalid column number: 0", "", 0, 15},
|
{"//line foo:123:0\n", "invalid column number: 0", filename, 0, 15},
|
||||||
{fmt.Sprintf("//line foo:10:%d\n", tooLarge), fmt.Sprintf("invalid column number: %d", tooLarge), "", 0, 14},
|
{fmt.Sprintf("//line foo:10:%d\n", tooLarge), fmt.Sprintf("invalid column number: %d", tooLarge), filename, 0, 14},
|
||||||
|
|
||||||
// effect of valid //line directives on lines
|
// effect of valid //line directives on lines
|
||||||
{"//line foo:123\n foo", valid, "foo", 123 - linebase, 3},
|
{"//line foo:123\n foo", valid, "foo", 123 - linebase, 3},
|
||||||
|
|
@ -242,33 +243,39 @@ func TestLineDirectives(t *testing.T) {
|
||||||
{"//line foo :123:1000\n\n", valid, "foo ", 124 - linebase, 0},
|
{"//line foo :123:1000\n\n", valid, "foo ", 124 - linebase, 0},
|
||||||
{"//line ::123:1234\n", valid, ":", 123 - linebase, 1234 - colbase},
|
{"//line ::123:1234\n", valid, ":", 123 - linebase, 1234 - colbase},
|
||||||
|
|
||||||
|
// //line directives with omitted filenames lead to empty filenames
|
||||||
|
{"//line :10\n", valid, "", 10 - linebase, 0},
|
||||||
|
{"//line :10:20\n", valid, filename, 10 - linebase, 20 - colbase},
|
||||||
|
{"//line bar:1\n//line :10\n", valid, "", 10 - linebase, 0},
|
||||||
|
{"//line bar:1\n//line :10:20\n", valid, "bar", 10 - linebase, 20 - colbase},
|
||||||
|
|
||||||
// ignored /*line directives
|
// ignored /*line directives
|
||||||
{"/**/", valid, "", 0, 4}, // no directive
|
{"/**/", valid, filename, 0, 4}, // no directive
|
||||||
{"/*line*/", valid, "", 0, 8}, // missing colon
|
{"/*line*/", valid, filename, 0, 8}, // missing colon
|
||||||
{"/*line foo*/", valid, "", 0, 12}, // missing colon
|
{"/*line foo*/", valid, filename, 0, 12}, // missing colon
|
||||||
{" //line foo:*/", valid, "", 0, 15}, // not a line start
|
{" //line foo:*/", valid, filename, 0, 15}, // not a line start
|
||||||
{"/* line foo:*/", valid, "", 0, 15}, // space between // and line
|
{"/* line foo:*/", valid, filename, 0, 15}, // space between // and line
|
||||||
|
|
||||||
// invalid /*line directives with one colon
|
// invalid /*line directives with one colon
|
||||||
{"/*line :*/", "invalid line number: ", "", 0, 8},
|
{"/*line :*/", "invalid line number: ", filename, 0, 8},
|
||||||
{"/*line :x*/", "invalid line number: x", "", 0, 8},
|
{"/*line :x*/", "invalid line number: x", filename, 0, 8},
|
||||||
{"/*line foo :*/", "invalid line number: ", "", 0, 12},
|
{"/*line foo :*/", "invalid line number: ", filename, 0, 12},
|
||||||
{"/*line foo:x*/", "invalid line number: x", "", 0, 11},
|
{"/*line foo:x*/", "invalid line number: x", filename, 0, 11},
|
||||||
{"/*line foo:0*/", "invalid line number: 0", "", 0, 11},
|
{"/*line foo:0*/", "invalid line number: 0", filename, 0, 11},
|
||||||
{"/*line foo:1 */", "invalid line number: 1 ", "", 0, 11},
|
{"/*line foo:1 */", "invalid line number: 1 ", filename, 0, 11},
|
||||||
{"/*line C:foo:0*/", "invalid line number: 0", "", 0, 13},
|
{"/*line C:foo:0*/", "invalid line number: 0", filename, 0, 13},
|
||||||
{fmt.Sprintf("/*line foo:%d*/", tooLarge), fmt.Sprintf("invalid line number: %d", tooLarge), "", 0, 11},
|
{fmt.Sprintf("/*line foo:%d*/", tooLarge), fmt.Sprintf("invalid line number: %d", tooLarge), filename, 0, 11},
|
||||||
|
|
||||||
// invalid /*line directives with two colons
|
// invalid /*line directives with two colons
|
||||||
{"/*line ::*/", "invalid line number: ", "", 0, 9},
|
{"/*line ::*/", "invalid line number: ", filename, 0, 9},
|
||||||
{"/*line ::x*/", "invalid line number: x", "", 0, 9},
|
{"/*line ::x*/", "invalid line number: x", filename, 0, 9},
|
||||||
{"/*line foo::123abc*/", "invalid line number: 123abc", "", 0, 12},
|
{"/*line foo::123abc*/", "invalid line number: 123abc", filename, 0, 12},
|
||||||
{"/*line foo::0*/", "invalid line number: 0", "", 0, 12},
|
{"/*line foo::0*/", "invalid line number: 0", filename, 0, 12},
|
||||||
{"/*line foo:0:1*/", "invalid line number: 0", "", 0, 11},
|
{"/*line foo:0:1*/", "invalid line number: 0", filename, 0, 11},
|
||||||
|
|
||||||
{"/*line :123:0*/", "invalid column number: 0", "", 0, 12},
|
{"/*line :123:0*/", "invalid column number: 0", filename, 0, 12},
|
||||||
{"/*line foo:123:0*/", "invalid column number: 0", "", 0, 15},
|
{"/*line foo:123:0*/", "invalid column number: 0", filename, 0, 15},
|
||||||
{fmt.Sprintf("/*line foo:10:%d*/", tooLarge), fmt.Sprintf("invalid column number: %d", tooLarge), "", 0, 14},
|
{fmt.Sprintf("/*line foo:10:%d*/", tooLarge), fmt.Sprintf("invalid column number: %d", tooLarge), filename, 0, 14},
|
||||||
|
|
||||||
// effect of valid /*line directives on lines
|
// effect of valid /*line directives on lines
|
||||||
{"/*line foo:123*/ foo", valid, "foo", 123 - linebase, 3},
|
{"/*line foo:123*/ foo", valid, "foo", 123 - linebase, 3},
|
||||||
|
|
@ -287,8 +294,15 @@ func TestLineDirectives(t *testing.T) {
|
||||||
{"/*line foo:123abc:1:1000*/", valid, "foo:123abc", 1 - linebase, 1000 - colbase},
|
{"/*line foo:123abc:1:1000*/", valid, "foo:123abc", 1 - linebase, 1000 - colbase},
|
||||||
{"/*line foo :123:1000*/\n", valid, "foo ", 124 - linebase, 0},
|
{"/*line foo :123:1000*/\n", valid, "foo ", 124 - linebase, 0},
|
||||||
{"/*line ::123:1234*/", valid, ":", 123 - linebase, 1234 - colbase},
|
{"/*line ::123:1234*/", valid, ":", 123 - linebase, 1234 - colbase},
|
||||||
|
|
||||||
|
// /*line directives with omitted filenames lead to the previously used filenames
|
||||||
|
{"/*line :10*/", valid, "", 10 - linebase, 0},
|
||||||
|
{"/*line :10:20*/", valid, filename, 10 - linebase, 20 - colbase},
|
||||||
|
{"//line bar:1\n/*line :10*/", valid, "", 10 - linebase, 0},
|
||||||
|
{"//line bar:1\n/*line :10:20*/", valid, "bar", 10 - linebase, 20 - colbase},
|
||||||
} {
|
} {
|
||||||
_, err := Parse(nil, strings.NewReader(test.src), nil, nil, 0)
|
base := NewFileBase(filename)
|
||||||
|
_, err := Parse(base, strings.NewReader(test.src), nil, nil, 0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("%s: no error reported", test.src)
|
t.Errorf("%s: no error reported", test.src)
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue