Skip to main content

cargo/diagnostics/
mod.rs

1use anyhow::bail;
2use cargo_util_schemas::manifest::RustVersion;
3use cargo_util_schemas::manifest::TomlToolLints;
4
5use crate::CargoResult;
6use crate::core::Workspace;
7use crate::core::{Edition, Features, MaybePackage, Package};
8use crate::util::GlobalContext;
9
10mod lint;
11mod report;
12
13pub mod rules;
14
15pub use lint::{Lint, LintGroup, LintLevel, LintLevelSource};
16pub use report::{AsIndex, get_key_value, get_key_value_span, rel_cwd_manifest_path};
17pub use rules::{LINT_GROUPS, LINTS};
18
19pub struct DiagnosticStats {
20    warning_count: usize,
21    error_count: usize,
22}
23
24impl DiagnosticStats {
25    pub fn new() -> Self {
26        Self {
27            warning_count: 0,
28            error_count: 0,
29        }
30    }
31
32    pub fn record_warning(&mut self) {
33        self.warning_count += 1;
34    }
35
36    pub fn record_error(&mut self) {
37        self.error_count += 1;
38    }
39
40    pub fn record_lint(&mut self, lint: LintLevel) {
41        match lint {
42            LintLevel::Forbid | LintLevel::Deny => {
43                self.record_error();
44            }
45            LintLevel::Warn => {
46                self.record_warning();
47            }
48            LintLevel::Allow => {}
49        }
50    }
51
52    pub fn report_summary(
53        &self,
54        action: &str,
55        name: Option<&str>,
56        gctx: &GlobalContext,
57    ) -> CargoResult<()> {
58        if 0 < self.warning_count {
59            let plural = if self.warning_count == 1 { "" } else { "s" };
60            let name = name
61                .map(|n| format!("`{n}`"))
62                .unwrap_or_else(|| "workspace".to_owned());
63            gctx.shell().warn(format!(
64                "{name} (manifest) generated {} warning{plural}",
65                self.warning_count
66            ))?;
67        }
68
69        if 0 < self.error_count {
70            let plural = if self.error_count == 1 { "" } else { "s" };
71            let name = name
72                .map(|n| format!("`{n}`"))
73                .unwrap_or_else(|| "workspace".to_owned());
74            bail!(
75                "could not {action} {name} (manifest) due to {} previous error{plural}",
76                self.error_count
77            )
78        }
79
80        Ok(())
81    }
82}
83
84/// Scope at which a lint runs: package-level or workspace-level.
85pub enum ManifestFor<'a> {
86    /// Lint runs for a specific package.
87    Package(&'a Package),
88    /// Lint runs for workspace-level config.
89    Workspace {
90        ws: &'a Workspace<'a>,
91        maybe_pkg: &'a MaybePackage,
92    },
93}
94
95impl ManifestFor<'_> {
96    fn lint_level(&self, pkg_lints: &TomlToolLints, lint: &Lint) -> (LintLevel, LintLevelSource) {
97        lint.level(pkg_lints, self.rust_version(), self.unstable_features())
98    }
99
100    pub fn rust_version(&self) -> Option<&RustVersion> {
101        match self {
102            ManifestFor::Package(p) => p.rust_version(),
103            ManifestFor::Workspace { ws, maybe_pkg: _ } => ws.lowest_rust_version(),
104        }
105    }
106
107    pub fn contents(&self) -> Option<&str> {
108        match self {
109            ManifestFor::Package(p) => p.manifest().contents(),
110            ManifestFor::Workspace { ws: _, maybe_pkg } => maybe_pkg.contents(),
111        }
112    }
113
114    pub fn document(&self) -> Option<&toml::Spanned<toml::de::DeTable<'static>>> {
115        match self {
116            ManifestFor::Package(p) => p.manifest().document(),
117            ManifestFor::Workspace { ws: _, maybe_pkg } => maybe_pkg.document(),
118        }
119    }
120
121    pub fn edition(&self) -> Edition {
122        match self {
123            ManifestFor::Package(p) => p.manifest().edition(),
124            ManifestFor::Workspace { ws: _, maybe_pkg } => maybe_pkg.edition(),
125        }
126    }
127
128    pub fn unstable_features(&self) -> &Features {
129        match self {
130            ManifestFor::Package(p) => p.manifest().unstable_features(),
131            ManifestFor::Workspace { ws: _, maybe_pkg } => maybe_pkg.unstable_features(),
132        }
133    }
134}
135
136impl<'a> From<&'a Package> for ManifestFor<'a> {
137    fn from(value: &'a Package) -> ManifestFor<'a> {
138        ManifestFor::Package(value)
139    }
140}
141
142impl<'a> From<(&'a Workspace<'a>, &'a MaybePackage)> for ManifestFor<'a> {
143    fn from((ws, maybe_pkg): (&'a Workspace<'a>, &'a MaybePackage)) -> ManifestFor<'a> {
144        ManifestFor::Workspace { ws, maybe_pkg }
145    }
146}