diff --git a/format/testdata/fuzz/FuzzFormatIdempotency/75eaf4940c1e9b30 b/format/testdata/fuzz/FuzzFormatIdempotency/75eaf4940c1e9b30 new file mode 100644 index 0000000..41b458d --- /dev/null +++ b/format/testdata/fuzz/FuzzFormatIdempotency/75eaf4940c1e9b30 @@ -0,0 +1,2 @@ +go test fuzz v1 +string("A:A:") diff --git a/parser/parser.go b/parser/parser.go index 0117774..f0e4c38 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -160,12 +160,20 @@ func (p *state) parseLine(line []token.Token) { return } - // Label (ident immediately followed by a colon). + // Label (ident immediately followed by a colon). Stacked labels + // share one line ("a: b: MOVQ …"): each peels off as its own Label, + // the same statement sequence the canonical layout puts on separate + // lines, so a file and its formatted image parse alike. A directive + // never peels: it stays with the instruction parse, as in the + // formatter's label handling. if len(line) >= 2 && line[1].Kind == token.Colon { - lbl := &ast.Label{Name: line[0], Colon: line[1]} - p.addStmt(lbl) - // A label may share its line with an instruction: "loop: MOVQ …". - if rest := dropColon(line); len(rest) > 0 { + rest := line + for len(rest) >= 2 && rest[0].Kind == token.Ident && rest[1].Kind == token.Colon && + !isDirective(rest[0].Text) { + p.addStmt(&ast.Label{Name: rest[0], Colon: rest[1]}) + rest = rest[2:] + } + if len(rest) > 0 { p.parseInstr(rest) } p.pending = nil @@ -182,12 +190,10 @@ func (p *state) parseLine(line []token.Token) { p.pending = nil } -// dropColon removes the leading "ident :" of a label, returning the remainder. -func dropColon(line []token.Token) []token.Token { - if len(line) >= 2 && line[1].Kind == token.Colon { - return line[2:] - } - return nil +// isDirective reports whether the identifier names a top-level declaration +// directive rather than an instruction or a label. +func isDirective(s string) bool { + return s == "TEXT" || s == "DATA" || s == "GLOBL" } func (p *state) addStmt(s ast.Stmt) {