Skip to content

Commit 7a80337

Browse files
authored
Merge pull request #8156 from roc-lang/sort-problems
Sort unused variable warnings
2 parents eb72820 + cef3366 commit 7a80337

File tree

18 files changed

+452
-353
lines changed

18 files changed

+452
-353
lines changed

src/canonicalize/Mod.zig

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6228,6 +6228,13 @@ 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 { ident: base.Ident.Idx, region: Region };
6233+
6234+
// Collect all unused variables first so we can sort them
6235+
var unused_vars = std.ArrayList(UnusedVar).init(self.env.gpa);
6236+
defer unused_vars.deinit();
6237+
62316238
// Iterate through all identifiers in this scope
62326239
var iterator = scope.idents.iterator();
62336240
while (iterator.next()) |entry| {
@@ -6247,10 +6254,26 @@ fn checkScopeForUnusedVariables(self: *Self, scope: *const Scope) std.mem.Alloca
62476254
// Get the region for this pattern to provide good error location
62486255
const region = self.env.store.getPatternRegion(pattern_idx);
62496256

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

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)