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