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" +
|
"\tA: M:\n" +
|
||||||
"\tRET\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
|
// 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
|
// 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:
|
case len(line) >= 2 && line[1].Kind == token.Colon:
|
||||||
inf.kind = kLabel
|
inf.kind = kLabel
|
||||||
if macros[line[0].Text] {
|
if namesMacro(line[0].Text, macros) {
|
||||||
// The label names a macro, so what the line becomes
|
// The label names a macro, so what the line becomes
|
||||||
// exists only after expansion; the line must not be
|
// exists only after expansion; the line must not be
|
||||||
// restructured, and no mnemonic of it aligns.
|
// restructured, and no mnemonic of it aligns.
|
||||||
@@ -74,11 +74,11 @@ func Source(src string) string {
|
|||||||
// either: its expansion decides what the line becomes.
|
// either: its expansion decides what the line becomes.
|
||||||
rest := line[2:]
|
rest := line[2:]
|
||||||
for len(rest) >= 2 && rest[0].Kind == token.Ident && rest[1].Kind == token.Colon &&
|
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:]
|
rest = rest[2:]
|
||||||
}
|
}
|
||||||
if len(rest) > 0 && rest[0].Kind == token.Ident && !isDirective(rest[0].Text) &&
|
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)
|
inf.mnemLen = len(rest[0].Text)
|
||||||
if funcID >= 0 && inf.mnemLen > maxWidth[funcID] {
|
if funcID >= 0 && inf.mnemLen > maxWidth[funcID] {
|
||||||
maxWidth[funcID] = inf.mnemLen
|
maxWidth[funcID] = inf.mnemLen
|
||||||
@@ -150,7 +150,7 @@ func Source(src string) string {
|
|||||||
// A stacked label naming a macro is never peeled: its expansion
|
// A stacked label naming a macro is never peeled: its expansion
|
||||||
// decides what the line becomes, exactly as the first label's.
|
// 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 &&
|
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 + ":"})
|
outs = append(outs, outLine{kind: kLabel, text: rest[0].Text + ":"})
|
||||||
rest = rest[2:]
|
rest = rest[2:]
|
||||||
}
|
}
|
||||||
@@ -165,7 +165,7 @@ func Source(src string) string {
|
|||||||
// parser wants a symbol per line), so a directive sharing
|
// parser wants a symbol per line), so a directive sharing
|
||||||
// the label's line stays there.
|
// the label's line stays there.
|
||||||
outs[len(outs)-1].text += " " + trimLineRight(renderOps(rest), rest[len(rest)-1])
|
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])})
|
outs = append(outs, outLine{kind: kInstr, text: trimLineRight(renderInstr(rest, maxWidth[inf.funcID]), rest[len(rest)-1])})
|
||||||
if strings.EqualFold(rest[0].Text, "RET") {
|
if strings.EqualFold(rest[0].Text, "RET") {
|
||||||
inBody = false
|
inBody = false
|
||||||
@@ -208,6 +208,21 @@ func macroNames(lines [][]token.Token) map[string]bool {
|
|||||||
return names
|
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
|
// 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
|
// 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
|
// 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