feat(natu): lint direct listToAttrs maps

This commit was merged in pull request #109.
This commit is contained in:
2026-09-27 18:05:16 -04:00
parent b6469567e1
commit 0e5731145e
8 changed files with 166 additions and 7 deletions

View File

@@ -0,0 +1,58 @@
use crate::{ast, rules::NodeRule};
pub struct ListToAttrsMap;
impl NodeRule for ListToAttrsMap {
type Node = ast::Binding;
const ID: &'static str = "list_to_attrs_map";
fn select(data: &ast::NodeData) -> Option<&Self::Node> {
let ast::NodeData::Binding(binding) = data;
Some(binding)
}
fn check(node: &Self::Node) -> Option<&'static str> {
matches_list_to_attrs_map(&node.value)
.then_some("Use `lib.genAttrs'` instead of `lib.listToAttrs` after `map`")
}
}
fn matches_list_to_attrs_map(expr: &ast::Expr) -> bool {
let ast::Expr::Apply(application) = unwrap_expr(expr) else {
return false;
};
is_lib_list_to_attrs(&application.lambda) && is_map_application(&application.argument)
}
fn unwrap_expr(expr: &ast::Expr) -> &ast::Expr {
match expr {
ast::Expr::Other(expressions) if expressions.len() == 1 => unwrap_expr(&expressions[0]),
_ => expr,
}
}
fn is_lib_list_to_attrs(expr: &ast::Expr) -> bool {
matches!(
unwrap_expr(expr),
ast::Expr::Select(select) if select.receiver == "lib" && select.attr.name == "listToAttrs"
)
}
fn is_map_application(expr: &ast::Expr) -> bool {
let ast::Expr::Apply(application) = unwrap_expr(expr) else {
return false;
};
is_map(&application.lambda)
}
fn is_map(expr: &ast::Expr) -> bool {
match unwrap_expr(expr) {
ast::Expr::Ident(ident) => ident.name == "map",
ast::Expr::Apply(application) => is_map(&application.lambda),
_ => false,
}
}