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.
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.