- Notifications
You must be signed in to change notification settings - Fork 18.5k
Open
Labels
NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Description
This is an unrealistic edge-case, found by a fuzzer.
Both of the go sources in the reproducer below, are parsed successfully, but the printed source (with go/printer) contains a syntax error:
func TestReproducer(t *testing.T) { cases := []string{ "package A\nimport(\"a\"/*\f*/\n\"bb\")", "package A\nfunc test() {\"a\"/*\f*/\n\"bb\"}", } for _, src := range cases { fset := token.NewFileSet() f, err := parser.ParseFile(fset, "test.go", src, parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Fatal(err) } ast.Print(fset, f) var out strings.Builder if err := Fprint(&out, fset, f); err != nil { t.Fatal(err) } _, err = parser.ParseFile(fset, "test.go", out.String(), parser.ParseComments|parser.SkipObjectResolution) if err != nil { t.Logf("source:\n%s\nformatted as:\n%s", src, out.String()) t.Error(err) // test.go:4:11: expected ';', found "bb" (and 1 more errors) } } }[mateusz@arch go (master)]$ go test -run NotNewl -v go/printer -v === RUN TestFormFeedNotNewline printer_test.go:887: source: package A import("a"/* */ "bb") formatted as: package A import ( "a" /* */"bb" ) printer_test.go:888: test.go:4:11: expected ';', found "bb" (and 1 more errors) printer_test.go:887: source: package A func test() {"a"/* */ "bb"} formatted as: package A func test() { "a" /* */"bb" } printer_test.go:888: test.go:4:11: expected ';', found "bb" (and 1 more errors) This issue happens in the (*printer).print method.
Lines 1006 to 1020 in b521ebb
| if !p.impliedSemi { | |
| n := nlimit(next.Line - p.pos.Line) | |
| // don't exceed maxNewlines if we already wrote one | |
| if wroteNewline && n == maxNewlines { | |
| n = maxNewlines - 1 | |
| } | |
| if n > 0 { | |
| ch := byte('\n') | |
| if droppedFF { | |
| ch = '\f' // use formfeed since we dropped one before | |
| } | |
| p.writeByte(ch, n) | |
| impliedSemi = false | |
| } | |
| } |
specifically here:
Line 1007 in b521ebb
| n := nlimit(next.Line - p.pos.Line) |
next.Line is equal to 3 and p.pos.Line is also 3, which causes zero newlines to be inserted, this happens because the (*printer).writeString method treats \f as a newline and incorrectly increases the p.pos.Line field.
Lines 312 to 334 in b521ebb
| for i := 0; i < len(s); i++ { | |
| // Raw string literals may contain any character except back quote (`). | |
| if ch := s[i]; ch == '\n' || ch == '\f' { | |
| // account for line break | |
| nlines++ | |
| li = i | |
| // A line break inside a literal will break whatever column | |
| // formatting is in place; ignore any further alignment through | |
| // the end of the line. | |
| p.endAlignment = true | |
| } | |
| } | |
| p.pos.Offset += len(s) | |
| if nlines > 0 { | |
| p.pos.Line += nlines | |
| p.out.Line += nlines | |
| c := len(s) - li | |
| p.pos.Column = c | |
| p.out.Column = c | |
| } else { | |
| p.pos.Column += len(s) | |
| p.out.Column += len(s) | |
| } |
This was a nightmare to debug, but it seems like a fix is a one-liner, will send a CL.
Metadata
Metadata
Assignees
Labels
NeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.