diff --git a/ast/ast_evaluator.c2 b/ast/ast_evaluator.c2 index f861e230d..af25fa4fd 100644 --- a/ast/ast_evaluator.c2 +++ b/ast/ast_evaluator.c2 @@ -281,17 +281,17 @@ fn Value Evaluator.eval_call(Evaluator* caller, const CallExpr* c) { // Create a new stack frame and link it to the caller's // TODO: handle Stack frames as separate allocated objects - Evaluator eval; - - if (num_args > elemsof(eval.args)) { - return Value.error("too many arguments in pure function evaluation"); - } + Evaluator eval /*@(noinit)*/; eval.prev = caller; eval.fd = fd; eval.num_args = num_args; eval.depth = caller.depth + 1; eval.complexity = caller.complexity + 1; + if (num_args > elemsof(eval.args)) { + return Value.error("too many arguments in pure function evaluation"); + } + //VarDecl** params = fd.getParams(); for (u32 i = 0; i < num_args; i++) { diff --git a/ast/value.c2 b/ast/value.c2 index a3bc1b693..c6c973b78 100644 --- a/ast/value.c2 +++ b/ast/value.c2 @@ -799,7 +799,7 @@ fn f64 fabs(f64 d) { // string and trying to avoid using exponential notation // TODO: should be in the C2 library as a type function f64.str() public fn char *ftoa(char *dest, usize size, f64 d) { - char[32] buf; + char[32] buf /*@(noinit)*/; usize pos = 0; if (size < 2) { diff --git a/ast/var_decl.c2 b/ast/var_decl.c2 index b51af6b2a..0b63ca965 100644 --- a/ast/var_decl.c2 +++ b/ast/var_decl.c2 @@ -51,6 +51,7 @@ type VarDeclBits struct { u32 has_init_call : 1; // local variables only u32 attr_weak : 1; // globals only u32 addr_used : 1; + u32 attr_noinit : 1; u32 auto_attr : 2; // AutoAttr, for parameters only u32 printf_format : 1; // for parameters only } @@ -301,6 +302,14 @@ public fn bool VarDecl.hasInitCall(const VarDecl* d) { return d.base.varDeclBits.has_init_call; } +public fn void VarDecl.setAttrNoInit(VarDecl* d) { + d.base.varDeclBits.attr_noinit = 1; +} + +public fn bool VarDecl.hasAttrNoInit(const VarDecl* d) { + return d.base.varDeclBits.attr_noinit; +} + public fn void VarDecl.setAttrWeak(VarDecl* d) { d.base.varDeclBits.attr_weak = 1; } diff --git a/ast_utils/attr.c2 b/ast_utils/attr.c2 index 0fd59c378..5050ab6e2 100644 --- a/ast_utils/attr.c2 +++ b/ast_utils/attr.c2 @@ -42,6 +42,7 @@ public type AttrKind enum u8 { AutoLine, // Var, function param only AutoFunc, // Var, function param only Embed, // Var, globals only + NoInit, // Var } const char*[] attrKind_names = { @@ -67,6 +68,7 @@ const char*[] attrKind_names = { "auto_line", "auto_func", "embed", + "noinit", } static_assert(elemsof(AttrKind), elemsof(attrKind_names)); @@ -146,6 +148,7 @@ const AttrReq[] Required_arg = { [AttrKind.AutoLine] = AttrReq.NoArg, [AttrKind.AutoFunc] = AttrReq.NoArg, [AttrKind.Embed] = AttrReq.String, + [AttrKind.NoInit] = AttrReq.NoArg, } static_assert(elemsof(AttrKind), elemsof(Required_arg)); diff --git a/ast_utils/string_buffer.c2 b/ast_utils/string_buffer.c2 index 7fc270f43..13d84e120 100644 --- a/ast_utils/string_buffer.c2 +++ b/ast_utils/string_buffer.c2 @@ -167,7 +167,7 @@ public fn void Buf.stripTrailingSpaces(Buf* buf) { } public fn void Buf.print(Buf* buf, const char* format @(printf_format), ...) { - char[4096] tmp; + char[4096] tmp /*@(noinit)*/; // NOTE: no growing va_list args; va_start(args, format); @@ -178,7 +178,7 @@ public fn void Buf.print(Buf* buf, const char* format @(printf_format), ...) { } public fn void Buf.vprintf(Buf* buf, const char* format, va_list args) { - char[4096] tmp; + char[4096] tmp /*@(noinit)*/; // NOTE: no growing i32 len = vsnprintf(tmp, sizeof(tmp), format, args); assert(len < sizeof(tmp)); diff --git a/common/ast_builder.c2 b/common/ast_builder.c2 index 6959c2d58..86c182985 100644 --- a/common/ast_builder.c2 +++ b/common/ast_builder.c2 @@ -512,6 +512,9 @@ fn void Builder.actOnVarAttr(Builder* b, Decl* d, const Attr* a) { case Embed: b.storeAttr(d, a); break; + case NoInit: + vd.setAttrNoInit(); + break; default: b.diags.error(a.loc, "attribute '%s' is not applicable to variables", kind2name(a.kind)); diff --git a/common/file/file_utils.c2 b/common/file/file_utils.c2 index ebcf18808..4cbf2b4b4 100644 --- a/common/file/file_utils.c2 +++ b/common/file/file_utils.c2 @@ -116,7 +116,7 @@ public fn const char* make_path3(char *buf, usize size, const char* dir, const c // returns 0 on success, errno on failure // create a directory path, OK if exists already public fn i32 create_path(const char* path) { - char[file_utils.Max_path] tmp; + char[file_utils.Max_path] tmp /*@(noinit)*/; char *p = tmp; if (!*path) return 0; *p++ = *path++; diff --git a/generator/c/c_generator.c2 b/generator/c/c_generator.c2 index f9fd26151..85abfebfc 100644 --- a/generator/c/c_generator.c2 +++ b/generator/c/c_generator.c2 @@ -1299,8 +1299,7 @@ public fn void generate(string_pool.Pool* astPool, return; } - Generator gen; - gen.init(astPool, target, kind, output_dir, dir, diags, sm, build_info, mainFunc); + Generator gen.init(astPool, target, kind, output_dir, dir, diags, sm, build_info, mainFunc); gen.auxPool = auxPool; gen.enable_asserts = enable_asserts; gen.fast_build = fast_build; diff --git a/generator/c/c_generator_special.c2 b/generator/c/c_generator_special.c2 index f3eee2a0a..1183b8ac2 100644 --- a/generator/c/c_generator_special.c2 +++ b/generator/c/c_generator_special.c2 @@ -62,7 +62,7 @@ fn void Generator.createMakefile(Generator* gen, out.print("CC=%s\n", cc); out.add("CFLAGS=-Wall -Wextra -Wno-unused -Wno-switch\n"); - out.add("CFLAGS+=-Wno-unused-parameter -Wno-missing-field-initializers -Wno-format-zero-length\n"); + out.add("CFLAGS+=-Wno-unused-parameter -Wno-missing-field-initializers -Wno-missing-braces -Wno-format-zero-length\n"); out.add("CFLAGS+=-pipe -std=c99 -funsigned-char\n"); if (gen.fast_build) out.add("CFLAGS+=-O0 -g\n"); diff --git a/generator/c/c_generator_stmt.c2 b/generator/c/c_generator_stmt.c2 index f23f01fb7..0e7b43be7 100644 --- a/generator/c/c_generator_stmt.c2 +++ b/generator/c/c_generator_stmt.c2 @@ -40,6 +40,10 @@ fn void Generator.emitVarDecl(Generator* gen, VarDecl* vd, string_buffer.Buf* ou out.add(" = "); } gen.emitExpr(out, ie); + } else { + if (!vd.hasAttrNoInit()) { + out.add(" = { 0 }"); + } } } diff --git a/parser/c2_parser.c2 b/parser/c2_parser.c2 index b0cc5b41f..2e8ac09d7 100644 --- a/parser/c2_parser.c2 +++ b/parser/c2_parser.c2 @@ -370,7 +370,10 @@ fn void Parser.parseTopLevel(Parser* p) { // returns if embed attribute was seen fn void Parser.parseOptionalAttributes(Parser* p) { - if (p.tok.kind != Kind.At) return; + if (p.tok.kind == Kind.At) p.parseAttributes(); +} + +fn void Parser.parseAttributes(Parser* p) { p.consumeToken(); p.expectAndConsume(Kind.LParen); diff --git a/parser/c2_parser_stmt.c2 b/parser/c2_parser_stmt.c2 index 0c3776072..bbcb71aa6 100644 --- a/parser/c2_parser_stmt.c2 +++ b/parser/c2_parser_stmt.c2 @@ -21,6 +21,7 @@ import constants local; import token local; import src_loc local; import string_list; +import string local; fn Stmt* Parser.parseStmt(Parser* p) { // TODO use Jump Table (combined one for multiple purposes?) @@ -516,7 +517,7 @@ fn Stmt* Parser.parseWhileStmt(Parser* p) { } fn Stmt* Parser.parseDeclStmt(Parser* p, bool checkSemi, bool allowLocal, bool isCondition) { - VarDecl*[MaxMultiDecl] decls; + VarDecl*[MaxMultiDecl] decls /*@(noinit)*/; u32 num_decls = 0; bool has_local = false; if (p.tok.kind == Kind.KW_local) { @@ -541,6 +542,7 @@ fn Stmt* Parser.parseDeclStmt(Parser* p, bool checkSemi, bool allowLocal, bool i p.consumeToken(); // TODO same as parseVarDecl() + bool has_noinit = false; bool has_init_call = false; need_semi = true; Expr* initValue = nil; @@ -555,7 +557,14 @@ fn Stmt* Parser.parseDeclStmt(Parser* p, bool checkSemi, bool allowLocal, bool i need_semi = checkSemi && initValue.needsSemi(); break; case At: - p.error("local variables cannot have attributes"); + // check for noinit attribute + p.consumeToken(); + p.expectAndConsume(Kind.LParen); + p.expectIdentifier(); + if (strcmp(p.pool.idx2str(p.tok.name_idx), "noinit")) + p.error("local variables cannot have attributes"); + has_noinit = true; + p.expectAndConsume(Kind.RParen); break; case Dot: if (p.peekToken(1) == Kind.Identifier @@ -583,7 +592,9 @@ fn Stmt* Parser.parseDeclStmt(Parser* p, bool checkSemi, bool allowLocal, bool i default: break; } - decls[num_decls++] = p.builder.actOnVarDecl(name, loc, &ref, assignLoc, initValue, has_local, has_init_call); + VarDecl* vd = p.builder.actOnVarDecl(name, loc, &ref, assignLoc, initValue, has_local, has_init_call); + if (has_noinit) vd.setAttrNoInit(); + decls[num_decls++] = vd; if (p.tok.kind != Kind.Comma) break; p.consumeToken(); diff --git a/parser/c2_tokenizer.c2 b/parser/c2_tokenizer.c2 index 1d26b3d89..f6ecafa30 100644 --- a/parser/c2_tokenizer.c2 +++ b/parser/c2_tokenizer.c2 @@ -888,7 +888,7 @@ check_overflow: } fn void Tokenizer.lex_floating_point(Tokenizer* t, Token* result, const char* start) { - char[4096] buf; + char[128] buf /*@(noinit)*/; const char* p = start; usize pos = 0; u8 seen_dot = 0; @@ -943,7 +943,7 @@ too_large: } fn void Tokenizer.lex_floating_point_hex(Tokenizer* t, Token* result, const char* start) { - char[4096] buf; + char[128] buf /*@(noinit)*/; const char* p = start; usize pos = 0; u8 seen_dot = 0; diff --git a/test/auto_args/struct_function_before_self.c2t b/test/auto_args/struct_function_before_self.c2t index 1bc148004..d96d6515d 100644 --- a/test/auto_args/struct_function_before_self.c2t +++ b/test/auto_args/struct_function_before_self.c2t @@ -27,7 +27,7 @@ static void test_Foo_test(const char* file, uint32_t line, test_Foo* f, void* p) int32_t main(void) { - test_Foo f; + test_Foo f = { 0 }; test_Foo_test("file1.c2", 11, &f, NULL); return 0; } diff --git a/test/auto_args/struct_function_ok.c2t b/test/auto_args/struct_function_ok.c2t index faf6075f7..91ce0f63b 100644 --- a/test/auto_args/struct_function_ok.c2t +++ b/test/auto_args/struct_function_ok.c2t @@ -27,7 +27,7 @@ static void test_Foo_test(test_Foo* f, const char* file, uint32_t line, void* p) int32_t main(void) { - test_Foo f; + test_Foo f = { 0 }; test_Foo_test(&f, "file1.c2", 11, NULL); test_Foo_test(&f, "file1.c2", 12, NULL); return 0; diff --git a/test/c_generator/asm/gen_asm.c2t b/test/c_generator/asm/gen_asm.c2t index ae4b0af2d..f92da83d0 100644 --- a/test/c_generator/asm/gen_asm.c2t +++ b/test/c_generator/asm/gen_asm.c2t @@ -48,16 +48,16 @@ static void test_basic_asm(void); static int32_t test_add(int32_t x, int32_t y) { - int32_t result; + int32_t result = { 0 }; __asm__ volatile ("add %[Rd], %[Rm], %[Rn]" : [Rd] "=r" (result) : [Rm] "r" (x), [Rn] "r" (y)); return result; } static void test_clobbers(void) { - int32_t from; - int32_t to; - int32_t count; + int32_t from = { 0 }; + int32_t to = { 0 }; + int32_t count = { 0 }; __asm__ volatile ("movc3 %0, %1, %2" : : "g" (from), "g" (to), "g" (count) @@ -67,8 +67,8 @@ static void test_clobbers(void) static inline uint64_t test_rdtsc(void) { - uint32_t lo; - uint32_t hi; + uint32_t lo = { 0 }; + uint32_t hi = { 0 }; __asm__ volatile ("rdtsc" : "=a" (lo), "=d" (hi)); uint64_t res = hi; res <<= 32; diff --git a/test/c_generator/expr/prec_expr.c2t b/test/c_generator/expr/prec_expr.c2t index 0cea9e323..4ab74fc1e 100644 --- a/test/c_generator/expr/prec_expr.c2t +++ b/test/c_generator/expr/prec_expr.c2t @@ -30,7 +30,7 @@ public fn i32 main(i32 argc, const char** argv) { int32_t main(int32_t argc, const char** argv) { - int32_t a; + int32_t a = { 0 }; a = (1 << 2) + 3; a = ((1 ^ 2) | 3) & 4; diff --git a/test/c_generator/functions/inline/inline_prefix.c2t b/test/c_generator/functions/inline/inline_prefix.c2t index 95cb2f710..af7849e3c 100644 --- a/test/c_generator/functions/inline/inline_prefix.c2t +++ b/test/c_generator/functions/inline/inline_prefix.c2t @@ -47,7 +47,7 @@ type Point struct { static inline void test1_myfunc(const test2_Point* p) { - test2_Point points[3]; + test2_Point points[3] = { 0 }; points[1] = *p; } diff --git a/test/c_generator/functions/inline/inline_stmts.c2t b/test/c_generator/functions/inline/inline_stmts.c2t index a3e858bf9..707ef2257 100644 --- a/test/c_generator/functions/inline/inline_stmts.c2t +++ b/test/c_generator/functions/inline/inline_stmts.c2t @@ -114,7 +114,7 @@ next: static inline int32_t test1_test_fn(const char* format, ...) { - const char* p; + const char* p = { 0 }; { const char* q = format; if (q) { @@ -137,7 +137,7 @@ int32_t test1_test_fn(const char* format, ...) if (format) p = format; else p = format; { - const char* q; + const char* q = { 0 }; while ((q = format)) { } } diff --git a/test/c_generator/functions/inline/lib_public_inline.c2t b/test/c_generator/functions/inline/lib_public_inline.c2t index e0dfa81c3..049f4a83d 100644 --- a/test/c_generator/functions/inline/lib_public_inline.c2t +++ b/test/c_generator/functions/inline/lib_public_inline.c2t @@ -41,7 +41,7 @@ typedef void (*test1_Handler)(int32_t* _arg0); static inline int32_t test1_test_fn(test1_Handler a, int32_t** b) { - int32_t c[4]; + int32_t c[4] = { 0 }; a(*b); return 123; } diff --git a/test/c_generator/functions/inline/nonpublic_inline.c2t b/test/c_generator/functions/inline/nonpublic_inline.c2t index 18c6a03fb..5883aceed 100644 --- a/test/c_generator/functions/inline/nonpublic_inline.c2t +++ b/test/c_generator/functions/inline/nonpublic_inline.c2t @@ -42,6 +42,6 @@ void test1_test_fn(void) { test1_non_public(); test1_num++; - test1_Priv p; + test1_Priv p = { 0 }; test1_Handler h = test1_non_public; } diff --git a/test/c_generator/functions/struct_functions/chained_struct_calls.c2t b/test/c_generator/functions/struct_functions/chained_struct_calls.c2t index 2e2cb1b1f..3a2a7ad7c 100644 --- a/test/c_generator/functions/struct_functions/chained_struct_calls.c2t +++ b/test/c_generator/functions/struct_functions/chained_struct_calls.c2t @@ -73,7 +73,7 @@ static test_B* test_C_getB(test_C* c) static void test_test1(void) { - test_C c; + test_C c = { 0 }; uint32_t n = test_A_run(test_B_getA(test_C_getB(&c))); } diff --git a/test/c_generator/functions/struct_functions/local.c2t b/test/c_generator/functions/struct_functions/local.c2t index 6300ed5f3..d2b0f4a29 100644 --- a/test/c_generator/functions/struct_functions/local.c2t +++ b/test/c_generator/functions/struct_functions/local.c2t @@ -33,7 +33,7 @@ static void test_Type_init(test_Type* _arg0) int32_t main(void) { - test_Type t; + test_Type t = { 0 }; test_Type_init(&t); return 0; } diff --git a/test/c_generator/functions/struct_functions/local_arg.c2t b/test/c_generator/functions/struct_functions/local_arg.c2t index fd526fc56..a012d8815 100644 --- a/test/c_generator/functions/struct_functions/local_arg.c2t +++ b/test/c_generator/functions/struct_functions/local_arg.c2t @@ -33,7 +33,7 @@ static void test_Type_init(test_Type* _arg0, int32_t _arg1) int32_t main(void) { - test_Type t; + test_Type t = { 0 }; test_Type_init(&t, 4); return 0; } diff --git a/test/c_generator/functions/struct_functions/local_member.c2t b/test/c_generator/functions/struct_functions/local_member.c2t index b229e2543..07624d027 100644 --- a/test/c_generator/functions/struct_functions/local_member.c2t +++ b/test/c_generator/functions/struct_functions/local_member.c2t @@ -31,7 +31,7 @@ static void test_Type_init(test_Type* _arg0) int32_t main(void) { - test_Outer o; + test_Outer o = { 0 }; test_Type_init(&o.t); return 0; } diff --git a/test/c_generator/functions/struct_functions/local_normal_callback.c2t b/test/c_generator/functions/struct_functions/local_normal_callback.c2t index 6254c39aa..73e88d31f 100644 --- a/test/c_generator/functions/struct_functions/local_normal_callback.c2t +++ b/test/c_generator/functions/struct_functions/local_normal_callback.c2t @@ -28,7 +28,7 @@ struct test_Type_ { int32_t main(void) { - test_Type t; + test_Type t = { 0 }; t.init(1); return 0; } diff --git a/test/c_generator/functions/struct_functions/local_ptr.c2t b/test/c_generator/functions/struct_functions/local_ptr.c2t index 4a276de1c..4ddc3852f 100644 --- a/test/c_generator/functions/struct_functions/local_ptr.c2t +++ b/test/c_generator/functions/struct_functions/local_ptr.c2t @@ -33,7 +33,7 @@ static void test_Type_init(test_Type* _arg0) int32_t main(void) { - test_Type* t; + test_Type* t = { 0 }; test_Type_init(t); return 0; } diff --git a/test/c_generator/stmts/local_array_decl.c2t b/test/c_generator/stmts/local_array_decl.c2t index 0b1392169..4b6c86f47 100644 --- a/test/c_generator/stmts/local_array_decl.c2t +++ b/test/c_generator/stmts/local_array_decl.c2t @@ -26,6 +26,6 @@ static void test_func1(void); static void test_func1(void) { - int32_t board2[20][20]; + int32_t board2[20][20] = { 0 }; } diff --git a/test/c_generator/stmts/while_stmt_expr.c2t b/test/c_generator/stmts/while_stmt_expr.c2t index 0a5e48d96..df8fbb1e3 100644 --- a/test/c_generator/stmts/while_stmt_expr.c2t +++ b/test/c_generator/stmts/while_stmt_expr.c2t @@ -30,7 +30,7 @@ int32_t main(int32_t argc, const char** argv) } { - int32_t b; + int32_t b = { 0 }; while ((b = 0)) { } } diff --git a/test/expr/assign_list.c2t b/test/expr/assign_list.c2t index dade89a36..c66800703 100644 --- a/test/expr/assign_list.c2t +++ b/test/expr/assign_list.c2t @@ -44,7 +44,7 @@ static test_Point test_new_point(int32_t x, int32_t y) int32_t main(void) { printf("p0={ %d, %d }\n", test_p0.x, test_p0.y); - test_Point p1; + test_Point p1 = { 0 }; p1 = (test_Point){ 3, 4 }; printf("p1={ %d, %d }\n", p1.x, p1.y); test_print_point("p2", (test_Point){ 5, 6 }); diff --git a/tools/c2cat.c2 b/tools/c2cat.c2 index adddb2e5d..fd397588a 100644 --- a/tools/c2cat.c2 +++ b/tools/c2cat.c2 @@ -71,6 +71,7 @@ const char*[] attr_names = { "weak", "opaque", "cname", + "cdef", "no_typedef", "constructor", "destructor", @@ -78,6 +79,8 @@ const char*[] attr_names = { "auto_file", "auto_line", "auto_func", + "embed", + "noinit", } fn bool is_attribute(const char* str) { diff --git a/tools/tester/expect_file.c2 b/tools/tester/expect_file.c2 index 4851c63b4..8f026a32d 100644 --- a/tools/tester/expect_file.c2 +++ b/tools/tester/expect_file.c2 @@ -51,21 +51,24 @@ public type ExpectFile struct @(opaque) { ExpectMode mode; string_buffer.Buf* output; // will be set in check() + const char* testFilename; // during analysis u32 lastLineNr; u32 expIndex; // cache for Lines[expIndex] const char* expectedLine; + u32 expectedLineNr; bool consecutive; line_db.Db* lines; } -public fn ExpectFile* create(const char* name, ExpectMode m) { +public fn ExpectFile* create(const char* name, ExpectMode m, const char* testFilename) { ExpectFile* f = calloc(1, sizeof(ExpectFile)); strcpy(f.filename, name); f.mode = m; + f.testFilename = testFilename; f.lines = line_db.create(); return f; } @@ -84,7 +87,7 @@ public fn void ExpectFile.addLine(ExpectFile* f, u32 line_nr, const char* start, if (strncmp(start, "//", 2) == 0) return; // ignore comments f.lastLineNr = line_nr; - f.lines.add(start, end, consecutive); + f.lines.add(start, end, line_nr, consecutive); } fn bool ExpectFile.checkLine(ExpectFile* f, u32 line_nr, const char* start, const char* end) { @@ -92,30 +95,20 @@ fn bool ExpectFile.checkLine(ExpectFile* f, u32 line_nr, const char* start, cons skipInitialWhitespace(&start, end); skipTrailingWhitespace(start, &end); - char[EXPECT_MAX_LINE] line; u32 len = (u32)(end - start); if (len == 0) return true; // ignore empty lines - if (start[0] == '/' && start[1] == '/') return true; // ignore comment lines - - if (len >= EXPECT_MAX_LINE) { - color_print2(f.output, colError, " in file %s: line %d: line too long", f.filename, line_nr); - return false; - } - if (len != 0) memcpy(line, start, len); - line[len] = 0; -#if TesterDebug - printf("got %d [%s]\n", line_nr, line); -#endif + if (strncmp(start, "//", 2) == 0) return true; // ignore comment lines if (!f.expectedLine) { if (f.mode == ExpectMode.COMPLETE) { - color_print2(f.output, colError, " in file %s: line %d\n unexpected line '%s'", f.filename, line_nr, line); + color_print2(f.output, colError, "%s:%d: unexpected '%.*s'", f.testFilename, f.expectedLineNr+1, (i32)len, start); + color_print2(f.output, colError, " in file %s: line %d\n unexpected line '%.*s'", f.filename, line_nr, (i32)len, start); return false; } else { return true; } } - if (strcmp(f.expectedLine, line) == 0) { + if (strncmp(f.expectedLine, start, len) == 0) { #if TesterDebug printf("-> match\n"); #endif @@ -123,7 +116,8 @@ fn bool ExpectFile.checkLine(ExpectFile* f, u32 line_nr, const char* start, cons f.setExpected(); } else { if (f.mode == ExpectMode.COMPLETE || f.consecutive) { - color_print2(f.output, colError, " in file %s: line %d\n expected '%s'\n got '%s'", f.filename, line_nr, f.expectedLine, line); + color_print2(f.output, colError, "%s:%d: expected '%s', got '%.*s'", f.testFilename, f.expectedLineNr, f.expectedLine, (i32)len, start); + color_print2(f.output, colError, " in file %s: line %d\n expected '%s'\n got '%.*s'", f.filename, line_nr, f.expectedLine, (i32)len, start); return false; } } @@ -136,6 +130,7 @@ fn void ExpectFile.setExpected(ExpectFile* f) { f.consecutive = false; } else { f.expectedLine = f.lines.getLine(f.expIndex); + f.expectedLineNr = f.lines.getLineNr(f.expIndex); f.consecutive = f.lines.getConsecutive(f.expIndex); } #if TesterDebug @@ -164,29 +159,26 @@ public fn bool ExpectFile.check(ExpectFile* f, string_buffer.Buf* output, const f.setExpected(); u32 line_nr = 1; - while (1) { + while (*lineStart) { // cut it up into lines (even if empty) const char* cp = lineStart; while (*cp) { if (*cp == '\n') break; cp++; } - - if (*cp == 0) break; if (!f.checkLine(line_nr, lineStart, cp)) { result = false; - goto out; + break; } line_nr++; - cp++; // skip newline + if (*cp == '\n') cp++; // skip newline lineStart = cp; } - - if (f.expectedLine != nil) { + if (!*lineStart && f.expectedLine != nil) { + color_print2(f.output, colError, "%s:%d: expected '%s'", f.testFilename, f.expectedLineNr, f.expectedLine); color_print2(f.output, colError, " in file %s: expected '%s'", f.filename, f.expectedLine); result = false; } -out: file.close(); return result; } diff --git a/tools/tester/line_db.c2 b/tools/tester/line_db.c2 index 33e9bdd38..3aa0e6729 100644 --- a/tools/tester/line_db.c2 +++ b/tools/tester/line_db.c2 @@ -20,6 +20,7 @@ import string local; type Entry struct { char* line; + u32 line_nr; bool consecutive; } @@ -57,7 +58,7 @@ fn void Db.resize(Db* db, u32 capacity) { db.entries = entries2; } -public fn void Db.add(Db* db, const char* start, const char* end, bool consecutive) { +public fn void Db.add(Db* db, const char* start, const char* end, u32 line_nr, bool consecutive) { u32 len = (u32)(end - start); if (db.count == db.capacity) { db.resize((u32)(db.capacity ? db.capacity * 2 : 4)); @@ -68,6 +69,7 @@ public fn void Db.add(Db* db, const char* start, const char* end, bool consecuti entry.line = malloc(len + 1); // add 0-terminator memcpy(entry.line, start, len); entry.line[len] = 0; + entry.line_nr = line_nr; entry.consecutive = consecutive; } @@ -75,6 +77,10 @@ public fn const char* Db.getLine(const Db* db, u32 idx) { return db.entries[idx].line; } +public fn u32 Db.getLineNr(const Db* db, u32 idx) { + return db.entries[idx].line_nr; +} + public fn bool Db.getConsecutive(const Db* db, u32 idx) { return db.entries[idx].consecutive; } diff --git a/tools/tester/test_db.c2 b/tools/tester/test_db.c2 index 3c53d08b3..9e71c10da 100644 --- a/tools/tester/test_db.c2 +++ b/tools/tester/test_db.c2 @@ -390,7 +390,7 @@ fn bool Db.parseExpect(Db* db) { } db.skipLine(); // skip rest of the line - db.currentExpect = expect_file.create(filename, em); + db.currentExpect = expect_file.create(filename, em, db.filename); // TODO check for name duplicates db.addExpected(db.currentExpect); while (*db.cur != 0 && !strstart(db.cur, "// @", nil)) { @@ -573,7 +573,7 @@ fn void Db.parseLineOutside(Db* db, const char* start, const char* end) { char[128] name; if (!db.readUntil(name, elemsof(name), cp, '}', "filename")) return; - db.currentExpect = expect_file.create(name, ExpectMode.ATLEAST); + db.currentExpect = expect_file.create(name, ExpectMode.ATLEAST, db.filename); // TODO check for name duplicates db.addExpected(db.currentExpect); db.mode = Mode.InExpectFile;