diff --git a/crates/cargo-util-schemas/src/manifest/mod.rs b/crates/cargo-util-schemas/src/manifest/mod.rs index 57e97e96484..94013794b8b 100644 --- a/crates/cargo-util-schemas/src/manifest/mod.rs +++ b/crates/cargo-util-schemas/src/manifest/mod.rs @@ -1506,7 +1506,7 @@ pub struct TomlLintConfig { pub priority: i8, } -#[derive(Serialize, Deserialize, Debug, Copy, Clone)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq)] #[serde(rename_all = "kebab-case")] pub enum TomlLintLevel { Forbid, diff --git a/src/cargo/util/lints.rs b/src/cargo/util/lints.rs index 204feb0459f..cf752fa7f34 100644 --- a/src/cargo/util/lints.rs +++ b/src/cargo/util/lints.rs @@ -68,6 +68,13 @@ pub struct LintGroup { pub edition_lint_opts: Option<(Edition, LintLevel)>, } +const TEST_DUMMY_UNSTABLE: LintGroup = LintGroup { + name: "test_dummy_unstable", + desc: "test_dummy_unstable is meant to only be used in tests", + default_level: LintLevel::Allow, + edition_lint_opts: None, +}; + #[derive(Copy, Clone, Debug)] pub struct Lint { pub name: &'static str, @@ -79,23 +86,37 @@ pub struct Lint { impl Lint { pub fn level(&self, lints: &TomlToolLints, edition: Edition) -> LintLevel { + let edition_level = self + .edition_lint_opts + .filter(|(e, _)| edition >= *e) + .map(|(_, l)| l); + + if self.default_level == LintLevel::Forbid || edition_level == Some(LintLevel::Forbid) { + return LintLevel::Forbid; + } + let level = self .groups .iter() .map(|g| g.name) .chain(std::iter::once(self.name)) .filter_map(|n| lints.get(n).map(|l| (n, l))) - .max_by_key(|(n, l)| (l.priority(), std::cmp::Reverse(*n))); + .max_by_key(|(n, l)| { + ( + l.level() == TomlLintLevel::Forbid, + l.priority(), + std::cmp::Reverse(*n), + ) + }); match level { Some((_, toml_lint)) => toml_lint.level().into(), None => { - if let Some((lint_edition, lint_level)) = self.edition_lint_opts { - if edition >= lint_edition { - return lint_level; - } + if let Some(level) = edition_level { + level + } else { + self.default_level } - self.default_level } } } @@ -145,7 +166,7 @@ impl From for LintLevel { const IM_A_TEAPOT: Lint = Lint { name: "im_a_teapot", desc: "`im_a_teapot` is specified", - groups: &[], + groups: &[TEST_DUMMY_UNSTABLE], default_level: LintLevel::Allow, edition_lint_opts: None, }; diff --git a/tests/testsuite/lints_table.rs b/tests/testsuite/lints_table.rs index 27d840f07f2..451a3dc59c9 100644 --- a/tests/testsuite/lints_table.rs +++ b/tests/testsuite/lints_table.rs @@ -899,3 +899,43 @@ warning: `im_a_teapot` is specified ) .run(); } + +#[cargo_test] +fn forbid_not_overridden() { + let p = project() + .file( + "Cargo.toml", + r#" +cargo-features = ["test-dummy-unstable"] + +[package] +name = "foo" +version = "0.0.1" +edition = "2015" +authors = [] +im-a-teapot = true + +[lints.cargo] +im-a-teapot = { level = "warn", priority = 10 } +test-dummy-unstable = { level = "forbid", priority = -1 } + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -Zcargo-lints") + .masquerade_as_nightly_cargo(&["cargo-lints", "test-dummy-unstable"]) + .with_status(101) + .with_stderr( + "\ +error: `im_a_teapot` is specified + --> Cargo.toml:9:1 + | +9 | im-a-teapot = true + | ^^^^^^^^^^^^^^^^^^ + | + = note: `cargo::im_a_teapot` is set to `forbid` +", + ) + .run(); +}