Add the Xatu Rust package as a minimal hello-world skeleton with Cargo workspace metadata and Bazel library, binary, and test targets. Include the Statix and Deadnix evaluation notes so the new linter work starts with the prior rule and architecture research.
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.
|