Skip to content

Commit 484f0f2

Browse files
committed
Auto merge of #13262 - epage:unused, r=ehuss
fix(manifest): Provide unused key warnings for lints table ### What does this PR try to resolve? The use of `flatten` was getting in the way of `serde_ignored`. A common workaround is to add our own `unused` tracking but that would cause duplicates with `workspace.lints` (or we'd just ignore it). Since the manual deserializer was relatively simple, I went that route. Fixes #12917 ### How should we test and review this PR? Per commit A test was added for the issue. I then was worried about regressions in `workspace = false` errors (and I was right) so I added a test for that. To get `workspace = false` to work nicely, I made it share code with other `workspace: bool` fields. ### Additional information
2 parents bde42f1 + 6e86530 commit 484f0f2

File tree

2 files changed

+149
-18
lines changed

2 files changed

+149
-18
lines changed

crates/cargo-util-schemas/src/manifest.rs

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -410,11 +410,7 @@ impl<'de> de::Deserialize<'de> for InheritableBtreeMap {
410410
if let Ok(w) = TomlInheritedField::deserialize(
411411
serde_value::ValueDeserializer::<D::Error>::new(value.clone()),
412412
) {
413-
return if w.workspace {
414-
Ok(InheritableField::Inherit(w))
415-
} else {
416-
Err(de::Error::custom("`workspace` cannot be false"))
417-
};
413+
return Ok(InheritableField::Inherit(w));
418414
}
419415
BTreeMap::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
420416
.map(InheritableField::Value)
@@ -424,13 +420,14 @@ impl<'de> de::Deserialize<'de> for InheritableBtreeMap {
424420
#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
425421
#[serde(rename_all = "kebab-case")]
426422
pub struct TomlInheritedField {
427-
#[serde(deserialize_with = "bool_no_false")]
428-
workspace: bool,
423+
workspace: WorkspaceValue,
429424
}
430425

431426
impl TomlInheritedField {
432427
pub fn new() -> Self {
433-
TomlInheritedField { workspace: true }
428+
TomlInheritedField {
429+
workspace: WorkspaceValue,
430+
}
434431
}
435432
}
436433

@@ -440,12 +437,25 @@ impl Default for TomlInheritedField {
440437
}
441438
}
442439

443-
fn bool_no_false<'de, D: de::Deserializer<'de>>(deserializer: D) -> Result<bool, D::Error> {
444-
let b: bool = Deserialize::deserialize(deserializer)?;
445-
if b {
446-
Ok(b)
447-
} else {
448-
Err(de::Error::custom("`workspace` cannot be false"))
440+
#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
441+
#[serde(try_from = "bool")]
442+
#[serde(into = "bool")]
443+
struct WorkspaceValue;
444+
445+
impl TryFrom<bool> for WorkspaceValue {
446+
type Error = String;
447+
fn try_from(other: bool) -> Result<WorkspaceValue, Self::Error> {
448+
if other {
449+
Ok(WorkspaceValue)
450+
} else {
451+
Err("`workspace` cannot be false".to_owned())
452+
}
453+
}
454+
}
455+
456+
impl From<WorkspaceValue> for bool {
457+
fn from(_: WorkspaceValue) -> bool {
458+
true
449459
}
450460
}
451461

@@ -1250,12 +1260,9 @@ impl TomlPlatform {
12501260
}
12511261
}
12521262

1253-
#[derive(Deserialize, Serialize, Debug, Clone)]
1254-
#[serde(expecting = "a lints table")]
1255-
#[serde(rename_all = "kebab-case")]
1263+
#[derive(Serialize, Debug, Clone)]
12561264
pub struct InheritableLints {
12571265
#[serde(skip_serializing_if = "is_false")]
1258-
#[serde(deserialize_with = "bool_no_false", default)]
12591266
pub workspace: bool,
12601267
#[serde(flatten)]
12611268
pub lints: TomlLints,
@@ -1265,6 +1272,54 @@ fn is_false(b: &bool) -> bool {
12651272
!b
12661273
}
12671274

1275+
impl<'de> Deserialize<'de> for InheritableLints {
1276+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1277+
where
1278+
D: de::Deserializer<'de>,
1279+
{
1280+
struct InheritableLintsVisitor;
1281+
1282+
impl<'de> de::Visitor<'de> for InheritableLintsVisitor {
1283+
// The type that our Visitor is going to produce.
1284+
type Value = InheritableLints;
1285+
1286+
// Format a message stating what data this Visitor expects to receive.
1287+
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1288+
formatter.write_str("a lints table")
1289+
}
1290+
1291+
// Deserialize MyMap from an abstract "map" provided by the
1292+
// Deserializer. The MapAccess input is a callback provided by
1293+
// the Deserializer to let us see each entry in the map.
1294+
fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
1295+
where
1296+
M: de::MapAccess<'de>,
1297+
{
1298+
let mut lints = TomlLints::new();
1299+
let mut workspace = false;
1300+
1301+
// While there are entries remaining in the input, add them
1302+
// into our map.
1303+
while let Some(key) = access.next_key()? {
1304+
if key == "workspace" {
1305+
workspace = match access.next_value()? {
1306+
Some(WorkspaceValue) => true,
1307+
None => false,
1308+
};
1309+
} else {
1310+
let value = access.next_value()?;
1311+
lints.insert(key, value);
1312+
}
1313+
}
1314+
1315+
Ok(InheritableLints { workspace, lints })
1316+
}
1317+
}
1318+
1319+
deserializer.deserialize_map(InheritableLintsVisitor)
1320+
}
1321+
}
1322+
12681323
pub type TomlLints = BTreeMap<String, TomlToolLints>;
12691324

12701325
pub type TomlToolLints = BTreeMap<String, TomlLint>;

tests/testsuite/lints.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,37 @@ Caused by:
148148
.run();
149149
}
150150

151+
#[cargo_test]
152+
fn warn_on_unused_key() {
153+
let foo = project()
154+
.file(
155+
"Cargo.toml",
156+
r#"
157+
[package]
158+
name = "foo"
159+
version = "0.0.1"
160+
161+
[workspace.lints.rust]
162+
rust-2018-idioms = { level = "allow", unused = true }
163+
[lints.rust]
164+
rust-2018-idioms = { level = "allow", unused = true }
165+
"#,
166+
)
167+
.file("src/lib.rs", "")
168+
.build();
169+
170+
foo.cargo("check")
171+
.with_stderr(
172+
"\
173+
[WARNING] [CWD]/Cargo.toml: unused manifest key: lints.rust.rust-2018-idioms.unused
174+
[WARNING] [CWD]/Cargo.toml: unused manifest key: workspace.lints.rust.rust-2018-idioms.unused
175+
[CHECKING] foo v0.0.1 ([CWD])
176+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
177+
",
178+
)
179+
.run();
180+
}
181+
151182
#[cargo_test]
152183
fn fail_on_tool_injection() {
153184
let foo = project()
@@ -276,6 +307,51 @@ error: usage of an `unsafe` block
276307
.run();
277308
}
278309

310+
#[cargo_test]
311+
fn workspace_cant_be_false() {
312+
let foo = project()
313+
.file(
314+
"Cargo.toml",
315+
r#"
316+
[package]
317+
name = "foo"
318+
version = "0.0.1"
319+
authors = []
320+
321+
[lints]
322+
workspace = false
323+
324+
[workspace.lints.rust]
325+
"unsafe_code" = "deny"
326+
"#,
327+
)
328+
.file(
329+
"src/lib.rs",
330+
"
331+
pub fn foo(num: i32) -> u32 {
332+
unsafe { std::mem::transmute(num) }
333+
}
334+
",
335+
)
336+
.build();
337+
338+
foo.cargo("check")
339+
.with_status(101)
340+
.with_stderr_contains(
341+
"\
342+
error: failed to parse manifest at `[CWD]/Cargo.toml`
343+
344+
Caused by:
345+
TOML parse error at line 8, column 29
346+
|
347+
8 | workspace = false
348+
| ^^^^^
349+
`workspace` cannot be false
350+
",
351+
)
352+
.run();
353+
}
354+
279355
#[cargo_test]
280356
fn workspace_lint_deny() {
281357
let foo = project()

0 commit comments

Comments
 (0)