ww source: reject raw NUL characters

This commit is contained in:
2026-08-21 23:03:14 +09:00
parent 9381f8fb8e
commit 4b9968e4e6
10 changed files with 1304 additions and 320 deletions

View File

@@ -18,11 +18,11 @@ toklit(Arena *a, Tok t)
}
static int
runrow(const char *src, const char *expect)
runrown(const char *src, size_t len, const char *expect, int expecterrs)
{
Arena *a = newarena();
Lex l;
lexinit(&l, a, "<test>", src, strlen(src));
lexinit(&l, a, "<test>", src, len);
char *got = amalloc(a, 1);
got[0] = '\0';
@@ -48,15 +48,23 @@ runrow(const char *src, const char *expect)
got[n] = '\0';
}
int ok = strcmp(got, expect) == 0;
int ok = strcmp(got, expect) == 0
&& (expecterrs < 0 || l.errs == expecterrs);
if (!ok) {
fprintf(stderr, "lex mismatch:\n src: %s\n"
" want: %s\n got: %s\n", src, expect, got);
fprintf(stderr, "lex mismatch:\n want: %s (%d errors)\n"
" got: %s (%d errors)\n", expect, expecterrs, got,
l.errs);
}
freearena(a);
return ok;
}
static int
runrow(const char *src, const char *expect)
{
return runrown(src, strlen(src), expect, -1);
}
struct row { const char *src, *expect; };
static const struct row rows[] = {
@@ -133,11 +141,11 @@ static const struct row rows[] = {
* not the token stream. The verbatim Hare messages live at lexunicode
* in cmd/wcc/lex.c. */
static int
runerr(const char *src)
runerrn(const char *src, size_t len)
{
Arena *a = newarena();
Lex l;
lexinit(&l, a, "<test>", src, strlen(src));
lexinit(&l, a, "<test>", src, len);
for (;;) {
Tok t = lexnext(&l);
if (t.kind == TK_EOF)
@@ -151,6 +159,12 @@ runerr(const char *src)
return ok;
}
static int
runerr(const char *src)
{
return runerrn(src, strlen(src));
}
static int
runrewind(void)
{
@@ -205,6 +219,93 @@ static const char *const errrows[] = {
"'\\uDFFF'", /* top of the UTF-16 surrogate range */
};
static const struct {
const char *src;
size_t len;
const char *expect;
int errs;
} nulrows[] = {
{ "\0package main;", sizeof "\0package main;" - 1,
"package IDENT(main) ;", 1 },
{ "pack\0age main;", sizeof "pack\0age main;" - 1,
"package IDENT(main) ;", 1 },
{ "1\0_0", sizeof "1\0_0" - 1, "INT(10)", 1 },
{ "1f\0oo", sizeof "1f\0oo" - 1, "INT(1) IDENT(foo)", 1 },
{ "=\0=", sizeof "=\0=" - 1, "==", 1 },
{ "/\0/ comment\nfn", sizeof "/\0/ comment\nfn" - 1, "fn", 1 },
{ "/* end *\0/ fn", sizeof "/* end *\0/ fn" - 1, "fn", 1 },
{ "\"a\0b\"", sizeof "\"a\0b\"" - 1, "STR(ab)", 1 },
{ "'\0A'", sizeof "'\0A'" - 1, "RUNE(65)", 1 },
{ "fn\0", sizeof "fn\0" - 1, "fn", 1 },
{ "f\0\0n", sizeof "f\0\0n" - 1, "fn", 2 },
};
static int
runescapenul(void)
{
static const char line[] = "\"\\\0n\"";
static const char hex[] = "\"\\x0" "\0" "0\"";
Arena *a = newarena();
Lex l;
lexinit(&l, a, "<test>", line, sizeof line - 1);
Tok t = lexnext(&l);
int ok = t.kind == TK_STR && t.tlen == 1 && t.text[0] == '\n'
&& l.errs == 1 && lexnext(&l).kind == TK_EOF;
lexinit(&l, a, "<test>", hex, sizeof hex - 1);
t = lexnext(&l);
ok = ok && t.kind == TK_STR && t.tlen == 1 && t.text[0] == '\0'
&& l.errs == 1 && lexnext(&l).kind == TK_EOF;
if (!ok)
fprintf(stderr, "NUL escape recovery failed\n");
freearena(a);
return ok;
}
static int
runsuffixnul(void)
{
static const char src[] = "1i" "\0" "8";
Arena *a = newarena();
Lex l;
lexinit(&l, a, "<test>", src, sizeof src - 1);
Tok t = lexnext(&l);
int ok = t.kind == TK_INT && t.v.uval == 1 && t.tsuffix != NULL
&& strcmp(t.tsuffix, "i8") == 0 && l.errs == 1
&& lexnext(&l).kind == TK_EOF;
if (!ok)
fprintf(stderr, "NUL typed-suffix recovery failed\n");
freearena(a);
return ok;
}
static int
runlongdirective(void)
{
static const char pre[] = "//ww:module ";
static const char tail[] = "\nfn";
const size_t pathlen = 16384;
const size_t len = sizeof pre - 1 + pathlen + sizeof tail - 1;
char *src = malloc(len);
if (!src)
return 0;
memcpy(src, pre, sizeof pre - 1);
memset(src + sizeof pre - 1, 'a', pathlen);
memcpy(src + sizeof pre - 1 + pathlen, tail, sizeof tail - 1);
Arena *a = newarena();
Lex l;
lexinit(&l, a, "<test>", src, len);
Tok m = lexnext(&l);
Tok f = lexnext(&l);
int ok = m.kind == TK_MODPATH && m.tlen == pathlen
&& m.text[0] == 'a' && m.text[pathlen - 1] == 'a'
&& f.kind == TK_FN && l.errs == 0;
if (!ok)
fprintf(stderr, "long module directive failed\n");
freearena(a);
free(src);
return ok;
}
int
main(void)
{
@@ -225,6 +326,19 @@ main(void)
fail++;
}
}
for (size_t i = 0; i < sizeof nulrows / sizeof nulrows[0]; i++) {
if (!runrown(nulrows[i].src, nulrows[i].len,
nulrows[i].expect, nulrows[i].errs)) {
fprintf(stderr, "NUL row %zu failed\n", i);
fail++;
}
}
if (!runescapenul())
fail++;
if (!runsuffixnul())
fail++;
if (!runlongdirective())
fail++;
if (fail) {
fprintf(stderr, "%d/%zu lex tests failed\n", fail,
sizeof rows / sizeof rows[0]);