fix(parser): reject macro parameter lists the toolchain rejects
The C variadic spellings ("..." and the GNU "name..."), an empty or
trailing parameter, a missing comma, a stray token and an unterminated
list all parsed silently before: non-identifier tokens were skipped, so
"#define M(...)" defined a zero-parameter macro and invocations were
diagnosed only by argument count, if at all. The toolchain rejects the
definition itself ("bad definition for macro"), and so does the
preprocessor now: the parameter list must be identifiers separated by
single commas and closed by the parenthesis, and a rejected definition
leaves the name unbound. The spellings join the fuzz corpus.
Assisted-by: GLM 5.3
This commit is contained in:
1 parent
373ec51061
commit
63ed141522
3 files changed
+102
-14
No files matched your search
@@ -321,6 +321,55 @@ func TestWrongArgumentCountIsAnError(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestBadMacroDefinitionsAreRejected pins the preprocessor's reading of a
|
||||
// parameter list against the toolchain's: the list is identifiers separated
|
||||
// by single commas and closed by the parenthesis. The C variadic spellings
|
||||
// ("...", and the GNU "name...", one token here because the lexer reads dots
|
||||
// as identifier characters), an empty or trailing parameter, a missing
|
||||
// comma, a stray token and an unterminated list are all a bad definition,
|
||||
// rejected at the definition exactly as the toolchain rejects them, so a use
|
||||
// never silently binds to a parameter list the writer did not write.
|
||||
func TestBadMacroDefinitionsAreRejected(t *testing.T) {
|
||||
bad := map[string]string{
|
||||
"ellipsis": "#define M(...) ADDQ $1, AX\n",
|
||||
"ellipsis after a name": "#define M(a, ...) ADDQ a, AX\n",
|
||||
"gnu dots": "#define M(args...) ADDQ args, AX\n",
|
||||
"trailing comma": "#define M(a,) ADDQ a, AX\n",
|
||||
"leading comma": "#define M(,a) ADDQ a, AX\n",
|
||||
"missing comma": "#define M(a b) ADDQ a, AX\n",
|
||||
"numeric parameter": "#define M(1) ADDQ $1, AX\n",
|
||||
"unterminated parameter list": "#define M(a\n",
|
||||
}
|
||||
for name, def := range bad {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
f, errs := ParseWithOptions("t_amd64.s", def+"TEXT ·f(SB), NOSPLIT, $0\n\tM(BX)\n\tRET\n", Options{Expand: true})
|
||||
if f == nil {
|
||||
t.Fatal("ParseWithOptions returned a nil file")
|
||||
}
|
||||
if len(errs) == 0 || !strings.Contains(errs[0].Error(), "bad definition for macro M") {
|
||||
t.Fatalf("got %v, want the definition rejected", errs)
|
||||
}
|
||||
})
|
||||
}
|
||||
// The plain shapes stay accepted, with the invocation checked against
|
||||
// the parameter count the writer wrote.
|
||||
for _, def := range []string{
|
||||
"#define M() ADDQ $1, AX\n",
|
||||
"#define M(a) ADDQ a, AX\n",
|
||||
"#define M(a, b) ADDQ a, b\n",
|
||||
} {
|
||||
f, errs := ParseWithOptions("t_amd64.s", def+"TEXT ·f(SB), NOSPLIT, $0\n\tM(BX)\n\tRET\n", Options{Expand: true})
|
||||
if f == nil || len(f.Decls) == 0 {
|
||||
t.Fatalf("%s: unusable file", def)
|
||||
}
|
||||
for _, err := range errs {
|
||||
if strings.Contains(err.Error(), "bad definition") {
|
||||
t.Fatalf("%s: definition wrongly rejected: %v", def, errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestConditionalsSelectOneBranch(t *testing.T) {
|
||||
_, got := expand(t, `
|
||||
#define MODE2
|
||||
|
||||
Reference in new issue
Block a user