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 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}