1use clippy_config::Conf;
2use clippy_config::types::{DisallowedPath, create_disallowed_map};
3use clippy_utils::diagnostics::span_lint_and_then;
4use rustc_hir::def::{CtorKind, DefKind, Res};
5use rustc_hir::def_id::DefIdMap;
6use rustc_hir::{Expr, ExprKind};
7use rustc_lint::{LateContext, LateLintPass};
8use rustc_middle::ty::TyCtxt;
9use rustc_session::impl_lint_pass;
10
11declare_clippy_lint! {
12 /// ### What it does
13 /// Denies the configured methods and functions in clippy.toml
14 ///
15 /// Note: Even though this lint is warn-by-default, it will only trigger if
16 /// methods are defined in the clippy.toml file.
17 ///
18 /// ### Why is this bad?
19 /// Some methods are undesirable in certain contexts, and it's beneficial to
20 /// lint for them as needed.
21 ///
22 /// ### Example
23 /// An example clippy.toml configuration:
24 /// ```toml
25 /// # clippy.toml
26 /// disallowed-methods = [
27 /// # Can use a string as the path of the disallowed method.
28 /// "std::boxed::Box::new",
29 /// # Can also use an inline table with a `path` key.
30 /// { path = "std::time::Instant::now" },
31 /// # When using an inline table, can add a `reason` for why the method
32 /// # is disallowed.
33 /// { path = "std::vec::Vec::leak", reason = "no leaking memory" },
34 /// # Can also add a `replacement` that will be offered as a suggestion.
35 /// { path = "std::sync::Mutex::new", reason = "prefer faster & simpler non-poisonable mutex", replacement = "parking_lot::Mutex::new" },
36 /// ]
37 /// ```
38 ///
39 /// ```rust,ignore
40 /// let xs = vec![1, 2, 3, 4];
41 /// xs.leak(); // Vec::leak is disallowed in the config.
42 /// // The diagnostic contains the message "no leaking memory".
43 ///
44 /// let _now = Instant::now(); // Instant::now is disallowed in the config.
45 ///
46 /// let _box = Box::new(3); // Box::new is disallowed in the config.
47 /// ```
48 ///
49 /// Use instead:
50 /// ```rust,ignore
51 /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.
52 /// xs.push(123); // Vec::push is _not_ disallowed in the config.
53 /// ```
54 #[clippy::version = "1.49.0"]
55 pub DISALLOWED_METHODS,
56 style,
57 "use of a disallowed method call"
58}
59
60pub struct DisallowedMethods {
61 disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>,
62}
63
64impl DisallowedMethods {
65 pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
66 Self {
67 disallowed: create_disallowed_map(tcx, &conf.disallowed_methods),
68 }
69 }
70}
71
72impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
73
74impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
75 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
76 let (id, span) = match &expr.kind {
77 ExprKind::Path(path)
78 if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) =
79 cx.qpath_res(path, expr.hir_id) =>
80 {
81 (id, expr.span)
82 },
83 ExprKind::MethodCall(name, ..) if let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => {
84 (id, name.ident.span)
85 },
86 _ => return,
87 };
88 if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) {
89 span_lint_and_then(
90 cx,
91 DISALLOWED_METHODS,
92 span,
93 format!("use of a disallowed method `{path}`"),
94 disallowed_path.diag_amendment(span),
95 );
96 }
97 }
98}
99