cmd/compile/internal/syntax: permit /*line file:line:col*/ directives

R=go1.11

This implements parsing of /*line file:line*/ and /*line file:line:col*/
directives and also extends the optional column format to regular //line
directives, per #22662.

For a line directive to be recognized, its comment text must start with
the prefix "line " which is followed by one of the following:

:line
:line:col
filename:line
filename:line:col

with at least one : present. The line and col values must be unsigned
decimal integers; everything before is considered part of the filename.

Valid line directives are:

//line :123
//line :123:8
//line foo.go:123
//line C:foo.go:123	(filename is "C:foo.go")
//line C:foo.go:123:8	(filename is "C:foo.go")
/*line ::123*/		(filename is ":")

No matter the comment format, at the moment all directives act as if
they were in //line comments, and column information is ignored.
To be addressed in subsequent CLs.

For #22662.

Change-Id: I1a2dc54bacc94bc6cdedc5229ee13278971f314e
Reviewed-on: https://go-review.googlesource.com/86037
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Robert Griesemer 2018-01-03 15:52:22 -08:00
parent e87f2a1b70
commit ac45cb9aa0
4 changed files with 170 additions and 37 deletions

View file

@ -47,8 +47,9 @@ func (p *parser) init(base *src.PosBase, r io.Reader, errh ErrorHandler, pragh P
p.error_at(p.pos_at(line, col), msg)
},
func(line, col uint, text string) {
if strings.HasPrefix(text, "line ") {
p.updateBase(line, col+5, text[5:])
const prefix = "line "
if strings.HasPrefix(text, prefix) {
p.updateBase(line, col+uint(len(prefix)), text[len(prefix):])
return
}
if pragh != nil {
@ -69,23 +70,54 @@ func (p *parser) init(base *src.PosBase, r io.Reader, errh ErrorHandler, pragh P
const lineMax = 1<<24 - 1 // TODO(gri) this limit is defined for src.Pos - fix
func (p *parser) updateBase(line, col uint, text string) {
// Want to use LastIndexByte below but it's not defined in Go1.4 and bootstrap fails.
i := strings.LastIndex(text, ":") // look from right (Windows filenames may contain ':')
if i < 0 {
i, n, ok := trailingDigits(text)
if i == 0 {
return // ignore (not a line directive)
}
nstr := text[i+1:]
n, err := strconv.Atoi(nstr)
if err != nil || n <= 0 || n > lineMax {
p.error_at(p.pos_at(line, col+uint(i+1)), "invalid line number: "+nstr)
// i > 0
if !ok {
// text has a suffix :xxx but xxx is not a number
p.error_at(p.pos_at(line, col+i), "invalid line number: "+text[i:])
return
}
filename := text[:i]
i2, n2, ok2 := trailingDigits(text[:i-1])
if ok2 {
//line filename:line:col
i, i2 = i2, i
n, n2 = n2, n
if n2 == 0 {
p.error_at(p.pos_at(line, col+i2), "invalid column number: "+text[i2:])
return
}
text = text[:i2-1] // lop off :col
}
if n == 0 || n > lineMax {
p.error_at(p.pos_at(line, col+i), "invalid line number: "+text[i:])
return
}
filename := text[:i-1] // lop off :line
absFilename := filename
if p.fileh != nil {
absFilename = p.fileh(filename)
}
p.base = src.NewLinePragmaBase(src.MakePos(p.base.Pos().Base(), line, col), filename, absFilename, uint(n))
// TODO(gri) pass column n2 to NewLinePragmaBase
p.base = src.NewLinePragmaBase(src.MakePos(p.base.Pos().Base(), line, col), filename, absFilename, uint(n) /*uint(n2)*/)
}
func trailingDigits(text string) (uint, uint, bool) {
// Want to use LastIndexByte below but it's not defined in Go1.4 and bootstrap fails.
i := strings.LastIndex(text, ":") // look from right (Windows filenames may contain ':')
if i < 0 {
return 0, 0, false // no ":"
}
// i >= 0
n, err := strconv.ParseUint(text[i+1:], 10, 0)
return uint(i + 1), uint(n), err == nil
}
func (p *parser) got(tok token) bool {