From 213cd84642e189cdea7b6303a05b6055bd65e663 Mon Sep 17 00:00:00 2001 From: alexion Date: Sat, 19 Sep 2026 20:17:24 -0400 Subject: [PATCH] refactor(xatu): narrow lint rule set --- AGENTS.md | 4 +- src/flake/apps.nix | 6 -- src/xatu/BUILD | 5 - src/xatu/rules.rs | 39 +++----- src/xatu/rules/deprecated_to_path.rs | 41 --------- src/xatu/rules/empty_inherit.rs | 24 ----- src/xatu/rules/empty_let_in.rs | 24 ----- src/xatu/testdata/clean.nix | 5 - src/xatu/testdata/lint_findings.nix | 5 - src/xatu/testdata/lint_interactions.nix | 8 -- src/xatu/testdata/lint_variants.nix | 10 -- src/xatu/xatu_test.rs | 117 ------------------------ statix.toml | 3 - 13 files changed, 15 insertions(+), 276 deletions(-) delete mode 100644 src/xatu/rules/deprecated_to_path.rs delete mode 100644 src/xatu/rules/empty_inherit.rs delete mode 100644 src/xatu/rules/empty_let_in.rs delete mode 100644 src/xatu/testdata/lint_interactions.nix delete mode 100644 src/xatu/testdata/lint_variants.nix delete mode 100644 statix.toml diff --git a/AGENTS.md b/AGENTS.md index 5661726..aab6be4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -103,8 +103,10 @@ - In this monorepo, "dotfiles" refers to the dotfiles/NixOS configuration under `src/dotfiles/` and `configs/nixos/`. - The flake does not currently define `formatter.x86_64-linux`. Do not use `nix fmt` as a validation command until a formatter is added. -- The `fix` app passes its current directory to Statix and Deadnix. +- The `fix` app passes its current directory to Deadnix. Run `nix run .#fix` from the repository root so path exclusions match. +- Git pathspec searches started in a subdirectory default to that subtree. + Use `git -C ` or top-anchored pathspecs for repository-wide history searches. - Ariadne extraction must preserve the game-root season prefix for archived source. Different game installs can contain compiled scripts with the same internal path. - Clew owns Ren'Py extraction, preparation, and compiled-script AST export. Ariadne owns analysis over Clew-produced data. diff --git a/src/flake/apps.nix b/src/flake/apps.nix index 6f5212f..321cdb3 100644 --- a/src/flake/apps.nix +++ b/src/flake/apps.nix @@ -15,16 +15,10 @@ in name = "fix"; runtimeInputs = [ formatter - pkgs.statix pkgs.deadnix ]; text = '' treefmt - statix fix \ - --ignore 'configs/nixos/hosts/*/hardware-configuration.nix' \ - --ignore 'src/xatu/testdata/**' \ - --ignore third_party \ - . deadnix --edit \ --exclude \ configs/nixos/hosts/neogaia/hardware-configuration.nix \ diff --git a/src/xatu/BUILD b/src/xatu/BUILD index d88e2a7..4a72c08 100644 --- a/src/xatu/BUILD +++ b/src/xatu/BUILD @@ -8,9 +8,6 @@ rust_library( "file_store.rs", "parser.rs", "rules.rs", - "rules/deprecated_to_path.rs", - "rules/empty_inherit.rs", - "rules/empty_let_in.rs", "rules/unquoted_uri.rs", "xatu.rs", ], @@ -34,8 +31,6 @@ rust_test( "testdata/clean.nix", "testdata/invalid.nix", "testdata/lint_findings.nix", - "testdata/lint_interactions.nix", - "testdata/lint_variants.nix", ], deps = [ ":xatu_lib", diff --git a/src/xatu/rules.rs b/src/xatu/rules.rs index 8def783..45af16e 100644 --- a/src/xatu/rules.rs +++ b/src/xatu/rules.rs @@ -1,13 +1,7 @@ use crate::FileId; use crate::ast; -use crate::rules::deprecated_to_path::DeprecatedToPath; -use crate::rules::empty_inherit::EmptyInherit; -use crate::rules::empty_let_in::EmptyLetIn; use crate::rules::unquoted_uri::UnquotedUri; -pub mod deprecated_to_path; -pub mod empty_inherit; -pub mod empty_let_in; pub mod unquoted_uri; #[derive(Debug)] @@ -33,6 +27,7 @@ impl std::fmt::Display for Diagnostic { } } +#[expect(dead_code, reason = "retained for planned node rules")] pub trait NodeRule { type Node; @@ -52,33 +47,23 @@ pub trait TokenRule { } pub fn run_rules(file: &ast::ParsedFile) -> Vec { - let node_diagnostics = file.nodes.iter().flat_map(|node| { - [ - run_node_rule::(node), - run_node_rule::(node), - run_node_rule::(node), - ] - .into_iter() - .flatten() + let node_diagnostics = file.nodes.iter().flat_map(|_| std::iter::empty()); + let token_diagnostics = file + .tokens + .iter() + .flat_map(|token| [run_token_rule::(token)]) + .flatten(); + + node_diagnostics + .chain(token_diagnostics) .map(|finding| Diagnostic { file_id: file.file_id, finding, }) - }); - - let token_diagnostics = file.tokens.iter().flat_map(|token| { - [run_token_rule::(token)] - .into_iter() - .flatten() - .map(|finding| Diagnostic { - file_id: file.file_id, - finding, - }) - }); - - node_diagnostics.chain(token_diagnostics).collect() + .collect() } +#[expect(dead_code, reason = "retained for planned node rules")] fn run_node_rule(node: &ast::Node) -> Option where R: NodeRule, diff --git a/src/xatu/rules/deprecated_to_path.rs b/src/xatu/rules/deprecated_to_path.rs deleted file mode 100644 index 98837ab..0000000 --- a/src/xatu/rules/deprecated_to_path.rs +++ /dev/null @@ -1,41 +0,0 @@ -use crate::{ast, rules::NodeRule}; - -pub struct DeprecatedToPath; - -impl NodeRule for DeprecatedToPath { - type Node = ast::Apply; - - const ID: &'static str = "deprecated_to_path"; - - fn select(data: &ast::NodeData) -> Option<&Self::Node> { - match data { - ast::NodeData::Apply(apply) => Some(apply), - _ => None, - } - } - - fn check(node: &Self::Node) -> Option<&'static str> { - if calls_to_path(&node.function) { - Some("builtins.toPath is deprecated") - } else { - None - } - } -} - -fn calls_to_path(function: &ast::Expr) -> bool { - // TODO: Once Xatu supports name resolution, report unqualified `toPath` only - // when it resolves to `builtins.toPath`. - match function { - ast::Expr::Ident(ident) => ident.name == "toPath", - ast::Expr::Select(select) => is_builtins_to_path(select), - } -} - -fn is_builtins_to_path(select: &ast::Select) -> bool { - matches!( - &select.receiver, - ast::Expr::Ident(ident) if ident.name == "builtins" - ) && select.attrs.len() == 1 - && select.attrs[0].name == "toPath" -} diff --git a/src/xatu/rules/empty_inherit.rs b/src/xatu/rules/empty_inherit.rs deleted file mode 100644 index fee8bae..0000000 --- a/src/xatu/rules/empty_inherit.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::{ast, rules::NodeRule}; - -pub struct EmptyInherit; - -impl NodeRule for EmptyInherit { - type Node = ast::Inherit; - - const ID: &'static str = "empty_inherit"; - - fn select(data: &ast::NodeData) -> Option<&Self::Node> { - match data { - ast::NodeData::Inherit(inherit) => Some(inherit), - _ => None, - } - } - - fn check(node: &Self::Node) -> Option<&'static str> { - if node.attr_count == 0 { - Some("inherit statement has no attributes") - } else { - None - } - } -} diff --git a/src/xatu/rules/empty_let_in.rs b/src/xatu/rules/empty_let_in.rs deleted file mode 100644 index fb0c1e9..0000000 --- a/src/xatu/rules/empty_let_in.rs +++ /dev/null @@ -1,24 +0,0 @@ -use crate::{ast, rules::NodeRule}; - -pub struct EmptyLetIn; - -impl NodeRule for EmptyLetIn { - type Node = ast::LetIn; - - const ID: &'static str = "empty_let_in"; - - fn select(data: &ast::NodeData) -> Option<&Self::Node> { - match data { - ast::NodeData::LetIn(let_in) => Some(let_in), - _ => None, - } - } - - fn check(node: &Self::Node) -> Option<&'static str> { - if node.binding_count == 0 { - Some("let expression has no bindings") - } else { - None - } - } -} diff --git a/src/xatu/testdata/clean.nix b/src/xatu/testdata/clean.nix index 5ef32ac..0963365 100644 --- a/src/xatu/testdata/clean.nix +++ b/src/xatu/testdata/clean.nix @@ -1,11 +1,6 @@ { pkgs }: { - nonEmpty = - let - value = pkgs; - in - value; path = ./. + "/foo"; url = "https://example.com"; inherit pkgs; diff --git a/src/xatu/testdata/lint_findings.nix b/src/xatu/testdata/lint_findings.nix index 3eb8cbb..1b922e8 100644 --- a/src/xatu/testdata/lint_findings.nix +++ b/src/xatu/testdata/lint_findings.nix @@ -1,8 +1,3 @@ -{ src }: - { - inherit; - empty = let in src; - path = builtins.toPath src; url = https://example.com; } diff --git a/src/xatu/testdata/lint_interactions.nix b/src/xatu/testdata/lint_interactions.nix deleted file mode 100644 index 89a5948..0000000 --- a/src/xatu/testdata/lint_interactions.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ src }: - -let - inherit; -in -{ - value = src; -} diff --git a/src/xatu/testdata/lint_variants.nix b/src/xatu/testdata/lint_variants.nix deleted file mode 100644 index 34ac99c..0000000 --- a/src/xatu/testdata/lint_variants.nix +++ /dev/null @@ -1,10 +0,0 @@ -{ src }: - -{ - commentedEmpty = - let - # This comment must not suppress the finding. - in - src; - path = toPath src; -} diff --git a/src/xatu/xatu_test.rs b/src/xatu/xatu_test.rs index bc3fe3c..57ce7f4 100644 --- a/src/xatu/xatu_test.rs +++ b/src/xatu/xatu_test.rs @@ -41,123 +41,6 @@ fn xatu_rejects_invalid_source() -> googletest::Result<()> { Ok(()) } -#[gtest] -fn xatu_reports_deprecated_builtin_to_path() -> googletest::Result<()> { - let report = xatu::future_sight(vec![fixture_source("testdata/lint_findings.nix")?]).or_fail()?; - - let findings = report - .diagnostics - .iter() - .map(|diagnostic| { - ( - report.files.get(diagnostic.file_id).path.clone(), - diagnostic.finding.rule_id, - ) - }) - .collect::>(); - expect_that!( - findings, - contains(eq(&( - PathBuf::from("testdata/lint_findings.nix"), - "deprecated_to_path" - ))) - ); - Ok(()) -} - -#[gtest] -fn xatu_reports_deprecated_unqualified_to_path() -> googletest::Result<()> { - let report = xatu::future_sight(vec![fixture_source("testdata/lint_variants.nix")?]).or_fail()?; - - let findings = report - .diagnostics - .iter() - .map(|diagnostic| { - ( - report.files.get(diagnostic.file_id).path.clone(), - diagnostic.finding.rule_id, - ) - }) - .collect::>(); - expect_that!( - findings, - contains(eq(&( - PathBuf::from("testdata/lint_variants.nix"), - "deprecated_to_path" - ))) - ); - Ok(()) -} - -#[gtest] -fn xatu_reports_empty_inherit() -> googletest::Result<()> { - let report = xatu::future_sight(vec![fixture_source("testdata/lint_findings.nix")?]).or_fail()?; - - let findings = report - .diagnostics - .iter() - .map(|diagnostic| { - ( - report.files.get(diagnostic.file_id).path.clone(), - diagnostic.finding.rule_id, - ) - }) - .collect::>(); - expect_that!( - findings, - contains(eq(&( - PathBuf::from("testdata/lint_findings.nix"), - "empty_inherit" - ))) - ); - Ok(()) -} - -#[gtest] -fn xatu_reports_empty_let_in() -> googletest::Result<()> { - let source = fixture_source("testdata/lint_findings.nix")?; - let range_start = source.source.find("let in src").or_fail()?; - let range_end = range_start + "let in src".len(); - let report = xatu::future_sight(vec![source]).or_fail()?; - - let finding = report - .diagnostics - .iter() - .map(|diagnostic| &diagnostic.finding) - .find(|finding| finding.rule_id == "empty_let_in") - .or_fail()?; - let expected_range = format!("{range_start}..{range_end}"); - expect_that!(finding.range.to_string(), eq(&expected_range)); - Ok(()) -} - -#[gtest] -fn xatu_reports_commented_empty_let_in() -> googletest::Result<()> { - let report = xatu::future_sight(vec![fixture_source("testdata/lint_variants.nix")?]).or_fail()?; - - let findings = report - .diagnostics - .iter() - .map(|diagnostic| diagnostic.finding.rule_id) - .collect::>(); - expect_that!(findings, contains(eq(&"empty_let_in"))); - Ok(()) -} - -#[gtest] -fn xatu_reports_empty_let_with_empty_inherit() -> googletest::Result<()> { - let report = - xatu::future_sight(vec![fixture_source("testdata/lint_interactions.nix")?]).or_fail()?; - - let findings = report - .diagnostics - .iter() - .map(|diagnostic| diagnostic.finding.rule_id) - .collect::>(); - expect_that!(findings, eq(&vec!["empty_let_in", "empty_inherit"])); - Ok(()) -} - #[gtest] fn xatu_reports_unquoted_uri() -> googletest::Result<()> { let report = xatu::future_sight(vec![fixture_source("testdata/lint_findings.nix")?]).or_fail()?; diff --git a/statix.toml b/statix.toml deleted file mode 100644 index 1a0d92f..0000000 --- a/statix.toml +++ /dev/null @@ -1,3 +0,0 @@ -disabled = [ - "repeated_keys", -]