fix: P1审查修复 — error.c arena化 + Self类型解析 + trait查找加固 + 缓冲区安全

P1-①: error_init/add 从 malloc/realloc/strdup 改为 arena_alloc/arena_strdup
P1-②: impl 方法中 Self 类型在 sema 解析为实际结构体名
P1-③: trait 方法 fallback 增加前缀校验(strncmp),method_name 统一更新
P1-④: codegen args[16] 增加溢出检查,移除 parser 未使用的 mods/uses 数组
新增: 36_self_type.l 集成测试(Self 类型 + trait 方法)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 17:09:28 +08:00
parent 17c19fd9b9
commit 466be76fd8
9 changed files with 99 additions and 56 deletions
+2
View File
@@ -353,6 +353,7 @@ static LLVMValueRef codegen_expr(CgCtx* ctx, AstNode* node) {
LLVMValueRef fn = find_fn(ctx, node->as.call.name); LLVMValueRef fn = find_fn(ctx, node->as.call.name);
if (!fn) return NULL; if (!fn) return NULL;
LLVMValueRef args[16]; LLVMValueRef args[16];
if (node->as.call.arg_count > 16) { ctx->error = "函数参数过多(最多16)"; return NULL; }
for (size_t i = 0; i < node->as.call.arg_count; i++) { for (size_t i = 0; i < node->as.call.arg_count; i++) {
args[i] = codegen_expr(ctx, node->as.call.args[i]); args[i] = codegen_expr(ctx, node->as.call.args[i]);
if (!args[i]) return NULL; if (!args[i]) return NULL;
@@ -440,6 +441,7 @@ static LLVMValueRef codegen_expr(CgCtx* ctx, AstNode* node) {
LLVMValueRef fn = find_fn(ctx, mangled); LLVMValueRef fn = find_fn(ctx, mangled);
if (!fn) return NULL; if (!fn) return NULL;
// 参数列表: [receiver, 用户参数...] // 参数列表: [receiver, 用户参数...]
if (node->as.method_call.arg_count + 1 > 16) { ctx->error = "方法参数过多(最多15)"; return NULL; }
LLVMValueRef args[16]; LLVMValueRef args[16];
args[0] = codegen_expr(ctx, node->as.method_call.receiver); args[0] = codegen_expr(ctx, node->as.method_call.receiver);
if (!args[0]) return NULL; if (!args[0]) return NULL;
+11 -11
View File
@@ -1,22 +1,25 @@
#include "error.h" #include "error.h"
#include "arena.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
void error_init(ErrorList* list) { void error_init(ErrorList* list, Arena* a) {
list->capacity = 8; list->capacity = 64;
list->errors = malloc(list->capacity * sizeof(ErrorInfo)); list->errors = arena_alloc(a, list->capacity * sizeof(ErrorInfo));
list->count = 0; list->count = 0;
list->arena = a;
if (!list->errors) list->capacity = 0; if (!list->errors) list->capacity = 0;
} }
void error_add(ErrorList* list, const char* filename, int line, int col, const char* fmt, ...) { void error_add(ErrorList* list, const char* filename, int line, int col, const char* fmt, ...) {
if (!list->errors) return; if (!list->errors) return;
if (list->count >= list->capacity) { if (list->count >= list->capacity) {
size_t new_cap = list->capacity * 2; size_t new_cap = list->capacity + 64;
ErrorInfo* new_errs = realloc(list->errors, new_cap * sizeof(ErrorInfo)); ErrorInfo* new_errs = arena_alloc(list->arena, new_cap * sizeof(ErrorInfo));
if (!new_errs) return; if (!new_errs) return;
memcpy(new_errs, list->errors, list->capacity * sizeof(ErrorInfo));
list->errors = new_errs; list->errors = new_errs;
list->capacity = new_cap; list->capacity = new_cap;
} }
@@ -26,12 +29,9 @@ void error_add(ErrorList* list, const char* filename, int line, int col, const c
int n = vsnprintf(buf, sizeof(buf), fmt, args); int n = vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args); va_end(args);
if (n < 0) return; if (n < 0) return;
char* msg = strdup(buf); char* msg = arena_strdup(list->arena, buf);
char* fname = strdup(filename); char* fname = arena_strdup(list->arena, filename);
if (!msg || !fname) { if (!msg || !fname) return;
free(msg); free(fname);
return;
}
list->errors[list->count++] = (ErrorInfo){ list->errors[list->count++] = (ErrorInfo){
.message = msg, .message = msg,
.filename = fname, .filename = fname,
+4 -1
View File
@@ -3,6 +3,8 @@
#include <stddef.h> #include <stddef.h>
typedef struct Arena Arena;
typedef struct { typedef struct {
const char* message; const char* message;
const char* filename; const char* filename;
@@ -14,9 +16,10 @@ typedef struct {
ErrorInfo* errors; ErrorInfo* errors;
size_t count; size_t count;
size_t capacity; size_t capacity;
Arena* arena;
} ErrorList; } ErrorList;
void error_init(ErrorList* list); void error_init(ErrorList* list, Arena* a);
void error_add(ErrorList* list, const char* filename, int line, int col, const char* fmt, ...); void error_add(ErrorList* list, const char* filename, int line, int col, const char* fmt, ...);
void error_print(const ErrorList* list); void error_print(const ErrorList* list);
+1 -1
View File
@@ -86,7 +86,7 @@ int main(int argc, char** argv) {
if (!arena.memory) { fprintf(stderr, "内存分配失败\n"); free(source); return 1; } if (!arena.memory) { fprintf(stderr, "内存分配失败\n"); free(source); return 1; }
ErrorInfo error = {0}; ErrorInfo error = {0};
ErrorList error_list; error_init(&error_list); ErrorList error_list; error_init(&error_list, &arena);
// 3. 词法分析 // 3. 词法分析
size_t token_count; size_t token_count;
+4 -14
View File
@@ -1031,8 +1031,6 @@ AstNode* parse(Arena* a, const Token* tokens, size_t count,
AstNode* aliases[64]; int alias_count = 0; AstNode* aliases[64]; int alias_count = 0;
AstNode* enums[64]; int enum_count = 0; AstNode* enums[64]; int enum_count = 0;
AstNode* impls[64]; int impl_count = 0; AstNode* impls[64]; int impl_count = 0;
AstNode* mods[64]; int mod_count = 0;
AstNode* uses[64]; int use_count = 0;
while (peek(&p)->kind != TOK_EOF && !error->message) { while (peek(&p)->kind != TOK_EOF && !error->message) {
// pub 前缀 // pub 前缀
bool is_pub = false; bool is_pub = false;
@@ -1179,20 +1177,12 @@ AstNode* parse(Arena* a, const Token* tokens, size_t count,
if (enum_count >= 64) break; if (enum_count >= 64) break;
enums[enum_count++] = sub->as.program.enums[i]; enums[enum_count++] = sub->as.program.enums[i];
} }
if (mod_count < 64) mods[mod_count++] = ast_make_mod_decl(a, mod_name, sub, tok_loc(mn)); /* mod 内容已内联合并到当前文件 */ ;
} else if (peek(&p)->kind == TOK_USE) { } else if (peek(&p)->kind == TOK_USE) {
/* TODO: use 语句待实现符号导入 */ ;
advance(&p); advance(&p);
const Token* path_tok = expect(&p, TOK_IDENT, error, "use 后应为模块名"); while (peek(&p)->kind != TOK_SEMICOLON && peek(&p)->kind != TOK_EOF) advance(&p);
if (!path_tok) return NULL; if (peek(&p)->kind == TOK_SEMICOLON) advance(&p);
if (!expect(&p, TOK_COLON_COLON, error, "缺少 '::'")) return NULL;
const Token* item_tok = expect(&p, TOK_IDENT, error, "use 后应为项目名");
if (!item_tok) return NULL;
if (!expect(&p, TOK_SEMICOLON, error, "缺少 ';'")) return NULL;
if (use_count >= 64) { error->message = "use 过多 (最多64)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
uses[use_count++] = ast_make_use_decl(a,
arena_strdup_impl(p.arena, path_tok->start, path_tok->length),
arena_strdup_impl(p.arena, item_tok->start, item_tok->length),
tok_loc(path_tok));
} else if (peek(&p)->kind == TOK_FN) { } else if (peek(&p)->kind == TOK_FN) {
if (fn_count >= 256) { error->message = "函数过多 (最多256)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; } if (fn_count >= 256) { error->message = "函数过多 (最多256)"; error->filename = p.filename; error->line = peek(&p)->line; error->col = peek(&p)->col; return NULL; }
functions[fn_count++] = parse_function(&p, is_pub, error); functions[fn_count++] = parse_function(&p, is_pub, error);
+24 -4
View File
@@ -558,19 +558,20 @@ static void analyze_method_call(AstNode* node, Scope* scope, ErrorList* errors,
snprintf(mangled, sizeof(mangled), "%s$%s", recv_struct, snprintf(mangled, sizeof(mangled), "%s$%s", recv_struct,
node->as.method_call.method_name); node->as.method_call.method_name);
Symbol* sym = scope_lookup(scope, mangled); Symbol* sym = scope_lookup(scope, mangled);
// trait 方法 fallback: 搜索所有作用域中以 $method_name 结尾的符号 // trait 方法 fallback: 搜索所有作用域中以 $method_name 结尾且以 StructName 开头的符号
if (!sym || sym->kind != SYM_FUNCTION) { if (!sym || sym->kind != SYM_FUNCTION) {
char suffix[256]; char suffix[256];
snprintf(suffix, sizeof(suffix), "$%s", node->as.method_call.method_name); snprintf(suffix, sizeof(suffix), "$%s", node->as.method_call.method_name);
size_t suf_len = strlen(suffix); size_t suf_len = strlen(suffix);
size_t recv_len = strlen(recv_struct);
for (const Scope* sc = scope; sc; sc = sc->parent) { for (const Scope* sc = scope; sc; sc = sc->parent) {
for (Symbol* s = sc->head; s; s = s->next) { for (Symbol* s = sc->head; s; s = s->next) {
if (s->kind == SYM_FUNCTION) { if (s->kind == SYM_FUNCTION) {
size_t name_len = strlen(s->name); size_t name_len = strlen(s->name);
if (name_len > suf_len && strcmp(s->name + name_len - suf_len, suffix) == 0) { if (name_len > suf_len + recv_len
&& strncmp(s->name, recv_struct, recv_len) == 0
&& strcmp(s->name + name_len - suf_len, suffix) == 0) {
sym = s; sym = s;
// 更新 method_name 为找到的完整函数名(codegen 需要)
node->as.method_call.method_name = s->name;
break; break;
} }
} }
@@ -578,6 +579,10 @@ static void analyze_method_call(AstNode* node, Scope* scope, ErrorList* errors,
if (sym) break; if (sym) break;
} }
} }
// 更新 method_name 为符号的实际名称(codegen 需要通过它找到 LLVM 函数)
if (sym && sym->kind == SYM_FUNCTION) {
node->as.method_call.method_name = sym->name;
}
if (!sym || sym->kind != SYM_FUNCTION) { if (!sym || sym->kind != SYM_FUNCTION) {
error_add(errors, "<sema>", node->loc.line, node->loc.col, error_add(errors, "<sema>", node->loc.line, node->loc.col,
"结构体 '%s' 没有方法 '%s'", recv_struct, "结构体 '%s' 没有方法 '%s'", recv_struct,
@@ -700,6 +705,21 @@ static void analyze_node(AstNode* node, Scope* scope, ErrorList* errors, Arena*
} }
for (size_t j = 0; j < impl->as.impl_block.method_count; j++) { for (size_t j = 0; j < impl->as.impl_block.method_count; j++) {
AstNode* method = impl->as.impl_block.methods[j]; AstNode* method = impl->as.impl_block.methods[j];
// Self → 实际结构体名(参数类型)
for (size_t k = 0; k < method->as.function.param_count; k++) {
AstNode* p = method->as.function.params[k];
if (p->as.parameter.type == TYPE_STRUCT
&& p->as.parameter.struct_type_name
&& strcmp(p->as.parameter.struct_type_name, "Self") == 0) {
p->as.parameter.struct_type_name = st_name;
}
}
// Self → 实际结构体名(返回类型)
if (method->as.function.return_type == TYPE_STRUCT
&& method->as.function.return_struct_type_name
&& strcmp(method->as.function.return_struct_type_name, "Self") == 0) {
method->as.function.return_struct_type_name = st_name;
}
// 构造改名后的函数名 // 构造改名后的函数名
char mangled[256]; char mangled[256];
snprintf(mangled, sizeof(mangled), "%s$%s", st_name, snprintf(mangled, sizeof(mangled), "%s$%s", st_name,
+28
View File
@@ -0,0 +1,28 @@
trait Eq {
fn eq(self: Self, other: Self) -> bool;
}
struct Point { x: i64, y: i64 }
extend Eq Point {
fn eq(self: Point, other: Point) -> bool {
return self.x == other.x && self.y == other.y;
}
}
fn main() -> i64 {
let p1 = Point { x: 10, y: 20 };
let p2 = Point { x: 10, y: 20 };
let p3 = Point { x: 99, y: 1 };
if p1.eq(p2) {
print_str("same");
} else {
print_str("diff");
}
if p1.eq(p3) {
print_str("same");
} else {
print_str("diff");
}
return 0;
}
+1 -1
View File
@@ -491,7 +491,7 @@ void test_codegen_match() {
AstNode* fns[] = { main_fn }; AstNode* fns[] = { main_fn };
AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, enums, 1, NULL, 0, loc_at(1, 1)); AstNode* prog = ast_make_program(&a, fns, 1, NULL, 0, NULL, 0, enums, 1, NULL, 0, loc_at(1, 1));
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(prog, &errors, &a); sema_analyze(prog, &errors, &a);
ASSERT(errors.count == 0); ASSERT(errors.count == 0);
+24 -24
View File
@@ -14,7 +14,7 @@ void test_type_error() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count > 0); ASSERT(errors.count > 0);
arena_destroy(&a); arena_destroy(&a);
@@ -29,7 +29,7 @@ void test_undefined_var() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count > 0); ASSERT(errors.count > 0);
arena_destroy(&a); arena_destroy(&a);
@@ -45,7 +45,7 @@ void test_simple_ok() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count == 0); ASSERT(errors.count == 0);
arena_destroy(&a); arena_destroy(&a);
@@ -61,7 +61,7 @@ void test_let_mut_assign_ok() {
ErrorInfo parse_err = {0}; ErrorInfo parse_err = {0};
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count == 0); ASSERT(errors.count == 0);
arena_destroy(&a); arena_destroy(&a);
@@ -77,7 +77,7 @@ void test_assign_immutable_error() {
ErrorInfo parse_err = {0}; ErrorInfo parse_err = {0};
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count > 0); // 不可变变量赋值应报错 ASSERT(errors.count > 0); // 不可变变量赋值应报错
arena_destroy(&a); arena_destroy(&a);
@@ -93,7 +93,7 @@ void test_str_type_ok() {
ErrorInfo parse_err = {0}; ErrorInfo parse_err = {0};
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count == 0); ASSERT(errors.count == 0);
arena_destroy(&a); arena_destroy(&a);
@@ -109,7 +109,7 @@ void test_str_concat_type_ok() {
ErrorInfo parse_err = {0}; ErrorInfo parse_err = {0};
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count == 0); ASSERT(errors.count == 0);
arena_destroy(&a); arena_destroy(&a);
@@ -128,7 +128,7 @@ void test_struct_field_type_mismatch() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count > 0); // y 字段类型不匹配: true 是 bool, 不是 i64 ASSERT(errors.count > 0); // y 字段类型不匹配: true 是 bool, 不是 i64
arena_destroy(&a); arena_destroy(&a);
@@ -145,7 +145,7 @@ void test_struct_undefined() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count > 0); // Unknown 未定义 ASSERT(errors.count > 0); // Unknown 未定义
arena_destroy(&a); arena_destroy(&a);
@@ -162,7 +162,7 @@ void test_struct_field_count_mismatch() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count > 0); // 缺少字段 'y' ASSERT(errors.count > 0); // 缺少字段 'y'
arena_destroy(&a); arena_destroy(&a);
@@ -182,7 +182,7 @@ void test_type_alias_ok() {
ASSERT(ast != NULL); ASSERT(ast != NULL);
ASSERT(ast->as.program.alias_count == 1); ASSERT(ast->as.program.alias_count == 1);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count == 0); ASSERT(errors.count == 0);
arena_destroy(&a); arena_destroy(&a);
@@ -200,7 +200,7 @@ void test_type_alias_struct() {
ASSERT(ast != NULL); ASSERT(ast != NULL);
ASSERT(ast->as.program.alias_count == 1); ASSERT(ast->as.program.alias_count == 1);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count == 0); ASSERT(errors.count == 0);
arena_destroy(&a); arena_destroy(&a);
@@ -217,7 +217,7 @@ void test_struct_nested_type_ok() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count == 0); // 嵌套结构体类型检查应通过 ASSERT(errors.count == 0); // 嵌套结构体类型检查应通过
arena_destroy(&a); arena_destroy(&a);
@@ -236,7 +236,7 @@ void test_enum_ok() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count == 0); ASSERT(errors.count == 0);
arena_destroy(&a); arena_destroy(&a);
@@ -253,7 +253,7 @@ void test_enum_bad_variant() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count > 0); // Yellow 不存在 ASSERT(errors.count > 0); // Yellow 不存在
arena_destroy(&a); arena_destroy(&a);
@@ -272,7 +272,7 @@ void test_array_ok() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count == 0); ASSERT(errors.count == 0);
arena_destroy(&a); arena_destroy(&a);
@@ -289,7 +289,7 @@ void test_array_index_type_error() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count > 0); // true 不是 i64 ASSERT(errors.count > 0); // true 不是 i64
arena_destroy(&a); arena_destroy(&a);
@@ -306,7 +306,7 @@ void test_array_not_indexable() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count > 0); // i64 不是数组 ASSERT(errors.count > 0); // i64 不是数组
arena_destroy(&a); arena_destroy(&a);
@@ -323,7 +323,7 @@ void test_array_assign_ok() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count == 0); ASSERT(errors.count == 0);
arena_destroy(&a); arena_destroy(&a);
@@ -342,7 +342,7 @@ void test_method_call_ok() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count == 0); ASSERT(errors.count == 0);
arena_destroy(&a); arena_destroy(&a);
@@ -359,7 +359,7 @@ void test_method_undefined() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count > 0); // nope 不是 Point 的方法 ASSERT(errors.count > 0); // nope 不是 Point 的方法
arena_destroy(&a); arena_destroy(&a);
@@ -378,7 +378,7 @@ void test_match_enum_sema_ok() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count == 0); // match 去糖后应通过类型检查 ASSERT(errors.count == 0); // match 去糖后应通过类型检查
arena_destroy(&a); arena_destroy(&a);
@@ -395,7 +395,7 @@ void test_match_int_sema_ok() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count == 0); ASSERT(errors.count == 0);
arena_destroy(&a); arena_destroy(&a);
@@ -412,7 +412,7 @@ void test_match_wildcard_only_sema_ok() {
AstNode* ast = parse(&a, toks, tc, "test", &parse_err); AstNode* ast = parse(&a, toks, tc, "test", &parse_err);
ASSERT(ast != NULL); ASSERT(ast != NULL);
ErrorList errors; error_init(&errors); ErrorList errors; error_init(&errors, &a);
sema_analyze(ast, &errors, &a); sema_analyze(ast, &errors, &a);
ASSERT(errors.count == 0); // 纯通配符 match 应通过 ASSERT(errors.count == 0); // 纯通配符 match 应通过
arena_destroy(&a); arena_destroy(&a);