| 
 | 1 | +use clippy_utils::diagnostics::span_lint_and_then;  | 
 | 2 | +use rustc_data_structures::fx::FxHashMap;  | 
 | 3 | +use rustc_hir::{GenericParamKind, Generics, HirId, Item, ItemKind, Mod};  | 
 | 4 | +use rustc_lint::{LateContext, LateLintPass, LintContext};  | 
 | 5 | +use rustc_session::impl_lint_pass;  | 
 | 6 | +use rustc_span::{Ident, Span, Symbol};  | 
 | 7 | + | 
 | 8 | +declare_clippy_lint! {  | 
 | 9 | +    /// ### What it does  | 
 | 10 | +    /// Checks for generic parameters that shadow types in scope.  | 
 | 11 | +    ///  | 
 | 12 | +    /// ### Why restrict this?  | 
 | 13 | +    /// To avoid confusion and potential bugs, as it can lead to  | 
 | 14 | +    /// misunderstandings about which type is being referred to.  | 
 | 15 | +    ///  | 
 | 16 | +    /// ### Example  | 
 | 17 | +    /// ```no_run  | 
 | 18 | +    /// struct Foo;  | 
 | 19 | +    /// struct Bar<Foo> { f: Foo }  | 
 | 20 | +    /// ```  | 
 | 21 | +    ///  | 
 | 22 | +    /// Use instead:  | 
 | 23 | +    /// ```no_run  | 
 | 24 | +    /// struct Foo;  | 
 | 25 | +    /// struct Bar<F> { f: F } // use different generic parameter name  | 
 | 26 | +    /// ```  | 
 | 27 | +    #[clippy::version = "1.89.0"]  | 
 | 28 | +    pub SHADOW_TYPE_GENERIC,  | 
 | 29 | +    restriction,  | 
 | 30 | +    "shadowing of type in scope by generic parameter"  | 
 | 31 | +}  | 
 | 32 | + | 
 | 33 | +#[derive(Default)]  | 
 | 34 | +pub(crate) struct ShadowTypeGeneric {  | 
 | 35 | +    module_types: FxHashMap<Symbol, Span>,  | 
 | 36 | +}  | 
 | 37 | + | 
 | 38 | +impl_lint_pass!(ShadowTypeGeneric => [SHADOW_TYPE_GENERIC]);  | 
 | 39 | + | 
 | 40 | +impl<'tcx> LateLintPass<'tcx> for ShadowTypeGeneric {  | 
 | 41 | +    fn check_mod(&mut self, cx: &LateContext<'tcx>, module: &'tcx Mod<'tcx>, _: HirId) {  | 
 | 42 | +        self.module_types.clear();  | 
 | 43 | +        let items = module.item_ids.iter().map(|&id| cx.tcx.hir_item(id));  | 
 | 44 | +        for item in items {  | 
 | 45 | +            if item.span.in_external_macro(cx.sess().source_map()) || item.span.from_expansion() {  | 
 | 46 | +                continue;  | 
 | 47 | +            }  | 
 | 48 | + | 
 | 49 | +            if let ItemKind::Enum(ident, _, _) | ItemKind::Struct(ident, _, _) = item.kind {  | 
 | 50 | +                self.module_types.insert(ident.name, ident.span);  | 
 | 51 | +            }  | 
 | 52 | +        }  | 
 | 53 | +    }  | 
 | 54 | + | 
 | 55 | +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {  | 
 | 56 | +        if item.span.in_external_macro(cx.sess().source_map()) || item.span.from_expansion() {  | 
 | 57 | +            return;  | 
 | 58 | +        }  | 
 | 59 | +        if let ItemKind::Enum(ident, generics, _) | ItemKind::Struct(ident, generics, _) = item.kind {  | 
 | 60 | +            self.check(cx, ident, generics);  | 
 | 61 | +        }  | 
 | 62 | +    }  | 
 | 63 | +}  | 
 | 64 | + | 
 | 65 | +impl ShadowTypeGeneric {  | 
 | 66 | +    fn check(&self, cx: &LateContext<'_>, ident: Ident, generics: &Generics<'_>) {  | 
 | 67 | +        // Look for generic parameters such as `T`.  | 
 | 68 | +        let generic_params = generics  | 
 | 69 | +            .params  | 
 | 70 | +            .iter()  | 
 | 71 | +            .filter(|gen_param| matches!(gen_param.kind, GenericParamKind::Type { .. }));  | 
 | 72 | + | 
 | 73 | +        // Match generic parameters with module types and split into spans lists.  | 
 | 74 | +        let (gen_param_spans, type_spans): (Vec<_>, Vec<_>) = generic_params  | 
 | 75 | +            .filter_map(|gen_param| {  | 
 | 76 | +                self.module_types  | 
 | 77 | +                    .get(&gen_param.name.ident().name)  | 
 | 78 | +                    .map(|type_span| (gen_param.span, type_span))  | 
 | 79 | +            })  | 
 | 80 | +            .unzip();  | 
 | 81 | + | 
 | 82 | +        let (msg, help) = match gen_param_spans.len() {  | 
 | 83 | +            0 => {  | 
 | 84 | +                // No generic parameters shadowing types in scope  | 
 | 85 | +                return;  | 
 | 86 | +            },  | 
 | 87 | +            1 => (  | 
 | 88 | +                format!("generic parameter in `{ident}` shadows type in scope"),  | 
 | 89 | +                "consider using a different name for the generic parameter",  | 
 | 90 | +            ),  | 
 | 91 | +            _ => (  | 
 | 92 | +                format!("generic parameters in `{ident}` shadow types in scope"),  | 
 | 93 | +                "consider using different names for the generic parameters",  | 
 | 94 | +            ),  | 
 | 95 | +        };  | 
 | 96 | + | 
 | 97 | +        span_lint_and_then(cx, SHADOW_TYPE_GENERIC, gen_param_spans, msg, |diag| {  | 
 | 98 | +            diag.span_labels(  | 
 | 99 | +                type_spans,  | 
 | 100 | +                &format!("this type is being shadowed by a generic parameter in `{ident}`"),  | 
 | 101 | +            );  | 
 | 102 | +            diag.help(help);  | 
 | 103 | +        });  | 
 | 104 | +    }  | 
 | 105 | +}  | 
0 commit comments