1
1
use clippy_config:: Conf ;
2
2
use 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;
6
3
use 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} ;
7
5
use 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 } ;
9
7
use rustc_lint:: { LateContext , LateLintPass } ;
10
8
use rustc_session:: impl_lint_pass;
11
9
use rustc_span:: symbol:: Symbol ;
@@ -158,7 +156,8 @@ declare_clippy_lint! {
158
156
}
159
157
160
158
pub struct ItemNameRepetitions {
161
- modules : Vec < ( Symbol , String , OwnerId ) > ,
159
+ /// The module path the lint pass is in.
160
+ modules : Vec < ModInfo > ,
162
161
enum_threshold : u64 ,
163
162
struct_threshold : u64 ,
164
163
avoid_breaking_exported_api : bool ,
@@ -167,6 +166,17 @@ pub struct ItemNameRepetitions {
167
166
allowed_prefixes : FxHashSet < String > ,
168
167
}
169
168
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
+
170
180
impl ItemNameRepetitions {
171
181
pub fn new ( conf : & ' static Conf ) -> Self {
172
182
Self {
@@ -458,71 +468,109 @@ fn check_enum_tuple_path_match(variant_name: &str, variant_data: VariantData<'_>
458
468
}
459
469
460
470
impl 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
+ }
466
476
}
467
477
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
+ }
489
483
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
+ }
492
489
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
+ } ,
495
509
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
+ } ,
504
524
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
+ } ;
510
538
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) ;
512
541
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 (
516
565
cx,
517
566
MODULE_NAME_REPETITIONS ,
518
567
ident. span ,
519
568
"item name starts with its containing module's name" ,
520
- ) ,
521
- _ => ( ) ,
569
+ ) ;
522
570
}
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 )
526
574
{
527
575
span_lint (
528
576
cx,
@@ -534,17 +582,13 @@ impl LateLintPass<'_> for ItemNameRepetitions {
534
582
}
535
583
}
536
584
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
+ } ) ;
547
592
}
548
- self . modules . push ( ( ident. name , item_camel, item. owner_id ) ) ;
549
593
}
550
594
}
0 commit comments