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:
1 parent
f932c5811c
commit
373ec51061
3 files changed
+33
-5
No files matched your search
@@ -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
@@ -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")
|
||||
Reference in new issue
Block a user