From 843a659bf84785816b20e7b062679da4d7d9d61b Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Fri, 17 Nov 2023 00:44:39 +0100 Subject: [PATCH] add autofix for `local variable is never mutated` error --- src/features/code_actions.zig | 26 ++++++++++++++++++++++++++ tests/lsp_features/code_actions.zig | 20 ++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/features/code_actions.zig b/src/features/code_actions.zig index aebeb25a2f..7b8fa60082 100644 --- a/src/features/code_actions.zig +++ b/src/features/code_actions.zig @@ -43,6 +43,7 @@ pub const Builder = struct { // autofix: comment out code // fix: remove code }, + .var_never_mutated => try handleVariableNeverMutated(builder, actions, loc), } } @@ -289,6 +290,28 @@ fn handlePointlessDiscard(builder: *Builder, actions: *std.ArrayListUnmanaged(ty }); } +fn handleVariableNeverMutated(builder: *Builder, actions: *std.ArrayListUnmanaged(types.CodeAction), loc: offsets.Loc) !void { + const source = builder.handle.tree.source; + + const var_keyword_end = 1 + (std.mem.lastIndexOfNone(u8, source[0..loc.start], &std.ascii.whitespace) orelse return); + + const var_keyword_loc: offsets.Loc = .{ + .start = var_keyword_end -| "var".len, + .end = var_keyword_end, + }; + + if (!std.mem.eql(u8, offsets.locToSlice(source, var_keyword_loc), "var")) return; + + try actions.append(builder.arena, .{ + .title = "use 'const'", + .kind = .@"source.fixAll", + .isPreferred = true, + .edit = try builder.createWorkspaceEdit(&.{ + builder.createTextEditLoc(var_keyword_loc, "const"), + }), + }); +} + fn detectIndentation(source: []const u8) []const u8 { // Essentially I'm looking for the first indentation in the file. var i: usize = 0; @@ -431,6 +454,7 @@ const DiagnosticKind = union(enum) { non_camelcase_fn, undeclared_identifier, unreachable_code, + var_never_mutated, const IdCat = enum { @"function parameter", @@ -464,6 +488,8 @@ const DiagnosticKind = union(enum) { return .non_camelcase_fn; } else if (std.mem.startsWith(u8, msg, "use of undeclared identifier")) { return .undeclared_identifier; + } else if (std.mem.eql(u8, msg, "local variable is never mutated")) { + return .var_never_mutated; } return null; } diff --git a/tests/lsp_features/code_actions.zig b/tests/lsp_features/code_actions.zig index f3d6b02c9b..d0afa5152c 100644 --- a/tests/lsp_features/code_actions.zig +++ b/tests/lsp_features/code_actions.zig @@ -106,7 +106,7 @@ test "code actions - remove pointless discard" { try testAutofix( \\fn foo(a: u32) u32 { \\ _ = a; - \\ var b: ?u32 = a; + \\ const b: ?u32 = a; \\ _ = b; \\ const c = b; \\ _ = c; @@ -119,7 +119,7 @@ test "code actions - remove pointless discard" { \\ , \\fn foo(a: u32) u32 { - \\ var b: ?u32 = a; + \\ const b: ?u32 = a; \\ const c = b; \\ if (c) |d| { \\ return d; @@ -130,6 +130,22 @@ test "code actions - remove pointless discard" { ); } +test "code actions - correct unnecessary uses of var" { + try testAutofix( + \\test { + \\ var foo = 5; + \\ _ = foo; + \\} + \\ + , + \\test { + \\ const foo = 5; + \\ _ = foo; + \\} + \\ + ); +} + /// does not check for correct formatting fn testAutofix(before: []const u8, after: []const u8) !void { var ctx = try Context.init();