@@ -159,6 +159,51 @@ declare_clippy_lint! {
159159 "referencing `const` with interior mutability"
160160}
161161
162+ declare_clippy_lint ! {
163+ /// ### What it does
164+ ///
165+ /// Checks whether a global variable defined.
166+ ///
167+ /// ### Why restrict this?
168+ ///
169+ /// - Global variables can be modified from any part of the program, making it difficult to
170+ /// track and control their state.
171+ /// - Global variables introduce implicit dependencies that are not visible in function
172+ /// signatures, making the code harder to understand and maintain.
173+ /// - Global variables introduce persistent state, complicating unit tests and making them
174+ /// prone to side effects.
175+ /// - Global variables create tight coupling between different parts of the program, making it
176+ /// harder to modify one part without affecting others.
177+ ///
178+ /// ### Example
179+ ///
180+ /// ```no_run
181+ /// use std::sync::Mutex;
182+ ///
183+ /// struct State {}
184+ ///
185+ /// static STATE: Mutex<State> = Mutex::new(State {});
186+ ///
187+ /// fn foo() {
188+ /// // Access global variable `STATE`.
189+ /// }
190+ /// ```
191+ ///
192+ /// Use instead:
193+ ///
194+ /// ```no_run
195+ /// struct State {}
196+ ///
197+ /// fn foo(state: &mut State) {
198+ /// // Access `state` argument instead of a global variable.
199+ /// }
200+ /// ```
201+ #[ clippy:: version = "1.88.0" ]
202+ pub GLOBAL_VARIABLES ,
203+ nursery,
204+ "declaring global variables"
205+ }
206+
162207#[ derive( Clone , Copy ) ]
163208enum IsFreeze {
164209 /// The type and all possible values are `Freeze`
@@ -256,7 +301,7 @@ pub struct NonCopyConst<'tcx> {
256301 freeze_tys : FxHashMap < Ty < ' tcx > , IsFreeze > ,
257302}
258303
259- impl_lint_pass ! ( NonCopyConst <' _> => [ DECLARE_INTERIOR_MUTABLE_CONST , BORROW_INTERIOR_MUTABLE_CONST ] ) ;
304+ impl_lint_pass ! ( NonCopyConst <' _> => [ DECLARE_INTERIOR_MUTABLE_CONST , BORROW_INTERIOR_MUTABLE_CONST , GLOBAL_VARIABLES ] ) ;
260305
261306impl < ' tcx > NonCopyConst < ' tcx > {
262307 pub fn new ( tcx : TyCtxt < ' tcx > , conf : & ' static Conf ) -> Self {
@@ -695,43 +740,82 @@ impl<'tcx> NonCopyConst<'tcx> {
695740
696741impl < ' tcx > LateLintPass < ' tcx > for NonCopyConst < ' tcx > {
697742 fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > ) {
698- if let ItemKind :: Const ( ident, .., body_id) = item. kind
699- && !ident. is_special ( )
700- && let ty = cx. tcx . type_of ( item. owner_id ) . instantiate_identity ( )
701- && match self . is_ty_freeze ( cx. tcx , cx. typing_env ( ) , ty) {
702- IsFreeze :: No => true ,
703- IsFreeze :: Yes => false ,
704- IsFreeze :: Maybe => match cx. tcx . const_eval_poly ( item. owner_id . to_def_id ( ) ) {
705- Ok ( val) if let Ok ( is_freeze) = self . is_value_freeze ( cx. tcx , cx. typing_env ( ) , ty, val) => !is_freeze,
706- _ => !self . is_init_expr_freeze (
707- cx. tcx ,
708- cx. typing_env ( ) ,
709- cx. tcx . typeck ( item. owner_id ) ,
710- GenericArgs :: identity_for_item ( cx. tcx , item. owner_id ) ,
711- cx. tcx . hir_body ( body_id) . value ,
712- ) ,
713- } ,
714- }
715- && !item. span . in_external_macro ( cx. sess ( ) . source_map ( ) )
743+ let ty = || cx. tcx . type_of ( item. owner_id ) . instantiate_identity ( ) ;
744+
745+ let is_init_expr_freeze = |this : & mut Self , body_id| {
746+ this. is_init_expr_freeze (
747+ cx. tcx ,
748+ cx. typing_env ( ) ,
749+ cx. tcx . typeck ( item. owner_id ) ,
750+ GenericArgs :: identity_for_item ( cx. tcx , item. owner_id ) ,
751+ cx. tcx . hir_body ( body_id) . value ,
752+ )
753+ } ;
754+
755+ let is_thread_local_or_external_macro = || {
756+ item. span . in_external_macro ( cx. sess ( ) . source_map ( ) )
716757 // Only needed when compiling `std`
717- && !is_thread_local ( cx, item)
718- {
719- span_lint_and_then (
720- cx,
721- DECLARE_INTERIOR_MUTABLE_CONST ,
722- ident. span ,
723- "named constant with interior mutability" ,
724- |diag| {
725- let Some ( sync_trait) = cx. tcx . lang_items ( ) . sync_trait ( ) else {
726- return ;
727- } ;
728- if implements_trait ( cx, ty, sync_trait, & [ ] ) {
729- diag. help ( "did you mean to make this a `static` item" ) ;
730- } else {
731- diag. help ( "did you mean to make this a `thread_local!` item" ) ;
758+ || is_thread_local ( cx, item)
759+ } ;
760+
761+ match item. kind {
762+ ItemKind :: Const ( ident, .., body_id) => {
763+ if !clippy_utils:: is_lint_allowed ( cx, DECLARE_INTERIOR_MUTABLE_CONST , item. hir_id ( ) )
764+ && !ident. is_special ( )
765+ && let ty = ty ( )
766+ && match self . is_ty_freeze ( cx. tcx , cx. typing_env ( ) , ty) {
767+ IsFreeze :: No => true ,
768+ IsFreeze :: Yes => false ,
769+ IsFreeze :: Maybe => match cx. tcx . const_eval_poly ( item. owner_id . to_def_id ( ) ) {
770+ Ok ( val) if let Ok ( is_freeze) = self . is_value_freeze ( cx. tcx , cx. typing_env ( ) , ty, val) => {
771+ !is_freeze
772+ } ,
773+ _ => !is_init_expr_freeze ( self , body_id) ,
774+ } ,
732775 }
733- } ,
734- ) ;
776+ && !is_thread_local_or_external_macro ( )
777+ {
778+ span_lint_and_then (
779+ cx,
780+ DECLARE_INTERIOR_MUTABLE_CONST ,
781+ ident. span ,
782+ "named constant with interior mutability" ,
783+ |diag| {
784+ let Some ( sync_trait) = cx. tcx . lang_items ( ) . sync_trait ( ) else {
785+ return ;
786+ } ;
787+
788+ diag. help ( if implements_trait ( cx, ty, sync_trait, & [ ] ) {
789+ "did you mean to make this a `static` item"
790+ } else {
791+ "did you mean to make this a `thread_local!` item"
792+ } ) ;
793+ } ,
794+ ) ;
795+ }
796+ } ,
797+ ItemKind :: Static ( _, ident, _, body_id) => {
798+ if !clippy_utils:: is_lint_allowed ( cx, GLOBAL_VARIABLES , item. hir_id ( ) )
799+ && !ident. is_special ( )
800+ && match self . is_ty_freeze ( cx. tcx , cx. typing_env ( ) , ty ( ) ) {
801+ IsFreeze :: No => true ,
802+ IsFreeze :: Yes => false ,
803+ IsFreeze :: Maybe => !is_init_expr_freeze ( self , body_id) ,
804+ }
805+ && !is_thread_local_or_external_macro ( )
806+ {
807+ span_lint_and_then (
808+ cx,
809+ GLOBAL_VARIABLES ,
810+ ident. span ,
811+ "global variable should not be used" ,
812+ |diag| {
813+ diag. help ( "consider passing this as a function argument or using a `thread_local`" ) ;
814+ } ,
815+ ) ;
816+ }
817+ } ,
818+ _ => { } ,
735819 }
736820 }
737821
0 commit comments