Skip to content

Commit 5bbc4b8

Browse files
authored
fix(linter/homeless-try): allow try in comptime blocks (#180)
Allow `try` in non-root `comptime` scopes. ```zig const std = @import("std"); fn foo(x: u32) void { comptime { try bar(x); } } fn bar(x: u32) !void { if (x == 0) { return error.Unreachable; } } test { foo(1); } ```
1 parent 94dabb8 commit 5bbc4b8

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed

docs/rules/homeless-try.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,35 @@ fn foo() !void {
3838
var my_str = try std.heap.page_allocator.alloc(u8, 8);
3939
}
4040
```
41+
42+
Zig allows `try` in comptime scopes in or nested within functions. This rule
43+
does not flag these cases.
44+
45+
```zig
46+
const std = @import("std");
47+
fn foo(x: u32) void {
48+
comptime {
49+
// valid
50+
try bar(x);
51+
}
52+
}
53+
fn bar(x: u32) !void {
54+
return if (x == 0) error.Unreachable else void;
55+
}
56+
```
57+
58+
Zig also allows `try` on functions whose error union sets are empty. ZLint
59+
does _not_ respect this case. Please refactor such functions to not return
60+
an error union.
61+
62+
```zig
63+
const std = @import("std");
64+
fn foo() !u32 {
65+
// compiles, but treated as a violation. `bar` should return `u32`.
66+
const x = try bar();
67+
return x + 1;
68+
}
69+
fn bar() u32 {
70+
return 1;
71+
}
72+
```

src/linter/rules/homeless_try.zig

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,36 @@
2929
//! var my_str = try std.heap.page_allocator.alloc(u8, 8);
3030
//! }
3131
//! ```
32+
//!
33+
//! Zig allows `try` in comptime scopes in or nested within functions. This rule
34+
//! does not flag these cases.
35+
//! ```zig
36+
//! const std = @import("std");
37+
//! fn foo(x: u32) void {
38+
//! comptime {
39+
//! // valid
40+
//! try bar(x);
41+
//! }
42+
//! }
43+
//! fn bar(x: u32) !void {
44+
//! return if (x == 0) error.Unreachable else void;
45+
//! }
46+
//! ```
47+
//!
48+
//! Zig also allows `try` on functions whose error union sets are empty. ZLint
49+
//! does _not_ respect this case. Please refactor such functions to not return
50+
//! an error union.
51+
//! ```zig
52+
//! const std = @import("std");
53+
//! fn foo() !u32 {
54+
//! // compiles, but treated as a violation. `bar` should return `u32`.
55+
//! const x = try bar();
56+
//! return x + 1;
57+
//! }
58+
//! fn bar() u32 {
59+
//! return 1;
60+
//! }
61+
//! ```
3262

3363
const std = @import("std");
3464
const util = @import("util");
@@ -68,6 +98,9 @@ pub fn runOnNode(_: *const HomelessTry, wrapper: NodeWrapper, ctx: *LinterContex
6898

6999
while (it.next()) |scope| {
70100
const flags = scope_flags[scope.int()];
101+
// `try` is allowed in non-error returning comptime code; it causes
102+
// a compilation error.
103+
if (flags.s_comptime) return;
71104
const is_function_sig = flags.s_function and !flags.s_block;
72105
// functions create two scopes: one for the signature (binds params,
73106
// return type references symbols here) and one for the function body.
@@ -213,6 +246,23 @@ test HomelessTry {
213246
\\ return try std.fmt.parseInt(u32, this.i, 0);
214247
\\}
215248
,
249+
// comptime code
250+
\\const std = @import("std");
251+
\\fn foo(x: u32) void {
252+
\\ comptime {
253+
\\ try bar(x);
254+
\\ }
255+
\\}
256+
,
257+
\\const std = @import("std");
258+
\\fn foo(x: bool) void {
259+
\\ comptime {
260+
\\ if (x) {
261+
\\ try bar(x);
262+
\\ }
263+
\\ }
264+
\\}
265+
,
216266
};
217267

218268
const fail = &[_][:0]const u8{

0 commit comments

Comments
 (0)