Skip to content

Commit 77dda9c

Browse files
committed
Auto merge of rust-lang#7791 - JamesHinshelwood:reason-in-disallowed-type, r=giraffate
Allow giving reasons for `disallowed_types` Similar to rust-lang#7609 but for the `disallowed_type` lint. The permitted form of configuration is the same as for `disallowed_methods`. changelog: Allow giving reasons for [`disallowed_type`]
2 parents 7272366 + 886cbb1 commit 77dda9c

File tree

6 files changed

+88
-39
lines changed

6 files changed

+88
-39
lines changed

clippy_lints/src/disallowed_type.rs

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
use clippy_utils::diagnostics::span_lint;
1+
use clippy_utils::diagnostics::span_lint_and_then;
22

3-
use rustc_data_structures::fx::FxHashSet;
3+
use rustc_data_structures::fx::FxHashMap;
44
use rustc_hir::{
55
def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, TraitBoundModifier, Ty, TyKind, UseKind,
66
};
77
use rustc_lint::{LateContext, LateLintPass};
88
use rustc_session::{declare_tool_lint, impl_lint_pass};
9-
use rustc_span::{Span, Symbol};
9+
use rustc_span::Span;
10+
11+
use crate::utils::conf;
1012

1113
declare_clippy_lint! {
1214
/// ### What it does
@@ -19,7 +21,15 @@ declare_clippy_lint! {
1921
/// An example clippy.toml configuration:
2022
/// ```toml
2123
/// # clippy.toml
22-
/// disallowed-types = ["std::collections::BTreeMap"]
24+
/// disallowed-types = [
25+
/// # Can use a string as the path of the disallowed type.
26+
/// "std::collections::BTreeMap",
27+
/// # Can also use an inline table with a `path` key.
28+
/// { path = "std::net::TcpListener" },
29+
/// # When using an inline table, can add a `reason` for why the type
30+
/// # is disallowed.
31+
/// { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
32+
/// ]
2333
/// ```
2434
///
2535
/// ```rust,ignore
@@ -38,33 +48,30 @@ declare_clippy_lint! {
3848
}
3949
#[derive(Clone, Debug)]
4050
pub struct DisallowedType {
41-
disallowed: FxHashSet<Vec<Symbol>>,
42-
def_ids: FxHashSet<DefId>,
43-
prim_tys: FxHashSet<PrimTy>,
51+
conf_disallowed: Vec<conf::DisallowedType>,
52+
def_ids: FxHashMap<DefId, Option<String>>,
53+
prim_tys: FxHashMap<PrimTy, Option<String>>,
4454
}
4555

4656
impl DisallowedType {
47-
pub fn new(disallowed: &FxHashSet<String>) -> Self {
57+
pub fn new(conf_disallowed: Vec<conf::DisallowedType>) -> Self {
4858
Self {
49-
disallowed: disallowed
50-
.iter()
51-
.map(|s| s.split("::").map(Symbol::intern).collect::<Vec<_>>())
52-
.collect(),
53-
def_ids: FxHashSet::default(),
54-
prim_tys: FxHashSet::default(),
59+
conf_disallowed,
60+
def_ids: FxHashMap::default(),
61+
prim_tys: FxHashMap::default(),
5562
}
5663
}
5764

5865
fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
5966
match res {
6067
Res::Def(_, did) => {
61-
if self.def_ids.contains(did) {
62-
emit(cx, &cx.tcx.def_path_str(*did), span);
68+
if let Some(reason) = self.def_ids.get(did) {
69+
emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref());
6370
}
6471
},
6572
Res::PrimTy(prim) => {
66-
if self.prim_tys.contains(prim) {
67-
emit(cx, prim.name_str(), span);
73+
if let Some(reason) = self.prim_tys.get(prim) {
74+
emit(cx, prim.name_str(), span, reason.as_deref());
6875
}
6976
},
7077
_ => {},
@@ -76,14 +83,21 @@ impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]);
7683

7784
impl<'tcx> LateLintPass<'tcx> for DisallowedType {
7885
fn check_crate(&mut self, cx: &LateContext<'_>) {
79-
for path in &self.disallowed {
80-
let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>();
81-
match clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>()) {
86+
for conf in &self.conf_disallowed {
87+
let (path, reason) = match conf {
88+
conf::DisallowedType::Simple(path) => (path, None),
89+
conf::DisallowedType::WithReason { path, reason } => (
90+
path,
91+
reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
92+
),
93+
};
94+
let segs: Vec<_> = path.split("::").collect();
95+
match clippy_utils::path_to_res(cx, &segs) {
8296
Res::Def(_, id) => {
83-
self.def_ids.insert(id);
97+
self.def_ids.insert(id, reason);
8498
},
8599
Res::PrimTy(ty) => {
86-
self.prim_tys.insert(ty);
100+
self.prim_tys.insert(ty, reason);
87101
},
88102
_ => {},
89103
}
@@ -107,11 +121,16 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedType {
107121
}
108122
}
109123

110-
fn emit(cx: &LateContext<'_>, name: &str, span: Span) {
111-
span_lint(
124+
fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) {
125+
span_lint_and_then(
112126
cx,
113127
DISALLOWED_TYPE,
114128
span,
115129
&format!("`{}` is not allowed according to config", name),
130+
|diag| {
131+
if let Some(reason) = reason {
132+
diag.note(reason);
133+
}
134+
},
116135
);
117136
}

clippy_lints/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -757,8 +757,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
757757
store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison));
758758
store.register_early_pass(move || Box::new(module_style::ModStyle));
759759
store.register_late_pass(|| Box::new(unused_async::UnusedAsync));
760-
let disallowed_types = conf.disallowed_types.iter().cloned().collect::<FxHashSet<_>>();
761-
store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(&disallowed_types)));
760+
let disallowed_types = conf.disallowed_types.clone();
761+
store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(disallowed_types.clone())));
762762
let import_renames = conf.enforced_import_renames.clone();
763763
store.register_late_pass(move || Box::new(missing_enforced_import_rename::ImportRename::new(import_renames.clone())));
764764
let scripts = conf.allowed_scripts.clone();

clippy_lints/src/utils/conf.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ pub enum DisallowedMethod {
2323
WithReason { path: String, reason: Option<String> },
2424
}
2525

26+
/// A single disallowed type, used by the `DISALLOWED_TYPE` lint.
27+
#[derive(Clone, Debug, Deserialize)]
28+
#[serde(untagged)]
29+
pub enum DisallowedType {
30+
Simple(String),
31+
WithReason { path: String, reason: Option<String> },
32+
}
33+
2634
/// Conf with parse errors
2735
#[derive(Default)]
2836
pub struct TryConf {
@@ -255,7 +263,7 @@ define_Conf! {
255263
/// Lint: DISALLOWED_TYPE.
256264
///
257265
/// The list of disallowed types, written as fully qualified paths.
258-
(disallowed_types: Vec<String> = Vec::new()),
266+
(disallowed_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
259267
/// Lint: UNREADABLE_LITERAL.
260268
///
261269
/// Should the fraction of a decimal be linted to include separators.

tests/ui-toml/toml_disallowed_type/clippy.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,9 @@ disallowed-types = [
77
"std::time::Instant",
88
"std::io::Read",
99
"std::primitive::usize",
10-
"bool"
10+
"bool",
11+
# can give path and reason with an inline table
12+
{ path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
13+
# can use an inline table but omit reason
14+
{ path = "std::net::TcpListener" },
1115
]

tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ struct GenArg<const U: usize>([u8; U]);
2525

2626
static BAD: foo::atomic::AtomicPtr<()> = foo::atomic::AtomicPtr::new(std::ptr::null_mut());
2727

28+
fn ip(_: std::net::Ipv4Addr) {}
29+
30+
fn listener(_: std::net::TcpListener) {}
31+
2832
#[allow(clippy::diverging_sub_expression)]
2933
fn main() {
3034
let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();

tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,59 +60,73 @@ error: `usize` is not allowed according to config
6060
LL | struct GenArg<const U: usize>([u8; U]);
6161
| ^^^^^
6262

63+
error: `std::net::Ipv4Addr` is not allowed according to config
64+
--> $DIR/conf_disallowed_type.rs:28:10
65+
|
66+
LL | fn ip(_: std::net::Ipv4Addr) {}
67+
| ^^^^^^^^^^^^^^^^^^
68+
|
69+
= note: no IPv4 allowed (from clippy.toml)
70+
71+
error: `std::net::TcpListener` is not allowed according to config
72+
--> $DIR/conf_disallowed_type.rs:30:16
73+
|
74+
LL | fn listener(_: std::net::TcpListener) {}
75+
| ^^^^^^^^^^^^^^^^^^^^^
76+
6377
error: `std::collections::HashMap` is not allowed according to config
64-
--> $DIR/conf_disallowed_type.rs:30:48
78+
--> $DIR/conf_disallowed_type.rs:34:48
6579
|
6680
LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
6781
| ^^^^^^^^^^^^^^^^^^^^^^^^^
6882

6983
error: `std::collections::HashMap` is not allowed according to config
70-
--> $DIR/conf_disallowed_type.rs:30:12
84+
--> $DIR/conf_disallowed_type.rs:34:12
7185
|
7286
LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
7387
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7488

7589
error: `std::time::Instant` is not allowed according to config
76-
--> $DIR/conf_disallowed_type.rs:31:13
90+
--> $DIR/conf_disallowed_type.rs:35:13
7791
|
7892
LL | let _ = Sneaky::now();
7993
| ^^^^^^
8094

8195
error: `std::sync::atomic::AtomicU32` is not allowed according to config
82-
--> $DIR/conf_disallowed_type.rs:32:13
96+
--> $DIR/conf_disallowed_type.rs:36:13
8397
|
8498
LL | let _ = foo::atomic::AtomicU32::new(0);
8599
| ^^^^^^^^^^^^^^^^^^^^^^
86100

87101
error: `std::sync::atomic::AtomicU32` is not allowed according to config
88-
--> $DIR/conf_disallowed_type.rs:33:17
102+
--> $DIR/conf_disallowed_type.rs:37:17
89103
|
90104
LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
91105
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
92106

93107
error: `std::sync::atomic::AtomicU32` is not allowed according to config
94-
--> $DIR/conf_disallowed_type.rs:33:48
108+
--> $DIR/conf_disallowed_type.rs:37:48
95109
|
96110
LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
97111
| ^^^^^^^^^^^^^^^^^^^^^^
98112

99113
error: `syn::TypePath` is not allowed according to config
100-
--> $DIR/conf_disallowed_type.rs:34:43
114+
--> $DIR/conf_disallowed_type.rs:38:43
101115
|
102116
LL | let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
103117
| ^^^^^^^^^^^^^
104118

105119
error: `syn::Ident` is not allowed according to config
106-
--> $DIR/conf_disallowed_type.rs:35:13
120+
--> $DIR/conf_disallowed_type.rs:39:13
107121
|
108122
LL | let _ = syn::Ident::new("", todo!());
109123
| ^^^^^^^^^^
110124

111125
error: `usize` is not allowed according to config
112-
--> $DIR/conf_disallowed_type.rs:37:12
126+
--> $DIR/conf_disallowed_type.rs:41:12
113127
|
114128
LL | let _: usize = 64_usize;
115129
| ^^^^^
116130

117-
error: aborting due to 19 previous errors
131+
error: aborting due to 21 previous errors
118132

0 commit comments

Comments
 (0)