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::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 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 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 stats, workspace.gctx())?;
141                    }
142                }
143            }
144            ParsePassRule::DiagnosticWorkspace { .. } | ParsePassRule::LintWorkspace { .. } => {}
145            ParsePassRule::DiagnosticPackage { rule } => {
146                rule(workspace, pkg, &path, &mut 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(workspace, pkg, &path, level, &mut stats, workspace.gctx())?;
158                    }
159                }
160            }
161        }
162    }
163
164    stats.report_summary("parse", Some(&*pkg.name()), workspace.gctx())?;
165
166    Ok(())
167}
168
169fn emit_parse_ws_diagnostics(
170    workspace: &Workspace<'_>,
171    rules: &[ParsePassRule<'_>],
172) -> CargoResult<()> {
173    let mut stats = DiagnosticStats::new();
174
175    let cargo_lints = match workspace.root_maybe() {
176        MaybePackage::Package(pkg) => {
177            let toml = pkg.manifest().normalized_toml();
178            if let Some(ws) = &toml.workspace {
179                ws.lints.as_ref()
180            } else {
181                toml.lints.as_ref().map(|l| &l.lints)
182            }
183        }
184        MaybePackage::Virtual(vm) => vm
185            .normalized_toml()
186            .workspace
187            .as_ref()
188            .unwrap()
189            .lints
190            .as_ref(),
191    }
192    .and_then(|t| t.get("cargo"))
193    .cloned()
194    .unwrap_or(manifest::TomlToolLints::default());
195
196    for rule in rules {
197        match rule {
198            ParsePassRule::DiagnosticManifest { rule } => {
199                let manifest = (workspace, workspace.root_maybe()).into();
200                rule(
201                    manifest,
202                    workspace.root_manifest(),
203                    &mut stats,
204                    workspace.gctx(),
205                )?;
206            }
207            ParsePassRule::LintManifest { rule, lint } => {
208                if workspace.gctx().cli_unstable().cargo_lints {
209                    let manifest: ManifestFor<'_> = (workspace, workspace.root_maybe()).into();
210                    let level = manifest.lint_level(&cargo_lints, lint);
211                    if level.level != LintLevel::Allow {
212                        rule(
213                            manifest,
214                            workspace.root_manifest(),
215                            level,
216                            &mut stats,
217                            workspace.gctx(),
218                        )?;
219                    }
220                }
221            }
222            ParsePassRule::DiagnosticWorkspace { rule } => {
223                rule(
224                    workspace,
225                    workspace.root_maybe(),
226                    workspace.root_manifest(),
227                    &mut stats,
228                    workspace.gctx(),
229                )?;
230            }
231            ParsePassRule::LintWorkspace { rule, lint } => {
232                if workspace.gctx().cli_unstable().cargo_lints {
233                    let level = lint.level(
234                        &cargo_lints,
235                        workspace.lowest_rust_version(),
236                        workspace.root_maybe().unstable_features(),
237                    );
238                    if level.level != LintLevel::Allow {
239                        rule(
240                            workspace,
241                            workspace.root_maybe(),
242                            workspace.root_manifest(),
243                            level,
244                            &mut stats,
245                            workspace.gctx(),
246                        )?;
247                    }
248                }
249            }
250            ParsePassRule::DiagnosticPackage { .. } | ParsePassRule::LintPackage { .. } => {}
251        }
252    }
253
254    stats.report_summary("parse", None, workspace.gctx())?;
255    Ok(())
256}