1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
use crate::linter::mlint_report::{MlintReport, StateLintReports};
use cargo_metadata::Metadata;
use mingling::LazyRes;
use mingling::consts::REMAINS;
use mingling::macros::{arg, chain, dispatcher, pack};
use mingling::picker::EntryPicker;
use tokio::task::JoinSet;
dispatcher!("lint", CMDMinglingLinter => EntryMinglingLinter);
/// Main linting function that processes all packages in the metadata.
///
/// Iterates through all packages and their targets (e.g., binaries, libraries, tests),
/// reads Rust source files (`.rs`), parses them into ASTs, runs lint checks,
/// and enriches each report with metadata information.
async fn linter_main(metadata: &Metadata) -> Vec<MlintReport> {
let mut join_set = JoinSet::new();
for package in &metadata.packages {
for target in &package.targets {
let path = &target.src_path;
// Only process Rust source files (with `.rs` extension)
if !path.as_str().ends_with(".rs") {
continue;
}
// Clone/move all data needed inside the blocking closure
let path_str = path.as_str().to_string();
let package_id = package.id.to_string();
let target_name = target.name.clone();
let target_kind = target.kind.first().map(|k| k.to_string());
join_set.spawn_blocking(move || {
// Read the source file content
let source = std::fs::read_to_string(&path_str).ok()?;
// Parse the source file into an AST
let ast = syn::parse_file(&source).ok()?;
// Run all lint checks and collect reports
let reports = crate::lints::run_all_lints(&ast, &source);
// Enrich each report with metadata information
let enriched: Vec<MlintReport> = reports
.into_iter()
.map(|mut r| {
r.file_name = path_str.clone();
r.source_code = source.clone();
r.package_id = Some(package_id.clone());
r.target_name = Some(target_name.clone());
r.target_kind = target_kind.clone();
r.target_src_path = Some(path_str.clone());
r
})
.collect();
Some(enriched)
});
}
}
let mut all_reports = Vec::new();
while let Some(res) = join_set.join_next().await {
// `spawn_blocking` panics are propagated, `None` means task skipped (read/parse failure)
if let Ok(Some(reports)) = res {
all_reports.extend(reports);
}
}
all_reports
}
pack!(StateBeginLinter = ());
#[chain]
pub fn handle_lint(args: EntryMinglingLinter) -> StateBeginLinter {
let (with_checker, checker_args) = args
.pick_or(&arg![with_checker: Option<String>], || {
Some("cargo,check".to_string())
})
.pick(&REMAINS)
.unwrap();
// If with_checker is not set, proceed directly to the mingling lint phase
let Some(with_checker) = with_checker else {
return StateBeginLinter::new(());
};
let with_checker: Vec<&str> = with_checker.split(',').collect();
let checker_args: Vec<String> = checker_args.into();
// Run the outer checker (e.g. cargo check) with output passed through directly
execute_checker(&with_checker, checker_args.as_slice());
StateBeginLinter::new(())
}
/// Run the outer checker (e.g. cargo check) with output passed through directly.
fn execute_checker(with_checker: &[&str], checker_args: &[String]) {
if with_checker.is_empty() {
return;
}
let checker_str = with_checker.join(" ");
let args_str = checker_args.join(" ");
let full_cmd = if args_str.is_empty() {
checker_str
} else {
format!("{} {}", checker_str, args_str)
};
let mut cmd = if cfg!(target_os = "windows") {
let mut c = std::process::Command::new("cmd");
c.args(["/C", &full_cmd]);
c
} else {
let mut c = std::process::Command::new("sh");
c.args(["-c", &full_cmd]);
c
};
// Pass through stdin/stdout/stderr so the user sees everything
let _ = cmd.status();
}
#[chain]
pub async fn handle_state_begin_linter(
_: StateBeginLinter,
metadata: &mut LazyRes<crate::metadata::setup::ResMetadata>,
) -> StateLintReports {
let metadata = metadata.get_ref().data();
let reports = linter_main(metadata).await;
StateLintReports::new(reports)
}
|