11use rustc_abi:: ExternAbi ;
22use rustc_attr_data_structures:: { AttributeKind , ReprAttr , find_attr} ;
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
@@ -493,22 +496,82 @@ declare_lint! {
493496declare_lint_pass ! ( NonUpperCaseGlobals => [ NON_UPPER_CASE_GLOBALS ] ) ;
494497
495498impl 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 ) {
497500 let name = ident. name . as_str ( ) ;
498501 if name. chars ( ) . any ( |c| c. is_lowercase ( ) ) {
499502 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+
500512 // We cannot provide meaningful suggestions
501513 // if the characters are in the category of "Lowercase Letter".
502514 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+ }
504524 } else {
505525 NonUpperCaseGlobalSub :: Label { span : ident. span }
506526 } ;
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+ } ) ;
512575 }
513576 }
514577}
@@ -520,26 +583,36 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
520583 hir:: ItemKind :: Static ( _, ident, ..)
521584 if !find_attr ! ( attrs, AttributeKind :: NoMangle ( ..) ) =>
522585 {
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+ ) ;
524592 }
525593 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+ ) ;
527600 }
528601 _ => { }
529602 }
530603 }
531604
532605 fn check_trait_item ( & mut self , cx : & LateContext < ' _ > , ti : & hir:: TraitItem < ' _ > ) {
533606 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 ) ;
535608 }
536609 }
537610
538611 fn check_impl_item ( & mut self , cx : & LateContext < ' _ > , ii : & hir:: ImplItem < ' _ > ) {
539612 if let hir:: ImplItemKind :: Const ( ..) = ii. kind
540613 && !assoc_item_in_trait_impl ( cx, ii)
541614 {
542- NonUpperCaseGlobals :: check_upper_case ( cx, "associated constant" , & ii. ident ) ;
615+ NonUpperCaseGlobals :: check_upper_case ( cx, "associated constant" , None , & ii. ident ) ;
543616 }
544617 }
545618
@@ -555,6 +628,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
555628 NonUpperCaseGlobals :: check_upper_case (
556629 cx,
557630 "constant in pattern" ,
631+ None ,
558632 & segment. ident ,
559633 ) ;
560634 }
@@ -564,7 +638,12 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
564638
565639 fn check_generic_param ( & mut self , cx : & LateContext < ' _ > , param : & hir:: GenericParam < ' _ > ) {
566640 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+ ) ;
568647 }
569648 }
570649}
0 commit comments