Skip to content

Commit 078b5aa

Browse files
committed
Support replacements in disallowed_methods
1 parent 48b8bf2 commit 078b5aa

File tree

9 files changed

+87
-28
lines changed

9 files changed

+87
-28
lines changed

clippy_config/src/types.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use rustc_errors::{Applicability, Diag};
2+
use rustc_span::Span;
13
use serde::de::{self, Deserializer, Visitor};
24
use serde::{Deserialize, Serialize, ser};
35
use std::collections::HashMap;
@@ -13,7 +15,11 @@ pub struct Rename {
1315
#[serde(untagged)]
1416
pub enum DisallowedPath {
1517
Simple(String),
16-
WithReason { path: String, reason: Option<String> },
18+
WithReason {
19+
path: String,
20+
reason: Option<String>,
21+
replacement: Option<String>,
22+
},
1723
}
1824

1925
impl DisallowedPath {
@@ -23,12 +29,34 @@ impl DisallowedPath {
2329
path
2430
}
2531

32+
pub fn diag_amendment(&self, span: Span) -> impl FnOnce(&mut Diag<'_, ()>) + use<'_> {
33+
move |diag| {
34+
if let Some(replacement) = self.replacement() {
35+
diag.span_suggestion(
36+
span,
37+
self.reason().map_or_else(|| String::from("use"), ToOwned::to_owned),
38+
replacement,
39+
Applicability::MachineApplicable,
40+
);
41+
} else if let Some(reason) = self.reason() {
42+
diag.note(reason.to_owned());
43+
}
44+
}
45+
}
46+
2647
pub fn reason(&self) -> Option<&str> {
2748
match &self {
2849
Self::WithReason { reason, .. } => reason.as_deref(),
2950
Self::Simple(_) => None,
3051
}
3152
}
53+
54+
fn replacement(&self) -> Option<&str> {
55+
match &self {
56+
Self::WithReason { replacement, .. } => replacement.as_deref(),
57+
Self::Simple(_) => None,
58+
}
59+
}
3260
}
3361

3462
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]

clippy_lints/src/await_holding_invalid.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use clippy_config::Conf;
2+
use clippy_config::types::DisallowedPath;
23
use clippy_utils::diagnostics::span_lint_and_then;
34
use clippy_utils::{create_disallowed_map, match_def_path, paths};
45
use rustc_hir as hir;
@@ -173,7 +174,7 @@ declare_clippy_lint! {
173174
impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]);
174175

175176
pub struct AwaitHolding {
176-
def_ids: DefIdMap<(&'static str, Option<&'static str>)>,
177+
def_ids: DefIdMap<(&'static str, &'static DisallowedPath)>,
177178
}
178179

179180
impl AwaitHolding {
@@ -246,25 +247,21 @@ impl AwaitHolding {
246247
);
247248
},
248249
);
249-
} else if let Some(&(path, reason)) = self.def_ids.get(&adt.did()) {
250-
emit_invalid_type(cx, ty_cause.source_info.span, path, reason);
250+
} else if let Some(&(path, disallowed_path)) = self.def_ids.get(&adt.did()) {
251+
emit_invalid_type(cx, ty_cause.source_info.span, path, disallowed_path);
251252
}
252253
}
253254
}
254255
}
255256
}
256257

