Skip to content

Commit 56a5ba9

Browse files
committed
Suppress nightly lint emission in Clippy
1 parent bc43b89 commit 56a5ba9

File tree

5 files changed

+141
-5
lines changed

5 files changed

+141
-5
lines changed

clippy_dev/src/update_lints.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ pub fn run(update_mode: UpdateMode) {
125125
update_mode,
126126
&gen_deprecated(deprecated_lints.iter()),
127127
);
128+
process_file(
129+
"clippy_lints/src/lib.nightly_lints.rs",
130+
update_mode,
131+
&gen_nightly_lint_list(internal_lints.iter(), usable_lints.iter()),
132+
);
128133

129134
let all_group_lints = usable_lints.iter().filter(|l| {
130135
matches!(
@@ -320,6 +325,36 @@ fn gen_deprecated<'a>(lints: impl Iterator<Item = &'a Lint>) -> String {
320325
output
321326
}
322327

328+
fn gen_nightly_lint_list<'a>(
329+
internal_lints: impl Iterator<Item = &'a Lint>,
330+
usable_lints: impl Iterator<Item = &'a Lint>,
331+
) -> String {
332+
let details: Vec<_> = internal_lints
333+
.map(|l| (false, l))
334+
.chain(usable_lints.map(|l| (true, l)))
335+
.filter(|(_, l)| l.version.as_ref().map_or(false, |v| v == "nightly"))
336+
.map(|(p, l)| (p, &l.module, l.name.to_uppercase()))
337+
.collect();
338+
339+
let mut output = GENERATED_FILE_COMMENT.to_string();
340+
output.push_str("clippy_utils::nightly::set_nightly_lints([\n");
341+
// The test lint "FOREVER_NIGHTLY_LINT" is in the `internal_warn` group which is
342+
// not processed by `update_lints`. For testing purposes we still need the lint to be
343+
// registered in the `nightly_lints` list. This manually adds this one lint.
344+
output.push_str(" #[cfg(feature = \"internal\")]\n");
345+
output.push_str(" LintId::of(utils::internal_lints::FOREVER_NIGHTLY_LINT),\n");
346+
347+
for (is_public, module_name, lint_name) in details {
348+
if !is_public {
349+
output.push_str(" #[cfg(feature = \"internal\")]\n");
350+
}
351+
output.push_str(&format!(" LintId::of({}::{}),\n", module_name, lint_name));
352+
}
353+
output.push_str("])\n");
354+
355+
output
356+
}
357+
323358
/// Generates the code for registering lints
324359
#[must_use]
325360
fn gen_register_lint_list<'a>(

clippy_lints/src/lib.nightly_lints.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// This file was generated by `cargo dev update_lints`.
2+
// Use that command to update this file and do not edit by hand.
3+
// Manual edits will be overwritten.
4+
5+
clippy_utils::nightly::set_nightly_lints([
6+
#[cfg(feature = "internal")]
7+
LintId::of(utils::internal_lints::FOREVER_NIGHTLY_LINT),
8+
LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
9+
LintId::of(borrow_as_ptr::BORROW_AS_PTR),
10+
LintId::of(manual_bits::MANUAL_BITS),
11+
LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
12+
])

clippy_lints/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
467467
register_removed_non_tool_lints(store);
468468

469469
include!("lib.deprecated.rs");
470+
include!("lib.nightly_lints.rs");
470471

471472
include!("lib.register_lints.rs");
472473
include!("lib.register_restriction.rs");

clippy_utils/src/diagnostics.rs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
//! Thank you!
99
//! ~The `INTERNAL_METADATA_COLLECTOR` lint
1010
11+
use crate::nightly::LintLevelProvider;
12+
1113
use rustc_errors::{Applicability, DiagnosticBuilder};
1214
use rustc_hir::HirId;
1315
use rustc_lint::{LateContext, Lint, LintContext};
@@ -46,7 +48,11 @@ fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) {
4648
/// 17 | std::mem::forget(seven);
4749
/// | ^^^^^^^^^^^^^^^^^^^^^^^
4850
/// ```
49-
pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<MultiSpan>, msg: &str) {
51+
pub fn span_lint<T: LintContext + LintLevelProvider>(cx: &T, lint: &'static Lint, sp: impl Into<MultiSpan>, msg: &str) {
52+
if crate::nightly::suppress_lint(cx, lint) {
53+
return;
54+
}
55+
5056
cx.struct_span_lint(lint, sp, |diag| {
5157
let mut diag = diag.build(msg);
5258
docs_link(&mut diag, lint);
@@ -74,7 +80,7 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult
7480
/// |
7581
/// = help: consider using `f64::NAN` if you would like a constant representing NaN
7682
/// ```
77-
pub fn span_lint_and_help<'a, T: LintContext>(
83+
pub fn span_lint_and_help<'a, T: LintContext + LintLevelProvider>(
7884
cx: &'a T,
7985
lint: &'static Lint,
8086
span: Span,
@@ -117,14 +123,18 @@ pub fn span_lint_and_help<'a, T: LintContext>(
117123
/// 10 | forget(&SomeStruct);
118124
/// | ^^^^^^^^^^^
119125
/// ```
120-
pub fn span_lint_and_note<'a, T: LintContext>(
126+
pub fn span_lint_and_note<'a, T: LintContext + LintLevelProvider>(
121127
cx: &'a T,
122128
lint: &'static Lint,
123129
span: impl Into<MultiSpan>,
124130
msg: &str,
125131
note_span: Option<Span>,
126132
note: &str,
127133
) {
134+
if crate::nightly::suppress_lint(cx, lint) {
135+
return;
136+
}
137+
128138
cx.struct_span_lint(lint, span, |diag| {
129139
let mut diag = diag.build(msg);
130140
if let Some(note_span) = note_span {
@@ -143,10 +153,14 @@ pub fn span_lint_and_note<'a, T: LintContext>(
143153
/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
144154
pub fn span_lint_and_then<C, S, F>(cx: &C, lint: &'static Lint, sp: S, msg: &str, f: F)
145155
where
146-
C: LintContext,
156+
C: LintContext + LintLevelProvider,
147157
S: Into<MultiSpan>,
148158
F: FnOnce(&mut DiagnosticBuilder<'_>),
149159
{
160+
if crate::nightly::suppress_lint(cx, lint) {
161+
return;
162+
}
163+
150164
cx.struct_span_lint(lint, sp, |diag| {
151165
let mut diag = diag.build(msg);
152166
f(&mut diag);
@@ -156,6 +170,10 @@ where
156170
}
157171

158172
pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) {
173+
if crate::nightly::suppress_lint(cx, lint) {
174+
return;
175+
}
176+
159177
cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
160178
let mut diag = diag.build(msg);
161179
docs_link(&mut diag, lint);
@@ -171,6 +189,10 @@ pub fn span_lint_hir_and_then(
171189
msg: &str,
172190
f: impl FnOnce(&mut DiagnosticBuilder<'_>),
173191
) {
192+
if crate::nightly::suppress_lint(cx, lint) {
193+
return;
194+
}
195+
174196
cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
175197
let mut diag = diag.build(msg);
176198
f(&mut diag);
@@ -199,7 +221,7 @@ pub fn span_lint_hir_and_then(
199221
/// = note: `-D fold-any` implied by `-D warnings`
200222
/// ```
201223
#[cfg_attr(feature = "internal", allow(clippy::collapsible_span_lint_calls))]
202-
pub fn span_lint_and_sugg<'a, T: LintContext>(
224+
pub fn span_lint_and_sugg<'a, T: LintContext + LintLevelProvider>(
203225
cx: &'a T,
204226
lint: &'static Lint,
205227
sp: Span,

clippy_utils/src/nightly.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
44
use std::lazy::SyncOnceCell;
55

6+
use rustc_data_structures::stable_set::FxHashSet;
7+
use rustc_lint::{EarlyContext, LateContext, Level, Lint, LintId};
8+
use rustc_middle::lint::{LevelAndSource, LintLevelSource};
69
use rustc_session::Session;
710

811
static IS_NIGHTLY_RUN: SyncOnceCell<bool> = SyncOnceCell::new();
12+
static NIGHTLY_LINTS: SyncOnceCell<FxHashSet<LintId>> = SyncOnceCell::new();
913

1014
/// This function is used to determine if nightly lints should be enabled or disabled
1115
/// in this Clippy run.
@@ -33,3 +37,65 @@ pub fn eval_is_nightly_run(sess: &Session) {
3337
pub fn is_nightly_run() -> bool {
3438
*IS_NIGHTLY_RUN.get().unwrap_or(&false)
3539
}
40+
41+
/// This function takes a list of all nightly lints that will be surpressed before
42+
/// the emission if nightly lints are disabled.
43+
///
44+
/// It's only allowed to call this once. This is done by [`clippy_lints::lib`]
45+
#[doc(hidden)]
46+
pub fn set_nightly_lints<const N: usize>(lints: [LintId; N]) {
47+
// The from trait for HashMaps is only implemented for the normal hasher. Here we have to add each
48+
// item individually
49+
let mut nightly_lints = FxHashSet::default();
50+
lints.iter().copied().for_each(|lint| {
51+
nightly_lints.insert(lint);
52+
});
53+
NIGHTLY_LINTS
54+
.set(nightly_lints)
55+
.expect("`NIGHTLY_LINTS` should only be set once.");
56+
}
57+
58+
/// Returns true if the lint is a registered nightly lint. Note that a lint will still be a
59+
/// registered nightly lint if nightly lints are enabled as usual.
60+
///
61+
/// Please use [`is_nightly_run`] to determine if Clippy's nightly features
62+
/// should be enabled.
63+
#[inline]
64+
pub fn is_nightly_lint(lint: &'static Lint) -> bool {
65+
NIGHTLY_LINTS
66+
.get()
67+
.map_or(false, |lints| lints.contains(&LintId::of(lint)))
68+
}
69+
70+
/// This function checks if the given lint is a nightly lint and should be suppressed in the current
71+
/// context.
72+
pub fn suppress_lint<T: LintLevelProvider>(cx: &T, lint: &'static Lint) -> bool {
73+
if !is_nightly_run() && is_nightly_lint(lint) {
74+
let (_, level_src) = cx.get_lint_level(lint);
75+
if level_src == LintLevelSource::Default
76+
|| level_src == LintLevelSource::CommandLine(sym!(warnings), Level::Deny)
77+
{
78+
return true;
79+
}
80+
}
81+
82+
false
83+
}
84+
85+
/// This trait is used to retrieve the lint level for the lint based on the
86+
/// current linting context.
87+
pub trait LintLevelProvider {
88+
fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource;
89+
}
90+
91+
impl LintLevelProvider for LateContext<'_> {
92+
fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
93+
self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs)
94+
}
95+
}
96+
97+
impl LintLevelProvider for EarlyContext<'_> {
98+
fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
99+
self.builder.lint_level(lint)
100+
}
101+
}

0 commit comments

Comments
 (0)