Skip to content

Commit 2df02f0

Browse files
committed
fix(lints): Feature-gate the im_a_teapot lint
1 parent a1f8e45 commit 2df02f0

File tree

3 files changed

+71
-14
lines changed

3 files changed

+71
-14
lines changed

src/cargo/core/features.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ impl FromStr for Edition {
343343
}
344344
}
345345

346-
#[derive(PartialEq)]
346+
#[derive(Debug, PartialEq)]
347347
enum Status {
348348
Stable,
349349
Unstable,
@@ -387,11 +387,11 @@ macro_rules! features {
387387
$(
388388
$(#[$attr])*
389389
#[doc = concat!("\n\n\nSee <https://doc.rust-lang.org/nightly/cargo/", $docs, ">.")]
390-
pub fn $feature() -> &'static Feature {
390+
pub const fn $feature() -> &'static Feature {
391391
fn get(features: &Features) -> bool {
392392
stab!($stab) == Status::Stable || features.$feature
393393
}
394-
static FEAT: Feature = Feature {
394+
const FEAT: Feature = Feature {
395395
name: stringify!($feature),
396396
stability: stab!($stab),
397397
version: $version,
@@ -512,6 +512,7 @@ features! {
512512
}
513513

514514
/// Status and metadata for a single unstable feature.
515+
#[derive(Debug)]
515516
pub struct Feature {
516517
/// Feature name. This is valid Rust identifier so no dash only underscore.
517518
name: &'static str,

src/cargo/util/lints.rs

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::core::dependency::DepKind;
22
use crate::core::FeatureValue::Dep;
3-
use crate::core::{Edition, FeatureValue, Package};
3+
use crate::core::{Edition, Feature, FeatureValue, Features, Package};
44
use crate::util::interning::InternedString;
55
use crate::{CargoResult, GlobalContext};
66
use annotate_snippets::{Level, Snippet};
@@ -13,10 +13,10 @@ use std::path::Path;
1313
use toml_edit::ImDocument;
1414

1515
fn get_span(document: &ImDocument<String>, path: &[&str], get_value: bool) -> Option<Range<usize>> {
16-
let mut table = document.as_item().as_table_like().unwrap();
16+
let mut table = document.as_item().as_table_like()?;
1717
let mut iter = path.into_iter().peekable();
1818
while let Some(key) = iter.next() {
19-
let (key, item) = table.get_key_value(key).unwrap();
19+
let (key, item) = table.get_key_value(key)?;
2020
if iter.peek().is_none() {
2121
return if get_value {
2222
item.span()
@@ -82,6 +82,7 @@ pub struct Lint {
8282
pub groups: &'static [LintGroup],
8383
pub default_level: LintLevel,
8484
pub edition_lint_opts: Option<(Edition, LintLevel)>,
85+
pub feature_gate: Option<&'static Feature>,
8586
}
8687

8788
impl Lint {
@@ -90,7 +91,17 @@ impl Lint {
9091
pkg_lints: &TomlToolLints,
9192
ws_lints: Option<&TomlToolLints>,
9293
edition: Edition,
94+
unstable_features: &Features,
9395
) -> (LintLevel, LintLevelReason) {
96+
// We should return `Allow` if a lint is behind a feature, but it is
97+
// not enabled, that way the lint does not run.
98+
if self
99+
.feature_gate
100+
.is_some_and(|f| !unstable_features.is_enabled(f))
101+
{
102+
return (LintLevel::Allow, LintLevelReason::Default);
103+
}
104+
94105
self.groups
95106
.iter()
96107
.map(|g| {
@@ -164,7 +175,7 @@ impl From<TomlLintLevel> for LintLevel {
164175
}
165176
}
166177

167-
#[derive(Copy, Clone, Debug)]
178+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
168179
pub enum LintLevelReason {
169180
Default,
170181
Edition(Edition),
@@ -228,6 +239,7 @@ const IM_A_TEAPOT: Lint = Lint {
228239
groups: &[TEST_DUMMY_UNSTABLE],
229240
default_level: LintLevel::Allow,
230241
edition_lint_opts: None,
242+
feature_gate: Some(Feature::test_dummy_unstable()),
231243
};
232244

233245
pub fn check_im_a_teapot(
@@ -239,7 +251,13 @@ pub fn check_im_a_teapot(
239251
gctx: &GlobalContext,
240252
) -> CargoResult<()> {
241253
let manifest = pkg.manifest();
242-
let (lint_level, reason) = IM_A_TEAPOT.level(pkg_lints, ws_lints, manifest.edition());
254+
let (lint_level, reason) = IM_A_TEAPOT.level(
255+
pkg_lints,
256+
ws_lints,
257+
manifest.edition(),
258+
manifest.unstable_features(),
259+
);
260+
243261
if lint_level == LintLevel::Allow {
244262
return Ok(());
245263
}
@@ -295,6 +313,7 @@ const IMPLICIT_FEATURES: Lint = Lint {
295313
groups: &[],
296314
default_level: LintLevel::Allow,
297315
edition_lint_opts: None,
316+
feature_gate: None,
298317
};
299318

300319
pub fn check_implicit_features(
@@ -305,19 +324,20 @@ pub fn check_implicit_features(
305324
error_count: &mut usize,
306325
gctx: &GlobalContext,
307326
) -> CargoResult<()> {
308-
let edition = pkg.manifest().edition();
327+
let manifest = pkg.manifest();
328+
let edition = manifest.edition();
309329
// In Edition 2024+, instead of creating optional features, the dependencies are unused.
310330
// See `UNUSED_OPTIONAL_DEPENDENCY`
311331
if edition >= Edition::Edition2024 {
312332
return Ok(());
313333
}
314334

315-
let (lint_level, reason) = IMPLICIT_FEATURES.level(pkg_lints, ws_lints, edition);
335+
let (lint_level, reason) =
336+
IMPLICIT_FEATURES.level(pkg_lints, ws_lints, edition, manifest.unstable_features());
316337
if lint_level == LintLevel::Allow {
317338
return Ok(());
318339
}
319340

320-
let manifest = pkg.manifest();
321341
let activated_opt_deps = manifest
322342
.resolved_toml()
323343
.features()
@@ -373,6 +393,7 @@ const UNUSED_OPTIONAL_DEPENDENCY: Lint = Lint {
373393
groups: &[],
374394
default_level: LintLevel::Warn,
375395
edition_lint_opts: None,
396+
feature_gate: None,
376397
};
377398

378399
pub fn unused_dependencies(
@@ -383,18 +404,23 @@ pub fn unused_dependencies(
383404
error_count: &mut usize,
384405
gctx: &GlobalContext,
385406
) -> CargoResult<()> {
386-
let edition = pkg.manifest().edition();
407+
let manifest = pkg.manifest();
408+
let edition = manifest.edition();
387409
// Unused optional dependencies can only exist on edition 2024+
388410
if edition < Edition::Edition2024 {
389411
return Ok(());
390412
}
391413

392-
let (lint_level, reason) = UNUSED_OPTIONAL_DEPENDENCY.level(pkg_lints, ws_lints, edition);
414+
let (lint_level, reason) = UNUSED_OPTIONAL_DEPENDENCY.level(
415+
pkg_lints,
416+
ws_lints,
417+
edition,
418+
manifest.unstable_features(),
419+
);
393420
if lint_level == LintLevel::Allow {
394421
return Ok(());
395422
}
396423
let mut emitted_source = None;
397-
let manifest = pkg.manifest();
398424
let original_toml = manifest.original_toml();
399425
// Unused dependencies were stripped from the manifest, leaving only the used ones
400426
let used_dependencies = manifest

tests/testsuite/lints_table.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,3 +1078,33 @@ implicit-features = "warn"
10781078
)
10791079
.run();
10801080
}
1081+
1082+
#[cargo_test]
1083+
fn check_feature_gated() {
1084+
let p = project()
1085+
.file(
1086+
"Cargo.toml",
1087+
r#"
1088+
[package]
1089+
name = "foo"
1090+
version = "0.0.1"
1091+
edition = "2015"
1092+
authors = []
1093+
1094+
[lints.cargo]
1095+
im-a-teapot = "warn"
1096+
"#,
1097+
)
1098+
.file("src/lib.rs", "")
1099+
.build();
1100+
1101+
p.cargo("check -Zcargo-lints")
1102+
.masquerade_as_nightly_cargo(&["cargo-lints"])
1103+
.with_stderr(
1104+
"\
1105+
[CHECKING] foo v0.0.1 ([CWD])
1106+
[FINISHED] [..]
1107+
",
1108+
)
1109+
.run();
1110+
}

0 commit comments

Comments
 (0)