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
85pub enum ManifestFor<'a> {
87 Package(&'a Package),
89 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}