M include/identifier.h => include/identifier.h +6 -0
@@ 4,6 4,12 @@
#include <stdbool.h>
#include <stdint.h>
+// Maximum length of an identifier, as the sum of the lengths (excluding NUL
+// terminators) of its parts plus one for each namespace deliniation.
+//
+// In other words, the length of "a::b::c" is 5.
+#define IDENT_MAX 255
+
struct identifier {
char *name;
struct identifier *ns;
M src/parse.c => src/parse.c +14 -0
@@ 108,13 108,19 @@ bool
parse_identifier(struct lexer *lexer, struct identifier *ident, bool trailing)
{
struct token tok = {0};
+ struct location loc = {0};
struct identifier *i = ident;
*ident = (struct identifier){0};
+ size_t len = 0;
bool found_trailing = false;
while (!i->name) {
switch (lex(lexer, &tok)) {
case T_NAME:
+ len += strlen(tok.name);
i->name = xstrdup(tok.name);
+ if (loc.file == 0) {
+ loc = tok.loc;
+ }
token_finish(&tok);
break;
default:
@@ 130,6 136,7 @@ parse_identifier(struct lexer *lexer, struct identifier *ident, bool trailing)
struct identifier *ns;
switch (lex(lexer, &tok)) {
case T_DOUBLE_COLON:
+ len++;
ns = xcalloc(1, sizeof(struct identifier));
*ns = *i;
i->ns = ns;
@@ 140,6 147,13 @@ parse_identifier(struct lexer *lexer, struct identifier *ident, bool trailing)
break;
}
}
+
+ if (len > IDENT_MAX) {
+ xfprintf(stderr, "%s:%d:%d: identifier exceeds maximum length\n",
+ sources[loc.file], loc.lineno, loc.colno);
+ errline(loc);
+ exit(EXIT_FAILURE);
+ }
return found_trailing;
}
M tests/26-regression.ha => tests/26-regression.ha +14 -0
@@ 190,4 190,18 @@ export fn main() void = {
control_never();
assert(rt::compile("fn a() void = { abort(): int; };") as rt::exited != rt::EXIT_SUCCESS);
+
+ // identifier exceeds maximum length
+ let buf: [1024]u8 = [0...];
+ let buf = buf[..0];
+ static append(buf, rt::toutf8("let a")...);
+ // IDENT_MAX (in identifier.h) is defined as 255
+ for (let i = 0z; i < 255 / 2; i += 1) {
+ static append(buf, rt::toutf8("::a")...);
+ };
+ const n = len(buf);
+ static append(buf, rt::toutf8(" = 0;")...);
+ assert(rt::compile(*(&buf: *str)) as rt::exited == rt::EXIT_SUCCESS);
+ static insert(buf[n], rt::toutf8("::a")...);
+ assert(rt::compile(*(&buf: *str)) as rt::exited != rt::EXIT_SUCCESS);
};