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