diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go index 6fb267a1fa5..1ab7a033bc8 100644 --- a/src/cmd/compile/internal/gc/noder.go +++ b/src/cmd/compile/internal/gc/noder.go @@ -41,7 +41,7 @@ func parseFiles(filenames []string) uint { } defer f.Close() - p.file, _ = syntax.Parse(base, f, p.error, p.pragma, syntax.CheckBranches) // errors are tracked via p.error + p.file, _ = syntax.Parse(base, f, p.error, p.pragma, fileh, syntax.CheckBranches) // errors are tracked via p.error }(filename) } @@ -70,6 +70,10 @@ func yyerrorpos(pos src.Pos, format string, args ...interface{}) { var pathPrefix string +func fileh(name string) string { + return objabi.AbsFile("", name, pathPrefix) +} + func absFilename(name string) string { return objabi.AbsFile(Ctxt.Pathname, name, pathPrefix) } diff --git a/src/cmd/compile/internal/syntax/nodes_test.go b/src/cmd/compile/internal/syntax/nodes_test.go index be9d5d897c9..1bba9eeacff 100644 --- a/src/cmd/compile/internal/syntax/nodes_test.go +++ b/src/cmd/compile/internal/syntax/nodes_test.go @@ -291,7 +291,7 @@ func testPos(t *testing.T, list []test, prefix, suffix string, extract func(*Fil } // build syntaxt tree - file, err := ParseBytes(nil, []byte(src), nil, nil, 0) + file, err := ParseBytes(nil, []byte(src), nil, nil, nil, 0) if err != nil { t.Errorf("parse error: %s: %v (%s)", src, err, test.nodetyp) continue diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go index bcf56d5faac..b9129b0d9cd 100644 --- a/src/cmd/compile/internal/syntax/parser.go +++ b/src/cmd/compile/internal/syntax/parser.go @@ -16,9 +16,10 @@ const debug = false const trace = false type parser struct { - base *src.PosBase - errh ErrorHandler - mode Mode + base *src.PosBase + errh ErrorHandler + fileh FilenameHandler + mode Mode scanner first error // first error encountered @@ -29,9 +30,10 @@ type parser struct { indent []byte // tracing support } -func (p *parser) init(base *src.PosBase, r io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) { +func (p *parser) init(base *src.PosBase, r io.Reader, errh ErrorHandler, pragh PragmaHandler, fileh FilenameHandler, mode Mode) { p.base = base p.errh = errh + p.fileh = fileh p.mode = mode p.scanner.init( r, @@ -76,7 +78,11 @@ func (p *parser) updateBase(line, col uint, text string) { p.error_at(p.pos_at(line, col+uint(i+1)), "invalid line number: "+nstr) return } - p.base = src.NewLinePragmaBase(src.MakePos(p.base.Pos().Base(), line, col), text[:i], uint(n)) + absFile := text[:i] + if p.fileh != nil { + absFile = p.fileh(absFile) + } + p.base = src.NewLinePragmaBase(src.MakePos(p.base.Pos().Base(), line, col), absFile, uint(n)) } func (p *parser) got(tok token) bool { diff --git a/src/cmd/compile/internal/syntax/parser_test.go b/src/cmd/compile/internal/syntax/parser_test.go index 4c317dab602..0478088ec89 100644 --- a/src/cmd/compile/internal/syntax/parser_test.go +++ b/src/cmd/compile/internal/syntax/parser_test.go @@ -131,7 +131,7 @@ func verifyPrint(filename string, ast1 *File) { panic(err) } - ast2, err := ParseBytes(src.NewFileBase(filename, filename), buf1.Bytes(), nil, nil, 0) + ast2, err := ParseBytes(src.NewFileBase(filename, filename), buf1.Bytes(), nil, nil, nil, 0) if err != nil { panic(err) } @@ -155,7 +155,7 @@ func verifyPrint(filename string, ast1 *File) { } func TestIssue17697(t *testing.T) { - _, err := ParseBytes(nil, nil, nil, nil, 0) // return with parser error, don't panic + _, err := ParseBytes(nil, nil, nil, nil, nil, 0) // return with parser error, don't panic if err == nil { t.Errorf("no error reported") } @@ -199,8 +199,16 @@ func TestLineDirectives(t *testing.T) { // test effect of //line directive on (relative) position information {"//line foo:123\n foo", "syntax error: package statement must be first", "foo", 123 - linebase, 3}, {"//line foo:123\n//line bar:345\nfoo", "syntax error: package statement must be first", "bar", 345 - linebase, 0}, + + {"//line " + runtime.GOROOT() + "/src/a/a.go:123\n foo", "syntax error: package statement must be first", "$GOROOT/src/a/a.go", 123 - linebase, 3}, } { - _, err := ParseBytes(nil, []byte(test.src), nil, nil, 0) + fileh := func(name string) string { + if strings.HasPrefix(name, runtime.GOROOT()) { + return "$GOROOT" + name[len(runtime.GOROOT()):] + } + return name + } + _, err := ParseBytes(nil, []byte(test.src), nil, nil, fileh, 0) if err == nil { t.Errorf("%s: no error reported", test.src) continue diff --git a/src/cmd/compile/internal/syntax/printer_test.go b/src/cmd/compile/internal/syntax/printer_test.go index 14652f4ac65..bbf75a957da 100644 --- a/src/cmd/compile/internal/syntax/printer_test.go +++ b/src/cmd/compile/internal/syntax/printer_test.go @@ -29,7 +29,7 @@ func TestPrintString(t *testing.T) { "package p; type _ = int; type T1 = struct{}; type ( _ = *struct{}; T2 = float32 )", // TODO(gri) expand } { - ast, err := ParseBytes(nil, []byte(want), nil, nil, 0) + ast, err := ParseBytes(nil, []byte(want), nil, nil, nil, 0) if err != nil { t.Error(err) continue diff --git a/src/cmd/compile/internal/syntax/syntax.go b/src/cmd/compile/internal/syntax/syntax.go index ed5e2547247..f58d5efd292 100644 --- a/src/cmd/compile/internal/syntax/syntax.go +++ b/src/cmd/compile/internal/syntax/syntax.go @@ -39,11 +39,15 @@ type ErrorHandler func(err error) // appropriate. type Pragma uint16 -// A PragmaHandler is used to process //line and //go: directives as +// A PragmaHandler is used to process //go: directives as // they're scanned. The returned Pragma value will be unioned into the // next FuncDecl node. type PragmaHandler func(pos src.Pos, text string) Pragma +// A FilenameHandler is used to process each filename encountered +// in //line directives. The returned value is used as the absolute filename. +type FilenameHandler func(name string) string + // Parse parses a single Go source file from src and returns the corresponding // syntax tree. If there are errors, Parse will return the first error found, // and a possibly partially constructed syntax tree, or nil if no correct package @@ -55,8 +59,11 @@ type PragmaHandler func(pos src.Pos, text string) Pragma // // If a PragmaHandler is provided, it is called with each pragma encountered. // +// If a FilenameHandler is provided, it is called to process each filename +// encountered in //line directives. +// // The Mode argument is currently ignored. -func Parse(base *src.PosBase, src io.Reader, errh ErrorHandler, pragh PragmaHandler, mode Mode) (_ *File, first error) { +func Parse(base *src.PosBase, src io.Reader, errh ErrorHandler, pragh PragmaHandler, fileh FilenameHandler, mode Mode) (_ *File, first error) { defer func() { if p := recover(); p != nil { if err, ok := p.(Error); ok { @@ -68,14 +75,14 @@ func Parse(base *src.PosBase, src io.Reader, errh ErrorHandler, pragh PragmaHand }() var p parser - p.init(base, src, errh, pragh, mode) + p.init(base, src, errh, pragh, fileh, mode) p.next() return p.fileOrNil(), p.first } // ParseBytes behaves like Parse but it reads the source from the []byte slice provided. -func ParseBytes(base *src.PosBase, src []byte, errh ErrorHandler, pragh PragmaHandler, mode Mode) (*File, error) { - return Parse(base, &bytesReader{src}, errh, pragh, mode) +func ParseBytes(base *src.PosBase, src []byte, errh ErrorHandler, pragh PragmaHandler, fileh FilenameHandler, mode Mode) (*File, error) { + return Parse(base, &bytesReader{src}, errh, pragh, fileh, mode) } type bytesReader struct { @@ -101,5 +108,5 @@ func ParseFile(filename string, errh ErrorHandler, pragh PragmaHandler, mode Mod return nil, err } defer f.Close() - return Parse(src.NewFileBase(filename, filename), f, errh, pragh, mode) + return Parse(src.NewFileBase(filename, filename), f, errh, pragh, nil, mode) } diff --git a/src/cmd/internal/objabi/line.go b/src/cmd/internal/objabi/line.go index ed509b70017..1c671b211f8 100644 --- a/src/cmd/internal/objabi/line.go +++ b/src/cmd/internal/objabi/line.go @@ -44,7 +44,7 @@ func AbsFile(dir, file, pathPrefix string) string { abs = "??" } - return filepath.Clean(abs) + return abs } // Does s have t as a path prefix?