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