authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-11-27 18:55:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2015-11-27 18:55:06-07:00
log4cc95174a77f7cbb42b371bb892c55a8349bc7fa
treeb147a81dc7630c5c0dc4686c3ad7705cd0dd0e01
parent4068897b6b167569718c61efcb4371693fe89b74

add tests for compile errors


5 files changed, 147 insertions(+), 15 deletions(-)

README.md+5-1
......@@ -32,11 +32,15 @@ readable, safe, optimal, and concise code to solve any computing problem.
3232
3333## Roadmap
3434
35 * test framework to test for compile errors
3635 * Simple .so library
3736 * Multiple files
3837 * inline assembly and syscalls
3938 * running code at compile time
39 * print! macro that takes var args
40 * panic! macro that prints a stack trace to stderr in debug mode and calls
41 abort() in release mode
42 * unreachable codegen to panic("unreachable") in debug mode, and nothing in
43 release mode
4044 * implement a simple game using SDL2
4145 * How should the Widget use case be solved? In Genesis I'm using C++ and inheritance.
4246
doc/vim/syntax/zig.vim+3-1
......@@ -1,14 +1,16 @@
11" Vim syntax file
22" Language: Zig
33" Maintainer: Andrew Kelley
4" Latest Revision: 24 November 2015
4" Latest Revision: 27 November 2015
55
66if exists("b:current_syntax")
77 finish
88endif
99
1010syn keyword zigKeyword fn return mut const extern unreachable export pub
11syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 void
1112
1213let b:current_syntax = "zig"
1314
1415hi def link zigKeyword Keyword
16hi def link zigType Type
src/codegen.cpp+18-1
......@@ -83,6 +83,7 @@ struct TypeNode {
8383
8484struct FnDefNode {
8585 bool add_implicit_return;
86 bool skip;
8687};
8788
8889struct CodeGenNode {
......@@ -214,7 +215,7 @@ static void find_declarations(CodeGen *g, AstNode *node) {
214215 if (buf_eql_str(name, "link")) {
215216 g->link_table.put(param, true);
216217 } else {
217 add_node_error(g, node,
218 add_node_error(g, directive_node,
218219 buf_sprintf("invalid directive: '%s'", buf_ptr(name)));
219220 }
220221 }
......@@ -242,6 +243,9 @@ static void find_declarations(CodeGen *g, AstNode *node) {
242243 if (entry) {
243244 add_node_error(g, node,
244245 buf_sprintf("redefinition of '%s'", buf_ptr(proto_name)));
246 assert(!node->codegen_node);
247 node->codegen_node = allocate<CodeGenNode>(1);
248 node->codegen_node->data.fn_def_node.skip = true;
245249 } else {
246250 FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);
247251 fn_table_entry->proto_node = proto_node;
......@@ -261,6 +265,12 @@ static void find_declarations(CodeGen *g, AstNode *node) {
261265 }
262266 case NodeTypeFnProto:
263267 {
268 for (int i = 0; i < node->data.fn_proto.directives->length; i += 1) {
269 AstNode *directive_node = node->data.fn_proto.directives->at(i);
270 Buf *name = &directive_node->data.directive.name;
271 add_node_error(g, directive_node,
272 buf_sprintf("invalid directive: '%s'", buf_ptr(name)));
273 }
264274 for (int i = 0; i < node->data.fn_proto.params.length; i += 1) {
265275 AstNode *child = node->data.fn_proto.params.at(i);
266276 find_declarations(g, child);
......@@ -363,11 +373,18 @@ static void analyze_node(CodeGen *g, AstNode *node) {
363373 break;
364374 case NodeTypeFnDef:
365375 {
376 if (node->codegen_node && node->codegen_node->data.fn_def_node.skip) {
377 // we detected an error with this function definition which prevents us
378 // from further analyzing it.
379 break;
380 }
381
366382 AstNode *proto_node = node->data.fn_def.fn_proto;
367383 assert(proto_node->type == NodeTypeFnProto);
368384 analyze_node(g, proto_node);
369385
370386 check_fn_def_control_flow(g, node);
387 analyze_node(g, node->data.fn_def.body);
371388 break;
372389 }
373390 case NodeTypeFnDecl:
src/util.hpp+2
......@@ -16,6 +16,8 @@
1616
1717#define BREAKPOINT __asm("int $0x03")
1818
19static const int COMPILE_FAILED_ERR_CODE = 10; // chosen with a random number generator
20
1921void zig_panic(const char *format, ...)
2022 __attribute__((cold))
2123 __attribute__ ((noreturn))
test/run_tests.cpp+119-12
......@@ -10,6 +10,7 @@
1010#include "os.hpp"
1111
1212#include <stdio.h>
13#include <stdarg.h>
1314
1415struct TestSourceFile {
1516 const char *relative_path;
......@@ -25,9 +26,10 @@ struct TestCase {
2526 ZigList<const char *> program_args;
2627};
2728
28ZigList<TestCase*> test_cases = {0};
29const char *tmp_source_path = ".tmp_source.zig";
30const char *tmp_exe_path = "./.tmp_exe";
29static ZigList<TestCase*> test_cases = {0};
30static const char *tmp_source_path = ".tmp_source.zig";
31static const char *tmp_exe_path = "./.tmp_exe";
32static const char *zig_exe = "./zig";
3133
3234static void add_simple_case(const char *case_name, const char *source, const char *output) {
3335 TestCase *test_case = allocate<TestCase>(1);
......@@ -45,7 +47,32 @@ static void add_simple_case(const char *case_name, const char *source, const cha
4547 test_cases.append(test_case);
4648}
4749
48static void add_all_test_cases(void) {
50static void add_compile_fail_case(const char *case_name, const char *source, int count, ...) {
51 va_list ap;
52 va_start(ap, count);
53
54 TestCase *test_case = allocate<TestCase>(1);
55 test_case->case_name = case_name;
56 test_case->source = source;
57
58 for (int i = 0; i < count; i += 1) {
59 const char *arg = va_arg(ap, const char *);
60 test_case->compile_errors.append(arg);
61 }
62
63 test_case->compiler_args.append("build");
64 test_case->compiler_args.append(tmp_source_path);
65 test_case->compiler_args.append("--output");
66 test_case->compiler_args.append(tmp_exe_path);
67 test_case->compiler_args.append("--release");
68 test_case->compiler_args.append("--strip");
69
70 test_cases.append(test_case);
71
72 va_end(ap);
73}
74
75static void add_compiling_test_cases(void) {
4976 add_simple_case("hello world with libc", R"SOURCE(
5077 #link("c")
5178 extern {
......@@ -102,23 +129,102 @@ static void add_all_test_cases(void) {
102129 )SOURCE", "OK\n");
103130}
104131
132static void add_compile_failure_test_cases(void) {
133 add_compile_fail_case("multiple function definitions", R"SOURCE(
134fn a() {}
135fn a() {}
136 )SOURCE", 1, "Line 3, column 1: redefinition of 'a'");
137
138 add_compile_fail_case("bad directive", R"SOURCE(
139#bogus1("")
140extern {
141 fn b();
142}
143#bogus2("")
144fn a() {}
145 )SOURCE", 2, "Line 2, column 1: invalid directive: 'bogus1'",
146 "Line 6, column 1: invalid directive: 'bogus2'");
147
148 add_compile_fail_case("unreachable with return", R"SOURCE(
149fn a() -> unreachable {return;}
150 )SOURCE", 1, "Line 2, column 24: return statement in function with unreachable return type");
151
152 add_compile_fail_case("control reaches end of non-void function", R"SOURCE(
153fn a() -> i32 {}
154 )SOURCE", 1, "Line 2, column 1: control reaches end of non-void function");
155
156 add_compile_fail_case("undefined function call", R"SOURCE(
157fn a() {
158 b();
159}
160 )SOURCE", 1, "Line 3, column 5: undefined function: 'b'");
161
162 add_compile_fail_case("wrong number of arguments", R"SOURCE(
163fn a() {
164 b(1);
165}
166fn b(a: i32, b: i32, c: i32) { }
167 )SOURCE", 1, "Line 3, column 5: wrong number of arguments. Expected 3, got 1.");
168
169 add_compile_fail_case("invalid type", R"SOURCE(
170fn a() -> bogus {}
171 )SOURCE", 1, "Line 2, column 11: invalid type name: 'bogus'");
172
173 add_compile_fail_case("pointer to unreachable", R"SOURCE(
174fn a() -> *mut unreachable {}
175 )SOURCE", 1, "Line 2, column 11: pointer to unreachable not allowed");
176
177 add_compile_fail_case("unreachable code", R"SOURCE(
178fn a() {
179 return;
180 b();
181}
182
183fn b() {}
184 )SOURCE", 1, "Line 4, column 5: unreachable code");
185}
186
187static void print_compiler_invokation(TestCase *test_case, Buf *zig_stderr) {
188 printf("%s", zig_exe);
189 for (int i = 0; i < test_case->compiler_args.length; i += 1) {
190 printf(" %s", test_case->compiler_args.at(i));
191 }
192 printf("\n");
193 printf("%s\n", buf_ptr(zig_stderr));
194}
195
105196static void run_test(TestCase *test_case) {
106197 os_write_file(buf_create_from_str(tmp_source_path), buf_create_from_str(test_case->source));
107198
108199 Buf zig_stderr = BUF_INIT;
109200 Buf zig_stdout = BUF_INIT;
110201 int return_code;
111 static const char *zig_exe = "./zig";
112202 os_exec_process(zig_exe, test_case->compiler_args, &return_code, &zig_stderr, &zig_stdout);
113203
204 if (test_case->compile_errors.length) {
205 if (return_code) {
206 for (int i = 0; i < test_case->compile_errors.length; i += 1) {
207 const char *err_text = test_case->compile_errors.at(i);
208 if (!strstr(buf_ptr(&zig_stderr), err_text)) {
209 printf("\n");
210 printf("========= Expected this compile error: =========\n");
211 printf("%s\n", err_text);
212 printf("================================================\n");
213 print_compiler_invokation(test_case, &zig_stderr);
214 exit(1);
215 }
216 }
217 return; // success
218 } else {
219 printf("\nCompile failed with return code 0 (Expected failure):\n");
220 print_compiler_invokation(test_case, &zig_stderr);
221 exit(1);
222 }
223 }
224
114225 if (return_code != 0) {
115226 printf("\nCompile failed with return code %d:\n", return_code);
116 printf("%s", zig_exe);
117 for (int i = 0; i < test_case->compiler_args.length; i += 1) {
118 printf(" %s", test_case->compiler_args.at(i));
119 }
120 printf("\n");
121 printf("%s\n", buf_ptr(&zig_stderr));
227 print_compiler_invokation(test_case, &zig_stderr);
122228 exit(1);
123229 }
124230
......@@ -164,7 +270,8 @@ static void cleanup(void) {
164270}
165271
166272int main(int argc, char **argv) {
167 add_all_test_cases();
273 add_compiling_test_cases();
274 add_compile_failure_test_cases();
168275 run_all_tests();
169276 cleanup();
170277}