fix(format): keep a macro-named label line whole
A label whose name is a macro expands into whatever the body is, so the line's statement structure exists only after expansion; splitting the label off changed clean input into a different statement sequence. The formatter now records the names #define'd above each line and renders such a label line unsplit, and the crashing input joins the corpus. Assisted-by: GLM 5.3
This commit is contained in:
1 parent
715298ed23
commit
f452b8a995
3 files changed
+49
No files matched your search
@@ -95,6 +95,26 @@ func TestFormatPreservesExpansionSemantics(t *testing.T) {
|
|||||||
"\tM(1, R0)\n" +
|
"\tM(1, R0)\n" +
|
||||||
"\tRET\n",
|
"\tRET\n",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// A label whose name is a macro: the expansion decides what the
|
||||||
|
// line becomes (here an instruction with several operands), so
|
||||||
|
// the canonical form keeps the line whole instead of splitting
|
||||||
|
// the label off, which would change the statement structure.
|
||||||
|
name: "label named as a macro",
|
||||||
|
src: "#define M B0, C0\n" +
|
||||||
|
"TEXT ·f(SB), $0\n" +
|
||||||
|
"\tM: A00\n" +
|
||||||
|
"\tRET\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// A use before the define: the name is not a macro yet at that
|
||||||
|
// point of the file, so the label splits as any other does.
|
||||||
|
name: "label before the define of its name",
|
||||||
|
src: "TEXT ·f(SB), $0\n" +
|
||||||
|
"\tM: A00\n" +
|
||||||
|
"\tRET\n" +
|
||||||
|
"#define M B0, C0\n",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
|||||||
@@ -27,11 +27,13 @@ func Source(src string) string {
|
|||||||
kind int
|
kind int
|
||||||
mnemLen int
|
mnemLen int
|
||||||
funcID int
|
funcID int
|
||||||
|
macroName bool // the line's label names a macro defined above it
|
||||||
}
|
}
|
||||||
|
|
||||||
infos := make([]info, len(lines))
|
infos := make([]info, len(lines))
|
||||||
funcID := -1
|
funcID := -1
|
||||||
maxWidth := map[int]int{} // funcID -> widest mnemonic
|
maxWidth := map[int]int{} // funcID -> widest mnemonic
|
||||||
|
macros := map[string]bool{} // names #define'd so far, in file order
|
||||||
for i, line := range lines {
|
for i, line := range lines {
|
||||||
inf := info{kind: kBlank, funcID: funcID}
|
inf := info{kind: kBlank, funcID: funcID}
|
||||||
if len(line) > 0 {
|
if len(line) > 0 {
|
||||||
@@ -45,6 +47,10 @@ func Source(src string) string {
|
|||||||
inf.kind = kComment
|
inf.kind = kComment
|
||||||
case line[0].Kind == token.Hash:
|
case line[0].Kind == token.Hash:
|
||||||
inf.kind = kPreproc
|
inf.kind = kPreproc
|
||||||
|
if len(line) >= 3 && line[1].Kind == token.Ident &&
|
||||||
|
line[1].Text == "define" && line[2].Kind == token.Ident {
|
||||||
|
macros[line[2].Text] = true
|
||||||
|
}
|
||||||
case line[0].Kind == token.Ident && isDirective(line[0].Text):
|
case line[0].Kind == token.Ident && isDirective(line[0].Text):
|
||||||
inf.kind = kDirective
|
inf.kind = kDirective
|
||||||
if line[0].Text == "TEXT" {
|
if line[0].Text == "TEXT" {
|
||||||
@@ -56,6 +62,14 @@ func Source(src string) string {
|
|||||||
}
|
}
|
||||||
case len(line) >= 2 && line[1].Kind == token.Colon:
|
case len(line) >= 2 && line[1].Kind == token.Colon:
|
||||||
inf.kind = kLabel
|
inf.kind = kLabel
|
||||||
|
if macros[line[0].Text] {
|
||||||
|
// The label names a macro, so what the line becomes
|
||||||
|
// exists only after expansion; the line must not be
|
||||||
|
// restructured, and no mnemonic of it aligns.
|
||||||
|
inf.macroName = true
|
||||||
|
infos[i] = inf
|
||||||
|
continue
|
||||||
|
}
|
||||||
// Peel stacked labels exactly as the render pass does; the
|
// Peel stacked labels exactly as the render pass does; the
|
||||||
// instruction after the last one is rendered at the
|
// instruction after the last one is rendered at the
|
||||||
// function's alignment width, so its mnemonic counts here.
|
// function's alignment width, so its mnemonic counts here.
|
||||||
@@ -115,6 +129,19 @@ func Source(src string) string {
|
|||||||
}
|
}
|
||||||
inBody = line[0].Text == "TEXT"
|
inBody = line[0].Text == "TEXT"
|
||||||
case kLabel:
|
case kLabel:
|
||||||
|
if inf.macroName && len(line) > 2 {
|
||||||
|
// A label that names a macro expands into whatever the body
|
||||||
|
// is: after expansion the line may not be a label at all, so
|
||||||
|
// its statement structure exists only as one line. The label
|
||||||
|
// keeps the canonical colon and everything behind it renders
|
||||||
|
// as one unaligned run, unsplit.
|
||||||
|
out := line[0].Text + ":"
|
||||||
|
if rest := renderOps(line[2:]); rest != "" {
|
||||||
|
out += " " + rest
|
||||||
|
}
|
||||||
|
outs = append(outs, outLine{kind: kLabel, text: trimLineRight(out, last)})
|
||||||
|
continue
|
||||||
|
}
|
||||||
// Every label, and a trailing instruction, becomes its own
|
// Every label, and a trailing instruction, becomes its own
|
||||||
// output line: separate outLines keep the blank-line pass
|
// output line: separate outLines keep the blank-line pass
|
||||||
// honest about what it is looking at.
|
// honest about what it is looking at.
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
go test fuzz v1
|
||||||
|
string("#define A B0\"\nA:A00")
|
||||||
Reference in new issue
Block a user