| 1 | use clippy_config::Conf; |
| 2 | use clippy_config::types::{DisallowedPath, create_disallowed_map}; |
| 3 | use clippy_utils::diagnostics::span_lint_and_then; |
| 4 | use rustc_hir::def::{CtorKind, DefKind, Res}; |
| 5 | use rustc_hir::def_id::DefIdMap; |
| 6 | use rustc_hir::{Expr, ExprKind}; |
| 7 | use rustc_lint::{LateContext, LateLintPass}; |
| 8 | use rustc_middle::ty::TyCtxt; |
| 9 | use rustc_session::impl_lint_pass; |
| 10 | |
| 11 | declare_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 | |
| 60 | pub struct DisallowedMethods { |
| 61 | disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, |
| 62 | } |
| 63 | |
| 64 | impl 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 | |
| 72 | impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); |
| 73 | |
| 74 | impl<'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 | |