1use anyhow::bail;
57use cargo_util_schemas::manifest::RustVersion;
58use cargo_util_schemas::manifest::TomlToolLints;
59
60use crate::CargoResult;
61use crate::core::Workspace;
62use crate::core::{Edition, Features, MaybePackage, Package};
63use crate::util::GlobalContext;
64
65mod lint;
66mod report;
67
68pub mod passes;
69pub mod rules;
70
71pub use lint::{Lint, LintGroup, LintLevel, LintLevelProduct, LintLevelSource};
72pub use report::{AsIndex, get_key_value, get_key_value_span, rel_cwd_manifest_path};
73pub use rules::{LINT_GROUPS, LINTS};
74
75pub struct DiagnosticStats {
76 warning_count: usize,
77 lint_warning_count: usize,
78 error_count: usize,
79}
80
81impl DiagnosticStats {
82 pub fn new() -> Self {
83 Self {
84 warning_count: 0,
85 lint_warning_count: 0,
86 error_count: 0,
87 }
88 }
89
90 pub fn lint_warning_count(&self) -> usize {
91 self.lint_warning_count
92 }
93
94 pub fn warning_count(&self) -> usize {
95 self.warning_count
96 }
97
98 pub fn error_count(&self) -> usize {
99 self.error_count
100 }
101
102 pub fn record_warning(&mut self) {
103 self.warning_count += 1;
104 }
105
106 pub fn record_error(&mut self) {
107 self.error_count += 1;
108 }
109
110 pub fn record_lint(&mut self, lint: LintLevel) {
111 match lint {
112 LintLevel::Forbid | LintLevel::Deny => {
113 self.record_error();
114 }
115 LintLevel::Warn => {
116 self.lint_warning_count += 1;
117 self.record_warning();
118 }
119 LintLevel::Allow => {}
120 }
121 }
122
123 pub fn report_summary(
124 &self,
125 action: &str,
126 name: Option<&str>,
127 gctx: &GlobalContext,
128 ) -> CargoResult<()> {
129 if 0 < self.warning_count {
130 let plural = if self.warning_count == 1 { "" } else { "s" };
131 let name = name
132 .map(|n| format!("`{n}`"))
133 .unwrap_or_else(|| "workspace".to_owned());
134 gctx.shell().warn(format!(
135 "{name} (manifest) generated {} warning{plural}",
136 self.warning_count
137 ))?;
138 }
139
140 if 0 < self.error_count {
141 let plural = if self.error_count == 1 { "" } else { "s" };
142 let name = name
143 .map(|n| format!("`{n}`"))
144 .unwrap_or_else(|| "workspace".to_owned());
145 bail!(
146 "could not {action} {name} (manifest) due to {} previous error{plural}",
147 self.error_count
148 )
149 }
150
151 Ok(())
152 }
153}
154
155impl std::ops::Add for DiagnosticStats {
156 type Output = DiagnosticStats;
157
158 fn add(mut self, rhs: Self) -> Self::Output {
159 self += rhs;
160 self
161 }
162}
163
164impl std::ops::AddAssign for DiagnosticStats {
165 fn add_assign(&mut self, rhs: Self) {
166 let DiagnosticStats {
167 warning_count,
168 lint_warning_count,
169 error_count,
170 } = rhs;
171 self.warning_count += warning_count;
172 self.lint_warning_count += lint_warning_count;
173 self.error_count += error_count;
174 }
175}
176
177pub enum ManifestFor<'a> {
179 Package(&'a Package),
181 Workspace {
183 ws: &'a Workspace<'a>,
184 maybe_pkg: &'a MaybePackage,
185 },
186}
187
188impl ManifestFor<'_> {
189 fn lint_level(&self, pkg_lints: &TomlToolLints, lint: &Lint) -> LintLevelProduct {
190 lint.level(pkg_lints, self.rust_version(), self.unstable_features())
191 }
192
193 pub fn rust_version(&self) -> Option<&RustVersion> {
194 match self {
195 ManifestFor::Package(p) => p.rust_version(),
196 ManifestFor::Workspace { ws, maybe_pkg: _ } => ws.lowest_rust_version(),
197 }
198 }
199
200 pub fn contents(&self) -> Option<&str> {
201 match self {
202 ManifestFor::Package(p) => p.manifest().contents(),
203 ManifestFor::Workspace { ws: _, maybe_pkg } => maybe_pkg.contents(),
204 }
205 }
206
207 pub fn document(&self) -> Option<&toml::Spanned<toml::de::DeTable<'static>>> {
208 match self {
209 ManifestFor::Package(p) => p.manifest().document(),
210 ManifestFor::Workspace { ws: _, maybe_pkg } => maybe_pkg.document(),
211 }
212 }
213
214 pub fn edition(&self) -> Edition {
215 match self {
216 ManifestFor::Package(p) => p.manifest().edition(),
217 ManifestFor::Workspace { ws: _, maybe_pkg } => maybe_pkg.edition(),
218 }
219 }
220
221 pub fn unstable_features(&self) -> &Features {
222 match self {
223 ManifestFor::Package(p) => p.manifest().unstable_features(),
224 ManifestFor::Workspace { ws: _, maybe_pkg } => maybe_pkg.unstable_features(),
225 }
226 }
227}
228
229impl<'a> From<&'a Package> for ManifestFor<'a> {
230 fn from(value: &'a Package) -> ManifestFor<'a> {
231 ManifestFor::Package(value)
232 }
233}
234
235impl<'a> From<(&'a Workspace<'a>, &'a MaybePackage)> for ManifestFor<'a> {
236 fn from((ws, maybe_pkg): (&'a Workspace<'a>, &'a MaybePackage)) -> ManifestFor<'a> {
237 ManifestFor::Workspace { ws, maybe_pkg }
238 }
239}