feat(natu): support inline suppressions

This commit was merged in pull request #105.
This commit is contained in:
2026-09-26 23:30:01 -04:00
parent ac5cc8d659
commit dd82367dcb
5 changed files with 49 additions and 21 deletions

View File

@@ -1,5 +1,3 @@
use std::path::Path;
use crate::FileId;
use crate::ast;
use crate::rules::manual_inherit::ManualInherit;
@@ -61,7 +59,7 @@ pub trait TokenRule {
fn check(token: &Self::Token) -> Option<&'static str>;
}
pub fn run_rules(file: &ast::ParsedFile, graph: &ScopeGraph, path: &Path) -> Vec<Diagnostic> {
pub fn run_rules(file: &ast::ParsedFile, graph: &ScopeGraph, source: &str) -> Vec<Diagnostic> {
let node_diagnostics = file
.nodes
.iter()
@@ -78,21 +76,21 @@ pub fn run_rules(file: &ast::ParsedFile, graph: &ScopeGraph, path: &Path) -> Vec
.flat_map(|token| [run_token_rule::<UnquotedUri>(token)])
.flatten();
let ignore_cfg =
path.starts_with("configs/nixos") || path.to_string_lossy().contains("/configs/nixos/");
let let_order_diagnostics = let_order::check(graph, ignore_cfg)
.into_iter()
.map(|finding| Finding {
rule_id: "let_order",
range: finding.range,
message: "Let bindings must follow dependency order",
binding: None,
expected_order: Some(finding.expected_order),
});
let let_order_diagnostics =
let_order::check(graph, source)
.into_iter()
.map(|finding| Finding {
rule_id: "let_order",
range: finding.range,
message: "Let bindings must follow dependency order",
binding: None,
expected_order: Some(finding.expected_order),
});
node_diagnostics
.chain(token_diagnostics)
.chain(let_order_diagnostics)
.filter(|finding| !is_suppressed(source, finding.range, finding.rule_id))
.map(|finding| Diagnostic {
file_id: file.file_id,
finding,
@@ -100,6 +98,24 @@ pub fn run_rules(file: &ast::ParsedFile, graph: &ScopeGraph, path: &Path) -> Vec
.collect()
}
pub fn is_suppressed(source: &str, range: ast::SourceRange, rule_id: &str) -> bool {
let line = source[range.start..].lines().next().unwrap_or_default();
let Some(comment) = line.split_once('#').map(|(_, comment)| comment.trim()) else {
return false;
};
if comment == "NOLINT" {
return true;
}
comment
.strip_prefix("NOLINT(")
.and_then(|rules| rules.strip_suffix(')'))
.is_some_and(|rules| {
rules
.split(',')
.any(|candidate| candidate.trim() == rule_id)
})
}
fn run_node_rule<R>(node: &ast::Node) -> Option<Finding>
where
R: NodeRule,