fix(xatu): count bindings in empty let expressions

This commit is contained in:
2026-09-19 18:21:01 -04:00
parent acf9ef726f
commit 309c3689ee
5 changed files with 11 additions and 7 deletions

View File

@@ -96,6 +96,8 @@
- `rules_rust` `rust_test` targets cannot set both `crate` and `srcs`. - `rules_rust` `rust_test` targets cannot set both `crate` and `srcs`.
Use one input style per test target. Use one input style per test target.
- Rust tests in this monorepo live in separate `*_test.rs` files, not inline `#[cfg(test)]` modules. - Rust tests in this monorepo live in separate `*_test.rs` files, not inline `#[cfg(test)]` modules.
- `rnix::ast::HasEntry::entries()` includes assignments and `inherit` statements.
Use `attrpath_values()` when code needs only assignment entries.
- Ariadne Rust tests are declared in Bazel `BUILD` files; do not add `[[test]]` entries to - Ariadne Rust tests are declared in Bazel `BUILD` files; do not add `[[test]]` entries to
`src/ariadne/Cargo.toml` unless explicitly requested. `src/ariadne/Cargo.toml` unless explicitly requested.
- In this monorepo, "dotfiles" refers to the dotfiles/NixOS configuration under `src/dotfiles/` and `configs/nixos/`. - In this monorepo, "dotfiles" refers to the dotfiles/NixOS configuration under `src/dotfiles/` and `configs/nixos/`.

View File

@@ -57,8 +57,7 @@ pub struct Select {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct LetIn { pub struct LetIn {
pub entry_count: usize, pub binding_count: usize,
pub inherit_count: usize,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]

View File

@@ -117,8 +117,11 @@ impl From<rnix::ast::LetIn> for ast::LetIn {
use rnix::ast::HasEntry; use rnix::ast::HasEntry;
Self { Self {
entry_count: let_in.entries().count(), binding_count: let_in.attrpath_values().count()
inherit_count: let_in.inherits().count(), + let_in
.inherits()
.map(|inherit| inherit.attrs().count())
.sum::<usize>(),
} }
} }
} }

View File

@@ -15,7 +15,7 @@ impl NodeRule for EmptyLetIn {
} }
fn check(node: &Self::Node) -> Option<&'static str> { fn check(node: &Self::Node) -> Option<&'static str> {
if node.entry_count == 0 && node.inherit_count == 0 { if node.binding_count == 0 {
Some("let expression has no bindings") Some("let expression has no bindings")
} else { } else {
None None

View File

@@ -145,7 +145,7 @@ fn xatu_reports_commented_empty_let_in() -> googletest::Result<()> {
} }
#[gtest] #[gtest]
fn empty_inherit_prevents_empty_let_in_finding() -> googletest::Result<()> { fn xatu_reports_empty_let_with_empty_inherit() -> googletest::Result<()> {
let report = let report =
xatu::future_sight(vec![fixture_source("testdata/lint_interactions.nix")?]).or_fail()?; xatu::future_sight(vec![fixture_source("testdata/lint_interactions.nix")?]).or_fail()?;
@@ -154,7 +154,7 @@ fn empty_inherit_prevents_empty_let_in_finding() -> googletest::Result<()> {
.iter() .iter()
.map(|diagnostic| diagnostic.finding.rule_id) .map(|diagnostic| diagnostic.finding.rule_id)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
expect_that!(findings, eq(&vec!["empty_inherit"])); expect_that!(findings, eq(&vec!["empty_let_in", "empty_inherit"]));
Ok(()) Ok(())
} }