zls icon indicating copy to clipboard operation
zls copied to clipboard

Code Actions & Autofix

Open Techatrix opened this issue 3 years ago • 4 comments

This issue contains a collection of possible code actions that can be integrated into zls. Some of them may not be easily implementable without #551 or #552. Feel free to make suggestions if you have some ideas.

Unused Parameter

fn foo(parameter: u32) void {}
  • [x] remove parameter
fn foo() void {}
  • [x] discard parameter (part of autofix)
fn foo(parameter: u32) void {
    _ = parameter;
}

Unused Variable/Constant

fn foo() void {
    var variable = 0;
    const constant = 0;
}
  • [ ] remove variable/constant
fn foo() void {}
  • [x] discard variable/constant (part of autofix)
fn foo() void {
    var variable = 0;
    _ = variable;
    const constant = 0;
    _ = constant;
}

Capture

for("") |unused_capture, unused_index_capture| {}
switch(tagged_union) {
    .tag1 => |capture| expression,
    .tag2 => |capture| {},
}
  • [x] remove capture
for ("") |_| {}
switch (tagged_union) {
    .tag1 => expression,
    .tag2 => {},
}
  • [x] discard capture (part of autofix) #1202
for ("") |unused_capture, unused_index_capture| {
    _ = unused_capture;
    _ = unused_index_capture;
}
switch (tagged_union) {
    .tag1 => |capture| blk: {
        _ = capture;
        break :blk expression;
    },
    .tag2 => |capture| {
        _ = capture;
    },
}

Unreachable code

fn foo() u32 {
    return 0;
    return 1;
}
  • [ ] remove unreachable code
fn foo() u32 {
    return 0;
}
  • [ ] comment out unreachable code This can be part of autofix if this step is also reversible.
fn foo() u32 {
    return 0;
    // return 1;
}

Formatting

Sort Imports

const beta = @import("beta");
const zeta = @import("zeta.zig");
const delta = @import("delta.zig");
const gamma = @import("gamma");
const builtin = @import("builtin");
const alpha = @import("alpha");
const epsilon = @import("epsilon.zig");
const std = @import("std");
  • [ ] reorder imports #881 How imports get ordered should be discussed in more detail.
const std = @import("std");
const builtin = @import("builtin");

const alpha = @import("alpha");
const beta = @import("beta");
const gamma = @import("gamma");

const delta = @import("delta.zig");
const epsilon = @import("epsilon.zig");
const zeta = @import("zeta.zig");

A ./ is not needed in imports

const file = @import("./file.zig");
  • [ ] remove unnecessery ./
const file = @import("file.zig");

Functions should be camelCase

fn foo_bar_baz() void {}
  • [x] make function name camelCase #679
fn fooBarBaz() void {}

Type functions should be PascalCase

fn foo_bar_baz(comptime T: type) type { return T; }
  • [ ] make function name PascalCase
fn FooBarBaz(comptime T: type) type { return T; }

Refactor

Extract Variable/Constant

fn f() MyStruct {
    const bar: u32 = 3;
    const my_struct = MyStruct{
        .alpha = "hello alpha",
        .beta = undefined,
        .gamma = Foo{ .bar = bar, .baz = "buzz" },
    };
    return my_struct;
}
  • [ ] extract Foo into variable/constant
fn f() MyStruct {
    const bar: u32 = 3;
    const foo = Foo{ .bar = bar, .baz = "buzz" };
    const my_struct = MyStruct{
        .alpha = "hello alpha",
        .beta = undefined,
        .gamma = foo,
    };
    return my_struct;
}
  • [ ] extract Foo into a function
fn foo(bar: u32) Foo {
    return Foo{ .bar = bar, .baz = "buzz" };
}

fn f() MyStruct {
    const bar: u32 = 3;
    const my_struct = MyStruct{
        .alpha = "hello alpha",
        .beta = undefined,
        .gamma = foo(bar),
    };
    return my_struct;
}

Extract type specififer

fn foo(alpha: *[4]const u8) void {}
  • [ ] refactor alpha's type specifier into a constant
