11use rustc_abi:: ExternAbi ;
22use rustc_attr_data_structures:: { AttributeKind , ReprAttr } ;
33use rustc_attr_parsing:: AttributeParser ;
4+ use rustc_errors:: Applicability ;
45use 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 } ;
68use rustc_hir:: { AttrArgs , AttrItem , Attribute , GenericParamKind , PatExprKind , PatKind } ;
9+ use rustc_middle:: hir:: nested_filter:: All ;
710use rustc_middle:: ty;
811use rustc_session:: config:: CrateType ;
912use rustc_session:: { declare_lint, declare_lint_pass} ;
@@ -13,7 +16,7 @@ use {rustc_ast as ast, rustc_hir as hir};
1316
1417use crate :: lints:: {
1518 NonCamelCaseType , NonCamelCaseTypeSub , NonSnakeCaseDiag , NonSnakeCaseDiagSub ,
16- NonUpperCaseGlobal , NonUpperCaseGlobalSub ,
19+ NonUpperCaseGlobal , NonUpperCaseGlobalSub , NonUpperCaseGlobalSubTool ,
1720} ;
1821use crate :: { EarlyContext , EarlyLintPass , LateContext , LateLintPass , LintContext } ;
1922
@@ -489,22 +492,82 @@ declare_lint! {
489492declare_lint_pass ! ( NonUpperCaseGlobals => [ NON_UPPER_CASE_GLOBALS ] ) ;
490493
491494impl 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 ) {
493496 let name = ident. name . as_str ( ) ;
494497 if name. chars ( ) . any ( |c| c. is_lowercase ( ) ) {
495498 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+
496508 // We cannot provide meaningful suggestions
497509 // if the characters are in the category of "Lowercase Letter".
498510 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+ }
500520 } else {
501521 NonUpperCaseGlobalSub :: Label { span : ident. span }
502522 } ;
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+ } ) ;
508571 }
509572 }
510573}
@@ -516,26 +579,36 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
516579 hir:: ItemKind :: Static ( _, ident, ..)
517580 if !ast:: attr:: contains_name ( attrs, sym:: no_mangle) =>
518581 {
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+ ) ;
520588 }
521589 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+ ) ;
523596 }
524597 _ => { }
525598 }
526599 }
527600
528601 fn check_trait_item ( & mut self , cx : & LateContext < ' _ > , ti : & hir:: TraitItem < ' _ > ) {
529602 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 ) ;
531604 }
532605 }
533606
534607 fn check_impl_item ( & mut self , cx : & LateContext < ' _ > , ii : & hir:: ImplItem < ' _ > ) {
535608 if let hir:: ImplItemKind :: Const ( ..) = ii. kind
536609 && !assoc_item_in_trait_impl ( cx, ii)
537610 {
538- NonUpperCaseGlobals :: check_upper_case ( cx, "associated constant" , & ii. ident ) ;
611+ NonUpperCaseGlobals :: check_upper_case ( cx, "associated constant" , None , & ii. ident ) ;
539612 }
540613 }
541614
@@ -551,6 +624,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
551624 NonUpperCaseGlobals :: check_upper_case (
552625 cx,
553626 "constant in pattern" ,
627+ None ,
554628 & segment. ident ,
555629 ) ;
556630 }
@@ -560,7 +634,12 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
560634
561635 fn check_generic_param ( & mut self , cx : & LateContext < ' _ > , param : & hir:: GenericParam < ' _ > ) {
562636 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+ ) ;
564643 }
565644 }
566645}
0 commit comments