11use clippy_config:: Conf ;
22use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help, span_lint_hir} ;
3- use clippy_utils:: is_bool;
4- use clippy_utils:: macros:: span_is_local;
5- use clippy_utils:: source:: is_present_in_source;
63use clippy_utils:: str_utils:: { camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case} ;
4+ use clippy_utils:: { is_bool, is_from_proc_macro} ;
75use rustc_data_structures:: fx:: FxHashSet ;
8- use rustc_hir:: { EnumDef , FieldDef , Item , ItemKind , OwnerId , QPath , TyKind , Variant , VariantData } ;
6+ use rustc_hir:: { Body , EnumDef , FieldDef , Item , ItemKind , QPath , TyKind , UseKind , Variant , VariantData } ;
97use rustc_lint:: { LateContext , LateLintPass } ;
108use rustc_session:: impl_lint_pass;
119use rustc_span:: symbol:: Symbol ;
@@ -158,7 +156,8 @@ declare_clippy_lint! {
158156}
159157
160158pub struct ItemNameRepetitions {
161- modules : Vec < ( Symbol , String , OwnerId ) > ,
159+ /// The module path the lint pass is in.
160+ modules : Vec < ModInfo > ,
162161 enum_threshold : u64 ,
163162 struct_threshold : u64 ,
164163 avoid_breaking_exported_api : bool ,
@@ -167,6 +166,17 @@ pub struct ItemNameRepetitions {
167166 allowed_prefixes : FxHashSet < String > ,
168167}
169168
169+ struct ModInfo {
170+ name : Symbol ,
171+ name_camel : String ,
172+ /// Does this module have the `pub` visibility modifier.
173+ is_public : bool ,
174+ /// How many bodies are between this module and the current lint pass position.
175+ ///
176+ /// Only the most recently seen module is updated when entering/exiting a body.
177+ in_body_count : u32 ,
178+ }
179+
170180impl ItemNameRepetitions {
171181 pub fn new ( conf : & ' static Conf ) -> Self {
172182 Self {
@@ -458,71 +468,109 @@ fn check_enum_tuple_path_match(variant_name: &str, variant_data: VariantData<'_>
458468}
459469
460470impl LateLintPass < ' _ > for ItemNameRepetitions {
461- fn check_item_post ( & mut self , _cx : & LateContext < ' _ > , item : & Item < ' _ > ) {
462- let Some ( _ident ) = item. kind . ident ( ) else { return } ;
463-
464- let last = self . modules . pop ( ) ;
465- assert ! ( last . is_some ( ) ) ;
471+ fn check_item_post ( & mut self , _ : & LateContext < ' _ > , item : & Item < ' _ > ) {
472+ if matches ! ( item. kind, ItemKind :: Mod ( .. ) ) {
473+ let prev = self . modules . pop ( ) ;
474+ debug_assert ! ( prev . is_some ( ) ) ;
475+ }
466476 }
467477
468- fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & Item < ' _ > ) {
469- let Some ( ident) = item. kind . ident ( ) else { return } ;
470-
471- let item_name = ident. name . as_str ( ) ;
472- let item_camel = to_camel_case ( item_name) ;
473- if !item. span . from_expansion ( ) && is_present_in_source ( cx, item. span )
474- && let [ .., ( mod_name, mod_camel, mod_owner_id) ] = & * self . modules
475- // constants don't have surrounding modules
476- && !mod_camel. is_empty ( )
477- {
478- if mod_name == & ident. name
479- && let ItemKind :: Mod ( ..) = item. kind
480- && ( !self . allow_private_module_inception || cx. tcx . visibility ( mod_owner_id. def_id ) . is_public ( ) )
481- {
482- span_lint (
483- cx,
484- MODULE_INCEPTION ,
485- item. span ,
486- "module has the same name as its containing module" ,
487- ) ;
488- }
478+ fn check_body ( & mut self , _: & LateContext < ' _ > , _: & Body < ' _ > ) {
479+ if let [ .., last] = & mut * self . modules {
480+ last. in_body_count += 1 ;
481+ }
482+ }
489483
490- // The `module_name_repetitions` lint should only trigger if the item has the module in its
491- // name. Having the same name is only accepted if `allow_exact_repetition` is set to `true`.
484+ fn check_body_post ( & mut self , _: & LateContext < ' _ > , _: & Body < ' _ > ) {
485+ if let [ .., last] = & mut * self . modules {
486+ last. in_body_count -= 1 ;
487+ }
488+ }
492489
493- let both_are_public =
494- cx. tcx . visibility ( item. owner_id ) . is_public ( ) && cx. tcx . visibility ( mod_owner_id. def_id ) . is_public ( ) ;
490+ fn check_item ( & mut self , cx : & LateContext < ' _ > , item : & Item < ' _ > ) {
491+ let ident = match item. kind {
492+ ItemKind :: Mod ( ident, _) => {
493+ if let [ .., prev] = & * self . modules
494+ && prev. name == ident. name
495+ && prev. in_body_count == 0
496+ && ( !self . allow_private_module_inception || prev. is_public )
497+ && !item. span . from_expansion ( )
498+ && !is_from_proc_macro ( cx, item)
499+ {
500+ span_lint (
501+ cx,
502+ MODULE_INCEPTION ,
503+ item. span ,
504+ "module has the same name as its containing module" ,
505+ ) ;
506+ }
507+ ident
508+ } ,
495509
496- if both_are_public && !self . allow_exact_repetitions && item_camel == * mod_camel {
497- span_lint (
498- cx,
499- MODULE_NAME_REPETITIONS ,
500- ident. span ,
501- "item name is the same as its containing module's name" ,
502- ) ;
503- }
510+ ItemKind :: Enum ( ident, _, def) => {
511+ if !ident. span . in_external_macro ( cx. tcx . sess . source_map ( ) ) {
512+ self . check_variants ( cx, item, & def) ;
513+ }
514+ ident
515+ } ,
516+ ItemKind :: Struct ( ident, _, data) => {
517+ if let VariantData :: Struct { fields, .. } = data
518+ && !ident. span . in_external_macro ( cx. tcx . sess . source_map ( ) )
519+ {
520+ self . check_fields ( cx, item, fields) ;
521+ }
522+ ident
523+ } ,
504524
505- let is_macro = matches ! ( item. kind, ItemKind :: Macro ( _, _, _) ) ;
506- if both_are_public && item_camel. len ( ) > mod_camel. len ( ) && !is_macro {
507- let matching = count_match_start ( mod_camel, & item_camel) ;
508- let rmatching = count_match_end ( mod_camel, & item_camel) ;
509- let nchars = mod_camel. chars ( ) . count ( ) ;
525+ ItemKind :: Const ( ident, ..)
526+ | ItemKind :: ExternCrate ( _, ident)
527+ | ItemKind :: Fn { ident, .. }
528+ | ItemKind :: Macro ( ident, ..)
529+ | ItemKind :: Static ( _, ident, ..)
530+ | ItemKind :: Trait ( _, _, _, ident, ..)
531+ | ItemKind :: TraitAlias ( ident, ..)
532+ | ItemKind :: TyAlias ( ident, ..)
533+ | ItemKind :: Union ( ident, ..)
534+ | ItemKind :: Use ( _, UseKind :: Single ( ident) ) => ident,
535+
536+ ItemKind :: ForeignMod { .. } | ItemKind :: GlobalAsm { .. } | ItemKind :: Impl ( _) | ItemKind :: Use ( ..) => return ,
537+ } ;
510538
511- let is_word_beginning = |c : char | c == '_' || c. is_uppercase ( ) || c. is_numeric ( ) ;
539+ let item_name = ident. name . as_str ( ) ;
540+ let item_camel = to_camel_case ( item_name) ;
512541
513- if matching. char_count == nchars {
514- match item_camel. chars ( ) . nth ( nchars) {
515- Some ( c) if is_word_beginning ( c) => span_lint (
542+ if let [ .., prev] = & * self . modules
543+ && prev. is_public
544+ && prev. in_body_count == 0
545+ && !item. span . from_expansion ( )
546+ && !matches ! ( item. kind, ItemKind :: Macro ( ..) )
547+ && cx. tcx . visibility ( item. owner_id ) . is_public ( )
548+ {
549+ if !self . allow_exact_repetitions && item_camel == prev. name_camel {
550+ if !is_from_proc_macro ( cx, item) {
551+ span_lint (
552+ cx,
553+ MODULE_NAME_REPETITIONS ,
554+ ident. span ,
555+ "item name is the same as its containing module's name" ,
556+ ) ;
557+ }
558+ } else if item_camel. len ( ) > prev. name_camel . len ( ) {
559+ if let Some ( s) = item_camel. strip_prefix ( & prev. name_camel )
560+ && let Some ( c) = s. chars ( ) . next ( )
561+ && ( c == '_' || c. is_uppercase ( ) || c. is_numeric ( ) )
562+ {
563+ if !is_from_proc_macro ( cx, item) {
564+ span_lint (
516565 cx,
517566 MODULE_NAME_REPETITIONS ,
518567 ident. span ,
519568 "item name starts with its containing module's name" ,
520- ) ,
521- _ => ( ) ,
569+ ) ;
522570 }
523- }
524- if rmatching . char_count == nchars
525- && !self . is_allowed_prefix ( & item_camel [ ..item_camel . len ( ) - rmatching . byte_count ] )
571+ } else if let Some ( s ) = item_camel . strip_suffix ( & prev . name_camel )
572+ && ! self . is_allowed_prefix ( s )
573+ && !is_from_proc_macro ( cx , item )
526574 {
527575 span_lint (
528576 cx,
@@ -534,17 +582,13 @@ impl LateLintPass<'_> for ItemNameRepetitions {
534582 }
535583 }
536584
537- if span_is_local ( item. span ) {
538- match item. kind {
539- ItemKind :: Enum ( _, _, def) => {
540- self . check_variants ( cx, item, & def) ;
541- } ,
542- ItemKind :: Struct ( _, _, VariantData :: Struct { fields, .. } ) => {
543- self . check_fields ( cx, item, fields) ;
544- } ,
545- _ => ( ) ,
546- }
585+ if matches ! ( item. kind, ItemKind :: Mod ( ..) ) {
586+ self . modules . push ( ModInfo {
587+ name : ident. name ,
588+ name_camel : item_camel,
589+ is_public : cx. tcx . visibility ( item. owner_id ) . is_public ( ) ,
590+ in_body_count : 0 ,
591+ } ) ;
547592 }
548- self . modules . push ( ( ident. name , item_camel, item. owner_id ) ) ;
549593 }
550594}
0 commit comments