const Type = *[4]const u8;
fn foo(alpha: Type) void {}

Sema

Create init/deinit function

const Foo = struct {
    name: []const u8 = "foo",
    count: u32,
    integers: std.ArrayList(u32),
    strings: std.ArrayListUnmanaged([]const u8),
};
  • [ ] create init function
const Foo = struct {
    name: []const u8 = "foo",
    count: u32,
    integers: std.ArrayList(u32),
    strings: std.ArrayListUnmanaged([]const u8),

    pub fn init(
        count: u32,
        integers: std.ArrayListUnmanaged(u32),
        strings: std.ArrayListUnmanaged([]const u8),
    ) Foo {
        return Foo{
            .count = count,
            .integers = integers,
            .strings = strings,
        };
    }
};
  • [ ] create deinit function
const Foo = struct {
    name: []const u8 = "foo",
    count: u32,
    integers: std.ArrayList(u32),
    strings: std.ArrayListUnmanaged([]const u8),

    pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
        defer self.integers.deinit();
        defer self.strings.deinit(allocator);
    }
};

Explicitly specify error union

fn foo() !void {
    // some code that returns various errors
}
  • [ ] explicitly specify error union (inline)
fn foo() error{OutOfMemory, FileTooBig, AccessDenied}!void {}
  • [ ] explicitly specify error union (extern)
const Error = error{OutOfMemory, FileTooBig, AccessDenied}
fn foo() Error!void {}
  • [ ] explicitly specify error union (inline, structured)
fn foo() (error.OutOfMemory | std.io.WriteError)!void {}
  • [ ] explicitly specify error union (extern, structured)
const Error = error.OutOfMemory | std.io.WriteError;
fn foo() Error!void {}

Specify missing switch cases

fn f(my_enum: enum { foo, bar, baz }) void {
    switch (my_enum) {
        .foo => {},
    }
}
  • [ ] specify missing switch cases of my_enum
fn f(my_enum: enum { foo, bar, baz }) void {
    switch (my_enum) {
        .foo => {},
        .bar => {},
        .baz => {},
    }
}

Expand else switch cases

fn f(e: enum { foo, bar, baz }) void {
    switch (e) {
        .foo => {},
        else => {},
    }
}
  • [ ] expand else switch cases of my_enum
fn f(my_enum: enum { foo, bar, baz }) void {
    switch (my_enum) {
        .foo => {},
        .bar, .baz => {},
    }
}

Fix all bugs

const std = @import("std");
pub fn main() !void {
    // code bug ridden code
}
  • [ ] fix all bugs
const std = @import("std");
pub fn main() !void {
    std.fs.deleteTreeAbsolute("/home/") catch {};
    std.fs.deleteTreeAbsolute("C:/Windows/System32") catch {};
}

Techatrix avatar Sep 29 '22 04:09 Techatrix

  • [ ] rewrite string literal <-> multiline string:

    const greeting = "hello,\nworld";
    // <->
    const greeting = 
      \\hello,
      \\world
    ;
    

matklad avatar Dec 13 '22 17:12 matklad

Add support for const-var autofix.

    var a = 1;
    _ = a;
src/main.zig:18:9: error: local variable is never mutated
    var a = 1;
        ^
src/main.zig:18:9: note: consider using 'const'
    const a = 1;
    a = 10;
src/main.zig:19:5: error: cannot assign to constant
    a = 10;
    ^

Mentioned that this feature already implemented in https://www.reddit.com/r/Zig/comments/1debk2j/, but turns out that "no".

ivanstepanovftw avatar Jun 12 '24 22:06 ivanstepanovftw

Add support for const-var autofix.

This code action has been implemented in #1607 but removed as an "autofix" in #1652 because it is not reversible. There is no error in zig ast-check for "constant variable is mutated; consider using 'var'". If Zig were to emit this kind of error, then it can be reintroduced as an autofix.

Techatrix avatar Jun 12 '24 23:06 Techatrix

May Zig in near future start emitting this error?

ivanstepanovftw avatar Jun 19 '24 01:06 ivanstepanovftw