feat(natu): report expected let order
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
mod ast;
|
mod ast;
|
||||||
mod file_store;
|
mod file_store;
|
||||||
mod parser;
|
mod parser;
|
||||||
@@ -23,7 +25,34 @@ impl Report {
|
|||||||
|
|
||||||
impl std::fmt::Display for Report {
|
impl std::fmt::Display for Report {
|
||||||
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut reported_let_order_files = HashSet::new();
|
||||||
|
|
||||||
for diagnostic in &self.diagnostics {
|
for diagnostic in &self.diagnostics {
|
||||||
|
if diagnostic.finding.rule_id == "let_order" {
|
||||||
|
if !reported_let_order_files.insert(diagnostic.file_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = self.files.get(diagnostic.file_id);
|
||||||
|
writeln!(
|
||||||
|
formatter,
|
||||||
|
"{} [let_order] Let bindings must follow dependency order",
|
||||||
|
file.path.display()
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for finding in self.diagnostics.iter().filter(|candidate| {
|
||||||
|
candidate.file_id == diagnostic.file_id
|
||||||
|
&& candidate.finding.rule_id == "let_order"
|
||||||
|
}) {
|
||||||
|
let (start, _) = line_range(&file.source, finding.finding.range);
|
||||||
|
let expected_order =
|
||||||
|
finding.finding.expected_order.as_ref().expect("let order");
|
||||||
|
writeln!(formatter, " line {start}: {}", expected_order.join(", "))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let file = self.files.get(diagnostic.file_id);
|
let file = self.files.get(diagnostic.file_id);
|
||||||
let (start, end) = line_range(&file.source, diagnostic.finding.range);
|
let (start, end) = line_range(&file.source, diagnostic.finding.range);
|
||||||
let location = if start == end {
|
let location = if start == end {
|
||||||
|
|||||||
@@ -50,7 +50,18 @@ fn natu_reports_out_of_order_let_bindings() -> googletest::Result<()> {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|diagnostic| diagnostic.finding.rule_id == "let_order")
|
.filter(|diagnostic| diagnostic.finding.rule_id == "let_order")
|
||||||
.count();
|
.count();
|
||||||
expect_that!(let_order_count, eq(4));
|
expect_that!(let_order_count, eq(2));
|
||||||
|
expect_that!(
|
||||||
|
report.to_string(),
|
||||||
|
eq(concat!(
|
||||||
|
"testdata/lint_findings.nix:11 [manual_inherit] Use `inherit` for this assignment\n",
|
||||||
|
"testdata/lint_findings.nix:12 [manual_inherit_from] Use `inherit` for this selected attribute\n",
|
||||||
|
"testdata/lint_findings.nix:13 [unquoted_uri] URI literals should be quoted\n",
|
||||||
|
"testdata/lint_findings.nix [let_order] Let bindings must follow dependency order\n",
|
||||||
|
" line 5: nestedSecond, nestedFirst\n",
|
||||||
|
" line 2: first, second, nested\n",
|
||||||
|
))
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ pub struct Finding {
|
|||||||
pub range: ast::SourceRange,
|
pub range: ast::SourceRange,
|
||||||
pub message: &'static str,
|
pub message: &'static str,
|
||||||
pub binding: Option<String>,
|
pub binding: Option<String>,
|
||||||
|
pub expected_order: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Diagnostic {
|
impl std::fmt::Display for Diagnostic {
|
||||||
@@ -79,7 +80,8 @@ pub fn run_rules(file: &ast::ParsedFile, graph: &ScopeGraph) -> Vec<Diagnostic>
|
|||||||
rule_id: "let_order",
|
rule_id: "let_order",
|
||||||
range: finding.range,
|
range: finding.range,
|
||||||
message: "Let bindings must follow dependency order",
|
message: "Let bindings must follow dependency order",
|
||||||
binding: Some(finding.binding),
|
binding: None,
|
||||||
|
expected_order: Some(finding.expected_order),
|
||||||
});
|
});
|
||||||
|
|
||||||
node_diagnostics
|
node_diagnostics
|
||||||
@@ -104,6 +106,7 @@ where
|
|||||||
range: node.range,
|
range: node.range,
|
||||||
message,
|
message,
|
||||||
binding: None,
|
binding: None,
|
||||||
|
expected_order: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,5 +122,6 @@ where
|
|||||||
range: token.range,
|
range: token.range,
|
||||||
message,
|
message,
|
||||||
binding: None,
|
binding: None,
|
||||||
|
expected_order: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::{
|
|||||||
|
|
||||||
pub struct Finding {
|
pub struct Finding {
|
||||||
pub range: ast::SourceRange,
|
pub range: ast::SourceRange,
|
||||||
pub binding: String,
|
pub expected_order: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(graph: &ScopeGraph) -> Vec<Finding> {
|
pub fn check(graph: &ScopeGraph) -> Vec<Finding> {
|
||||||
@@ -14,26 +14,26 @@ pub fn check(graph: &ScopeGraph) -> Vec<Finding> {
|
|||||||
.iter()
|
.iter()
|
||||||
.flat_map(|let_scope| {
|
.flat_map(|let_scope| {
|
||||||
let expected = expected_order(graph, let_scope);
|
let expected = expected_order(graph, let_scope);
|
||||||
let positions = expected
|
let is_ordered = expected.iter().enumerate().all(|(position, declaration)| {
|
||||||
.iter()
|
let_scope
|
||||||
.enumerate()
|
.bindings
|
||||||
.map(|(position, declaration)| (*declaration, position))
|
.get(position)
|
||||||
.collect::<Vec<_>>();
|
.is_some_and(|binding| binding.declaration == *declaration)
|
||||||
|
});
|
||||||
|
|
||||||
let_scope
|
(!is_ordered).then(|| Finding {
|
||||||
.bindings
|
range: let_scope.bindings[0].range,
|
||||||
.iter()
|
expected_order: expected
|
||||||
.enumerate()
|
.iter()
|
||||||
.filter_map(move |(position, binding)| {
|
.filter_map(|declaration| {
|
||||||
positions
|
let_scope
|
||||||
.iter()
|
.bindings
|
||||||
.find(|(declaration, _)| *declaration == binding.declaration)
|
.iter()
|
||||||
.filter(|(_, expected_position)| position != *expected_position)
|
.find(|binding| binding.declaration == *declaration)
|
||||||
.map(|_| Finding {
|
.map(|binding| binding.name.clone())
|
||||||
range: binding.range,
|
})
|
||||||
binding: binding.name.clone(),
|
.collect(),
|
||||||
})
|
})
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user