Skip to content

Commit a68178c

Browse files
committed
implement disallowed trait methods
1 parent 76a75bf commit a68178c

File tree

5 files changed

+98
-21
lines changed

5 files changed

+98
-21
lines changed

clippy_lints/src/disallowed_methods.rs

+65-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
use clippy_config::types::DisallowedPath;
22
use clippy_utils::diagnostics::span_lint_and_then;
33
use clippy_utils::{fn_def_id, get_parent_expr, path_def_id};
4-
use rustc_hir::def_id::DefIdMap;
5-
use rustc_hir::{Expr, ExprKind};
4+
use itertools::Itertools as _;
5+
use rustc_ast::ast;
6+
use rustc_hir::def::Res;
7+
use rustc_hir::def_id::{DefId, DefIdMap};
8+
use rustc_hir::{Expr, ExprKind, PrimTy};
69
use rustc_lint::{LateContext, LateLintPass};
10+
use rustc_middle::ty::{AdtKind, TyKind};
711
use rustc_session::impl_lint_pass;
12+
use rustc_type_ir as ir;
813

914
declare_clippy_lint! {
1015
/// ### What it does
@@ -59,13 +64,16 @@ declare_clippy_lint! {
5964
pub struct DisallowedMethods {
6065
conf_disallowed: Vec<DisallowedPath>,
6166
disallowed: DefIdMap<usize>,
67+
// (Self, TraitMethod)
68+
disallowed_qualified_trait: rustc_data_structures::unord::UnordMap<(Res, DefId), usize>,
6269
}
6370

6471
impl DisallowedMethods {
6572
pub fn new(conf_disallowed: Vec<DisallowedPath>) -> Self {
6673
Self {
6774
conf_disallowed,
6875
disallowed: DefIdMap::default(),
76+
disallowed_qualified_trait: Default::default(),
6977
}
7078
}
7179
}
@@ -75,9 +83,31 @@ impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
7583
impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
7684
fn check_crate(&mut self, cx: &LateContext<'_>) {
7785
for (index, conf) in self.conf_disallowed.iter().enumerate() {
78-
let segs: Vec<_> = conf.path().split("::").collect();
79-
for id in clippy_utils::def_path_def_ids(cx, &segs) {
80-
self.disallowed.insert(id, index);
86+
let path = conf.path();
87+
if let Some(path) = path.strip_prefix('<') {
88+
// a qualified associated item
89+
let Some((tr, method)) = path.split_once(">::") else {
90+
continue;
91+
};
92+
let Some((self_ty, _as, trait_path)) = tr.split_whitespace().next_tuple() else {
93+
continue;
94+
};
95+
let self_segs: Vec<_> = self_ty.split("::").collect();
96+
let self_ress: Vec<_> = clippy_utils::def_path_res(cx, &self_segs);
97+
let mut method_segs: Vec<_> = trait_path.split("::").collect();
98+
method_segs.push(method);
99+
let method_id: Vec<_> = clippy_utils::def_path_def_ids(cx, &method_segs).collect();
100+
for self_res in &self_ress {
101+
for method_id in &method_id {
102+
self.disallowed_qualified_trait.insert((*self_res, *method_id), index);
103+
}
104+
}
105+
} else {
106+
// simple path
107+
let segs: Vec<_> = path.split("::").collect();
108+
for id in clippy_utils::def_path_def_ids(cx, &segs) {
109+
self.disallowed.insert(id, index);
110+
}
81111
}
82112
}
83113
}
@@ -96,7 +126,36 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
96126
};
97127
let conf = match self.disallowed.get(&def_id) {
98128
Some(&index) => &self.conf_disallowed[index],
99-
None => return,
129+
None => match expr.kind {
130+
ExprKind::MethodCall(_, self_arg, _, _) if !self.disallowed_qualified_trait.is_empty() => {
131+
let typeck = cx.typeck_results();
132+
let trait_method_def_id = typeck.type_dependent_def_id(expr.hir_id).unwrap();
133+
let self_ty = typeck.expr_ty(self_arg);
134+
let self_res: Res<rustc_hir::HirId> = match self_ty.kind() {
135+
TyKind::Bool | TyKind::Char | TyKind::Int(_) | TyKind::Uint(_) | TyKind::Float(_) => {
136+
Res::PrimTy(PrimTy::from_name(self_ty.primitive_symbol().unwrap()).unwrap())
137+
},
138+
TyKind::Str => Res::PrimTy(PrimTy::Str),
139+
TyKind::Adt(adt, _) => Res::Def(
140+
match adt.adt_kind() {
141+
AdtKind::Struct => rustc_hir::def::DefKind::Struct,
142+
AdtKind::Union => rustc_hir::def::DefKind::Union,
143+
AdtKind::Enum => rustc_hir::def::DefKind::Enum,
144+
},
145+
adt.did(),
146+
),
147+
// FIXME: these other kinds are not currently supported by disallowed_methods due to how
148+
// def_path_ref is implemented
149+
_ => return,
150+
};
151+
println!("disallowed: {:?}", self.disallowed_qualified_trait);
152+
match self.disallowed_qualified_trait.get(&(self_res, trait_method_def_id)) {
153+
Some(&index) => &self.conf_disallowed[index],
154+
None => return,
155+
}
156+
},
157+
_ => return,
158+
},
100159
};
101160
let msg = format!("use of a disallowed method `{}`", conf.path());
102161
span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| {

clippy_lints/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ extern crate rustc_session;
4444
extern crate rustc_span;
4545
extern crate rustc_target;
4646
extern crate rustc_trait_selection;
47+
extern crate rustc_type_ir;
4748
extern crate thin_vec;
4849

4950
#[macro_use]

tests/ui-toml/toml_disallowed_methods/clippy.toml

+1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ disallowed-methods = [
1414
"conf_disallowed_methods::Struct::method",
1515
"conf_disallowed_methods::Trait::provided_method",
1616
"conf_disallowed_methods::Trait::implemented_method",
17+
"<&conf_disallowed_methods::StructBad as conf_disallowed_methods::Trait>::provided_bad_method",
1718
]

tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs

+10
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use regex::Regex;
1313
fn local_fn() {}
1414

1515
struct Struct;
16+
struct StructBad;
1617

1718
impl Struct {
1819
fn method(&self) {}
@@ -21,12 +22,17 @@ impl Struct {
2122
trait Trait {
2223
fn provided_method(&self) {}
2324
fn implemented_method(&self);
25+
fn provided_bad_method(&self) {}
2426
}
2527

2628
impl Trait for Struct {
2729
fn implemented_method(&self) {}
2830
}
2931

32+
impl Trait for StructBad {
33+
fn implemented_method(&self) {}
34+
}
35+
3036
mod local_mod {
3137
pub fn f() {}
3238
}
@@ -58,4 +64,8 @@ fn main() {
5864
s.method();
5965
s.provided_method();
6066
s.implemented_method();
67+
_ = String::default();
68+
s.provided_bad_method();
69+
let s_bad = StructBad;
70+
s_bad.provided_bad_method();
6171
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: use of a disallowed method `regex::Regex::new`
2-
--> $DIR/conf_disallowed_methods.rs:35:14
2+
--> $DIR/conf_disallowed_methods.rs:41:14
33
|
44
LL | let re = Regex::new(r"ab.*c").unwrap();
55
| ^^^^^^^^^^^^^^^^^^^^
@@ -8,84 +8,90 @@ LL | let re = Regex::new(r"ab.*c").unwrap();
88
= help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]`
99

1010
error: use of a disallowed method `regex::Regex::is_match`
11-
--> $DIR/conf_disallowed_methods.rs:36:5
11+
--> $DIR/conf_disallowed_methods.rs:42:5
1212
|
1313
LL | re.is_match("abc");
1414
| ^^^^^^^^^^^^^^^^^^
1515
|
1616
= note: no matching allowed (from clippy.toml)
1717

1818
error: use of a disallowed method `std::iter::Iterator::sum`
19-
--> $DIR/conf_disallowed_methods.rs:39:5
19+
--> $DIR/conf_disallowed_methods.rs:45:5
2020
|
2121
LL | a.iter().sum::<i32>();
2222
| ^^^^^^^^^^^^^^^^^^^^^
2323

2424
error: use of a disallowed method `slice::sort_unstable`
25-
--> $DIR/conf_disallowed_methods.rs:41:5
25+
--> $DIR/conf_disallowed_methods.rs:47:5
2626
|
2727
LL | a.sort_unstable();
2828
| ^^^^^^^^^^^^^^^^^
2929

3030
error: use of a disallowed method `f32::clamp`
31-
--> $DIR/conf_disallowed_methods.rs:43:13
31+
--> $DIR/conf_disallowed_methods.rs:49:13
3232
|
3333
LL | let _ = 2.0f32.clamp(3.0f32, 4.0f32);
3434
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3535

3636
error: use of a disallowed method `regex::Regex::new`
37-
--> $DIR/conf_disallowed_methods.rs:46:61
37+
--> $DIR/conf_disallowed_methods.rs:52:61
3838
|
3939
LL | let indirect: fn(&str) -> Result<Regex, regex::Error> = Regex::new;
4040
| ^^^^^^^^^^
4141

4242
error: use of a disallowed method `f32::clamp`
43-
--> $DIR/conf_disallowed_methods.rs:49:28
43+
--> $DIR/conf_disallowed_methods.rs:55:28
4444
|
4545
LL | let in_call = Box::new(f32::clamp);
4646
| ^^^^^^^^^^
4747

4848
error: use of a disallowed method `regex::Regex::new`
49-
--> $DIR/conf_disallowed_methods.rs:50:53
49+
--> $DIR/conf_disallowed_methods.rs:56:53
5050
|
5151
LL | let in_method_call = ["^", "$"].into_iter().map(Regex::new);
5252
| ^^^^^^^^^^
5353

5454
error: use of a disallowed method `futures::stream::select_all`
55-
--> $DIR/conf_disallowed_methods.rs:53:31
55+
--> $DIR/conf_disallowed_methods.rs:59:31
5656
|
5757
LL | let same_name_as_module = select_all(vec![empty::<()>()]);
5858
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5959

6060
error: use of a disallowed method `conf_disallowed_methods::local_fn`
61-
--> $DIR/conf_disallowed_methods.rs:55:5
61+
--> $DIR/conf_disallowed_methods.rs:61:5
6262
|
6363
LL | local_fn();
6464
| ^^^^^^^^^^
6565

6666
error: use of a disallowed method `conf_disallowed_methods::local_mod::f`
67-
--> $DIR/conf_disallowed_methods.rs:56:5
67+
--> $DIR/conf_disallowed_methods.rs:62:5
6868
|
6969
LL | local_mod::f();
7070
| ^^^^^^^^^^^^^^
7171

7272
error: use of a disallowed method `conf_disallowed_methods::Struct::method`
73-
--> $DIR/conf_disallowed_methods.rs:58:5
73+
--> $DIR/conf_disallowed_methods.rs:64:5
7474
|
7575
LL | s.method();
7676
| ^^^^^^^^^^
7777

7878
error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method`
79-
--> $DIR/conf_disallowed_methods.rs:59:5
79+
--> $DIR/conf_disallowed_methods.rs:65:5
8080
|
8181
LL | s.provided_method();
8282
| ^^^^^^^^^^^^^^^^^^^
8383

8484
error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method`
85-
--> $DIR/conf_disallowed_methods.rs:60:5
85+
--> $DIR/conf_disallowed_methods.rs:66:5
8686
|
8787
LL | s.implemented_method();
8888
| ^^^^^^^^^^^^^^^^^^^^^^
8989

90-
error: aborting due to 14 previous errors
90+
error: use of a disallowed method `<conf_disallowed_methods::StructBad as conf_disallowed_methods::Trait>::provided_bad_method`
91+
--> $DIR/conf_disallowed_methods.rs:70:5
92+
|
93+
LL | s_bad.provided_bad_method();
94+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
95+
96+
error: aborting due to 15 previous errors
9197

0 commit comments

Comments
 (0)