const std = @import("std"); const os = std.os; const tests = @import("tests.zig"); pub fn addCases(cases: *tests.StackTracesContext) void { cases.addCase(.{ .name = "return", .source = \\pub fn main() !void { \\ return error.TheSkyIsFalling; \\} , .Debug = .{ .expect = \\error: TheSkyIsFalling \\source.zig:2:5: [address] in main (test) \\ return error.TheSkyIsFalling; \\ ^ \\ , }, .ReleaseSafe = .{ .exclude_os = .{ .windows, // TODO .linux, // defeated by aggressive inlining }, .expect = \\error: TheSkyIsFalling \\source.zig:2:5: [address] in [function] \\ return error.TheSkyIsFalling; \\ ^ \\ , }, .ReleaseFast = .{ .expect = \\error: TheSkyIsFalling \\ , }, .ReleaseSmall = .{ .expect = \\error: TheSkyIsFalling \\ , }, }); cases.addCase(.{ .name = "try return", .source = \\fn foo() !void { \\ return error.TheSkyIsFalling; \\} \\ \\pub fn main() !void { \\ try foo(); \\} , .Debug = .{ .expect = \\error: TheSkyIsFalling \\source.zig:2:5: [address] in foo (test) \\ return error.TheSkyIsFalling; \\ ^ \\source.zig:6:5: [address] in main (test) \\ try foo(); \\ ^ \\ , }, .ReleaseSafe = .{ .exclude_os = .{ .windows, // TODO }, .expect = \\error: TheSkyIsFalling \\source.zig:2:5: [address] in [function] \\ return error.TheSkyIsFalling; \\ ^ \\source.zig:6:5: [address] in [function] \\ try foo(); \\ ^ \\ , }, .ReleaseFast = .{ .expect = \\error: TheSkyIsFalling \\ , }, .ReleaseSmall = .{ .expect = \\error: TheSkyIsFalling \\ , }, }); cases.addCase(.{ .name = "try return + handled catch/if-else", .source = \\fn foo() !void { \\ return error.TheSkyIsFalling; \\} \\ \\pub fn main() !void { \\ foo() catch {}; // should not affect error trace \\ if (foo()) |_| {} else |_| { \\ // should also not affect error trace \\ } \\ try foo(); \\} , .Debug = .{ .expect = \\error: TheSkyIsFalling \\source.zig:2:5: [address] in foo (test) \\ return error.TheSkyIsFalling; \\ ^ \\source.zig:10:5: [address] in main (test) \\ try foo(); \\ ^ \\ , }, .ReleaseSafe = .{ .exclude_os = .{ .windows, // TODO .linux, // defeated by aggressive inlining }, .expect = \\error: TheSkyIsFalling \\source.zig:2:5: [address] in [function] \\ return error.TheSkyIsFalling; \\ ^ \\source.zig:10:5: [address] in [function] \\ try foo(); \\ ^ \\ , }, .ReleaseFast = .{ .expect = \\error: TheSkyIsFalling \\ , }, .ReleaseSmall = .{ .expect = \\error: TheSkyIsFalling \\ , }, }); cases.addCase(.{ .name = "catch and re-throw error", .source = \\fn foo() !void { \\ return error.TheSkyIsFalling; \\} \\ \\pub fn main() !void { \\ return foo() catch error.AndMyCarIsOutOfGas; \\} , .Debug = .{ .expect = \\error: AndMyCarIsOutOfGas \\source.zig:2:5: [address] in foo (test) \\ return error.TheSkyIsFalling; \\ ^ \\source.zig:6:5: [address] in main (test) \\ return foo() catch error.AndMyCarIsOutOfGas; \\ ^ \\ , }, .ReleaseSafe = .{ .exclude_os = .{ .windows, // TODO .linux, // defeated by aggressive inlining }, .expect = \\error: AndMyCarIsOutOfGas \\source.zig:2:5: [address] in [function] \\ return error.TheSkyIsFalling; \\ ^ \\source.zig:6:5: [address] in [function] \\ return foo() catch error.AndMyCarIsOutOfGas; \\ ^ \\ , }, .ReleaseFast = .{ .expect = \\error: AndMyCarIsOutOfGas \\ , }, .ReleaseSmall = .{ .expect = \\error: AndMyCarIsOutOfGas \\ , }, }); cases.addCase(.{ .name = "stored errors do not contribute to error trace", .source = \\fn foo() !void { \\ return error.TheSkyIsFalling; \\} \\ \\pub fn main() !void { \\ // Once an error is stored in a variable, it is popped from the trace \\ var x = foo(); \\ x = {}; \\ \\ // As a result, this error trace will still be clean \\ return error.SomethingUnrelatedWentWrong; \\} , .Debug = .{ .expect = \\error: SomethingUnrelatedWentWrong \\source.zig:11:5: [address] in main (test) \\ return error.SomethingUnrelatedWentWrong; \\ ^ \\ , }, .ReleaseSafe = .{ .exclude_os = .{ .windows, // TODO .linux, // defeated by aggressive inlining }, .expect = \\error: SomethingUnrelatedWentWrong \\source.zig:11:5: [address] in [function] \\ return error.SomethingUnrelatedWentWrong; \\ ^ \\ , }, .ReleaseFast = .{ .expect = \\error: SomethingUnrelatedWentWrong \\ , }, .ReleaseSmall = .{ .expect = \\error: SomethingUnrelatedWentWrong \\ , }, }); cases.addCase(.{ .name = "error passed to function has its trace preserved for duration of the call", .source = \\pub fn expectError(expected_error: anyerror, actual_error: anyerror!void) !void { \\ actual_error catch |err| { \\ if (err == expected_error) return {}; \\ }; \\ return error.TestExpectedError; \\} \\ \\fn alwaysErrors() !void { return error.ThisErrorShouldNotAppearInAnyTrace; } \\fn foo() !void { return error.Foo; } \\ \\pub fn main() !void { \\ try expectError(error.ThisErrorShouldNotAppearInAnyTrace, alwaysErrors()); \\ try expectError(error.ThisErrorShouldNotAppearInAnyTrace, alwaysErrors()); \\ try expectError(error.Foo, foo()); \\ \\ // Only the error trace for this failing check should appear: \\ try expectError(error.Bar, foo()); \\} , .Debug = .{ .expect = \\error: TestExpectedError \\source.zig:9:18: [address] in foo (test) \\fn foo() !void { return error.Foo; } \\ ^ \\source.zig:5:5: [address] in expectError (test) \\ return error.TestExpectedError; \\ ^ \\source.zig:17:5: [address] in main (test) \\ try expectError(error.Bar, foo()); \\ ^ \\ , }, .ReleaseSafe = .{ .exclude_os = .{ .windows, // TODO }, .expect = \\error: TestExpectedError \\source.zig:9:18: [address] in [function] \\fn foo() !void { return error.Foo; } \\ ^ \\source.zig:5:5: [address] in [function] \\ return error.TestExpectedError; \\ ^ \\source.zig:17:5: [address] in [function] \\ try expectError(error.Bar, foo()); \\ ^ \\ , }, .ReleaseFast = .{ .expect = \\error: TestExpectedError \\ , }, .ReleaseSmall = .{ .expect = \\error: TestExpectedError \\ , }, }); cases.addCase(.{ .name = "try return from within catch", .source = \\fn foo() !void { \\ return error.TheSkyIsFalling; \\} \\ \\fn bar() !void { \\ return error.AndMyCarIsOutOfGas; \\} \\ \\pub fn main() !void { \\ foo() catch { // error trace should include foo() \\ try bar(); \\ }; \\} , .Debug = .{ .expect = \\error: AndMyCarIsOutOfGas \\source.zig:2:5: [address] in foo (test) \\ return error.TheSkyIsFalling; \\ ^ \\source.zig:6:5: [address] in bar (test) \\ return error.AndMyCarIsOutOfGas; \\ ^ \\source.zig:11:9: [address] in main (test) \\ try bar(); \\ ^ \\ , }, .ReleaseSafe = .{ .exclude_os = .{ .windows, // TODO }, .expect = \\error: AndMyCarIsOutOfGas \\source.zig:2:5: [address] in [function] \\ return error.TheSkyIsFalling; \\ ^ \\source.zig:6:5: [address] in [function] \\ return error.AndMyCarIsOutOfGas; \\ ^ \\source.zig:11:9: [address] in [function] \\ try bar(); \\ ^ \\ , }, .ReleaseFast = .{ .expect = \\error: AndMyCarIsOutOfGas \\ , }, .ReleaseSmall = .{ .expect = \\error: AndMyCarIsOutOfGas \\ , }, }); cases.addCase(.{ .name = "try return from within if-else", .source = \\fn foo() !void { \\ return error.TheSkyIsFalling; \\} \\ \\fn bar() !void { \\ return error.AndMyCarIsOutOfGas; \\} \\ \\pub fn main() !void { \\ if (foo()) |_| {} else |_| { // error trace should include foo() \\ try bar(); \\ } \\} , .Debug = .{ .expect = \\error: AndMyCarIsOutOfGas \\source.zig:2:5: [address] in foo (test) \\ return error.TheSkyIsFalling; \\ ^ \\source.zig:6:5: [address] in bar (test) \\ return error.AndMyCarIsOutOfGas; \\ ^ \\source.zig:11:9: [address] in main (test) \\ try bar(); \\ ^ \\ , }, .ReleaseSafe = .{ .exclude_os = .{ .windows, // TODO }, .expect = \\error: AndMyCarIsOutOfGas \\source.zig:2:5: [address] in [function] \\ return error.TheSkyIsFalling; \\ ^ \\source.zig:6:5: [address] in [function] \\ return error.AndMyCarIsOutOfGas; \\ ^ \\source.zig:11:9: [address] in [function] \\ try bar(); \\ ^ \\ , }, .ReleaseFast = .{ .expect = \\error: AndMyCarIsOutOfGas \\ , }, .ReleaseSmall = .{ .expect = \\error: AndMyCarIsOutOfGas \\ , }, }); cases.addCase(.{ .name = "try try return return", .source = \\fn foo() !void { \\ try bar(); \\} \\ \\fn bar() !void { \\ return make_error(); \\} \\ \\fn make_error() !void { \\ return error.TheSkyIsFalling; \\} \\ \\pub fn main() !void { \\ try foo(); \\} , .Debug = .{ .expect = \\error: TheSkyIsFalling \\source.zig:10:5: [address] in make_error (test) \\ return error.TheSkyIsFalling; \\ ^ \\source.zig:6:5: [address] in bar (test) \\ return make_error(); \\ ^ \\source.zig:2:5: [address] in foo (test) \\ try bar(); \\ ^ \\source.zig:14:5: [address] in main (test) \\ try foo(); \\ ^ \\ , }, .ReleaseSafe = .{ .exclude_os = .{ .windows, // TODO }, .expect = \\error: TheSkyIsFalling \\source.zig:10:5: [address] in [function] \\ return error.TheSkyIsFalling; \\ ^ \\source.zig:6:5: [address] in [function] \\ return make_error(); \\ ^ \\source.zig:2:5: [address] in [function] \\ try bar(); \\ ^ \\source.zig:14:5: [address] in [function] \\ try foo(); \\ ^ \\ , }, .ReleaseFast = .{ .expect = \\error: TheSkyIsFalling \\ , }, .ReleaseSmall = .{ .expect = \\error: TheSkyIsFalling \\ , }, }); cases.addCase(.{ .exclude_os = .{ .openbsd, // integer overflow .windows, // TODO intermittent failures }, .name = "dumpCurrentStackTrace", .source = \\const std = @import("std"); \\ \\fn bar() void { \\ std.debug.dumpCurrentStackTrace(@returnAddress()); \\} \\fn foo() void { \\ bar(); \\} \\pub fn main() u8 { \\ foo(); \\ return 1; \\} , .Debug = .{ .expect = \\source.zig:7:8: [address] in foo (test) \\ bar(); \\ ^ \\source.zig:10:8: [address] in main (test) \\ foo(); \\ ^ \\ , }, }); }