Skip to content

Commit 0efbf08

Browse files
committed
Add disabled-in-tests configuration
1 parent 7250a1a commit 0efbf08

File tree

12 files changed

+192
-3
lines changed

12 files changed

+192
-3
lines changed

clippy_config/src/conf.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,8 @@ define_Conf! {
578578
/// Use the Cognitive Complexity lint instead.
579579
#[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)]
580580
cyclomatic_complexity_threshold: u64 = 25,
581+
/// Lints to disable in tests.
582+
disabled_in_tests: Vec<String> = Vec::new(),
581583
/// The list of disallowed macros, written as fully qualified paths.
582584
///
583585
/// **Fields:**

clippy_utils/src/diagnostics.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,31 @@
88
//! Thank you!
99
//! ~The `INTERNAL_METADATA_COLLECTOR` lint
1010
11+
use rustc_data_structures::fx::FxHashSet;
1112
use rustc_errors::{Applicability, Diag, DiagMessage, MultiSpan, SubdiagMessage};
1213
#[cfg(debug_assertions)]
1314
use rustc_errors::{EmissionGuarantee, SubstitutionPart, Suggestions};
1415
use rustc_hir::HirId;
1516
use rustc_lint::{LateContext, Lint, LintContext};
1617
use rustc_span::Span;
1718
use std::env;
19+
use std::sync::OnceLock;
20+
21+
pub static DISABLED: OnceLock<FxHashSet<&'static str>> = OnceLock::new();
22+
23+
#[allow(clippy::implicit_hasher)]
24+
pub fn set_disabled(disabled: FxHashSet<&'static str>) {
25+
DISABLED
26+
.set(disabled)
27+
.unwrap_or_else(|_| panic!("`set_disabled` was already called"));
28+
}
29+
30+
fn is_disabled(lint: &Lint) -> bool {
31+
DISABLED.get().is_some_and(|disabled| {
32+
let name = lint.name_lower();
33+
disabled.contains(name.strip_prefix("clippy::").unwrap_or(&name))
34+
})
35+
}
1836

1937
fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) {
2038
if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err()
@@ -104,6 +122,9 @@ fn validate_diag(diag: &Diag<'_, impl EmissionGuarantee>) {
104122
/// ```
105123
#[track_caller]
106124
pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
125+
if is_disabled(lint) {
126+
return;
127+
}
107128
#[expect(clippy::disallowed_methods)]
108129
cx.span_lint(lint, sp, |diag| {
109130
diag.primary_message(msg);
@@ -157,6 +178,9 @@ pub fn span_lint_and_help<T: LintContext>(
157178
help_span: Option<Span>,
158179
help: impl Into<SubdiagMessage>,
159180
) {
181+
if is_disabled(lint) {
182+
return;
183+
}
160184
#[expect(clippy::disallowed_methods)]
161185
cx.span_lint(lint, span, |diag| {
162186
diag.primary_message(msg);
@@ -218,6 +242,9 @@ pub fn span_lint_and_note<T: LintContext>(
218242
note_span: Option<Span>,
219243
note: impl Into<SubdiagMessage>,
220244
) {
245+
if is_disabled(lint) {
246+
return;
247+
}
221248
#[expect(clippy::disallowed_methods)]
222249
cx.span_lint(lint, span, |diag| {
223250
diag.primary_message(msg);
@@ -259,6 +286,9 @@ where
259286
M: Into<DiagMessage>,
260287
F: FnOnce(&mut Diag<'_, ()>),
261288
{
289+
if is_disabled(lint) {
290+
return;
291+
}
262292
#[expect(clippy::disallowed_methods)]
263293
cx.span_lint(lint, sp, |diag| {
264294
diag.primary_message(msg);
@@ -296,6 +326,9 @@ where
296326
/// the `#[allow]` will work.
297327
#[track_caller]
298328
pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: impl Into<DiagMessage>) {
329+
if is_disabled(lint) {
330+
return;
331+
}
299332
#[expect(clippy::disallowed_methods)]
300333
cx.tcx.node_span_lint(lint, hir_id, sp, |diag| {
301334
diag.primary_message(msg);
@@ -339,6 +372,9 @@ pub fn span_lint_hir_and_then(
339372
msg: impl Into<DiagMessage>,
340373
f: impl FnOnce(&mut Diag<'_, ()>),
341374
) {
375+
if is_disabled(lint) {
376+
return;
377+
}
342378
#[expect(clippy::disallowed_methods)]
343379
cx.tcx.node_span_lint(lint, hir_id, sp, |diag| {
344380
diag.primary_message(msg);

src/driver.rs

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
// FIXME: switch to something more ergonomic here, once available.
1010
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
11+
extern crate rustc_data_structures;
1112
extern crate rustc_driver;
1213
extern crate rustc_interface;
1314
extern crate rustc_session;
@@ -18,15 +19,18 @@ extern crate rustc_span;
1819
#[cfg(feature = "jemalloc")]
1920
extern crate tikv_jemalloc_sys as jemalloc_sys;
2021

22+
use clippy_config::Conf;
2123
use clippy_utils::sym;
2224
use declare_clippy_lint::LintListBuilder;
25+
use rustc_data_structures::fx::FxHashSet;
2326
use rustc_interface::interface;
24-
use rustc_session::EarlyDiagCtxt;
2527
use rustc_session::config::ErrorOutputType;
2628
use rustc_session::parse::ParseSess;
29+
use rustc_session::{EarlyDiagCtxt, Session};
2730
use rustc_span::symbol::Symbol;
2831

2932
use std::env;
33+
use std::fmt::Write;
3034
use std::fs::read_to_string;
3135
use std::path::Path;
3236
use std::process::exit;
@@ -130,6 +134,7 @@ impl rustc_driver::Callbacks for RustcCallbacks {
130134

131135
struct ClippyCallbacks {
132136
clippy_args_var: Option<String>,
137+
testing: bool,
133138
}
134139

135140
impl rustc_driver::Callbacks for ClippyCallbacks {
@@ -139,6 +144,7 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
139144
let conf_path = clippy_config::lookup_conf_file();
140145
let previous = config.register_lints.take();
141146
let clippy_args_var = self.clippy_args_var.take();
147+
let testing = self.testing;
142148
config.psess_created = Some(Box::new(move |psess| {
143149
track_clippy_args(psess, clippy_args_var.as_deref());
144150
track_files(psess);
@@ -157,11 +163,14 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
157163
(previous)(sess, lint_store);
158164
}
159165

166+
let conf = clippy_config::Conf::read(sess, &conf_path);
167+
let disabled = build_disabled_set(sess, conf, testing);
168+
clippy_utils::diagnostics::set_disabled(disabled);
169+
160170
let mut list_builder = LintListBuilder::default();
161171
list_builder.insert(clippy_lints::declared_lints::LINTS);
162172
list_builder.register(lint_store);
163173

164-
let conf = clippy_config::Conf::read(sess, &conf_path);
165174
clippy_lints::register_lint_passes(lint_store, conf);
166175

167176
#[cfg(feature = "internal")]
@@ -182,6 +191,33 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
182191
}
183192
}
184193

194+
fn build_disabled_set(sess: &Session, conf: &'static Conf, testing: bool) -> FxHashSet<&'static str> {
195+
if !testing {
196+
return FxHashSet::default();
197+
}
198+
let disabled = conf
199+
.disabled_in_tests
200+
.iter()
201+
.map(String::as_str)
202+
.collect::<FxHashSet<_>>();
203+
let declared_lint_names = clippy_lints::declared_lints::LINTS
204+
.iter()
205+
.map(|lint| lint.name_lower())
206+
.collect::<FxHashSet<_>>();
207+
#[allow(rustc::potential_query_instability)]
208+
for &name in &disabled {
209+
if !declared_lint_names.contains(name) {
210+
let mut msg = format!("unknown lint `{name}`");
211+
let replaced = name.replace('-', "_");
212+
if declared_lint_names.contains(&replaced) {
213+
writeln!(msg, ". Did you mean `{replaced}`?").unwrap();
214+
}
215+
sess.dcx().warn(msg);
216+
}
217+
}
218+
disabled
219+
}
220+
185221
#[allow(clippy::ignored_unit_patterns)]
186222
fn display_help() {
187223
println!("{}", help_message());
@@ -336,7 +372,13 @@ pub fn main() {
336372
let clippy_enabled = !cap_lints_allow && relevant_package && !info_query;
337373
if clippy_enabled {
338374
args.extend(clippy_args);
339-
rustc_driver::run_compiler(&args, &mut ClippyCallbacks { clippy_args_var });
375+
rustc_driver::run_compiler(
376+
&args,
377+
&mut ClippyCallbacks {
378+
clippy_args_var,
379+
testing: orig_args.iter().any(|arg| arg == "--test"),
380+
},
381+
);
340382
} else {
341383
rustc_driver::run_compiler(&args, &mut RustcCallbacks { clippy_args_var });
342384
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
disabled-in-tests = ["unwrap-used"]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
//@check-pass
2+
//@compile-flags: --test
3+
4+
fn main() {}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
warning: unknown lint `unwrap-used`. Did you mean `unwrap_used`?
2+
3+
warning: 1 warning emitted
4+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
disabled-in-tests = ["unwrap_used"]
3.7 MB
Binary file not shown.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//@compile-flags: --test
2+
3+
#![warn(clippy::unwrap_used)]
4+
#![warn(clippy::get_unwrap)]
5+
6+
fn main() {}
7+
8+
#[test]
9+
fn test() {
10+
let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
11+
let _ = &boxed_slice[1];
12+
//~^ get_unwrap
13+
}
14+
15+
#[cfg(test)]
16+
mod issue9612 {
17+
// should not lint in `#[cfg(test)]` modules
18+
#[test]
19+
fn test_fn() {
20+
let _a: u8 = 2.try_into().unwrap();
21+
let _a: u8 = 3.try_into().expect("");
22+
23+
util();
24+
}
25+
26+
#[allow(unconditional_panic)]
27+
fn util() {
28+
let _a: u8 = 4.try_into().unwrap();
29+
let _a: u8 = 5.try_into().expect("");
30+
// should still warn
31+
let _ = &Box::new([0])[1];
32+
//~^ get_unwrap
33+
}
34+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//@compile-flags: --test
2+
3+
#![warn(clippy::unwrap_used)]
4+
#![warn(clippy::get_unwrap)]
5+
6+
fn main() {}
7+
8+
#[test]
9+
fn test() {
10+
let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
11+
let _ = boxed_slice.get(1).unwrap();
12+
//~^ get_unwrap
13+
}
14+
15+
#[cfg(test)]
16+
mod issue9612 {
17+
// should not lint in `#[cfg(test)]` modules
18+
#[test]
19+
fn test_fn() {
20+
let _a: u8 = 2.try_into().unwrap();
21+
let _a: u8 = 3.try_into().expect("");
22+
23+
util();
24+
}
25+
26+
#[allow(unconditional_panic)]
27+
fn util() {
28+
let _a: u8 = 4.try_into().unwrap();
29+
let _a: u8 = 5.try_into().expect("");
30+
// should still warn
31+
let _ = Box::new([0]).get(1).unwrap();
32+
//~^ get_unwrap
33+
}
34+
}

0 commit comments

Comments
 (0)