From 6fa6bde92408a526021d690003f19368f5d57b20 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 22 Feb 2018 15:50:14 -0800 Subject: [PATCH] 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 --- src/cmd/compile/internal/syntax/parser.go | 13 ++- .../compile/internal/syntax/parser_test.go | 102 ++++++++++-------- 2 files changed, 68 insertions(+), 47 deletions(-) diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go index fd200f7381..3bed97b31c 100644 --- a/src/cmd/compile/internal/syntax/parser.go +++ b/src/cmd/compile/internal/syntax/parser.go @@ -52,7 +52,7 @@ func (p *parser) init(file *PosBase, r io.Reader, errh ErrorHandler, pragh Pragm if strings.HasPrefix(text, "line ") { var pos Pos // position immediately following the comment if msg[1] == '/' { - // line comment + // line comment (newline is part of the comment) pos = MakePos(p.file, line+1, colbase) } else { // regular comment @@ -83,6 +83,9 @@ func (p *parser) init(file *PosBase, r io.Reader, errh ErrorHandler, pragh Pragm 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) { i, n, ok := trailingDigits(text) if i == 0 { @@ -116,8 +119,12 @@ func (p *parser) updateBase(pos Pos, line, col uint, text string) { 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 - // 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) } @@ -133,7 +140,7 @@ func commentText(s string) string { if s[i-1] == '\r' { 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) { diff --git a/src/cmd/compile/internal/syntax/parser_test.go b/src/cmd/compile/internal/syntax/parser_test.go index cfac2e0118..c253a5c345 100644 --- a/src/cmd/compile/internal/syntax/parser_test.go +++ b/src/cmd/compile/internal/syntax/parser_test.go @@ -188,6 +188,7 @@ var tooLarge int = PosMax + 1 func TestLineDirectives(t *testing.T) { // valid line directives lead to a syntax error after them const valid = "syntax error: package statement must be first" + const filename = "directives.go" for _, test := range []struct { src, msg string @@ -195,33 +196,33 @@ func TestLineDirectives(t *testing.T) { line, col uint // 0-based }{ // ignored //line directives - {"//\n", valid, "", 1, 0}, // no directive - {"//line\n", valid, "", 1, 0}, // missing colon - {"//line foo\n", valid, "", 1, 0}, // missing colon - {" //line foo:\n", valid, "", 1, 0}, // not a line start - {"// line foo:\n", valid, "", 1, 0}, // space between // and line + {"//\n", valid, filename, 1, 0}, // no directive + {"//line\n", valid, filename, 1, 0}, // missing colon + {"//line foo\n", valid, filename, 1, 0}, // missing colon + {" //line foo:\n", valid, filename, 1, 0}, // not a line start + {"// line foo:\n", valid, filename, 1, 0}, // space between // and line // invalid //line directives with one colon - {"//line :\n", "invalid line number: ", "", 0, 8}, - {"//line :x\n", "invalid line number: x", "", 0, 8}, - {"//line foo :\n", "invalid line number: ", "", 0, 12}, - {"//line foo:x\n", "invalid line number: x", "", 0, 11}, - {"//line foo:0\n", "invalid line number: 0", "", 0, 11}, - {"//line foo:1 \n", "invalid line number: 1 ", "", 0, 11}, - {"//line foo:-12\n", "invalid line number: -12", "", 0, 11}, - {"//line C:foo:0\n", "invalid line number: 0", "", 0, 13}, - {fmt.Sprintf("//line foo:%d\n", tooLarge), fmt.Sprintf("invalid line number: %d", tooLarge), "", 0, 11}, + {"//line :\n", "invalid line number: ", filename, 0, 8}, + {"//line :x\n", "invalid line number: x", filename, 0, 8}, + {"//line foo :\n", "invalid line number: ", filename, 0, 12}, + {"//line foo:x\n", "invalid line number: x", filename, 0, 11}, + {"//line foo:0\n", "invalid line number: 0", filename, 0, 11}, + {"//line foo:1 \n", "invalid line number: 1 ", filename, 0, 11}, + {"//line foo:-12\n", "invalid line number: -12", filename, 0, 11}, + {"//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), filename, 0, 11}, // invalid //line directives with two colons - {"//line ::\n", "invalid line number: ", "", 0, 9}, - {"//line ::x\n", "invalid line number: x", "", 0, 9}, - {"//line foo::123abc\n", "invalid line number: 123abc", "", 0, 12}, - {"//line foo::0\n", "invalid line number: 0", "", 0, 12}, - {"//line foo:0:1\n", "invalid line number: 0", "", 0, 11}, + {"//line ::\n", "invalid line number: ", filename, 0, 9}, + {"//line ::x\n", "invalid line number: x", filename, 0, 9}, + {"//line foo::123abc\n", "invalid line number: 123abc", filename, 0, 12}, + {"//line foo::0\n", "invalid line number: 0", filename, 0, 12}, + {"//line foo:0:1\n", "invalid line number: 0", filename, 0, 11}, - {"//line :123:0\n", "invalid column number: 0", "", 0, 12}, - {"//line foo:123:0\n", "invalid column number: 0", "", 0, 15}, - {fmt.Sprintf("//line foo:10:%d\n", tooLarge), fmt.Sprintf("invalid column number: %d", tooLarge), "", 0, 14}, + {"//line :123:0\n", "invalid column number: 0", filename, 0, 12}, + {"//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), filename, 0, 14}, // effect of valid //line directives on lines {"//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 ::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 - {"/**/", valid, "", 0, 4}, // no directive - {"/*line*/", valid, "", 0, 8}, // missing colon - {"/*line foo*/", valid, "", 0, 12}, // missing colon - {" //line foo:*/", valid, "", 0, 15}, // not a line start - {"/* line foo:*/", valid, "", 0, 15}, // space between // and line + {"/**/", valid, filename, 0, 4}, // no directive + {"/*line*/", valid, filename, 0, 8}, // missing colon + {"/*line foo*/", valid, filename, 0, 12}, // missing colon + {" //line foo:*/", valid, filename, 0, 15}, // not a line start + {"/* line foo:*/", valid, filename, 0, 15}, // space between // and line // invalid /*line directives with one colon - {"/*line :*/", "invalid line number: ", "", 0, 8}, - {"/*line :x*/", "invalid line number: x", "", 0, 8}, - {"/*line foo :*/", "invalid line number: ", "", 0, 12}, - {"/*line foo:x*/", "invalid line number: x", "", 0, 11}, - {"/*line foo:0*/", "invalid line number: 0", "", 0, 11}, - {"/*line foo:1 */", "invalid line number: 1 ", "", 0, 11}, - {"/*line C:foo:0*/", "invalid line number: 0", "", 0, 13}, - {fmt.Sprintf("/*line foo:%d*/", tooLarge), fmt.Sprintf("invalid line number: %d", tooLarge), "", 0, 11}, + {"/*line :*/", "invalid line number: ", filename, 0, 8}, + {"/*line :x*/", "invalid line number: x", filename, 0, 8}, + {"/*line foo :*/", "invalid line number: ", filename, 0, 12}, + {"/*line foo:x*/", "invalid line number: x", filename, 0, 11}, + {"/*line foo:0*/", "invalid line number: 0", filename, 0, 11}, + {"/*line foo:1 */", "invalid line number: 1 ", filename, 0, 11}, + {"/*line C:foo:0*/", "invalid line number: 0", filename, 0, 13}, + {fmt.Sprintf("/*line foo:%d*/", tooLarge), fmt.Sprintf("invalid line number: %d", tooLarge), filename, 0, 11}, // invalid /*line directives with two colons - {"/*line ::*/", "invalid line number: ", "", 0, 9}, - {"/*line ::x*/", "invalid line number: x", "", 0, 9}, - {"/*line foo::123abc*/", "invalid line number: 123abc", "", 0, 12}, - {"/*line foo::0*/", "invalid line number: 0", "", 0, 12}, - {"/*line foo:0:1*/", "invalid line number: 0", "", 0, 11}, + {"/*line ::*/", "invalid line number: ", filename, 0, 9}, + {"/*line ::x*/", "invalid line number: x", filename, 0, 9}, + {"/*line foo::123abc*/", "invalid line number: 123abc", filename, 0, 12}, + {"/*line foo::0*/", "invalid line number: 0", filename, 0, 12}, + {"/*line foo:0:1*/", "invalid line number: 0", filename, 0, 11}, - {"/*line :123:0*/", "invalid column number: 0", "", 0, 12}, - {"/*line foo:123:0*/", "invalid column number: 0", "", 0, 15}, - {fmt.Sprintf("/*line foo:10:%d*/", tooLarge), fmt.Sprintf("invalid column number: %d", tooLarge), "", 0, 14}, + {"/*line :123:0*/", "invalid column number: 0", filename, 0, 12}, + {"/*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), filename, 0, 14}, // effect of valid /*line directives on lines {"/*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 :123:1000*/\n", valid, "foo ", 124 - linebase, 0}, {"/*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 { t.Errorf("%s: no error reported", test.src) continue -- 2.48.1