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,92 +468,127 @@ 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
- if both_are_public && item_camel. len ( ) > mod_camel. len ( ) {
506
- let matching = count_match_start ( mod_camel, & item_camel) ;
507
- let rmatching = count_match_end ( mod_camel, & item_camel) ;
508
- 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
+ } ;
509
538
510
- 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) ;
511
541
512
- if matching. char_count == nchars {
513
- match item_camel. chars ( ) . nth ( nchars) {
514
- 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
+ && cx. tcx . visibility ( item. owner_id ) . is_public ( )
547
+ {
548
+ if !self . allow_exact_repetitions && item_camel == prev. name_camel {
549
+ if !is_from_proc_macro ( cx, item) {
550
+ span_lint (
551
+ cx,
552
+ MODULE_NAME_REPETITIONS ,
553
+ ident. span ,
554
+ "item name is the same as its containing module's name" ,
555
+ ) ;
556
+ }
557
+ } else if item_camel. len ( ) > prev. name_camel . len ( ) {
558
+ if let Some ( s) = item_camel. strip_prefix ( & prev. name_camel )
559
+ && let Some ( c) = s. chars ( ) . next ( )
560
+ && ( c == '_' || c. is_uppercase ( ) || c. is_numeric ( ) )
561
+ {
562
+ if !is_from_proc_macro ( cx, item) {
563
+ span_lint (
515
564
cx,
516
565
MODULE_NAME_REPETITIONS ,
517
566
ident. span ,
518
567
"item name starts with its containing module's name" ,
519
- ) ,
520
- _ => ( ) ,
568
+ ) ;
521
569
}
522
- }
523
- if rmatching. char_count == nchars
524
- && !self . is_allowed_prefix ( & item_camel[ ..item_camel. len ( ) - rmatching. byte_count ] )
570
+ } else if let Some ( s) = item_camel. strip_suffix ( & prev. name_camel )
571
+ && !self . is_allowed_prefix ( s)
525
572
{
526
- span_lint (
527
- cx,
528
- MODULE_NAME_REPETITIONS ,
529
- ident. span ,
530
- "item name ends with its containing module's name" ,
531
- ) ;
573
+ if !is_from_proc_macro ( cx, item) {
574
+ span_lint (
575
+ cx,
576
+ MODULE_NAME_REPETITIONS ,
577
+ ident. span ,
578
+ "item name ends with its containing module's name" ,
579
+ ) ;
580
+ }
532
581
}
533
582
}
534
583
}
535
584
536
- if span_is_local ( item. span ) {
537
- match item. kind {
538
- ItemKind :: Enum ( _, def, _) => {
539
- self . check_variants ( cx, item, & def) ;
540
- } ,
541
- ItemKind :: Struct ( _, VariantData :: Struct { fields, .. } , _) => {
542
- self . check_fields ( cx, item, fields) ;
543
- } ,
544
- _ => ( ) ,
545
- }
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
+ } ) ;
546
592
}
547
- self . modules . push ( ( ident. name , item_camel, item. owner_id ) ) ;
548
593
}
549
594
}
0 commit comments