1use 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, cwd_rel_path, get_key_value, get_key_value_span, workspace_rel_path};
73pub use rules::{LINT_GROUPS, LINTS};
74
75pub struct PassOutput {
76 pub lint_warning_count: usize,
77}
78
79pub struct GlobalDiagnosticStats {
80 error_count: usize,
81 lint_warning_count: usize,
82}
83
84impl GlobalDiagnosticStats {
85 pub fn new() -> Self {
86 Self {
87 error_count: 0,
88 lint_warning_count: 0,
89 }
90 }
91
92 pub fn scope(&mut self) -> ScopedDiagnosticStats<'_> {
93 ScopedDiagnosticStats {
94 warning_count: 0,
95 error_count: 0,
96 global: self,
97 }
98 }
99
100 pub fn error_count(&self) -> usize {
101 self.error_count
102 }
103
104 pub fn lint_warning_count(&self) -> usize {
105 self.lint_warning_count
106 }
107
108 pub fn ok(&self) -> CargoResult<PassOutput> {
109 if 0 < self.error_count {
110 Err(crate::Error::new(crate::AlreadyPrintedError::new(
111 anyhow::format_err!("see above"),
112 )))
113 } else {
114 Ok(PassOutput {
115 lint_warning_count: self.lint_warning_count,
116 })
117 }
118 }
119}
120
121pub struct ScopedDiagnosticStats<'g> {
122 warning_count: usize,
123 error_count: usize,
124 global: &'g mut GlobalDiagnosticStats,
125}
126
127impl ScopedDiagnosticStats<'_> {
128 pub fn warning_count(&self) -> usize {
129 self.warning_count
130 }
131
132 pub fn error_count(&self) -> usize {
133 self.error_count
134 }
135
136 pub fn record_warning(&mut self) {
137 self.warning_count += 1;
138 }
139
140 pub fn record_error(&mut self) {
141 self.error_count += 1;
142 self.global.error_count += 1;
143 }
144
145 pub fn record_lint(&mut self, lint: LintLevel) {
146 match lint {
147 LintLevel::Forbid | LintLevel::Deny => {
148 self.record_error();
149 }
150 LintLevel::Warn => {
151 self.global.lint_warning_count += 1;
152 self.record_warning();
153 }
154 LintLevel::Allow => {}
155 }
156 }
157
158 pub fn report_summary(
162 &self,
163 action: &str,
164 name: Option<&str>,
165 gctx: &GlobalContext,
166 ) -> CargoResult<()> {
167 if 0 < self.warning_count {
168 let plural = if self.warning_count == 1 { "" } else { "s" };
169 let name = name
170 .map(|n| format!("`{n}`"))
171 .unwrap_or_else(|| "workspace".to_owned());
172 gctx.shell().warn(format!(
173 "{name} (manifest) generated {} warning{plural}",
174 self.warning_count
175 ))?;
176 }
177
178 if 0 < self.error_count {
179 let plural = if self.error_count == 1 { "" } else { "s" };
180 let name = name
181 .map(|n| format!("`{n}`"))
182 .unwrap_or_else(|| "workspace".to_owned());
183 gctx.shell().error(format!(
184 "could not {action} {name} (manifest) due to {} previous error{plural}",
185 self.error_count
186 ))?;
187 }
188
189 Ok(())
190 }
191}
192
193pub enum ManifestFor<'a> {
195 Package(&'a Package),
197 Workspace {
199 ws: &'a Workspace<'a>,
200 maybe_pkg: &'a MaybePackage,
201 },
202}
203
204impl ManifestFor<'_> {
205 fn lint_level(
206 &self,
207 pkg_lints: &TomlToolLints,
208 lint: &Lint,
209 gctx: &GlobalContext,
210 ) -> LintLevelProduct {
211 lint.level(
212 pkg_lints,
213 self.rust_version(),
214 self.unstable_features(),
215 gctx,
216 )
217 }
218
219 pub fn rust_version(&self) -> Option<&RustVersion> {
220 match self {
221 ManifestFor::Package(p) => p.rust_version(),
222 ManifestFor::Workspace { ws, maybe_pkg: _ } => ws.lowest_rust_version(),
223 }
224 }
225
226 pub fn contents(&self) -> Option<&str> {
227 match self {
228 ManifestFor::Package(p) => p.manifest().contents(),
229 ManifestFor::Workspace { ws: _, maybe_pkg } => maybe_pkg.contents(),
230 }
231 }
232
233 pub fn document(&self) -> Option<&toml::Spanned<toml::de::DeTable<'static>>> {
234 match self {
235 ManifestFor::Package(p) => p.manifest().document(),
236 ManifestFor::Workspace { ws: _, maybe_pkg } => maybe_pkg.document(),
237 }
238 }
239
240 pub fn edition(&self) -> Edition {
241 match self {
242 ManifestFor::Package(p) => p.manifest().edition(),
243 ManifestFor::Workspace { ws: _, maybe_pkg } => maybe_pkg.edition(),
244 }
245 }
246
247 pub fn unstable_features(&self) -> &Features {
248 match self {
249 ManifestFor::Package(p) => p.manifest().unstable_features(),
250 ManifestFor::Workspace { ws: _, maybe_pkg } => maybe_pkg.unstable_features(),
251 }
252 }
253}
254
255impl<'a> From<&'a Package> for ManifestFor<'a> {
256 fn from(value: &'a Package) -> ManifestFor<'a> {
257 ManifestFor::Package(value)
258 }
259}
260
261impl<'a> From<(&'a Workspace<'a>, &'a MaybePackage)> for ManifestFor<'a> {
262 fn from((ws, maybe_pkg): (&'a Workspace<'a>, &'a MaybePackage)) -> ManifestFor<'a> {
263 ManifestFor::Workspace { ws, maybe_pkg }
264 }
265}