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