chore: update
This commit is contained in:
93
docs/.obsidian/workspace.json
vendored
93
docs/.obsidian/workspace.json
vendored
@@ -4,41 +4,21 @@
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "e581539f950ca1bd",
|
||||
"id": "6d9088d392cb8604",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "0c587e5452c16787",
|
||||
"id": "22a4c1f201a1b00e",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "TODO.md",
|
||||
"file": "00 INBOX/valheim progression.md",
|
||||
"mode": "source",
|
||||
"source": true
|
||||
},
|
||||
"icon": "lucide-file",
|
||||
"title": "TODO"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "72e6501450a46859",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "06bc3d52246b600b",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "00 INBOX/ai/steamcmd servers.md",
|
||||
"mode": "source",
|
||||
"source": true
|
||||
},
|
||||
"icon": "lucide-file",
|
||||
"title": "steamcmd servers"
|
||||
"title": "valheim progression"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -111,7 +91,7 @@
|
||||
"state": {
|
||||
"type": "localgraph",
|
||||
"state": {
|
||||
"file": "TODO.md",
|
||||
"file": "00 INBOX/valheim progression.md",
|
||||
"options": {
|
||||
"collapse-filter": true,
|
||||
"search": "",
|
||||
@@ -134,7 +114,7 @@
|
||||
"repelStrength": 10,
|
||||
"linkStrength": 1,
|
||||
"linkDistance": 250,
|
||||
"scale": 1,
|
||||
"scale": 0.6666666666666666,
|
||||
"close": true
|
||||
}
|
||||
},
|
||||
@@ -251,43 +231,42 @@
|
||||
"table-editor-obsidian:Advanced Tables Toolbar": false
|
||||
}
|
||||
},
|
||||
"active": "0c587e5452c16787",
|
||||
"active": "22a4c1f201a1b00e",
|
||||
"lastOpenFiles": [
|
||||
"00 INBOX/how to setup a steamcmd server.md",
|
||||
"00 INBOX/ai/steamcmd servers.md",
|
||||
"03 wrk/ariadne.md",
|
||||
"00 INBOX/ai/ariadne-cli-skeleton.md",
|
||||
"00 INBOX/ai/ariadne-project-goals.md",
|
||||
"00 INBOX/ai/statix-architecture.md",
|
||||
"00 INBOX/ai/nix-statix-deadnix-rules.md",
|
||||
"00 INBOX/let order name resolution.md~",
|
||||
"00 INBOX/nix name resolution.md",
|
||||
"00 INBOX/let order name resolution.md",
|
||||
"00 INBOX/nix name resolution.md~",
|
||||
"00 INBOX/valheim progression.md",
|
||||
"TODO.md",
|
||||
"00 INBOX/ai/dictionary-cli.md",
|
||||
"00 INBOX/ai/dictionary-skill.md",
|
||||
"03 wrk/falinks.md",
|
||||
"03 wrk/machoke.md",
|
||||
"00 INBOX/home assistant agent integration.md",
|
||||
"02 notes/falinks public surfaces.md",
|
||||
"00 INBOX/ai/deadnix-architecture.md",
|
||||
"TODO.md~",
|
||||
"00 INBOX/ai/xatu-rule-implementation-order.md",
|
||||
"00 INBOX/ai/xatu-architecture-design.md",
|
||||
"00 INBOX/ai/nix-style-linter-plan.md",
|
||||
"00 INBOX/ai/nix-style-guide-agent-customization.md",
|
||||
"00 INBOX/ai/game-guest-readiness-plan.md",
|
||||
"00 INBOX/ai/xatu-node-rule-static-dispatch-plan.md",
|
||||
"00 INBOX/ai/nix-style-linter-pr-1-vendor-rnix.md",
|
||||
"00 INBOX/ai/nix-style-linter-pr-3-let-order.md",
|
||||
"00 INBOX/ai/nix-style-linter-pr-2-scaffold-cli.md",
|
||||
"00 INBOX/ai/homepage-pikachu-setup-plan.md",
|
||||
"00 INBOX/ai/source-driven-remote-deploy-plan.md",
|
||||
"00 INBOX/ai/monorepo-work-report.md",
|
||||
"00 INBOX/ai/clew-ast-exploration.md",
|
||||
"00 INBOX/ai/steamcmd servers.md",
|
||||
"00 INBOX/ai/ariadne-pipeline-stage-boundary.md",
|
||||
"00 INBOX/ai/ariadne-project-goals.md",
|
||||
"02 notes/steamcmd server setup.md",
|
||||
"00 INBOX/how to setup a steamcmd server.md",
|
||||
"00 INBOX/ai/clew-ast-exploration.md~",
|
||||
"03 wrk/machoke.md~",
|
||||
"02 notes/machoke public surface.md",
|
||||
"03 wrk/falinks.md~",
|
||||
"99 meta/templates/checkmark.md",
|
||||
"AGENTS.md",
|
||||
"99 meta/attachments/bases/unlinked mentions.base",
|
||||
"99 meta/templates/note.md",
|
||||
"AGENTS.md.md",
|
||||
"00 INBOX/ai",
|
||||
"03 wrk/ai",
|
||||
"03 wrk/brain.md",
|
||||
"01 sources/second brain setup for doing.md",
|
||||
"00 INBOX/ai/hoopa design rewrite proposal 2.md",
|
||||
"00 INBOX/ai/hoopa decisions.md",
|
||||
"00 INBOX/ai/second brain setup for doing.md",
|
||||
"00 INBOX/ai/hoopa design rewrite proposal.md",
|
||||
"00 INBOX/ai/hoopa design review findings.md",
|
||||
"00 INBOX/ai/hoopa follow up decisions.md",
|
||||
"01 sources/obsidian and ai were made for each other.md",
|
||||
"99 meta/Untitled",
|
||||
"03 wrk",
|
||||
"02 notes",
|
||||
"01 sources",
|
||||
"30 wrk"
|
||||
"99 meta/Untitled"
|
||||
]
|
||||
}
|
||||
@@ -1,791 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,261 +0,0 @@
|
||||
# Statix Architecture
|
||||
|
||||
## Purpose
|
||||
|
||||
This note explains how `statix` works as a Nix linter and fixer.
|
||||
It focuses on architecture, not the value of each rule.
|
||||
|
||||
Use this note when deciding how complete our repository-local Nix linter should become.
|
||||
|
||||
## What Statix Is
|
||||
|
||||
`statix` is a syntax-tree linter for Nix.
|
||||
It parses source text into a Nix AST, walks that tree, and runs rules on matching nodes.
|
||||
|
||||
It does not evaluate Nix.
|
||||
It does not import files.
|
||||
It does not know the final value of an attribute set.
|
||||
It sees source structure, not runtime semantics.
|
||||
|
||||
That design makes `statix` fast and deterministic.
|
||||
It also limits it to rules that the parser can prove from local syntax.
|
||||
|
||||
## Minimal Linter Vocabulary
|
||||
|
||||
- Source text is the bytes in a Nix file.
|
||||
- A parser converts source text into a syntax tree.
|
||||
- A syntax tree records nodes such as lambdas, lists, strings, and operators.
|
||||
- A token is a leaf item such as an identifier, comment, or URI literal.
|
||||
- A rule inspects one syntax element and optionally returns a diagnostic.
|
||||
- A diagnostic reports a span and a message.
|
||||
- A suggestion adds a replacement for that span.
|
||||
- A fixer applies suggestions back to source text.
|
||||
|
||||
## Component Map
|
||||
|
||||
The upstream architecture document names four major components.
|
||||
|
||||
### CLI component
|
||||
|
||||
The CLI owns operator interaction.
|
||||
It parses subcommands and options, loads configuration, discovers files, and prints output.
|
||||
|
||||
It supports these user flows:
|
||||
|
||||
- `check`: report diagnostics.
|
||||
- `fix`: apply all safe suggestions.
|
||||
- `single`: apply one suggestion at a position.
|
||||
- `explain`: print the long explanation for a rule code.
|
||||
- `dump`: print a sample configuration file.
|
||||
- `list`: print all available rules.
|
||||
|
||||
### Library component
|
||||
|
||||
The library owns lint rules and the data structures that rules return.
|
||||
It defines `Report`, `Diagnostic`, `Suggestion`, `Rule`, `Metadata`, `Explain`, and `Lint`.
|
||||
|
||||
Each rule is a small unit.
|
||||
It receives one syntax element and returns zero or one report.
|
||||
|
||||
The library also contains helpers that build replacement syntax.
|
||||
Those helpers parse small replacement fragments back into AST nodes.
|
||||
This makes fixes use valid Nix syntax instead of raw string assembly.
|
||||
|
||||
### Virtual filesystem component
|
||||
|
||||
The virtual filesystem stores file paths and file contents in memory.
|
||||
It assigns each file a small `FileId`.
|
||||
|
||||
The CLI reads files into this store before linting.
|
||||
Diagnostics then carry `FileId` references instead of path strings.
|
||||
|
||||
The VFS supports sequential iteration and parallel iteration.
|
||||
`statix check` uses parallel iteration across files.
|
||||
|
||||
### Macro component
|
||||
|
||||
The macro component reduces rule boilerplate.
|
||||
A `lint` macro generates rule metadata, explanation support, and the static rule object.
|
||||
|
||||
A rule author writes the rule structure, metadata, documentation, and validation logic.
|
||||
The macro wires that rule into the common linter interface.
|
||||
|
||||
## Check Pipeline
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Parse CLI options] --> B[Discover configuration]
|
||||
B --> C[Build ignore set]
|
||||
C --> D[Walk Nix files]
|
||||
D --> E[Read files into VFS]
|
||||
E --> F[Build active lint map]
|
||||
F --> G[Parse each file with rnix]
|
||||
G --> H[Walk syntax tree]
|
||||
H --> I[Run rules matching each syntax kind]
|
||||
I --> J[Collect reports]
|
||||
J --> K[Render diagnostics]
|
||||
K --> L[Exit 1 when reports exist]
|
||||
```
|
||||
|
||||
The key optimization is the active lint map.
|
||||
`statix` groups rules by `SyntaxKind` before it walks a file.
|
||||
During traversal, it looks up only rules that declared interest in the current node kind.
|
||||
|
||||
This avoids running every rule on every node.
|
||||
It also keeps each rule narrow.
|
||||
|
||||
## Rule Registration
|
||||
|
||||
Rules are built into the binary.
|
||||
There is no dynamic rule loading.
|
||||
|
||||
A registry macro imports each rule module and builds a static `LINTS` vector.
|
||||
The CLI filters that vector with configuration.
|
||||
Disabled rule names disappear from the active lint map.
|
||||
|
||||
This model has a simple trust boundary:
|
||||
|
||||
- all rule code ships with the tool;
|
||||
- configuration can disable rules;
|
||||
- configuration cannot define new rules.
|
||||
|
||||
## Rule Execution Model
|
||||
|
||||
Each rule declares the syntax kinds it wants to inspect.
|
||||
For example, one rule can ask for binary-operation nodes.
|
||||
Another rule can ask for URI tokens.
|
||||
|
||||
The traversal visits tree entries with `preorder_with_tokens`.
|
||||
That includes both nodes and tokens.
|
||||
Rules can therefore inspect structured AST nodes and raw tokens.
|
||||
|
||||
A rule returns a `Report` when it finds a problem.
|
||||
The report contains one or more diagnostics.
|
||||
Each diagnostic has a source range and a message.
|
||||
A diagnostic can also include a `Suggestion`.
|
||||
|
||||
Rules do not share state with each other.
|
||||
They also do not run a whole-program analysis.
|
||||
A rule sees the syntax element passed to it and whatever children or parents it chooses to inspect.
|
||||
|
||||
## Parse Errors
|
||||
|
||||
`statix` turns parser errors into reports.
|
||||
Those reports use code `0` and severity `Error`.
|
||||
|
||||
Parse errors flow through the same output machinery as lint findings.
|
||||
This lets the CLI render syntax errors and lint diagnostics with one reporter.
|
||||
|
||||
## Output Model
|
||||
|
||||
`statix` has several output formats.
|
||||
|
||||
The human format uses `ariadne` to render annotated source snippets.
|
||||
The `errfmt` format prints one compact line per diagnostic.
|
||||
The JSON format exists behind a feature flag.
|
||||
|
||||
The output layer receives reports and the VFS.
|
||||
It converts byte ranges into line and column positions at render time.
|
||||
|
||||
This separates rule logic from display logic.
|
||||
Rules do not need to know how the terminal or editor consumes diagnostics.
|
||||
|
||||
## Fix Pipeline
|
||||
|
||||
A fixable diagnostic contains a `Suggestion`.
|
||||
A suggestion contains the range to replace and replacement syntax.
|
||||
|
||||
`statix fix` uses the same parser and rule walk as `check`, but filters for reports that contain suggestions.
|
||||
It then sorts and filters suggestions to avoid overlapping edits.
|
||||
|
||||
The fixer applies selected reports to a copy of the source.
|
||||
Then it parses and scans the changed source again.
|
||||
It repeats this process until no more suggestions appear.
|
||||
|
||||
That iterative design matters because one edit can expose a later edit.
|
||||
It also avoids applying stale ranges after source text changes.
|
||||
|
||||
## Single Fix Pipeline
|
||||
|
||||
The `single` command applies one fix at a requested line and column.
|
||||
It converts the line and column to a byte offset.
|
||||
Then it finds the first fixable report whose diagnostic range contains that offset.
|
||||
|
||||
This shape supports editor workflows.
|
||||
An editor can request a fix for the diagnostic under the cursor.
|
||||
|
||||
## Configuration Model
|
||||
|
||||
Configuration is small.
|
||||
A `statix.toml` file can disable rules by name.
|
||||
It can also add ignore globs.
|
||||
|
||||
The CLI discovers configuration by walking ancestors from the configured path.
|
||||
If no config exists, it uses defaults.
|
||||
|
||||
The command also respects `.gitignore` unless unrestricted mode is enabled.
|
||||
Ignore handling happens before parsing.
|
||||
|
||||
## Architectural Strengths
|
||||
|
||||
`statix` has a clean separation between these concerns:
|
||||
|
||||
- file discovery;
|
||||
- parsing;
|
||||
- rule selection;
|
||||
- rule execution;
|
||||
- reporting;
|
||||
- fixing.
|
||||
|
||||
Rules stay small because the framework handles traversal and output.
|
||||
Fixes are safer because replacement syntax comes from parsed Nix fragments.
|
||||
The lint map avoids unnecessary rule calls.
|
||||
Parallel file traversal improves throughput for large trees.
|
||||
|
||||
The static rule registry is easy to audit.
|
||||
It fits a repository-local linter well when we want deterministic behavior.
|
||||
|
||||
## Architectural Limits
|
||||
|
||||
`statix` does not evaluate Nix.
|
||||
It cannot know imported module results, overlays, option merges, or generated attributes.
|
||||
|
||||
Most rules must remain local syntax rules.
|
||||
A rule that needs semantic knowledge can easily become wrong.
|
||||
|
||||
Fixes rely on byte ranges in source text.
|
||||
They must avoid overlapping ranges.
|
||||
They must rerun after edits because previous ranges become stale.
|
||||
|
||||
The macro system reduces rule boilerplate, but it hides generated behavior.
|
||||
That tradeoff is useful for many rules, but it can make debugging harder.
|
||||
|
||||
## Lessons for Our Linter
|
||||
|
||||
Start with a clear rule interface.
|
||||
A rule should declare its name, matching syntax kinds, and diagnostic output.
|
||||
|
||||
Keep parsing and diagnostics separate from output.
|
||||
The same diagnostic should support terminal output, editor output, and tests.
|
||||
|
||||
Use syntax-kind indexing when rules are local.
|
||||
It prevents unnecessary calls and makes rule ownership explicit.
|
||||
|
||||
Treat autofix as a second contract.
|
||||
A diagnostic can exist without a safe fix.
|
||||
A fix must define exact replacement ranges and avoid overlap.
|
||||
|
||||
Rerun analysis after applying a batch of fixes.
|
||||
Source ranges become invalid after edits.
|
||||
|
||||
Do not add Nix evaluation accidentally.
|
||||
If a rule needs evaluated values, design that as a separate analysis layer.
|
||||
|
||||
## Open Design Questions for Our Linter
|
||||
|
||||
- Should our linter support fixes, or stay report-only for now?
|
||||
- Should rules register by syntax kind like `statix`?
|
||||
- Should our suppression model live in the parser layer or rule layer?
|
||||
- Should parse errors use the same diagnostic type as style rules?
|
||||
- Should editor-friendly output be part of the first complete architecture?
|
||||
- Should future semantic rules use a separate pass from local syntax rules?
|
||||
@@ -22,6 +22,8 @@
|
||||
- dictionary cli tool
|
||||
- kitchen.md
|
||||
- Rust project creator
|
||||
- random number generator utility
|
||||
- reading material helper
|
||||
|
||||
# migration
|
||||
|
||||
@@ -98,6 +100,13 @@
|
||||
- https://www.youtube.com/watch?v=kzcI5F4tGiU
|
||||
- https://www.youtube.com/watch?v=iKwPaB5TUdI
|
||||
- https://www.youtube.com/watch?v=A-CtdQFRX64
|
||||
- xatu literature
|
||||
- https://rust-analyzer.github.io/book/contributing/syntax.html?utm_source=chatgpt.com
|
||||
- https://github.com/dotnet/roslyn/blob/main/docs/compilers/Design/Red-Green%20Trees.md?utm_source=chatgpt.com
|
||||
- https://github.com/rust-analyzer/rowan/blob/master/examples/s_expressions.rs?utm_source=chatgpt.com
|
||||
- https://dl.acm.org/doi/10.1145/964001.964011
|
||||
- https://web.cecs.pdx.edu/~apt/esop15.pdf?utm_source=chatgpt.com
|
||||
- https://www.researchgate.net/publication/2377179_Efficient_and_flexible_incremental_parsing?utm_source=chatgpt.com
|
||||
|
||||
# tech debt
|
||||
|
||||
|
||||
Reference in New Issue
Block a user