Skip to content

Commit e2e40f2

Browse files
Detect usage of invalid atomic ordering in memory fences
Detect usage of `core::sync::atomic::{fence, compiler_fence}` with `Ordering::Relaxed` and suggest valid alternatives.
1 parent ac795a6 commit e2e40f2

File tree

2 files changed

+65
-34
lines changed

2 files changed

+65
-34
lines changed

clippy_lints/src/atomic_ordering.rs

+64-33
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_session::declare_tool_lint;
99

1010
declare_clippy_lint! {
1111
/// **What it does:** Checks for usage of invalid atomic
12-
/// ordering in Atomic*::{load, store} calls.
12+
/// ordering in atomic loads/stores and memory fences.
1313
///
1414
/// **Why is this bad?** Using an invalid atomic ordering
1515
/// will cause a panic at run-time.
@@ -18,7 +18,7 @@ declare_clippy_lint! {
1818
///
1919
/// **Example:**
2020
/// ```rust,no_run
21-
/// # use std::sync::atomic::{AtomicBool, Ordering};
21+
/// # use std::sync::atomic::{self, AtomicBool, Ordering};
2222
///
2323
/// let x = AtomicBool::new(true);
2424
///
@@ -27,10 +27,13 @@ declare_clippy_lint! {
2727
///
2828
/// x.store(false, Ordering::Acquire);
2929
/// x.store(false, Ordering::AcqRel);
30+
///
31+
/// atomic::fence(Ordering::Relaxed);
32+
/// atomic::compiler_fence(Ordering::Relaxed);
3033
/// ```
3134
pub INVALID_ATOMIC_ORDERING,
3235
correctness,
33-
"usage of invalid atomic ordering in atomic load/store calls"
36+
"usage of invalid atomic ordering in atomic loads/stores and memory fences"
3437
}
3538

3639
declare_lint_pass!(AtomicOrdering => [INVALID_ATOMIC_ORDERING]);
@@ -66,37 +69,65 @@ fn match_ordering_def_path(cx: &LateContext<'_, '_>, did: DefId, orderings: &[&s
6669
.any(|ordering| match_def_path(cx, did, &["core", "sync", "atomic", "Ordering", ordering]))
6770
}
6871

69-
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AtomicOrdering {
70-
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
71-
if_chain! {
72-
if let ExprKind::MethodCall(ref method_path, _, args) = &expr.kind;
73-
let method = method_path.ident.name.as_str();
74-
if type_is_atomic(cx, &args[0]);
75-
if method == "load" || method == "store";
76-
let ordering_arg = if method == "load" { &args[1] } else { &args[2] };
77-
if let ExprKind::Path(ref ordering_qpath) = ordering_arg.kind;
78-
if let Some(ordering_def_id) = cx.tables.qpath_res(ordering_qpath, ordering_arg.hir_id).opt_def_id();
79-
then {
80-
if method == "load" &&
81-
match_ordering_def_path(cx, ordering_def_id, &["Release", "AcqRel"]) {
82-
span_help_and_lint(
83-
cx,
84-
INVALID_ATOMIC_ORDERING,
85-
ordering_arg.span,
86-
"atomic loads cannot have `Release` and `AcqRel` ordering",
87-
"consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`"
88-
);
89-
} else if method == "store" &&
90-
match_ordering_def_path(cx, ordering_def_id, &["Acquire", "AcqRel"]) {
91-
span_help_and_lint(
92-
cx,
93-
INVALID_ATOMIC_ORDERING,
94-
ordering_arg.span,
95-
"atomic stores cannot have `Acquire` and `AcqRel` ordering",
96-
"consider using ordering modes `Release`, `SeqCst` or `Relaxed`"
97-
);
98-
}
72+
fn check_atomic_load_store(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
73+
if_chain! {
74+
if let ExprKind::MethodCall(ref method_path, _, args) = &expr.kind;
75+
let method = method_path.ident.name.as_str();
76+
if type_is_atomic(cx, &args[0]);
77+
if method == "load" || method == "store";
78+
let ordering_arg = if method == "load" { &args[1] } else { &args[2] };
79+
if let ExprKind::Path(ref ordering_qpath) = ordering_arg.kind;
80+
if let Some(ordering_def_id) = cx.tables.qpath_res(ordering_qpath, ordering_arg.hir_id).opt_def_id();
81+
then {
82+
if method == "load" &&
83+
match_ordering_def_path(cx, ordering_def_id, &["Release", "AcqRel"]) {
84+
span_help_and_lint(
85+
cx,
86+
INVALID_ATOMIC_ORDERING,
87+
ordering_arg.span,
88+
"atomic loads cannot have `Release` and `AcqRel` ordering",
89+
"consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`"
90+
);
91+
} else if method == "store" &&
92+
match_ordering_def_path(cx, ordering_def_id, &["Acquire", "AcqRel"]) {
93+
span_help_and_lint(
94+
cx,
95+
INVALID_ATOMIC_ORDERING,
96+
ordering_arg.span,
97+
"atomic stores cannot have `Acquire` and `AcqRel` ordering",
98+
"consider using ordering modes `Release`, `SeqCst` or `Relaxed`"
99+
);
99100
}
100101
}
101102
}
102103
}
104+
105+
fn check_memory_fence(cx: &LateContext<'_, '_>, expr: &Expr<'_>) {
106+
if_chain! {
107+
if let ExprKind::Call(ref func, ref args) = expr.kind;
108+
if let ExprKind::Path(ref func_qpath) = func.kind;
109+
if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id();
110+
if ["fence", "compiler_fence"]
111+
.iter()
112+
.any(|func| match_def_path(cx, def_id, &["core", "sync", "atomic", func]));
113+
if let ExprKind::Path(ref ordering_qpath) = &args[0].kind;
114+
if let Some(ordering_def_id) = cx.tables.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id();
115+
if match_ordering_def_path(cx, ordering_def_id, &["Relaxed"]);
116+
then {
117+
span_help_and_lint(
118+
cx,
119+
INVALID_ATOMIC_ORDERING,
120+
args[0].span,
121+
"memory fences cannot have `Relaxed` ordering",
122+
"consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`"
123+
);
124+
}
125+
}
126+
}
127+
128+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AtomicOrdering {
129+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr<'_>) {
130+
check_atomic_load_store(cx, expr);
131+
check_memory_fence(cx, expr);
132+
}
133+
}

src/lintlist/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,7 @@ pub const ALL_LINTS: [Lint; 346] = [
836836
Lint {
837837
name: "invalid_atomic_ordering",
838838
group: "correctness",
839-
desc: "usage of invalid atomic ordering in atomic load/store calls",
839+
desc: "usage of invalid atomic ordering in atomic loads/stores and memory fences",
840840
deprecation: None,
841841
module: "atomic_ordering",
842842
},

0 commit comments

Comments
 (0)