chore: update
This commit is contained in:
@@ -105,6 +105,7 @@
|
|||||||
Do not use `nix fmt` as a validation command until a formatter is added.
|
Do not use `nix fmt` as a validation command until a formatter is added.
|
||||||
- The `fix` app passes its current directory to Deadnix.
|
- The `fix` app passes its current directory to Deadnix.
|
||||||
Run `nix run .#fix` from the repository root so path exclusions match.
|
Run `nix run .#fix` from the repository root so path exclusions match.
|
||||||
|
- During the Nix refactor, address each issue that comes up by adding either a Xatu rule or a new Nix style rule.
|
||||||
- Git pathspec searches started in a subdirectory default to that subtree.
|
- Git pathspec searches started in a subdirectory default to that subtree.
|
||||||
Use `git -C <repository-root>` or top-anchored pathspecs for repository-wide history searches.
|
Use `git -C <repository-root>` or top-anchored pathspecs for repository-wide history searches.
|
||||||
- Ariadne extraction must preserve the game-root season prefix for archived source.
|
- Ariadne extraction must preserve the game-root season prefix for archived source.
|
||||||
|
|||||||
93
docs/.obsidian/workspace.json
vendored
93
docs/.obsidian/workspace.json
vendored
@@ -4,41 +4,21 @@
|
|||||||
"type": "split",
|
"type": "split",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": "e581539f950ca1bd",
|
"id": "6d9088d392cb8604",
|
||||||
"type": "tabs",
|
"type": "tabs",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"id": "0c587e5452c16787",
|
"id": "22a4c1f201a1b00e",
|
||||||
"type": "leaf",
|
"type": "leaf",
|
||||||
"state": {
|
"state": {
|
||||||
"type": "markdown",
|
"type": "markdown",
|
||||||
"state": {
|
"state": {
|
||||||
"file": "TODO.md",
|
"file": "00 INBOX/valheim progression.md",
|
||||||
"mode": "source",
|
"mode": "source",
|
||||||
"source": true
|
"source": true
|
||||||
},
|
},
|
||||||
"icon": "lucide-file",
|
"icon": "lucide-file",
|
||||||
"title": "TODO"
|
"title": "valheim progression"
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -111,7 +91,7 @@
|
|||||||
"state": {
|
"state": {
|
||||||
"type": "localgraph",
|
"type": "localgraph",
|
||||||
"state": {
|
"state": {
|
||||||
"file": "TODO.md",
|
"file": "00 INBOX/valheim progression.md",
|
||||||
"options": {
|
"options": {
|
||||||
"collapse-filter": true,
|
"collapse-filter": true,
|
||||||
"search": "",
|
"search": "",
|
||||||
@@ -134,7 +114,7 @@
|
|||||||
"repelStrength": 10,
|
"repelStrength": 10,
|
||||||
"linkStrength": 1,
|
"linkStrength": 1,
|
||||||
"linkDistance": 250,
|
"linkDistance": 250,
|
||||||
"scale": 1,
|
"scale": 0.6666666666666666,
|
||||||
"close": true
|
"close": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -251,43 +231,42 @@
|
|||||||
"table-editor-obsidian:Advanced Tables Toolbar": false
|
"table-editor-obsidian:Advanced Tables Toolbar": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"active": "0c587e5452c16787",
|
"active": "22a4c1f201a1b00e",
|
||||||
"lastOpenFiles": [
|
"lastOpenFiles": [
|
||||||
"00 INBOX/how to setup a steamcmd server.md",
|
"00 INBOX/ai/statix-architecture.md",
|
||||||
"00 INBOX/ai/steamcmd servers.md",
|
"00 INBOX/ai/nix-statix-deadnix-rules.md",
|
||||||
"03 wrk/ariadne.md",
|
"00 INBOX/let order name resolution.md~",
|
||||||
"00 INBOX/ai/ariadne-cli-skeleton.md",
|
"00 INBOX/nix name resolution.md",
|
||||||
"00 INBOX/ai/ariadne-project-goals.md",
|
"00 INBOX/let order name resolution.md",
|
||||||
|
"00 INBOX/nix name resolution.md~",
|
||||||
|
"00 INBOX/valheim progression.md",
|
||||||
"TODO.md",
|
"TODO.md",
|
||||||
"00 INBOX/ai/dictionary-cli.md",
|
"00 INBOX/ai/deadnix-architecture.md",
|
||||||
"00 INBOX/ai/dictionary-skill.md",
|
"TODO.md~",
|
||||||
"03 wrk/falinks.md",
|
"00 INBOX/ai/xatu-rule-implementation-order.md",
|
||||||
"03 wrk/machoke.md",
|
"00 INBOX/ai/xatu-architecture-design.md",
|
||||||
"00 INBOX/home assistant agent integration.md",
|
"00 INBOX/ai/nix-style-linter-plan.md",
|
||||||
"02 notes/falinks public surfaces.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~",
|
"03 wrk/machoke.md~",
|
||||||
"02 notes/machoke public surface.md",
|
|
||||||
"03 wrk/falinks.md~",
|
"03 wrk/falinks.md~",
|
||||||
"99 meta/templates/checkmark.md",
|
|
||||||
"AGENTS.md",
|
|
||||||
"99 meta/attachments/bases/unlinked mentions.base",
|
"99 meta/attachments/bases/unlinked mentions.base",
|
||||||
"99 meta/templates/note.md",
|
|
||||||
"AGENTS.md.md",
|
|
||||||
"00 INBOX/ai",
|
"00 INBOX/ai",
|
||||||
"03 wrk/ai",
|
"03 wrk/ai",
|
||||||
"03 wrk/brain.md",
|
"99 meta/Untitled"
|
||||||
"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"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -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
|
- dictionary cli tool
|
||||||
- kitchen.md
|
- kitchen.md
|
||||||
- Rust project creator
|
- Rust project creator
|
||||||
|
- random number generator utility
|
||||||
|
- reading material helper
|
||||||
|
|
||||||
# migration
|
# migration
|
||||||
|
|
||||||
@@ -98,6 +100,13 @@
|
|||||||
- https://www.youtube.com/watch?v=kzcI5F4tGiU
|
- https://www.youtube.com/watch?v=kzcI5F4tGiU
|
||||||
- https://www.youtube.com/watch?v=iKwPaB5TUdI
|
- https://www.youtube.com/watch?v=iKwPaB5TUdI
|
||||||
- https://www.youtube.com/watch?v=A-CtdQFRX64
|
- 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
|
# tech debt
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user