257-
fn emit_invalid_type(cx: &LateContext<'_>, span: Span, path: &'static str, reason: Option<&'static str>) {
258+
fn emit_invalid_type(cx: &LateContext<'_>, span: Span, path: &'static str, disallowed_path: &'static DisallowedPath) {
258259
span_lint_and_then(
259260
cx,
260261
AWAIT_HOLDING_INVALID_TYPE,
261262
span,
262263
format!("holding a disallowed type across an await point `{path}`"),
263-
|diag| {
264-
if let Some(reason) = reason {
265-
diag.note(reason);
266-
}
267-
},
264+
disallowed_path.diag_amendment(span),
268265
);
269266
}
270267

clippy_lints/src/disallowed_macros.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use clippy_config::Conf;
2+
use clippy_config::types::DisallowedPath;
23
use clippy_utils::create_disallowed_map;
34
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
45
use clippy_utils::macros::macro_backtrace;
56
use rustc_data_structures::fx::FxHashSet;
6-
use rustc_errors::Diag;
77
use rustc_hir::def_id::DefIdMap;
88
use rustc_hir::{
99
Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty,
@@ -60,7 +60,7 @@ declare_clippy_lint! {
6060
}
6161

6262
pub struct DisallowedMacros {
63-
disallowed: DefIdMap<(&'static str, Option<&'static str>)>,
63+
disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>,
6464
seen: FxHashSet<ExpnId>,
6565
// Track the most recently seen node that can have a `derive` attribute.
6666
// Needed to use the correct lint level.
@@ -91,13 +91,9 @@ impl DisallowedMacros {
9191
return;
9292
}
9393

94-
if let Some(&(path, reason)) = self.disallowed.get(&mac.def_id) {
94+
if let Some(&(path, disallowed_path)) = self.disallowed.get(&mac.def_id) {
9595
let msg = format!("use of a disallowed macro `{path}`");
96-
let add_note = |diag: &mut Diag<'_, _>| {
97-
if let Some(reason) = reason {
98-
diag.note(reason);
99-
}
100-
};
96+
let add_note = disallowed_path.diag_amendment(mac.span);
10197
if matches!(mac.kind, MacroKind::Derive)
10298
&& let Some(derive_src) = derive_src
10399
{

clippy_lints/src/disallowed_methods.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use clippy_config::Conf;
2+
use clippy_config::types::DisallowedPath;
23
use clippy_utils::create_disallowed_map;
34
use clippy_utils::diagnostics::span_lint_and_then;
45
use rustc_hir::def::{CtorKind, DefKind, Res};
@@ -31,6 +32,8 @@ declare_clippy_lint! {
3132
/// # When using an inline table, can add a `reason` for why the method
3233
/// # is disallowed.
3334
/// { path = "std::vec::Vec::leak", reason = "no leaking memory" },
35+
/// # Can also add a `replacement` that will be offered as a suggestion.
36+
/// { path = "std::sync::Mutex::new", reason = "prefer faster & simpler non-poisonable mutex", replacement = "parking_lot::Mutex::new" },
3437
/// ]
3538
/// ```
3639
///
@@ -58,7 +61,7 @@ declare_clippy_lint! {
5861
}
5962

6063
pub struct DisallowedMethods {
61-
disallowed: DefIdMap<(&'static str, Option<&'static str>)>,
64+
disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>,
6265
}
6366

6467
impl DisallowedMethods {
@@ -85,17 +88,13 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
8588
},
8689
_ => return,
8790
};
88-
if let Some(&(path, reason)) = self.disallowed.get(&id) {
91+
if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) {
8992
span_lint_and_then(
9093
cx,
9194
DISALLOWED_METHODS,
9295
span,
9396
format!("use of a disallowed method `{path}`"),
94-
|diag| {
95-
if let Some(reason) = reason {
96-
diag.note(reason);
97-
}
98-
},
97+
disallowed_path.diag_amendment(span),
9998
);
10099
}
101100
}

clippy_utils/src/lib.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -753,11 +753,13 @@ pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item =
753753
pub fn create_disallowed_map(
754754
tcx: TyCtxt<'_>,
755755
disallowed: &'static [DisallowedPath],
756-
) -> DefIdMap<(&'static str, Option<&'static str>)> {
756+
) -> DefIdMap<(&'static str, &'static DisallowedPath)> {
757757
disallowed
758758
.iter()
759-
.map(|x| (x.path(), x.path().split("::").collect::<Vec<_>>(), x.reason()))
760-
.flat_map(|(name, path, reason)| def_path_def_ids(tcx, &path).map(move |id| (id, (name, reason))))
759+
.map(|x| (x.path(), x.path().split("::").collect::<Vec<_>>(), x))
760+
.flat_map(|(name, path, disallowed_path)| {
761+
def_path_def_ids(tcx, &path).map(move |id| (id, (name, disallowed_path)))
762+
})
761763
.collect()
762764
}
763765

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
disallowed-methods = [
2+
{ path = "replaceable_disallowed_methods::bad", replacement = "good" },
3+
{ path = "replaceable_disallowed_methods::questionable", replacement = "good", reason = "a better function exists" },
4+
]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn bad() {}
2+
fn questionable() {}
3+
fn good() {}
4+
5+
fn main() {
6+
good();
7+
good();
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn bad() {}
2+
fn questionable() {}
3+
fn good() {}
4+
5+
fn main() {
6+
bad();
7+
questionable();
8+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error: use of a disallowed method `replaceable_disallowed_methods::bad`
2+
--> tests/ui-toml/toml_replaceable_disallowed_methods/replaceable_disallowed_methods.rs:6:5
3+
|
4+
LL | bad();
5+
| ^^^ help: use: `good`
6+
|
7+
= note: `-D clippy::disallowed-methods` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]`
9+
10+
error: use of a disallowed method `replaceable_disallowed_methods::questionable`
11+
--> tests/ui-toml/toml_replaceable_disallowed_methods/replaceable_disallowed_methods.rs:7:5
12+
|
13+
LL | questionable();
14+
| ^^^^^^^^^^^^ help: a better function exists: `good`
15+
16+
error: aborting due to 2 previous errors
17+

0 commit comments

Comments
 (0)