792 lines
12 KiB
Markdown
792 lines
12 KiB
Markdown
# Nix Statix and Deadnix Rules
|
|
|
|
## Purpose
|
|
|
|
This note rewrites the current `statix` and `deadnix` findings as possible `nix-style` rules.
|
|
Use the `Decision` line on each rule for `keep`, `discard`, `update`, or `need-more-info`.
|
|
|
|
## Source Baseline
|
|
|
|
- `statix` rule names come from `nix run --inputs-from . nixpkgs#statix -- list`.
|
|
- `deadnix` checks come from `nix run --inputs-from . nixpkgs#deadnix -- --help`.
|
|
- `deadnix` reports categories instead of named rules.
|
|
- At collection time, `src/dotfiles/apps.nix` ran `statix fix .` and `deadnix --edit` in `nix run .#fix`.
|
|
- At collection time, `statix.toml` disabled `repeated_keys`.
|
|
|
|
## Statix Rules
|
|
|
|
### `bool_comparison`
|
|
|
|
Decision: keep
|
|
|
|
Source: `statix` `W01`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Do not compare a boolean expression to `true` or `false`.
|
|
Use the expression directly for true checks.
|
|
Use `!` for false checks.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
x == true
|
|
x == false
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
x
|
|
!x
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
Boolean literal comparisons add noise without changing behavior.
|
|
Direct predicates make conditions easier to scan.
|
|
|
|
### `empty_let_in`
|
|
|
|
Decision: keep
|
|
|
|
Source: `statix` `W02`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes when the `let` body has no comments.
|
|
|
|
#### Rule
|
|
|
|
Do not keep a `let` expression that declares no bindings.
|
|
Replace it with its body.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
let in value
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
value
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
An empty `let` adds syntax without naming a dependency.
|
|
It usually remains after editing or debugging.
|
|
|
|
### `manual_inherit`
|
|
|
|
Decision: Keep
|
|
|
|
Source: `statix` `W03`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Use `inherit` when an attribute assignment repeats the same name as its value.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
{
|
|
package = package;
|
|
}
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
{
|
|
inherit package;
|
|
}
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
`inherit` states that the attribute comes from the current scope.
|
|
It removes duplicated names.
|
|
|
|
### `manual_inherit_from`
|
|
|
|
Decision:
|
|
|
|
Source: `statix` `W04`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Use `inherit (source)` when an assignment selects an attribute with the same name.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
package = pkgs.package;
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
inherit (pkgs) package;
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
`inherit (source)` states that the value comes from an attribute set.
|
|
It avoids repeated selector names.
|
|
|
|
### `legacy_let_syntax`
|
|
|
|
Decision: keep
|
|
|
|
Source: `statix` `W05`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Do not use legacy `let { ... }` syntax.
|
|
Use documented Nix expressions.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
let {
|
|
body = x + y;
|
|
x = 2;
|
|
y = 3;
|
|
}
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
rec {
|
|
body = x + y;
|
|
x = 2;
|
|
y = 3;
|
|
}.body
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
Legacy `let` syntax is undocumented.
|
|
Documented forms are clearer to tools and readers.
|
|
|
|
### `collapsible_let_in`
|
|
|
|
Decision: keep
|
|
|
|
Source: `statix` `W06`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Collapse a `let` expression when its body is another `let` expression.
|
|
Put both binding groups in one `let` when scope permits it.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
let
|
|
a = 1;
|
|
in
|
|
let
|
|
b = 2;
|
|
in
|
|
a + b
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
let
|
|
a = 1;
|
|
b = 2;
|
|
in
|
|
a + b
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
Nested `let` expressions add structure when they do not add a scope boundary.
|
|
A single binding group shows the local dependencies together.
|
|
|
|
### `eta_reduction`
|
|
|
|
Decision: keep
|
|
|
|
Source: `statix` `W07`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Remove a lambda that only passes its argument to another function.
|
|
Keep the target function name when that preserves behavior.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
map (x: double x) values
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
map double values
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
The wrapper lambda adds no behavior.
|
|
The direct function reference exposes the operation.
|
|
|
|
### `useless_parens`
|
|
|
|
Decision: keep
|
|
|
|
Source: `statix` `W08`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Remove parentheses around expressions when they do not affect parsing.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
let
|
|
double = (x: x * 2);
|
|
in
|
|
(value)
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
let
|
|
double = x: x * 2;
|
|
in
|
|
value
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
Redundant parentheses make expressions harder to scan.
|
|
Required precedence parentheses remain valid.
|
|
|
|
### `empty_pattern`
|
|
|
|
Decision: keep
|
|
|
|
Source: `statix` `W10`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Use `_` instead of an empty variadic function pattern when the argument is ignored.
|
|
Do not apply this rule to NixOS-style modules that declare `imports`.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
{ ... }: value
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
_: value
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
`_` directly marks an ignored argument.
|
|
An empty variadic pattern looks like partial destructuring.
|
|
|
|
### `redundant_pattern_bind`
|
|
|
|
Decision: keep
|
|
|
|
Source: `statix` `W11`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Do not bind a variadic pattern when the pattern captures no fields.
|
|
Use a single argument name instead.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
args @ { ... }: args.nixpkgs
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
args: args.nixpkgs
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
The empty pattern adds no destructuring value.
|
|
A named argument shows that the whole argument is used.
|
|
|
|
### `unquoted_uri`
|
|
|
|
Decision: keep
|
|
|
|
Source: `statix` `W12`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Quote URI literals.
|
|
Do not use Nix's special unquoted URI syntax.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
url = github:owner/repository;
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
url = "github:owner/repository";
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
Quoted strings work for all URI forms.
|
|
They avoid ambiguity with punctuation after the URI.
|
|
|
|
### `empty_inherit`
|
|
|
|
Decision: keep
|
|
|
|
Source: `statix` `W14`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Remove `inherit` statements that inherit no names.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
inherit;
|
|
```
|
|
|
|
Use no replacement.
|
|
|
|
#### Rationale
|
|
|
|
An empty `inherit` has no effect.
|
|
It usually remains after a refactor.
|
|
|
|
### `deprecated_to_path`
|
|
|
|
Decision: keep
|
|
|
|
Source: `statix` `W17`.
|
|
Status at collection time: enabled.
|
|
Autofix: no.
|
|
|
|
#### Rule
|
|
|
|
Do not use `builtins.toPath` or unqualified `toPath`.
|
|
Use explicit path construction instead.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
builtins.toPath "/path"
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
/. + "/path"
|
|
./. + "/relative-path"
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
`toPath` is deprecated.
|
|
Explicit path construction states whether the result is absolute or relative.
|
|
|
|
### `bool_simplification`
|
|
|
|
Decision: keep
|
|
|
|
Source: `statix` `W18`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Use `!=` instead of negating an equality comparison.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
!(x == y)
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
x != y
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
The direct operator expresses inequality without nested boolean syntax.
|
|
|
|
### `useless_has_attr`
|
|
|
|
Decision: keep
|
|
|
|
Source: `statix` `W19`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Use `or` when an `if` expression only checks for an attribute before selecting it.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
if x ? a then x.a else default
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
x.a or default
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
The `or` form is the Nix operator for attribute defaults.
|
|
It avoids repeating the attribute path.
|
|
|
|
### `repeated_keys`
|
|
|
|
Decision: update so that it doesn't conflict with the custom module convention.
|
|
|
|
Source: `statix` `W20`.
|
|
Status at collection time: disabled in `statix.toml`.
|
|
Autofix: no.
|
|
|
|
#### Rule
|
|
|
|
Group repeated attribute path prefixes in a nested attribute set.
|
|
Apply this rule only when at least three sibling assignments share the same first key.
|
|
Do not apply it inside `rec` attribute sets.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
{
|
|
services.a = 1;
|
|
services.b = 2;
|
|
services.c = 3;
|
|
}
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
{
|
|
services = {
|
|
a = 1;
|
|
b = 2;
|
|
c = 3;
|
|
};
|
|
}
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
Grouping repeated prefixes reduces visual repetition.
|
|
The local config disables this rule, so it needs an explicit decision before promotion.
|
|
|
|
### `empty_list_concat`
|
|
|
|
Decision: keep
|
|
|
|
Source: `statix` `W23`.
|
|
Status at collection time: enabled.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Remove list concatenation with an empty list.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
[] ++ values
|
|
values ++ []
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
values
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
Concatenating `[]` changes no values.
|
|
It usually remains after conditional list construction changed.
|
|
|
|
## Deadnix Checks
|
|
|
|
### Unused `let` bindings
|
|
|
|
Decision: keep
|
|
|
|
Source: `deadnix` default check.
|
|
Status at collection time: active in `nix run .#fix`.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Remove `let` bindings when no live expression uses their names.
|
|
Do not keep a binding only because another dead binding uses it.
|
|
Use `# deadnix: skip` only for intentional unused declarations.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
let
|
|
unused = 1;
|
|
in
|
|
value
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
value
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
Unused bindings hide the dependencies that matter.
|
|
They also make refactors look unfinished.
|
|
|
|
### Unused `inherit` names
|
|
|
|
Decision: keep
|
|
|
|
Source: `deadnix` default check.
|
|
Status at collection time: active in `nix run .#fix`.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Remove names from `inherit` statements when no live expression uses those names.
|
|
Remove the whole `inherit` statement when it becomes empty.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
let
|
|
inherit (builtins) unused;
|
|
in
|
|
value
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
value
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
Unused inherited names create the same noise as unused `let` bindings.
|
|
An empty result also overlaps with `statix` `empty_inherit`.
|
|
|
|
### Unused lambda arguments
|
|
|
|
Decision: keep
|
|
|
|
Source: `deadnix` default check.
|
|
Status at collection time: active in `nix run .#fix`.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Mark an unused single lambda argument with `_`.
|
|
Do not keep a meaningful name for an ignored argument.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
arg: value
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
_: value
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
`_` states that the argument is intentionally ignored.
|
|
A named unused argument implies missing logic.
|
|
|
|
### Unused lambda pattern names
|
|
|
|
Decision: keep
|
|
|
|
Source: `deadnix` default check.
|
|
Status at collection time: active in `nix run .#fix`.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Remove unused names from lambda attribute patterns.
|
|
Keep only names used by the body or defaults.
|
|
Use `...` when callers can pass extra attributes.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
{ used, unused, ... }: used
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
{ used, ... }: used
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
Pattern names usually declare dependencies.
|
|
Unused names overstate the function's inputs.
|
|
|
|
### Unused lambda pattern aliases
|
|
|
|
Decision: keep
|
|
|
|
Source: `deadnix` default check.
|
|
Status at collection time: active in `nix run .#fix`.
|
|
Autofix: yes.
|
|
|
|
#### Rule
|
|
|
|
Remove a lambda pattern alias when the body does not use the alias.
|
|
Keep the alias only when code reads the whole argument set.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
args @ { used, ... }: used
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
{ used, ... }: used
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
The alias says the function needs the complete input set.
|
|
Keeping it unused makes the function contract look wider than it is.
|
|
|
|
### Ignored underscore bindings
|
|
|
|
Decision: keep
|
|
|
|
Source: `deadnix` `--no-underscore` option.
|
|
Status at collection time: inactive; current fixer does not pass `--no-underscore`.
|
|
Autofix: no separate rule.
|
|
|
|
#### Rule
|
|
|
|
Treat bindings whose names start with `_` as intentionally ignored.
|
|
Optionally skip unused diagnostics for those names.
|
|
|
|
Avoid, when the skip policy is active:
|
|
|
|
```nix
|
|
let
|
|
_unused = expensiveValue;
|
|
in
|
|
value
|
|
```
|
|
|
|
Use only for intentional placeholders.
|
|
|
|
#### Rationale
|
|
|
|
An underscore prefix is a common marker for intentionally unused names.
|
|
Skipping all underscore bindings can hide real dead code.
|
|
|
|
### Used underscore bindings
|
|
|
|
Decision: keep
|
|
|
|
Source: `deadnix` `--warn-used-underscore` option.
|
|
Status at collection time: inactive; current fixer does not pass `--warn-used-underscore`.
|
|
Autofix: no.
|
|
|
|
#### Rule
|
|
|
|
Do not use a binding whose name starts with `_`.
|
|
Rename it to a normal name when code references it.
|
|
|
|
Avoid:
|
|
|
|
```nix
|
|
let
|
|
_package = pkgs.hello;
|
|
in
|
|
_package
|
|
```
|
|
|
|
Use:
|
|
|
|
```nix
|
|
let
|
|
package = pkgs.hello;
|
|
in
|
|
package
|
|
```
|
|
|
|
#### Rationale
|
|
|
|
The underscore prefix communicates that the name is intentionally unused.
|
|
Using it contradicts that signal.
|
|
|
|
## Notes for Decisions
|
|
|
|
- `statix` rules are mostly syntax rewrites.
|
|
- `deadnix` rules need scope analysis and dead-code reachability.
|
|
- `deadnix` does not report `rec` attribute-set bindings as removable findings.
|
|
- `statix` `repeated_keys` is present in the installed rule list but disabled locally.
|
|
- Rules that change module argument patterns need extra care around NixOS modules and `callPackage` functions.
|