Skip to content

Commit d1e704e

Browse files
committed
new lint: missing-spin-loop
1 parent 56ccd30 commit d1e704e

12 files changed

+171
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3120,6 +3120,7 @@ Released 2018-09-13
31203120
[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
31213121
[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
31223122
[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
3123+
[`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop
31233124
[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
31243125
[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
31253126
[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files

clippy_lints/src/lib.register_all.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
102102
LintId::of(loops::ITER_NEXT_LOOP),
103103
LintId::of(loops::MANUAL_FLATTEN),
104104
LintId::of(loops::MANUAL_MEMCPY),
105+
LintId::of(loops::MISSING_SPIN_LOOP),
105106
LintId::of(loops::MUT_RANGE_BOUND),
106107
LintId::of(loops::NEEDLESS_COLLECT),
107108
LintId::of(loops::NEEDLESS_RANGE_LOOP),

clippy_lints/src/lib.register_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ store.register_lints(&[
212212
loops::ITER_NEXT_LOOP,
213213
loops::MANUAL_FLATTEN,
214214
loops::MANUAL_MEMCPY,
215+
loops::MISSING_SPIN_LOOP,
215216
loops::MUT_RANGE_BOUND,
216217
loops::NEEDLESS_COLLECT,
217218
loops::NEEDLESS_RANGE_LOOP,

clippy_lints/src/lib.register_perf.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
1010
LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
1111
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
1212
LintId::of(loops::MANUAL_MEMCPY),
13+
LintId::of(loops::MISSING_SPIN_LOOP),
1314
LintId::of(loops::NEEDLESS_COLLECT),
1415
LintId::of(methods::EXPECT_FUN_CALL),
1516
LintId::of(methods::EXTEND_WITH_DRAIN),
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use super::MISSING_SPIN_LOOP;
2+
use clippy_utils::diagnostics::span_lint_and_sugg;
3+
use clippy_utils::is_no_std_crate;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{Block, Expr, ExprKind};
6+
use rustc_lint::LateContext;
7+
//use rustc_middle::ty;
8+
use rustc_span::sym;
9+
10+
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
11+
if_chain! {
12+
if let ExprKind::Block(Block { stmts: [], expr: None, ..}, _) = body.kind;
13+
if let ExprKind::MethodCall(method, _, [_callee, _ordering], _) = cond.kind;
14+
if method.ident.name == sym::load;
15+
//if let ty::Adt(def, _substs) = cx.typeck_results().expr_ty(callee).kind();
16+
//if cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did);
17+
then {
18+
span_lint_and_sugg(
19+
cx,
20+
MISSING_SPIN_LOOP,
21+
body.span,
22+
"busy-waiting loop should at least have a spin loop hint",
23+
"try this",
24+
(if is_no_std_crate(cx) {
25+
"{ core::hint::spin_loop() }"
26+
}else {
27+
"{ std::hint::spin_loop() }"
28+
}).into(),
29+
Applicability::MachineApplicable
30+
);
31+
}
32+
}
33+
}

clippy_lints/src/loops/mod.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod for_loops_over_fallibles;
77
mod iter_next_loop;
88
mod manual_flatten;
99
mod manual_memcpy;
10+
mod missing_spin_loop;
1011
mod mut_range_bound;
1112
mod needless_collect;
1213
mod needless_range_loop;
@@ -560,6 +561,37 @@ declare_clippy_lint! {
560561
"for loops over `Option`s or `Result`s with a single expression can be simplified"
561562
}
562563

564+
declare_clippy_lint! {
565+
/// ### What it does
566+
/// Check for empty spin loops
567+
///
568+
/// ### Why is this bad?
569+
/// The loop body should have something like `thread::park()` or at least
570+
/// `std::hint::spin_loop()` to avoid needlessly burning cycles and conserve
571+
/// energy. Perhaps even better use an actual lock, if possible.
572+
///
573+
/// ### Example
574+
///
575+
/// ```ignore
576+
/// use core::sync::atomic::{AtomicBool, Ordering};
577+
/// let b = AtomicBool::new(true);
578+
/// // give a ref to `b` to another thread,wait for it to become false
579+
/// while b.load(Ordering::Acquire) {};
580+
/// ```
581+
/// Use instead:
582+
/// ```rust,no_run
583+
///# use core::sync::atomic::{AtomicBool, Ordering};
584+
///# let b = AtomicBool::new(true);
585+
/// while b.load(Ordering::Acquire) {
586+
/// std::hint::spin_loop()
587+
/// }
588+
/// ```
589+
#[clippy::version = "1.59.0"]
590+
pub MISSING_SPIN_LOOP,
591+
perf,
592+
"An empty busy waiting loop"
593+
}
594+
563595
declare_lint_pass!(Loops => [
564596
MANUAL_MEMCPY,
565597
MANUAL_FLATTEN,
@@ -579,6 +611,7 @@ declare_lint_pass!(Loops => [
579611
WHILE_IMMUTABLE_CONDITION,
580612
SAME_ITEM_PUSH,
581613
SINGLE_ELEMENT_LOOP,
614+
MISSING_SPIN_LOOP,
582615
]);
583616

584617
impl<'tcx> LateLintPass<'tcx> for Loops {
@@ -628,6 +661,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
628661

629662
if let Some(higher::While { condition, body }) = higher::While::hir(expr) {
630663
while_immutable_condition::check(cx, condition, body);
664+
missing_spin_loop::check(cx, condition, body);
631665
}
632666

633667
needless_collect::check(expr, cx);

tests/ui/missing_spin_loop.fixed

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// run-rustfix
2+
#![warn(clippy::missing_spin_loop)]
3+
4+
use core::sync::atomic::{AtomicBool, Ordering};
5+
6+
fn main() {
7+
let b = AtomicBool::new(true);
8+
// This should lint
9+
while b.load(Ordering::Acquire) { std::hint::spin_loop() }
10+
11+
// This is OK, as the body is not empty
12+
while b.load(Ordering::Acquire) {
13+
std::hint::spin_loop()
14+
}
15+
// TODO: also match on compare_exchange(_weak), but that could be
16+
// loop+match or while let
17+
}

tests/ui/missing_spin_loop.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// run-rustfix
2+
#![warn(clippy::missing_spin_loop)]
3+
4+
use core::sync::atomic::{AtomicBool, Ordering};
5+
6+
fn main() {
7+
let b = AtomicBool::new(true);
8+
// This should lint
9+
while b.load(Ordering::Acquire) {}
10+
11+
// This is OK, as the body is not empty
12+
while b.load(Ordering::Acquire) {
13+
std::hint::spin_loop()
14+
}
15+
// TODO: also match on compare_exchange(_weak), but that could be
16+
// loop+match or while let
17+
}

tests/ui/missing_spin_loop.stderr

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: busy-waiting loop should at least have a spin loop hint
2+
--> $DIR/missing_spin_loop.rs:9:37
3+
|
4+
LL | while b.load(Ordering::Acquire) {}
5+
| ^^ help: try this: `{ std::hint::spin_loop() }`
6+
|
7+
= note: `-D clippy::missing-spin-loop` implied by `-D warnings`
8+
9+
error: aborting due to previous error
10+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// run-rustfix
2+
#![warn(clippy::missing_spin_loop)]
3+
#![feature(lang_items, start, libc)]
4+
#![no_std]
5+
6+
use core::sync::atomic::{AtomicBool, Ordering};
7+
8+
#[start]
9+
fn main(_argc: isize, _argv: *const *const u8) -> isize {
10+
// This should trigger the lint
11+
let b = AtomicBool::new(true);
12+
// This should lint with `core::hint::spin_loop()`
13+
while b.load(Ordering::Acquire) { core::hint::spin_loop() }
14+
0
15+
}
16+
17+
#[panic_handler]
18+
fn panic(_info: &core::panic::PanicInfo) -> ! {
19+
loop {}
20+
}
21+
22+
#[lang = "eh_personality"]
23+
extern "C" fn eh_personality() {}

tests/ui/missing_spin_loop_no_std.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// run-rustfix
2+
#![warn(clippy::missing_spin_loop)]
3+
#![feature(lang_items, start, libc)]
4+
#![no_std]
5+
6+
use core::sync::atomic::{AtomicBool, Ordering};
7+
8+
#[start]
9+
fn main(_argc: isize, _argv: *const *const u8) -> isize {
10+
// This should trigger the lint
11+
let b = AtomicBool::new(true);
12+
// This should lint with `core::hint::spin_loop()`
13+
while b.load(Ordering::Acquire) {}
14+
0
15+
}
16+
17+
#[panic_handler]
18+
fn panic(_info: &core::panic::PanicInfo) -> ! {
19+
loop {}
20+
}
21+
22+
#[lang = "eh_personality"]
23+
extern "C" fn eh_personality() {}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: busy-waiting loop should at least have a spin loop hint
2+
--> $DIR/missing_spin_loop_no_std.rs:13:37
3+
|
4+
LL | while b.load(Ordering::Acquire) {}
5+
| ^^ help: try this: `{ core::hint::spin_loop() }`
6+
|
7+
= note: `-D clippy::missing-spin-loop` implied by `-D warnings`
8+
9+
error: aborting due to previous error
10+

0 commit comments

Comments
 (0)