Skip to content

go/printer: form feed is wrongly treated as a newline #69858

@mateusz834

Description

@mateusz834

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.

go/src/go/printer/printer.go

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:

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.

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.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions