1
1
use rustc_abi:: ExternAbi ;
2
2
use rustc_attr_data_structures:: { AttributeKind , ReprAttr } ;
3
3
use rustc_attr_parsing:: AttributeParser ;
4
+ use rustc_errors:: Applicability ;
4
5
use rustc_hir:: def:: { DefKind , Res } ;
5
- use rustc_hir:: intravisit:: FnKind ;
6
+ use rustc_hir:: def_id:: DefId ;
7
+ use rustc_hir:: intravisit:: { FnKind , Visitor } ;
6
8
use rustc_hir:: { AttrArgs , AttrItem , Attribute , GenericParamKind , PatExprKind , PatKind } ;
9
+ use rustc_middle:: hir:: nested_filter:: All ;
7
10
use rustc_middle:: ty;
8
11
use rustc_session:: config:: CrateType ;
9
12
use rustc_session:: { declare_lint, declare_lint_pass} ;
@@ -13,7 +16,7 @@ use {rustc_ast as ast, rustc_hir as hir};
13
16
14
17
use crate :: lints:: {
15
18
NonCamelCaseType , NonCamelCaseTypeSub , NonSnakeCaseDiag , NonSnakeCaseDiagSub ,
16
- NonUpperCaseGlobal , NonUpperCaseGlobalSub ,
19
+ NonUpperCaseGlobal , NonUpperCaseGlobalSub , NonUpperCaseGlobalSubTool ,
17
20
} ;
18
21
use crate :: { EarlyContext , EarlyLintPass , LateContext , LateLintPass , LintContext } ;
19
22
@@ -489,22 +492,82 @@ declare_lint! {
489
492
declare_lint_pass ! ( NonUpperCaseGlobals => [ NON_UPPER_CASE_GLOBALS ] ) ;
490
493
491
494
impl NonUpperCaseGlobals {
492
- fn check_upper_case ( cx : & LateContext < ' _ > , sort : & str , ident : & Ident ) {
495
+ fn check_upper_case ( cx : & LateContext < ' _ > , sort : & str , did : Option < LocalDefId > , ident : & Ident ) {
493
496
let name = ident. name . as_str ( ) ;
494
497
if name. chars ( ) . any ( |c| c. is_lowercase ( ) ) {
495
498
let uc = NonSnakeCase :: to_snake_case ( name) . to_uppercase ( ) ;
499
+
500
+ // If the item is exported, suggesting changing it's name would be breaking-change
501
+ // and could break users without a "nice" applicable fix, so let's avoid it.
502
+ let can_change_usages = if let Some ( did) = did {
503
+ !cx. tcx . effective_visibilities ( ( ) ) . is_exported ( did)
504
+ } else {
505
+ false
506
+ } ;
507
+
496
508
// We cannot provide meaningful suggestions
497
509
// if the characters are in the category of "Lowercase Letter".
498
510
let sub = if * name != uc {
499
- NonUpperCaseGlobalSub :: Suggestion { span : ident. span , replace : uc }
511
+ NonUpperCaseGlobalSub :: Suggestion {
512
+ span : ident. span ,
513
+ replace : uc. clone ( ) ,
514
+ applicability : if can_change_usages {
515
+ Applicability :: MachineApplicable
516
+ } else {
517
+ Applicability :: MaybeIncorrect
518
+ } ,
519
+ }
500
520
} else {
501
521
NonUpperCaseGlobalSub :: Label { span : ident. span }
502
522
} ;
503
- cx. emit_span_lint (
504
- NON_UPPER_CASE_GLOBALS ,
505
- ident. span ,
506
- NonUpperCaseGlobal { sort, name, sub } ,
507
- ) ;
523
+
524
+ struct UsageCollector < ' a , ' tcx > {
525
+ cx : & ' tcx LateContext < ' a > ,
526
+ did : DefId ,
527
+ collected : Vec < Span > ,
528
+ }
529
+
530
+ impl < ' v , ' tcx > Visitor < ' v > for UsageCollector < ' v , ' tcx > {
531
+ type NestedFilter = All ;
532
+
533
+ fn maybe_tcx ( & mut self ) -> Self :: MaybeTyCtxt {
534
+ self . cx . tcx
535
+ }
536
+
537
+ fn visit_path (
538
+ & mut self ,
539
+ path : & rustc_hir:: Path < ' v > ,
540
+ _id : rustc_hir:: HirId ,
541
+ ) -> Self :: Result {
542
+ if let Some ( final_seg) = path. segments . last ( )
543
+ && final_seg. res . opt_def_id ( ) == Some ( self . did )
544
+ {
545
+ self . collected . push ( final_seg. ident . span ) ;
546
+ }
547
+ }
548
+ }
549
+
550
+ cx. emit_span_lint_lazy ( NON_UPPER_CASE_GLOBALS , ident. span , || {
551
+ // Compute usages lazily as it can expansive and useless when the lint is allowed.
552
+ // cf. https://github.com/rust-lang/rust/pull/142645#issuecomment-2993024625
553
+ let usages = if can_change_usages
554
+ && * name != uc
555
+ && let Some ( did) = did
556
+ {
557
+ let mut usage_collector =
558
+ UsageCollector { cx, did : did. to_def_id ( ) , collected : Vec :: new ( ) } ;
559
+ cx. tcx . hir_walk_toplevel_module ( & mut usage_collector) ;
560
+ usage_collector
561
+ . collected
562
+ . into_iter ( )
563
+ . map ( |span| NonUpperCaseGlobalSubTool { span, replace : uc. clone ( ) } )
564
+ . collect ( )
565
+ } else {
566
+ vec ! [ ]
567
+ } ;
568
+
569
+ NonUpperCaseGlobal { sort, name, sub, usages }
570
+ } ) ;
508
571
}
509
572
}
510
573
}
@@ -516,26 +579,36 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
516
579
hir:: ItemKind :: Static ( _, ident, ..)
517
580
if !ast:: attr:: contains_name ( attrs, sym:: no_mangle) =>
518
581
{
519
- NonUpperCaseGlobals :: check_upper_case ( cx, "static variable" , & ident) ;
582
+ NonUpperCaseGlobals :: check_upper_case (
583
+ cx,
584
+ "static variable" ,
585
+ Some ( it. owner_id . def_id ) ,
586
+ & ident,
587
+ ) ;
520
588
}
521
589
hir:: ItemKind :: Const ( ident, ..) => {
522
- NonUpperCaseGlobals :: check_upper_case ( cx, "constant" , & ident) ;
590
+ NonUpperCaseGlobals :: check_upper_case (
591
+ cx,
592
+ "constant" ,
593
+ Some ( it. owner_id . def_id ) ,
594
+ & ident,
595
+ ) ;
523
596
}
524
597
_ => { }
525
598
}
526
599
}
527
600
528
601
fn check_trait_item ( & mut self , cx : & LateContext < ' _ > , ti : & hir:: TraitItem < ' _ > ) {
529
602
if let hir:: TraitItemKind :: Const ( ..) = ti. kind {
530
- NonUpperCaseGlobals :: check_upper_case ( cx, "associated constant" , & ti. ident ) ;
603
+ NonUpperCaseGlobals :: check_upper_case ( cx, "associated constant" , None , & ti. ident ) ;
531
604
}
532
605
}
533
606
534
607
fn check_impl_item ( & mut self , cx : & LateContext < ' _ > , ii : & hir:: ImplItem < ' _ > ) {
535
608
if let hir:: ImplItemKind :: Const ( ..) = ii. kind
536
609
&& !assoc_item_in_trait_impl ( cx, ii)
537
610
{
538
- NonUpperCaseGlobals :: check_upper_case ( cx, "associated constant" , & ii. ident ) ;
611
+ NonUpperCaseGlobals :: check_upper_case ( cx, "associated constant" , None , & ii. ident ) ;
539
612
}
540
613
}
541
614
@@ -551,6 +624,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
551
624
NonUpperCaseGlobals :: check_upper_case (
552
625
cx,
553
626
"constant in pattern" ,
627
+ None ,
554
628
& segment. ident ,
555
629
) ;
556
630
}
@@ -560,7 +634,12 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
560
634
561
635
fn check_generic_param ( & mut self , cx : & LateContext < ' _ > , param : & hir:: GenericParam < ' _ > ) {
562
636
if let GenericParamKind :: Const { .. } = param. kind {
563
- NonUpperCaseGlobals :: check_upper_case ( cx, "const parameter" , & param. name . ident ( ) ) ;
637
+ NonUpperCaseGlobals :: check_upper_case (
638
+ cx,
639
+ "const parameter" ,
640
+ Some ( param. def_id ) ,
641
+ & param. name . ident ( ) ,
642
+ ) ;
564
643
}
565
644
}
566
645
}
0 commit comments