|  | 
|  | 1 | +use clippy_utils::diagnostics::span_lint_and_then; | 
|  | 2 | +use clippy_utils::macros::macro_backtrace; | 
|  | 3 | +use rustc_data_structures::fx::FxHashSet; | 
|  | 4 | +use rustc_hir::def::{Namespace, Res}; | 
|  | 5 | +use rustc_hir::def_id::DefIdMap; | 
|  | 6 | +use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty}; | 
|  | 7 | +use rustc_lint::{LateContext, LateLintPass}; | 
|  | 8 | +use rustc_session::{declare_tool_lint, impl_lint_pass}; | 
|  | 9 | +use rustc_span::{ExpnId, Span}; | 
|  | 10 | + | 
|  | 11 | +use crate::utils::conf; | 
|  | 12 | + | 
|  | 13 | +declare_clippy_lint! { | 
|  | 14 | +    /// ### What it does | 
|  | 15 | +    /// Denies the configured macros in clippy.toml | 
|  | 16 | +    /// | 
|  | 17 | +    /// Note: Even though this lint is warn-by-default, it will only trigger if | 
|  | 18 | +    /// macros are defined in the clippy.toml file. | 
|  | 19 | +    /// | 
|  | 20 | +    /// ### Why is this bad? | 
|  | 21 | +    /// Some macros are undesirable in certain contexts, and it's beneficial to | 
|  | 22 | +    /// lint for them as needed. | 
|  | 23 | +    /// | 
|  | 24 | +    /// ### Example | 
|  | 25 | +    /// An example clippy.toml configuration: | 
|  | 26 | +    /// ```toml | 
|  | 27 | +    /// # clippy.toml | 
|  | 28 | +    /// disallowed-macros = [ | 
|  | 29 | +    ///     # Can use a string as the path of the disallowed macro. | 
|  | 30 | +    ///     "std::print", | 
|  | 31 | +    ///     # Can also use an inline table with a `path` key. | 
|  | 32 | +    ///     { path = "std::println" }, | 
|  | 33 | +    ///     # When using an inline table, can add a `reason` for why the macro | 
|  | 34 | +    ///     # is disallowed. | 
|  | 35 | +    ///     { path = "serde::Serialize", reason = "no serializing" }, | 
|  | 36 | +    /// ] | 
|  | 37 | +    /// ``` | 
|  | 38 | +    /// ``` | 
|  | 39 | +    /// use serde::Serialize; | 
|  | 40 | +    /// | 
|  | 41 | +    /// // Example code where clippy issues a warning | 
|  | 42 | +    /// println!("warns"); | 
|  | 43 | +    /// | 
|  | 44 | +    /// // The diagnostic will contain the message "no serializing" | 
|  | 45 | +    /// #[derive(Serialize)] | 
|  | 46 | +    /// struct Data { | 
|  | 47 | +    ///     name: String, | 
|  | 48 | +    ///     value: usize, | 
|  | 49 | +    /// } | 
|  | 50 | +    /// ``` | 
|  | 51 | +    #[clippy::version = "1.65.0"] | 
|  | 52 | +    pub DISALLOWED_MACROS, | 
|  | 53 | +    style, | 
|  | 54 | +    "use of a disallowed macro" | 
|  | 55 | +} | 
|  | 56 | + | 
|  | 57 | +pub struct DisallowedMacros { | 
|  | 58 | +    conf_disallowed: Vec<conf::DisallowedPath>, | 
|  | 59 | +    disallowed: DefIdMap<usize>, | 
|  | 60 | +    seen: FxHashSet<ExpnId>, | 
|  | 61 | +} | 
|  | 62 | + | 
|  | 63 | +impl DisallowedMacros { | 
|  | 64 | +    pub fn new(conf_disallowed: Vec<conf::DisallowedPath>) -> Self { | 
|  | 65 | +        Self { | 
|  | 66 | +            conf_disallowed, | 
|  | 67 | +            disallowed: DefIdMap::default(), | 
|  | 68 | +            seen: FxHashSet::default(), | 
|  | 69 | +        } | 
|  | 70 | +    } | 
|  | 71 | + | 
|  | 72 | +    fn check(&mut self, cx: &LateContext<'_>, span: Span) { | 
|  | 73 | +        if self.conf_disallowed.is_empty() { | 
|  | 74 | +            return; | 
|  | 75 | +        } | 
|  | 76 | + | 
|  | 77 | +        for mac in macro_backtrace(span) { | 
|  | 78 | +            if !self.seen.insert(mac.expn) { | 
|  | 79 | +                return; | 
|  | 80 | +            } | 
|  | 81 | + | 
|  | 82 | +            if let Some(&index) = self.disallowed.get(&mac.def_id) { | 
|  | 83 | +                let conf = &self.conf_disallowed[index]; | 
|  | 84 | + | 
|  | 85 | +                span_lint_and_then( | 
|  | 86 | +                    cx, | 
|  | 87 | +                    DISALLOWED_MACROS, | 
|  | 88 | +                    mac.span, | 
|  | 89 | +                    &format!("use of a disallowed macro `{}`", conf.path()), | 
|  | 90 | +                    |diag| { | 
|  | 91 | +                        if let Some(reason) = conf.reason() { | 
|  | 92 | +                            diag.note(&format!("{reason} (from clippy.toml)")); | 
|  | 93 | +                        } | 
|  | 94 | +                    }, | 
|  | 95 | +                ); | 
|  | 96 | +            } | 
|  | 97 | +        } | 
|  | 98 | +    } | 
|  | 99 | +} | 
|  | 100 | + | 
|  | 101 | +impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]); | 
|  | 102 | + | 
|  | 103 | +impl LateLintPass<'_> for DisallowedMacros { | 
|  | 104 | +    fn check_crate(&mut self, cx: &LateContext<'_>) { | 
|  | 105 | +        for (index, conf) in self.conf_disallowed.iter().enumerate() { | 
|  | 106 | +            let segs: Vec<_> = conf.path().split("::").collect(); | 
|  | 107 | +            if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::MacroNS)) { | 
|  | 108 | +                self.disallowed.insert(id, index); | 
|  | 109 | +            } | 
|  | 110 | +        } | 
|  | 111 | +    } | 
|  | 112 | + | 
|  | 113 | +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { | 
|  | 114 | +        self.check(cx, expr.span); | 
|  | 115 | +    } | 
|  | 116 | + | 
|  | 117 | +    fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { | 
|  | 118 | +        self.check(cx, stmt.span); | 
|  | 119 | +    } | 
|  | 120 | + | 
|  | 121 | +    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &Ty<'_>) { | 
|  | 122 | +        self.check(cx, ty.span); | 
|  | 123 | +    } | 
|  | 124 | + | 
|  | 125 | +    fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { | 
|  | 126 | +        self.check(cx, pat.span); | 
|  | 127 | +    } | 
|  | 128 | + | 
|  | 129 | +    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { | 
|  | 130 | +        self.check(cx, item.span); | 
|  | 131 | +        self.check(cx, item.vis_span); | 
|  | 132 | +    } | 
|  | 133 | + | 
|  | 134 | +    fn check_foreign_item(&mut self, cx: &LateContext<'_>, item: &ForeignItem<'_>) { | 
|  | 135 | +        self.check(cx, item.span); | 
|  | 136 | +        self.check(cx, item.vis_span); | 
|  | 137 | +    } | 
|  | 138 | + | 
|  | 139 | +    fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) { | 
|  | 140 | +        self.check(cx, item.span); | 
|  | 141 | +        self.check(cx, item.vis_span); | 
|  | 142 | +    } | 
|  | 143 | + | 
|  | 144 | +    fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) { | 
|  | 145 | +        self.check(cx, item.span); | 
|  | 146 | +    } | 
|  | 147 | + | 
|  | 148 | +    fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) { | 
|  | 149 | +        self.check(cx, path.span); | 
|  | 150 | +    } | 
|  | 151 | +} | 
0 commit comments