Skip to content

Commit e1e98ac

Browse files
committed
Add global_variables lint
1 parent 781fdab commit e1e98ac

File tree

6 files changed

+131
-0
lines changed

6 files changed

+131
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5729,6 +5729,7 @@ Released 2018-09-13
57295729
[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first
57305730
[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
57315731
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
5732+
[`global_variables`]: https://rust-lang.github.io/rust-clippy/master/index.html#global_variables
57325733
[`host_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#host_endian_bytes
57335734
[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
57345735
[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
192192
crate::functions::TOO_MANY_ARGUMENTS_INFO,
193193
crate::functions::TOO_MANY_LINES_INFO,
194194
crate::future_not_send::FUTURE_NOT_SEND_INFO,
195+
crate::global_variables::GLOBAL_VARIABLES_INFO,
195196
crate::if_let_mutex::IF_LET_MUTEX_INFO,
196197
crate::if_not_else::IF_NOT_ELSE_INFO,
197198
crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO,

clippy_lints/src/global_variables.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use clippy_utils::diagnostics;
2+
use rustc_hir::{Item, ItemKind};
3+
use rustc_lint::{LateContext, LateLintPass};
4+
use rustc_session::declare_lint_pass;
5+
6+
declare_clippy_lint! {
7+
/// ### What it does
8+
///
9+
/// Checks whether a global variable defined.
10+
///
11+
/// ### Why restrict this?
12+
///
13+
/// - Global variables can be modified from any part of the program, making it difficult to
14+
/// track and control their state.
15+
/// - Global variables introduce implicit dependencies that are not visible in function
16+
/// signatures, making the code harder to understand and maintain.
17+
/// - Global variables introduce persistent state, complicating unit tests and making them
18+
/// prone to side effects.
19+
/// - Global variables create tight coupling between different parts of the program, making it
20+
/// harder to modify one part without affecting others.
21+
///
22+
/// ### Example
23+
///
24+
/// ```no_run
25+
/// static STATE: State = Mutex::new(State::new());
26+
///
27+
/// fn foo() {
28+
/// // Access global variable `STATE`.
29+
/// }
30+
/// ```
31+
///
32+
/// Use instead:
33+
///
34+
/// ```no_run
35+
/// fn foo(state: &mut State) {
36+
/// // Access `state` argument instead of a global variable.
37+
/// }
38+
/// ```
39+
#[clippy::version = "1.88.0"]
40+
pub GLOBAL_VARIABLES,
41+
nursery,
42+
"global variables are discouraged"
43+
}
44+
45+
declare_lint_pass!(GlobalVariables => [GLOBAL_VARIABLES]);
46+
47+
impl<'tcx> LateLintPass<'tcx> for GlobalVariables {
48+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
49+
if matches!(item.kind, ItemKind::Static(..)) {
50+
let tcx = cx.tcx;
51+
52+
if !tcx
53+
.type_of(item.owner_id.def_id)
54+
.skip_binder()
55+
.is_freeze(tcx, cx.typing_env())
56+
{
57+
diagnostics::span_lint(cx, GLOBAL_VARIABLES, item.span, "found global variable");
58+
}
59+
}
60+
}
61+
}

clippy_lints/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ mod from_raw_with_void_ptr;
155155
mod from_str_radix_10;
156156
mod functions;
157157
mod future_not_send;
158+
mod global_variables;
158159
mod if_let_mutex;
159160
mod if_not_else;
160161
mod if_then_some_else_none;
@@ -943,5 +944,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
943944
store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf)));
944945
store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
945946
store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix));
947+
store.register_late_pass(|_| Box::new(global_variables::GlobalVariables));
946948
// add lints here, do not remove this comment, it's used in `new_lint`
947949
}

tests/ui/global_variables.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#![warn(clippy::global_variables)]
2+
3+
use std::sync::Mutex;
4+
use std::sync::atomic::AtomicU32;
5+
6+
macro_rules! define_global_variable_with_macro {
7+
() => {
8+
static GLOBAL_VARIABLE_0: AtomicU32 = AtomicU32::new(2);
9+
//~^ global_variables
10+
11+
static GLOBAL_VARIABLE_1: Mutex<u32> = Mutex::new(3);
12+
//~^ global_variables
13+
};
14+
}
15+
16+
fn main() {
17+
define_global_variable_with_macro!();
18+
19+
static GLOBAL_VARIABLE_2: AtomicU32 = AtomicU32::new(2);
20+
//~^ global_variables
21+
22+
static GLOBAL_VARIABLE_3: Mutex<u32> = Mutex::new(3);
23+
//~^ global_variables
24+
25+
static NOT_GLOBAL_VARIABLE_0: u32 = 1;
26+
static NOT_GLOBAL_VARIABLE_1: Vec<AtomicU32> = Vec::new();
27+
static NOT_GLOBAL_VARIABLE_2: Vec<Mutex<u32>> = Vec::new();
28+
}

tests/ui/global_variables.stderr

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
error: found global variable
2+
--> tests/ui/global_variables.rs:8:9
3+
|
4+
LL | static GLOBAL_VARIABLE_0: AtomicU32 = AtomicU32::new(2);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
...
7+
LL | define_global_variable_with_macro!();
8+
| ------------------------------------ in this macro invocation
9+
|
10+
= note: `-D clippy::global-variables` implied by `-D warnings`
11+
= help: to override `-D warnings` add `#[allow(clippy::global_variables)]`
12+
= note: this error originates in the macro `define_global_variable_with_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
13+
14+
error: found global variable
15+
--> tests/ui/global_variables.rs:11:9
16+
|
17+
LL | static GLOBAL_VARIABLE_1: Mutex<u32> = Mutex::new(3);
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
19+
...
20+
LL | define_global_variable_with_macro!();
21+
| ------------------------------------ in this macro invocation
22+
|
23+
= note: this error originates in the macro `define_global_variable_with_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
24+
25+
error: found global variable
26+
--> tests/ui/global_variables.rs:19:5
27+
|
28+
LL | static GLOBAL_VARIABLE_2: AtomicU32 = AtomicU32::new(2);
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30+
31+
error: found global variable
32+
--> tests/ui/global_variables.rs:22:5
33+
|
34+
LL | static GLOBAL_VARIABLE_3: Mutex<u32> = Mutex::new(3);
35+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
36+
37+
error: aborting due to 4 previous errors
38+

0 commit comments

Comments
 (0)