fix(format): treat a selector-folded label as naming its macro

The lexer folds NAME.selector into one identifier, and an object macro
reached through the selector expands with it travelling along, so a
label spelled NAME.selector: restructures exactly like the bare name
would.  The macro-name checks now resolve the prefix before the first
period, and the crashing input joins the corpus.

Assisted-by: GLM 5.3
This commit is contained in:
petrbalvin committed 2026-10-07 13:54:42 +02:00
1 parent f932c5811c
commit 373ec51061
3 files changed
+33 -5

No files matched your search

+11
View File
@@ -127,6 +127,17 @@ func TestFormatPreservesExpansionSemantics(t *testing.T) {
"\tA: M:\n" +
"\tRET\n",
},
{
// A label reached through a selector: the lexer folds NAME and
// .selector into one identifier, and the object macro behind the
// name expands with the selector travelling along, so the folded
// spelling restructures exactly like the bare name.
name: "label naming a macro through a selector",
src: "#define A B\n" +
"TEXT ·f(SB), $0\n" +
"\tA.: A0\n" +
"\tRET\n",
},
{
// A use above its define: the name is not a macro at that point
// of the file, so splitting would be safe; the whole-file set
+20 -5
View File
@@ -58,7 +58,7 @@ func Source(src string) string {
}
case len(line) >= 2 && line[1].Kind == token.Colon:
inf.kind = kLabel
if macros[line[0].Text] {
if namesMacro(line[0].Text, macros) {
// 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.
@@ -74,11 +74,11 @@ func Source(src string) string {
// either: its expansion decides what the line becomes.
rest := line[2:]
for len(rest) >= 2 && rest[0].Kind == token.Ident && rest[1].Kind == token.Colon &&
!isDirective(rest[0].Text) && !macros[rest[0].Text] {
!isDirective(rest[0].Text) && !namesMacro(rest[0].Text, macros) {
rest = rest[2:]
}
if len(rest) > 0 && rest[0].Kind == token.Ident && !isDirective(rest[0].Text) &&
!macros[rest[0].Text] {
!namesMacro(rest[0].Text, macros) {
inf.mnemLen = len(rest[0].Text)
if funcID >= 0 && inf.mnemLen > maxWidth[funcID] {
maxWidth[funcID] = inf.mnemLen
@@ -150,7 +150,7 @@ func Source(src string) string {
// A stacked label naming a macro is never peeled: its expansion
// decides what the line becomes, exactly as the first label's.
for len(rest) >= 2 && rest[0].Kind == token.Ident && rest[1].Kind == token.Colon &&
!isDirective(rest[0].Text) && !macros[rest[0].Text] {
!isDirective(rest[0].Text) && !namesMacro(rest[0].Text, macros) {
outs = append(outs, outLine{kind: kLabel, text: rest[0].Text + ":"})
rest = rest[2:]
}
@@ -165,7 +165,7 @@ func Source(src string) string {
// parser wants a symbol per line), so a directive sharing
// the label's line stays there.
outs[len(outs)-1].text += " " + trimLineRight(renderOps(rest), rest[len(rest)-1])
} else if len(rest) > 0 && rest[0].Kind == token.Ident && !macros[rest[0].Text] {
} else if len(rest) > 0 && rest[0].Kind == token.Ident && !namesMacro(rest[0].Text, macros) {
outs = append(outs, outLine{kind: kInstr, text: trimLineRight(renderInstr(rest, maxWidth[inf.funcID]), rest[len(rest)-1])})
if strings.EqualFold(rest[0].Text, "RET") {
inBody = false
@@ -208,6 +208,21 @@ func macroNames(lines [][]token.Token) map[string]bool {
return names
}
// namesMacro reports whether text names a macro from names, directly or
// through the selector suffix the lexer folds into one identifier: an object
// macro reached as "NAME.selector" expands with the selector travelling
// along, so such a label restructures exactly like the bare name. The check
// ignores the object-and-parameterised distinction and stays conservative.
func namesMacro(text string, names map[string]bool) bool {
if names[text] {
return true
}
if j := strings.IndexByte(text, '.'); j > 0 {
return names[text[:j]]
}
return false
}
// trimLineRight removes trailing spaces and tabs from a rendered line, which
// are layout the canonical form drops. The trim never eats into the text of
// the line's final token: a string or rune literal may legitimately end in
@@ -0,0 +1,2 @@
go test fuzz v1
string("#define A B\"\nA.:A0")