Skip to main content

cargo/diagnostics/
passes.rs

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}