Skip to content

Commit 5cbe978

Browse files
committed
Sort unused variable warnings
1 parent eb72820 commit 5cbe978

File tree

18 files changed

+455
-353
lines changed

18 files changed

+455
-353
lines changed

src/canonicalize/Mod.zig

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6228,6 +6228,16 @@ fn checkUsedUnderscoreVariable(
62286228
}
62296229

62306230
fn checkScopeForUnusedVariables(self: *Self, scope: *const Scope) std.mem.Allocator.Error!void {
6231+
// Define the type for unused variables
6232+
const UnusedVar = struct {
6233+
ident: base.Ident.Idx,
6234+
region: Region
6235+
};
6236+
6237+
// Collect all unused variables first so we can sort them
6238+
var unused_vars = std.ArrayList(UnusedVar).init(self.env.gpa);
6239+
defer unused_vars.deinit();
6240+
62316241
// Iterate through all identifiers in this scope
62326242
var iterator = scope.idents.iterator();
62336243
while (iterator.next()) |entry| {
@@ -6247,10 +6257,26 @@ fn checkScopeForUnusedVariables(self: *Self, scope: *const Scope) std.mem.Alloca
62476257
// Get the region for this pattern to provide good error location
62486258
const region = self.env.store.getPatternRegion(pattern_idx);
62496259

6250-
// Report unused variable
6251-
try self.env.pushDiagnostic(Diagnostic{ .unused_variable = .{
6260+
// Collect unused variable for sorting
6261+
try unused_vars.append(.{
62526262
.ident = ident_idx,
62536263
.region = region,
6264+
});
6265+
}
6266+
6267+
// Sort unused variables by region (earlier in file first)
6268+
std.mem.sort(UnusedVar, unused_vars.items, {}, struct {
6269+
fn lessThan(_: void, a: UnusedVar, b: UnusedVar) bool {
6270+
// Compare by start offset (position in file)
6271+
return a.region.start.offset < b.region.start.offset;
6272+
}
6273+
}.lessThan);
6274+
6275+
// Report unused variables in sorted order
6276+
for (unused_vars.items) |unused| {
6277+
try self.env.pushDiagnostic(Diagnostic{ .unused_variable = .{
6278+
.ident = unused.ident,
6279+
.region = unused.region,
62546280
} });
62556281
}
62566282
}
@@ -8679,3 +8705,79 @@ test "hex literal parsing logic integration" {
86798705
try std.testing.expectEqual(tc.expected_value, u128_val);
86808706
}
86818707
}
8708+
8709+
test "unused variables are sorted by region" {
8710+
const gpa = std.testing.allocator;
8711+
8712+
// Create a test program with unused variables in non-alphabetical order
8713+
const source =
8714+
\\app [main!] { pf: platform "../basic-cli/main.roc" }
8715+
\\
8716+
\\func = |_| {
8717+
\\ zebra = 5 # Line 3 - should be reported first
8718+
\\ apple = 10 # Line 4 - should be reported second
8719+
\\ monkey = 15 # Line 5 - should be reported third
8720+
\\ used = 20 # Line 6 - this one is used
8721+
\\ used
8722+
\\}
8723+
\\
8724+
\\main! = |_| func({})
8725+
;
8726+
8727+
var ctx = try ScopeTestContext.init(gpa);
8728+
defer ctx.deinit();
8729+
8730+
// Parse the source
8731+
const ast = try parse.AST.parseFromStr(gpa, source, "test.roc", &ctx.module_env.string_interner);
8732+
defer ast.deinit();
8733+
8734+
// Canonicalize the AST
8735+
const parsed_module = ast.parsed_module;
8736+
var self = try Self.initFromAST(parsed_module, &ctx.module_env, source);
8737+
try self.canonicalizeModule();
8738+
defer self.deinit();
8739+
8740+
// Check that we have unused variable diagnostics
8741+
var unused_var_diagnostics = std.ArrayList(struct {
8742+
ident: base.Ident.Idx,
8743+
region: Region,
8744+
}).init(gpa);
8745+
defer unused_var_diagnostics.deinit();
8746+
8747+
// Collect all unused variable diagnostics
8748+
for (ctx.module_env.diagnostics.items) |diagnostic| {
8749+
switch (diagnostic) {
8750+
.unused_variable => |data| {
8751+
try unused_var_diagnostics.append(.{
8752+
.ident = data.ident,
8753+
.region = data.region,
8754+
});
8755+
},
8756+
else => continue,
8757+
}
8758+
}
8759+
8760+
// We should have exactly 3 unused variables (zebra, apple, monkey)
8761+
try std.testing.expectEqual(@as(usize, 3), unused_var_diagnostics.items.len);
8762+
8763+
// Check that they are sorted by region (line number)
8764+
// The source positions should be in increasing order
8765+
var prev_offset: u32 = 0;
8766+
for (unused_var_diagnostics.items) |diagnostic| {
8767+
const current_offset = diagnostic.region.start.offset;
8768+
8769+
// Each unused variable should appear after the previous one in the source
8770+
try std.testing.expect(current_offset > prev_offset);
8771+
prev_offset = current_offset;
8772+
8773+
// Also verify the names are in the expected order (zebra, apple, monkey)
8774+
const ident_text = ctx.module_env.idents.getText(diagnostic.ident);
8775+
if (unused_var_diagnostics.items[0].ident.idx == diagnostic.ident.idx) {
8776+
try std.testing.expectEqualStrings("zebra", ident_text);
8777+
} else if (unused_var_diagnostics.items[1].ident.idx == diagnostic.ident.idx) {
8778+
try std.testing.expectEqualStrings("apple", ident_text);
8779+
} else if (unused_var_diagnostics.items[2].ident.idx == diagnostic.ident.idx) {
8780+
try std.testing.expectEqualStrings("monkey", ident_text);
8781+
}
8782+
}
8783+
}

src/snapshots/crash_and_ellipsis_test.md

Lines changed: 13 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/snapshots/expr/tuple_comprehensive.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ type=expr
2727
~~~
2828
# EXPECTED
2929
EMPTY TUPLE NOT ALLOWED - tuple_comprehensive.md:9:10:9:12
30+
UNUSED VARIABLE - tuple_comprehensive.md:10:2:10:8
3031
UNUSED VARIABLE - tuple_comprehensive.md:11:2:11:6
3132
UNUSED VARIABLE - tuple_comprehensive.md:12:2:12:8
32-
UNUSED VARIABLE - tuple_comprehensive.md:10:2:10:8
3333
UNUSED VARIABLE - tuple_comprehensive.md:13:2:13:8
34+
UNUSED VARIABLE - tuple_comprehensive.md:14:2:14:7
3435
UNUSED VARIABLE - tuple_comprehensive.md:15:2:15:11
3536
UNUSED VARIABLE - tuple_comprehensive.md:16:2:16:13
36-
UNUSED VARIABLE - tuple_comprehensive.md:14:2:14:7
3737
# PROBLEMS
3838
**EMPTY TUPLE NOT ALLOWED**
3939
I am part way through parsing this tuple, but it is empty:
@@ -45,6 +45,18 @@ I am part way through parsing this tuple, but it is empty:
4545

4646
If you want to represent nothing, try using an empty record: `{}`.
4747

48+
**UNUSED VARIABLE**
49+
Variable `single` is not used anywhere in your code.
50+
51+
If you don't need this variable, prefix it with an underscore like `_single` to suppress this warning.
52+
The unused variable is declared here:
53+
**tuple_comprehensive.md:10:2:10:8:**
54+
```roc
55+
single = (42)
56+
```
57+
^^^^^^
58+
59+
4860
**UNUSED VARIABLE**
4961
Variable `pair` is not used anywhere in your code.
5062

@@ -70,27 +82,27 @@ The unused variable is declared here:
7082

7183

7284
**UNUSED VARIABLE**
73-
Variable `single` is not used anywhere in your code.
85+
Variable `nested` is not used anywhere in your code.
7486

75-
If you don't need this variable, prefix it with an underscore like `_single` to suppress this warning.
87+
If you don't need this variable, prefix it with an underscore like `_nested` to suppress this warning.
7688
The unused variable is declared here:
77-
**tuple_comprehensive.md:10:2:10:8:**
89+
**tuple_comprehensive.md:13:2:13:8:**
7890
```roc
79-
single = (42)
91+
nested = ((1, 2), (3, 4))
8092
```
8193
^^^^^^
8294

8395

8496
**UNUSED VARIABLE**
85-
Variable `nested` is not used anywhere in your code.
97+
Variable `mixed` is not used anywhere in your code.
8698

87-
If you don't need this variable, prefix it with an underscore like `_nested` to suppress this warning.
99+
If you don't need this variable, prefix it with an underscore like `_mixed` to suppress this warning.
88100
The unused variable is declared here:
89-
**tuple_comprehensive.md:13:2:13:8:**
101+
**tuple_comprehensive.md:14:2:14:7:**
90102
```roc
91-
nested = ((1, 2), (3, 4))
103+
mixed = (add_one(5), "world", [1, 2, 3])
92104
```
93-
^^^^^^
105+
^^^^^
94106

95107

96108
**UNUSED VARIABLE**
@@ -117,18 +129,6 @@ The unused variable is declared here:
117129
^^^^^^^^^^^
118130

119131

120-
**UNUSED VARIABLE**
121-
Variable `mixed` is not used anywhere in your code.
122-
123-
If you don't need this variable, prefix it with an underscore like `_mixed` to suppress this warning.
124-
The unused variable is declared here:
125-
**tuple_comprehensive.md:14:2:14:7:**
126-
```roc
127-
mixed = (add_one(5), "world", [1, 2, 3])
128-
```
129-
^^^^^
130-
131-
132132
# TOKENS
133133
~~~zig
134134
OpenCurly(1:1-1:2),

0 commit comments

Comments
 (0)