Skip to content

Commit 5f13038

Browse files
committed
Auto merge of #13801 - Muscraft:add-lint-reason, r=epage
Add where lint was set `rustc` and `clippy` both show why the lint was emitted and where the level was set the first time it was emitted for a package. We already showed why the list was being emitted but did not show where the lint level was set. This PR adds where the lint was set at.
2 parents 955503e + dfc9bd2 commit 5f13038

File tree

7 files changed

+207
-71
lines changed

7 files changed

+207
-71
lines changed

src/cargo/core/workspace.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,21 @@ impl<'gctx> Workspace<'gctx> {
11801180
}
11811181

11821182
pub fn emit_lints(&self, pkg: &Package, path: &Path) -> CargoResult<()> {
1183+
let ws_lints = self
1184+
.root_maybe()
1185+
.workspace_config()
1186+
.inheritable()
1187+
.and_then(|i| i.lints().ok())
1188+
.unwrap_or_default();
1189+
1190+
let ws_cargo_lints = ws_lints
1191+
.get("cargo")
1192+
.cloned()
1193+
.unwrap_or_default()
1194+
.into_iter()
1195+
.map(|(k, v)| (k.replace('-', "_"), v))
1196+
.collect();
1197+
11831198
let mut error_count = 0;
11841199
let toml_lints = pkg
11851200
.manifest()
@@ -1197,9 +1212,30 @@ impl<'gctx> Workspace<'gctx> {
11971212
.map(|(name, lint)| (name.replace('-', "_"), lint))
11981213
.collect();
11991214

1200-
check_im_a_teapot(pkg, &path, &normalized_lints, &mut error_count, self.gctx)?;
1201-
check_implicit_features(pkg, &path, &normalized_lints, &mut error_count, self.gctx)?;
1202-
unused_dependencies(pkg, &path, &normalized_lints, &mut error_count, self.gctx)?;
1215+
check_im_a_teapot(
1216+
pkg,
1217+
&path,
1218+
&normalized_lints,
1219+
&ws_cargo_lints,
1220+
&mut error_count,
1221+
self.gctx,
1222+
)?;
1223+
check_implicit_features(
1224+
pkg,
1225+
&path,
1226+
&normalized_lints,
1227+
&ws_cargo_lints,
1228+
&mut error_count,
1229+
self.gctx,
1230+
)?;
1231+
unused_dependencies(
1232+
pkg,
1233+
&path,
1234+
&normalized_lints,
1235+
&ws_cargo_lints,
1236+
&mut error_count,
1237+
self.gctx,
1238+
)?;
12031239
if error_count > 0 {
12041240
Err(crate::util::errors::AlreadyPrintedError::new(anyhow!(
12051241
"encountered {error_count} errors(s) while running lints"

src/cargo/util/lints.rs

Lines changed: 105 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -85,40 +85,41 @@ pub struct Lint {
8585
}
8686

8787
impl Lint {
88-
pub fn level(&self, lints: &TomlToolLints, edition: Edition) -> LintLevel {
89-
let edition_level = self
90-
.edition_lint_opts
91-
.filter(|(e, _)| edition >= *e)
92-
.map(|(_, l)| l);
93-
94-
if self.default_level == LintLevel::Forbid || edition_level == Some(LintLevel::Forbid) {
95-
return LintLevel::Forbid;
96-
}
97-
98-
let level = self
99-
.groups
88+
pub fn level(
89+
&self,
90+
pkg_lints: &TomlToolLints,
91+
ws_lints: &TomlToolLints,
92+
edition: Edition,
93+
) -> (LintLevel, LintLevelReason) {
94+
self.groups
10095
.iter()
101-
.map(|g| g.name)
102-
.chain(std::iter::once(self.name))
103-
.filter_map(|n| lints.get(n).map(|l| (n, l)))
104-
.max_by_key(|(n, l)| {
96+
.map(|g| {
10597
(
106-
l.level() == TomlLintLevel::Forbid,
107-
l.priority(),
108-
std::cmp::Reverse(*n),
98+
g.name,
99+
level_priority(
100+
g.name,
101+
g.default_level,
102+
g.edition_lint_opts,
103+
pkg_lints,
104+
ws_lints,
105+
edition,
106+
),
109107
)
110-
});
111-
112-
match level {
113-
Some((_, toml_lint)) => toml_lint.level().into(),
114-
None => {
115-
if let Some(level) = edition_level {
116-
level
117-
} else {
118-
self.default_level
119-
}
120-
}
121-
}
108+
})
109+
.chain(std::iter::once((
110+
self.name,
111+
level_priority(
112+
self.name,
113+
self.default_level,
114+
self.edition_lint_opts,
115+
pkg_lints,
116+
ws_lints,
117+
edition,
118+
),
119+
)))
120+
.max_by_key(|(n, (l, _, p))| (l == &LintLevel::Forbid, *p, std::cmp::Reverse(*n)))
121+
.map(|(_, (l, r, _))| (l, r))
122+
.unwrap()
122123
}
123124
}
124125

@@ -163,6 +164,64 @@ impl From<TomlLintLevel> for LintLevel {
163164
}
164165
}
165166

167+
#[derive(Copy, Clone, Debug)]
168+
pub enum LintLevelReason {
169+
Default,
170+
Edition(Edition),
171+
Package,
172+
Workspace,
173+
}
174+
175+
impl Display for LintLevelReason {
176+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177+
match self {
178+
LintLevelReason::Default => write!(f, "by default"),
179+
LintLevelReason::Edition(edition) => write!(f, "in edition {}", edition),
180+
LintLevelReason::Package => write!(f, "in `[lints]`"),
181+
LintLevelReason::Workspace => write!(f, "in `[workspace.lints]`"),
182+
}
183+
}
184+
}
185+
186+
fn level_priority(
187+
name: &str,
188+
default_level: LintLevel,
189+
edition_lint_opts: Option<(Edition, LintLevel)>,
190+
pkg_lints: &TomlToolLints,
191+
ws_lints: &TomlToolLints,
192+
edition: Edition,
193+
) -> (LintLevel, LintLevelReason, i8) {
194+
let (unspecified_level, reason) = if let Some(level) = edition_lint_opts
195+
.filter(|(e, _)| edition >= *e)
196+
.map(|(_, l)| l)
197+
{
198+
(level, LintLevelReason::Edition(edition))
199+
} else {
200+
(default_level, LintLevelReason::Default)
201+
};
202+
203+
// Don't allow the group to be overridden if the level is `Forbid`
204+
if unspecified_level == LintLevel::Forbid {
205+
return (unspecified_level, reason, 0);
206+
}
207+
208+
if let Some(defined_level) = pkg_lints.get(name) {
209+
(
210+
defined_level.level().into(),
211+
LintLevelReason::Package,
212+
defined_level.priority(),
213+
)
214+
} else if let Some(defined_level) = ws_lints.get(name) {
215+
(
216+
defined_level.level().into(),
217+
LintLevelReason::Workspace,
218+
defined_level.priority(),
219+
)
220+
} else {
221+
(unspecified_level, reason, 0)
222+
}
223+
}
224+
166225
const IM_A_TEAPOT: Lint = Lint {
167226
name: "im_a_teapot",
168227
desc: "`im_a_teapot` is specified",
@@ -174,12 +233,13 @@ const IM_A_TEAPOT: Lint = Lint {
174233
pub fn check_im_a_teapot(
175234
pkg: &Package,
176235
path: &Path,
177-
lints: &TomlToolLints,
236+
pkg_lints: &TomlToolLints,
237+
ws_lints: &TomlToolLints,
178238
error_count: &mut usize,
179239
gctx: &GlobalContext,
180240
) -> CargoResult<()> {
181241
let manifest = pkg.manifest();
182-
let lint_level = IM_A_TEAPOT.level(lints, manifest.edition());
242+
let (lint_level, reason) = IM_A_TEAPOT.level(pkg_lints, ws_lints, manifest.edition());
183243
if lint_level == LintLevel::Allow {
184244
return Ok(());
185245
}
@@ -194,7 +254,10 @@ pub fn check_im_a_teapot(
194254
}
195255
let level = lint_level.to_diagnostic_level();
196256
let manifest_path = rel_cwd_manifest_path(path, gctx);
197-
let emitted_reason = format!("`cargo::{}` is set to `{lint_level}`", IM_A_TEAPOT.name);
257+
let emitted_reason = format!(
258+
"`cargo::{}` is set to `{lint_level}` {reason}",
259+
IM_A_TEAPOT.name
260+
);
198261

199262
let key_span = get_span(manifest.document(), &["package", "im-a-teapot"], false).unwrap();
200263
let value_span = get_span(manifest.document(), &["package", "im-a-teapot"], true).unwrap();
@@ -242,7 +305,8 @@ const IMPLICIT_FEATURES: Lint = Lint {
242305
pub fn check_implicit_features(
243306
pkg: &Package,
244307
path: &Path,
245-
lints: &TomlToolLints,
308+
pkg_lints: &TomlToolLints,
309+
ws_lints: &TomlToolLints,
246310
error_count: &mut usize,
247311
gctx: &GlobalContext,
248312
) -> CargoResult<()> {
@@ -253,7 +317,7 @@ pub fn check_implicit_features(
253317
return Ok(());
254318
}
255319

256-
let lint_level = IMPLICIT_FEATURES.level(lints, edition);
320+
let (lint_level, reason) = IMPLICIT_FEATURES.level(pkg_lints, ws_lints, edition);
257321
if lint_level == LintLevel::Allow {
258322
return Ok(());
259323
}
@@ -298,7 +362,7 @@ pub fn check_implicit_features(
298362
);
299363
if emitted_source.is_none() {
300364
emitted_source = Some(format!(
301-
"`cargo::{}` is set to `{lint_level}`",
365+
"`cargo::{}` is set to `{lint_level}` {reason}",
302366
IMPLICIT_FEATURES.name
303367
));
304368
message = message.footer(Level::Note.title(emitted_source.as_ref().unwrap()));
@@ -325,7 +389,8 @@ const UNUSED_OPTIONAL_DEPENDENCY: Lint = Lint {
325389
pub fn unused_dependencies(
326390
pkg: &Package,
327391
path: &Path,
328-
lints: &TomlToolLints,
392+
pkg_lints: &TomlToolLints,
393+
ws_lints: &TomlToolLints,
329394
error_count: &mut usize,
330395
gctx: &GlobalContext,
331396
) -> CargoResult<()> {
@@ -335,7 +400,7 @@ pub fn unused_dependencies(
335400
return Ok(());
336401
}
337402

338-
let lint_level = UNUSED_OPTIONAL_DEPENDENCY.level(lints, edition);
403+
let (lint_level, reason) = UNUSED_OPTIONAL_DEPENDENCY.level(pkg_lints, ws_lints, edition);
339404
if lint_level == LintLevel::Allow {
340405
return Ok(());
341406
}
@@ -401,7 +466,7 @@ pub fn unused_dependencies(
401466
);
402467
if emitted_source.is_none() {
403468
emitted_source = Some(format!(
404-
"`cargo::{}` is set to `{lint_level}`",
469+
"`cargo::{}` is set to `{lint_level}` {reason}",
405470
UNUSED_OPTIONAL_DEPENDENCY.name
406471
));
407472
message =

src/cargo/util/toml/mod.rs

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -411,15 +411,7 @@ fn resolve_toml(
411411
}
412412
resolved_toml.target = (!resolved_target.is_empty()).then_some(resolved_target);
413413

414-
let resolved_lints = original_toml
415-
.lints
416-
.clone()
417-
.map(|value| lints_inherit_with(value, || inherit()?.lints()))
418-
.transpose()?;
419-
resolved_toml.lints = resolved_lints.map(|lints| manifest::InheritableLints {
420-
workspace: false,
421-
lints,
422-
});
414+
resolved_toml.lints = original_toml.lints.clone();
423415

424416
let resolved_badges = original_toml
425417
.badges
@@ -803,7 +795,7 @@ impl InheritableFields {
803795
}
804796

805797
/// Gets the field `workspace.lint`.
806-
fn lints(&self) -> CargoResult<manifest::TomlLints> {
798+
pub fn lints(&self) -> CargoResult<manifest::TomlLints> {
807799
let Some(val) = &self.lints else {
808800
bail!("`workspace.lints` was not defined");
809801
};
@@ -1276,18 +1268,18 @@ fn to_real_manifest(
12761268
}
12771269
}
12781270

1279-
verify_lints(
1280-
resolved_toml.resolved_lints().expect("previously resolved"),
1281-
gctx,
1282-
warnings,
1283-
)?;
1284-
let default = manifest::TomlLints::default();
1285-
let rustflags = lints_to_rustflags(
1286-
resolved_toml
1287-
.resolved_lints()
1288-
.expect("previously resolved")
1289-
.unwrap_or(&default),
1290-
);
1271+
let resolved_lints = resolved_toml
1272+
.lints
1273+
.clone()
1274+
.map(|value| {
1275+
lints_inherit_with(value, || {
1276+
load_inheritable_fields(gctx, manifest_file, &workspace_config)?.lints()
1277+
})
1278+
})
1279+
.transpose()?;
1280+
1281+
verify_lints(resolved_lints.as_ref(), gctx, warnings)?;
1282+
let rustflags = lints_to_rustflags(&resolved_lints.unwrap_or_default());
12911283

12921284
let metadata = ManifestMetadata {
12931285
description: resolved_package

tests/testsuite/lints/implicit_features/edition_2021_warn/stderr.term.svg

Lines changed: 1 addition & 1 deletion
Loading

tests/testsuite/lints/unused_optional_dependencies/edition_2024/stderr.term.svg

Lines changed: 1 addition & 1 deletion
Loading

tests/testsuite/lints/unused_optional_dependencies/renamed_deps/stderr.term.svg

Lines changed: 1 addition & 1 deletion
Loading

0 commit comments

Comments
 (0)