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
84pub enum ManifestFor<'a> {
86 Package(&'a Package),
88 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}