Skip to content

Commit bbea437

Browse files
committed
Auto merge of #13837 - Muscraft:only-underscores-lint-names, r=epage
fix(lints): Remove ability to specify `-` in lint name In a recent Cargo Team meeting, it was discussed whether our lint should use `-` or `_` and if we should rewrite the wrong form to the correct one. It was decided that Cargo would use `_` for lint names and would not convert `-` to `_` automatically; instead, we would warn about an "unknown lint" and mention the similarly named lint with `_`, if found. The decision to ise `_` was made because it is the canonical representation, as well as [RFC #344](https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints) specifies: > Use snake case in the same way you would for function names. This PR implements these changes. Note: This adds an `unknown_lints` lint, that tries to mirror [the lint `rustc` has with the same name](https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unknown-lints).
2 parents 82dca28 + 6c08e58 commit bbea437

File tree

12 files changed

+401
-95
lines changed

12 files changed

+401
-95
lines changed

src/cargo/core/workspace.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1161,7 +1161,6 @@ impl<'gctx> Workspace<'gctx> {
11611161
.cloned()
11621162
.unwrap_or_default()
11631163
.into_iter()
1164-
.map(|(k, v)| (k.replace('-', "_"), v))
11651164
.collect();
11661165

11671166
for (path, maybe_pkg) in &self.packages.packages {
@@ -1214,10 +1213,6 @@ impl<'gctx> Workspace<'gctx> {
12141213
.get("cargo")
12151214
.cloned()
12161215
.unwrap_or(manifest::TomlToolLints::default());
1217-
let normalized_lints = cargo_lints
1218-
.into_iter()
1219-
.map(|(name, lint)| (name.replace('-', "_"), lint))
1220-
.collect();
12211216

12221217
// We should only be using workspace lints if the `[lints]` table is
12231218
// present in the manifest, and `workspace` is set to `true`
@@ -1242,7 +1237,7 @@ impl<'gctx> Workspace<'gctx> {
12421237
analyze_cargo_lints_table(
12431238
pkg,
12441239
&path,
1245-
&normalized_lints,
1240+
&cargo_lints,
12461241
ws_cargo_lints,
12471242
ws_contents,
12481243
ws_document,
@@ -1252,23 +1247,23 @@ impl<'gctx> Workspace<'gctx> {
12521247
check_im_a_teapot(
12531248
pkg,
12541249
&path,
1255-
&normalized_lints,
1250+
&cargo_lints,
12561251
ws_cargo_lints,
12571252
&mut error_count,
12581253
self.gctx,
12591254
)?;
12601255
check_implicit_features(
12611256
pkg,
12621257
&path,
1263-
&normalized_lints,
1258+
&cargo_lints,
12641259
ws_cargo_lints,
12651260
&mut error_count,
12661261
self.gctx,
12671262
)?;
12681263
unused_dependencies(
12691264
pkg,
12701265
&path,
1271-
&normalized_lints,
1266+
&cargo_lints,
12721267
ws_cargo_lints,
12731268
&mut error_count,
12741269
self.gctx,

src/cargo/util/lints.rs

Lines changed: 180 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -29,45 +29,68 @@ pub fn analyze_cargo_lints_table(
2929
let manifest = pkg.manifest();
3030
let manifest_path = rel_cwd_manifest_path(path, gctx);
3131
let ws_path = rel_cwd_manifest_path(ws_path, gctx);
32-
33-
for lint_name in pkg_lints
32+
let mut unknown_lints = Vec::new();
33+
for (lint_name, specified_in) in pkg_lints
3434
.keys()
35-
.chain(ws_lints.map(|l| l.keys()).unwrap_or_default())
35+
.map(|name| (name, SpecifiedIn::Package))
36+
.chain(
37+
ws_lints
38+
.map(|l| l.keys())
39+
.unwrap_or_default()
40+
.map(|name| (name, SpecifiedIn::Workspace)),
41+
)
3642
{
37-
if let Some((name, default_level, edition_lint_opts, feature_gate)) =
43+
let Some((name, default_level, edition_lint_opts, feature_gate)) =
3844
find_lint_or_group(lint_name)
39-
{
40-
let (_, reason, _) = level_priority(
41-
name,
42-
*default_level,
43-
*edition_lint_opts,
44-
pkg_lints,
45-
ws_lints,
46-
manifest.edition(),
47-
);
48-
49-
// Only run analysis on user-specified lints
50-
if !reason.is_user_specified() {
51-
continue;
52-
}
45+
else {
46+
unknown_lints.push((lint_name, specified_in));
47+
continue;
48+
};
5349

54-
// Only run this on lints that are gated by a feature
55-
if let Some(feature_gate) = feature_gate {
56-
verify_feature_enabled(
57-
name,
58-
feature_gate,
59-
reason,
60-
manifest,
61-
&manifest_path,
62-
ws_contents,
63-
ws_document,
64-
&ws_path,
65-
&mut error_count,
66-
gctx,
67-
)?;
68-
}
50+
let (_, reason, _) = level_priority(
51+
name,
52+
*default_level,
53+
*edition_lint_opts,
54+
pkg_lints,
55+
ws_lints,
56+
manifest.edition(),
57+
);
58+
59+
// Only run analysis on user-specified lints
60+
if !reason.is_user_specified() {
61+
continue;
62+
}
63+
64+
// Only run this on lints that are gated by a feature
65+
if let Some(feature_gate) = feature_gate {
66+
verify_feature_enabled(
67+
name,
68+
feature_gate,
69+
reason,
70+
manifest,
71+
&manifest_path,
72+
ws_contents,
73+
ws_document,
74+
&ws_path,
75+
&mut error_count,
76+
gctx,
77+
)?;
6978
}
7079
}
80+
81+
output_unknown_lints(
82+
unknown_lints,
83+
manifest,
84+
&manifest_path,
85+
pkg_lints,
86+
ws_lints,
87+
ws_contents,
88+
ws_document,
89+
&ws_path,
90+
&mut error_count,
91+
gctx,
92+
)?;
93+
7194
if error_count > 0 {
7295
Err(anyhow::anyhow!(
7396
"encountered {error_count} errors(s) while verifying lints",
@@ -117,31 +140,21 @@ fn verify_feature_enabled(
117140
gctx: &GlobalContext,
118141
) -> CargoResult<()> {
119142
if !manifest.unstable_features().is_enabled(feature_gate) {
120-
let dash_name = lint_name.replace("_", "-");
121143
let dash_feature_name = feature_gate.name().replace("_", "-");
122-
let title = format!("use of unstable lint `{}`", dash_name);
144+
let title = format!("use of unstable lint `{}`", lint_name);
123145
let label = format!(
124146
"this is behind `{}`, which is not enabled",
125147
dash_feature_name
126148
);
127-
let second_title = format!("`cargo::{}` was inherited", dash_name);
149+
let second_title = format!("`cargo::{}` was inherited", lint_name);
128150
let help = format!(
129151
"consider adding `cargo-features = [\"{}\"]` to the top of the manifest",
130152
dash_feature_name
131153
);
132154
let message = match reason {
133155
LintLevelReason::Package => {
134-
let span = get_span(
135-
manifest.document(),
136-
&["lints", "cargo", dash_name.as_str()],
137-
false,
138-
)
139-
.or(get_span(
140-
manifest.document(),
141-
&["lints", "cargo", lint_name],
142-
false,
143-
))
144-
.unwrap();
156+
let span =
157+
get_span(manifest.document(), &["lints", "cargo", lint_name], false).unwrap();
145158

146159
Level::Error
147160
.title(&title)
@@ -155,15 +168,10 @@ fn verify_feature_enabled(
155168
}
156169
LintLevelReason::Workspace => {
157170
let lint_span = get_span(
158-
ws_document,
159-
&["workspace", "lints", "cargo", dash_name.as_str()],
160-
false,
161-
)
162-
.or(get_span(
163171
ws_document,
164172
&["workspace", "lints", "cargo", lint_name],
165173
false,
166-
))
174+
)
167175
.unwrap();
168176
let inherit_span_key =
169177
get_span(manifest.document(), &["lints", "workspace"], false).unwrap();
@@ -395,6 +403,11 @@ impl LintLevelReason {
395403
}
396404
}
397405

406+
enum SpecifiedIn {
407+
Package,
408+
Workspace,
409+
}
410+
398411
fn level_priority(
399412
name: &str,
400413
default_level: LintLevel,
@@ -588,6 +601,120 @@ pub fn check_implicit_features(
588601
Ok(())
589602
}
590603

604+
const UNKNOWN_LINTS: Lint = Lint {
605+
name: "unknown_lints",
606+
desc: "unknown lint",
607+
groups: &[],
608+
default_level: LintLevel::Warn,
609+
edition_lint_opts: None,
610+
feature_gate: None,
611+
};
612+
613+
fn output_unknown_lints(
614+
unknown_lints: Vec<(&String, SpecifiedIn)>,
615+
manifest: &Manifest,
616+
manifest_path: &str,
617+
pkg_lints: &TomlToolLints,
618+
ws_lints: Option<&TomlToolLints>,
619+
ws_contents: &str,
620+
ws_document: &ImDocument<String>,
621+
ws_path: &str,
622+
error_count: &mut usize,
623+
gctx: &GlobalContext,
624+
) -> CargoResult<()> {
625+
let (lint_level, reason) = UNKNOWN_LINTS.level(
626+
pkg_lints,
627+
ws_lints,
628+
manifest.edition(),
629+
manifest.unstable_features(),
630+
);
631+
if lint_level == LintLevel::Allow {
632+
return Ok(());
633+
}
634+
635+
let level = lint_level.to_diagnostic_level();
636+
let mut emitted_source = None;
637+
for (lint_name, specified_in) in unknown_lints {
638+
if lint_level == LintLevel::Forbid || lint_level == LintLevel::Deny {
639+
*error_count += 1;
640+
}
641+
let title = format!("{}: `{lint_name}`", UNKNOWN_LINTS.desc);
642+
let second_title = format!("`cargo::{}` was inherited", lint_name);
643+
let underscore_lint_name = lint_name.replace("-", "_");
644+
let matching = if let Some(lint) = LINTS.iter().find(|l| l.name == underscore_lint_name) {
645+
Some((lint.name, "lint"))
646+
} else if let Some(group) = LINT_GROUPS.iter().find(|g| g.name == underscore_lint_name) {
647+
Some((group.name, "group"))
648+
} else {
649+
None
650+
};
651+
let help =
652+
matching.map(|(name, kind)| format!("there is a {kind} with a similar name: `{name}`"));
653+
654+
let mut message = match specified_in {
655+
SpecifiedIn::Package => {
656+
let span =
657+
get_span(manifest.document(), &["lints", "cargo", lint_name], false).unwrap();
658+
659+
level.title(&title).snippet(
660+
Snippet::source(manifest.contents())
661+
.origin(&manifest_path)
662+
.annotation(Level::Error.span(span))
663+
.fold(true),
664+
)
665+
}
666+
SpecifiedIn::Workspace => {
667+
let lint_span = get_span(
668+
ws_document,
669+
&["workspace", "lints", "cargo", lint_name],
670+
false,
671+
)
672+
.unwrap();
673+
let inherit_span_key =
674+
get_span(manifest.document(), &["lints", "workspace"], false).unwrap();
675+
let inherit_span_value =
676+
get_span(manifest.document(), &["lints", "workspace"], true).unwrap();
677+
678+
level
679+
.title(&title)
680+
.snippet(
681+
Snippet::source(ws_contents)
682+
.origin(&ws_path)
683+
.annotation(Level::Error.span(lint_span))
684+
.fold(true),
685+
)
686+
.footer(
687+
Level::Note.title(&second_title).snippet(
688+
Snippet::source(manifest.contents())
689+
.origin(&manifest_path)
690+
.annotation(
691+
Level::Note
692+
.span(inherit_span_key.start..inherit_span_value.end),
693+
)
694+
.fold(true),
695+
),
696+
)
697+
}
698+
};
699+
700+
if emitted_source.is_none() {
701+
emitted_source = Some(format!(
702+
"`cargo::{}` is set to `{lint_level}` {reason}",
703+
UNKNOWN_LINTS.name
704+
));
705+
message = message.footer(Level::Note.title(emitted_source.as_ref().unwrap()));
706+
}
707+
708+
if let Some(help) = help.as_ref() {
709+
message = message.footer(Level::Help.title(help));
710+
}
711+
712+
gctx.shell().print_message(message)?;
713+
}
714+
715+
Ok(())
716+
}
717+
591718
const UNUSED_OPTIONAL_DEPENDENCY: Lint = Lint {
592719
name: "unused_optional_dependency",
593720
desc: "unused optional dependency",

tests/testsuite/lints/implicit_features/edition_2021_warn/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ baz = { version = "0.1.0", optional = true }
2727
target-dep = { version = "0.1.0", optional = true }
2828
2929
[lints.cargo]
30-
implicit-features = "warn"
30+
implicit_features = "warn"
3131
"#,
3232
)
3333
.file("src/lib.rs", "")

tests/testsuite/lints/implicit_features/edition_2024/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ baz = { version = "0.1.0", optional = true }
2525
baz = ["dep:baz"]
2626
2727
[lints.cargo]
28-
unused-optional-dependency = "allow"
28+
unused_optional_dependency = "allow"
2929
"#,
3030
)
3131
.file("src/lib.rs", "")

tests/testsuite/lints/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
mod implicit_features;
2+
mod unknown_lints;
23
mod unused_optional_dependencies;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use cargo_test_support::prelude::*;
2+
use cargo_test_support::str;
3+
use cargo_test_support::{file, project};
4+
5+
#[cargo_test]
6+
fn case() {
7+
let p = project()
8+
.file(
9+
"Cargo.toml",
10+
r#"
11+
[package]
12+
name = "foo"
13+
version = "0.0.1"
14+
edition = "2015"
15+
authors = []
16+
17+
[lints.cargo]
18+
this-lint-does-not-exist = "warn"
19+
"#,
20+
)
21+
.file("src/lib.rs", "")
22+
.build();
23+
24+
snapbox::cmd::Command::cargo_ui()
25+
.masquerade_as_nightly_cargo(&["cargo-lints"])
26+
.current_dir(p.root())
27+
.arg("check")
28+
.arg("-Zcargo-lints")
29+
.assert()
30+
.success()
31+
.stdout_matches(str![""])
32+
.stderr_matches(file!["stderr.term.svg"]);
33+
}

0 commit comments

Comments
 (0)