1use std::path::Path;
2
3use cargo_util_schemas::manifest;
4
5use crate::CargoResult;
6use crate::GlobalContext;
7use crate::core::MaybePackage;
8use crate::core::Package;
9use crate::core::Workspace;
10use crate::diagnostics::GlobalDiagnosticStats;
11use crate::diagnostics::Lint;
12use crate::diagnostics::LintLevel;
13use crate::diagnostics::LintLevelProduct;
14use crate::diagnostics::ManifestFor;
15use crate::diagnostics::ScopedDiagnosticStats;
16
17#[derive(Clone)]
18pub enum ParsePassRule<'r> {
19 DiagnosticManifest {
20 rule: FnDiagnosticManifest,
21 },
22 LintManifest {
23 rule: FnLintManifest,
24 lint: &'r Lint,
25 },
26 DiagnosticWorkspace {
27 rule: FnDiagnosticWorkspace,
28 },
29 LintWorkspace {
30 rule: FnLintWorkspace,
31 lint: &'r Lint,
32 },
33 DiagnosticPackage {
34 rule: FnDiagnosticPackage,
35 },
36 LintPackage {
37 rule: FnLintPackage,
38 lint: &'r Lint,
39 },
40}
41
42type FnDiagnosticManifest = fn(
43 &Workspace<'_>,
44 ManifestFor<'_>,
45 &Path,
46 &mut ScopedDiagnosticStats<'_>,
47 &GlobalContext,
48) -> CargoResult<()>;
49
50type FnDiagnosticWorkspace = fn(
51 &Workspace<'_>,
52 &MaybePackage,
53 &Path,
54 &mut ScopedDiagnosticStats<'_>,
55 &GlobalContext,
56) -> CargoResult<()>;
57
58type FnDiagnosticPackage = fn(
59 &Workspace<'_>,
60 &Package,
61 &Path,
62 &mut ScopedDiagnosticStats<'_>,
63 &GlobalContext,
64) -> CargoResult<()>;
65
66type FnLintManifest = fn(
67 &Workspace<'_>,
68 manifest: ManifestFor<'_>,
69 manifest_path: &Path,
70 LintLevelProduct,
71 stats: &mut ScopedDiagnosticStats<'_>,
72 gctx: &GlobalContext,
73) -> CargoResult<()>;
74
75type FnLintWorkspace = fn(
76 &Workspace<'_>,
77 &MaybePackage,
78 &Path,
79 LintLevelProduct,
80 &mut ScopedDiagnosticStats<'_>,
81 &GlobalContext,
82) -> CargoResult<()>;
83
84type FnLintPackage = fn(
85 &Workspace<'_>,
86 &Package,
87 &Path,
88 LintLevelProduct,
89 &mut ScopedDiagnosticStats<'_>,
90 &GlobalContext,
91) -> CargoResult<()>;
92
93pub fn emit_parse_diagnostics(
94 workspace: &Workspace<'_>,
95 rules: &[ParsePassRule<'_>],
96) -> CargoResult<()> {
97 let mut stats = GlobalDiagnosticStats::new();
98
99 emit_parse_ws_diagnostics(workspace, rules, &mut stats)?;
100
101 for maybe_pkg in workspace.loaded_maybe() {
102 if let MaybePackage::Package(pkg) = maybe_pkg {
103 let path = pkg.manifest_path();
104 emit_parse_pkg_diagnostics(workspace, pkg, &path, rules, &mut stats)?;
105 }
106 }
107
108 stats.ok()
109}
110
111fn emit_parse_pkg_diagnostics(
112 workspace: &Workspace<'_>,
113 pkg: &Package,
114 path: &Path,
115 rules: &[ParsePassRule<'_>],
116 global_stats: &mut GlobalDiagnosticStats,
117) -> CargoResult<()> {
118 let mut pkg_stats = global_stats.scope();
119
120 let toml_lints = pkg
121 .manifest()
122 .normalized_toml()
123 .lints
124 .clone()
125 .map(|lints| lints.lints)
126 .unwrap_or(manifest::TomlLints::default());
127 let cargo_lints = toml_lints
128 .get("cargo")
129 .cloned()
130 .unwrap_or(manifest::TomlToolLints::default());
131
132 for rule in rules {
133 match rule {
134 ParsePassRule::DiagnosticManifest { rule } => {
135 let manifest = pkg.into();
136 rule(workspace, manifest, &path, &mut pkg_stats, workspace.gctx())?;
137 }
138 ParsePassRule::LintManifest { rule, lint } => {
139 if workspace.gctx().cli_unstable().cargo_lints {
140 let manifest: ManifestFor<'_> = pkg.into();
141 let level = manifest.lint_level(&cargo_lints, lint);
142 if level.level != LintLevel::Allow {
143 rule(
144 workspace,
145 manifest,
146 &path,
147 level,
148 &mut pkg_stats,
149 workspace.gctx(),
150 )?;
151 }
152 }
153 }
154 ParsePassRule::DiagnosticWorkspace { .. } | ParsePassRule::LintWorkspace { .. } => {}
155 ParsePassRule::DiagnosticPackage { rule } => {
156 rule(workspace, pkg, &path, &mut pkg_stats, workspace.gctx())?;
157 }
158 ParsePassRule::LintPackage { rule, lint } => {
159 if workspace.gctx().cli_unstable().cargo_lints {
160 let level = lint.level(
161 &cargo_lints,
162 pkg.rust_version(),
163 pkg.manifest().unstable_features(),
164 );
165
166 if level.level != LintLevel::Allow {
167 rule(
168 workspace,
169 pkg,
170 &path,
171 level,
172 &mut pkg_stats,
173 workspace.gctx(),
174 )?;
175 }
176 }
177 }
178 }
179 }
180
181 pkg_stats.report_summary("parse", Some(&*pkg.name()), workspace.gctx())?;
182
183 Ok(())
184}
185
186fn emit_parse_ws_diagnostics(
187 workspace: &Workspace<'_>,
188 rules: &[ParsePassRule<'_>],
189 global_stats: &mut GlobalDiagnosticStats,
190) -> CargoResult<()> {
191 let mut pkg_stats = global_stats.scope();
192
193 let cargo_lints = match workspace.root_maybe() {
194 MaybePackage::Package(pkg) => {
195 let toml = pkg.manifest().normalized_toml();
196 if let Some(ws) = &toml.workspace {
197 ws.lints.as_ref()
198 } else {
199 toml.lints.as_ref().map(|l| &l.lints)
200 }
201 }
202 MaybePackage::Virtual(vm) => vm
203 .normalized_toml()
204 .workspace
205 .as_ref()
206 .unwrap()
207 .lints
208 .as_ref(),
209 }
210 .and_then(|t| t.get("cargo"))
211 .cloned()
212 .unwrap_or(manifest::TomlToolLints::default());
213
214 for rule in rules {
215 match rule {
216 ParsePassRule::DiagnosticManifest { rule } => {
217 let manifest = (workspace, workspace.root_maybe()).into();
218 rule(
219 workspace,
220 manifest,
221 workspace.root_manifest(),
222 &mut pkg_stats,
223 workspace.gctx(),
224 )?;
225 }
226 ParsePassRule::LintManifest { rule, lint } => {
227 if workspace.gctx().cli_unstable().cargo_lints {
228 let manifest: ManifestFor<'_> = (workspace, workspace.root_maybe()).into();
229 let level = manifest.lint_level(&cargo_lints, lint);
230 if level.level != LintLevel::Allow {
231 rule(
232 workspace,
233 manifest,
234 workspace.root_manifest(),
235 level,
236 &mut pkg_stats,
237 workspace.gctx(),
238 )?;
239 }
240 }
241 }
242 ParsePassRule::DiagnosticWorkspace { rule } => {
243 rule(
244 workspace,
245 workspace.root_maybe(),
246 workspace.root_manifest(),
247 &mut pkg_stats,
248 workspace.gctx(),
249 )?;
250 }
251 ParsePassRule::LintWorkspace { rule, lint } => {
252 if workspace.gctx().cli_unstable().cargo_lints {
253 let level = lint.level(
254 &cargo_lints,
255 workspace.lowest_rust_version(),
256 workspace.root_maybe().unstable_features(),
257 );
258 if level.level != LintLevel::Allow {
259 rule(
260 workspace,
261 workspace.root_maybe(),
262 workspace.root_manifest(),
263 level,
264 &mut pkg_stats,
265 workspace.gctx(),
266 )?;
267 }
268 }
269 }
270 ParsePassRule::DiagnosticPackage { .. } | ParsePassRule::LintPackage { .. } => {}
271 }
272 }
273
274 pkg_stats.report_summary("parse", None, workspace.gctx())?;
275 Ok(())
276}