Drop the statix and deadnix packages from the fix app now that the repository has a local Nix style linter path. Remove the obsolete statix configuration, document the evaluated rule sets and architectures, and keep vendored third-party files out of treefmt.
12 KiB
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
statixrule names come fromnix run --inputs-from . nixpkgs#statix -- list.deadnixchecks come fromnix run --inputs-from . nixpkgs#deadnix -- --help.deadnixreports categories instead of named rules.- At collection time,
src/dotfiles/apps.nixranstatix fix .anddeadnix --editinnix run .#fix. - At collection time,
statix.tomldisabledrepeated_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:
x == true
x == false
Use:
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:
let in value
Use:
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:
{
package = package;
}
Use:
{
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:
package = pkgs.package;
Use:
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:
let {
body = x + y;
x = 2;
y = 3;
}
Use:
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:
let
a = 1;
in
let
b = 2;
in
a + b
Use:
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:
map (x: double x) values
Use:
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:
let
double = (x: x * 2);
in
(value)
Use:
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:
{ ... }: value
Use:
_: 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:
args @ { ... }: args.nixpkgs
Use:
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:
url = github:owner/repository;
Use:
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:
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:
builtins.toPath "/path"
Use:
/. + "/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:
!(x == y)
Use:
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:
if x ? a then x.a else default
Use:
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:
{
services.a = 1;
services.b = 2;
services.c = 3;
}
Use:
{
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:
[] ++ values
values ++ []
Use:
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:
let
unused = 1;
in
value
Use:
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:
let
inherit (builtins) unused;
in
value
Use:
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:
arg: value
Use:
_: 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:
{ used, unused, ... }: used
Use:
{ 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:
args @ { used, ... }: used
Use:
{ 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:
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:
let
_package = pkgs.hello;
in
_package
Use:
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
statixrules are mostly syntax rewrites.deadnixrules need scope analysis and dead-code reachability.deadnixdoes not reportrecattribute-set bindings as removable findings.statixrepeated_keysis present in the installed rule list but disabled locally.- Rules that change module argument patterns need extra care around NixOS modules and
callPackagefunctions.