Skip to content

Commit a29a4d4

Browse files
committed
Enable empty_loop lint for no_std crates
We skip the lint if the `loop {}` is in the `#[panic_handler]` as the main recommendation we give is to panic, which obviously isn't possible in a panic handler. Signed-off-by: Joe Richey <[email protected]>
1 parent 3807634 commit a29a4d4

File tree

4 files changed

+52
-15
lines changed

4 files changed

+52
-15
lines changed

clippy_lints/src/loops.rs

+13-14
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ use crate::utils::sugg::Sugg;
44
use crate::utils::usage::{is_unused, mutated_variables};
55
use crate::utils::{
66
contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
7-
is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method,
8-
match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_with_applicability, snippet_with_macro_callsite,
9-
span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq,
7+
is_in_panic_handler, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment,
8+
match_trait_method, match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_with_applicability,
9+
snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
10+
SpanlessEq,
1011
};
1112
use if_chain::if_chain;
1213
use rustc_ast::ast;
@@ -516,17 +517,15 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
516517
// (also matches an explicit "match" instead of "if let")
517518
// (even if the "match" or "if let" is used for declaration)
518519
if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind {
519-
// also check for empty `loop {}` statements
520-
// TODO(issue #6161): Enable for no_std crates (outside of #[panic_handler])
521-
if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) {
522-
span_lint_and_help(
523-
cx,
524-
EMPTY_LOOP,
525-
expr.span,
526-
"empty `loop {}` wastes CPU cycles",
527-
None,
528-
"You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.",
529-
);
520+
// also check for empty `loop {}` statements, skipping those in #[panic_handler]
521+
if block.stmts.is_empty() && block.expr.is_none() && !is_in_panic_handler(cx, expr) {
522+
let msg = "empty `loop {}` wastes CPU cycles";
523+
let help = if is_no_std_crate(cx.tcx.hir().krate()) {
524+
"You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body."
525+
} else {
526+
"You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body."
527+
};
528+
span_lint_and_help(cx, EMPTY_LOOP, expr.span, msg, None, help);
530529
}
531530

532531
// extract the expression from the first statement (if any) in a block

clippy_lints/src/utils/mod.rs

+14
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,20 @@ pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
439439
.map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id.to_def_id())
440440
}
441441

442+
/// Returns `true` if the expression is in the program's `#[panic_handler]`.
443+
pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
444+
let parent = cx.tcx.hir().get_parent_item(e.hir_id);
445+
if let Some(Node::Item(Item {
446+
kind: ItemKind::Fn(..), ..
447+
})) = cx.tcx.hir().find(parent)
448+
{
449+
let def_id = cx.tcx.hir().local_def_id(parent).to_def_id();
450+
Some(def_id) == cx.tcx.lang_items().panic_impl()
451+
} else {
452+
false
453+
}
454+
}
455+
442456
/// Gets the name of the item the expression is in, if available.
443457
pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
444458
let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);

tests/ui/empty_loop_no_std.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,18 @@ use core::panic::PanicInfo;
1010

1111
#[start]
1212
fn main(argc: isize, argv: *const *const u8) -> isize {
13+
// This should trigger the lint
1314
loop {}
1415
}
1516

1617
#[panic_handler]
1718
fn panic(_info: &PanicInfo) -> ! {
19+
// This should NOT trigger the lint
1820
loop {}
1921
}
2022

2123
#[lang = "eh_personality"]
22-
extern "C" fn eh_personality() {}
24+
extern "C" fn eh_personality() {
25+
// This should also trigger the lint
26+
loop {}
27+
}

tests/ui/empty_loop_no_std.stderr

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error: empty `loop {}` wastes CPU cycles
2+
--> $DIR/empty_loop_no_std.rs:14:5
3+
|
4+
LL | loop {}
5+
| ^^^^^^^
6+
|
7+
= note: `-D clippy::empty-loop` implied by `-D warnings`
8+
= help: You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body.
9+
10+
error: empty `loop {}` wastes CPU cycles
11+
--> $DIR/empty_loop_no_std.rs:26:5
12+
|
13+
LL | loop {}
14+
| ^^^^^^^
15+
|
16+
= help: You should either use `panic!()` or add a call pausing or sleeping the thread to the loop body.
17+
18+
error: aborting due to 2 previous errors
19+

0 commit comments

Comments
 (0)