Skip to content

Commit 0415d96

Browse files
committed
Migrate needless_continue to late pass
1 parent c1f6124 commit 0415d96

File tree

4 files changed

+166
-74
lines changed

4 files changed

+166
-74
lines changed

clippy_lints/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
618618
store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements));
619619
store.register_early_pass(|| Box::new(precedence::Precedence));
620620
store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
621-
store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
621+
store.register_late_pass(|_| Box::new(needless_continue::NeedlessContinue));
622622
store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
623623
store.register_late_pass(|_| Box::new(create_dir::CreateDir));
624624
store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType));

clippy_lints/src/loops/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,10 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
871871

872872
while_let_on_iterator::check(cx, expr);
873873

874-
if let Some(higher::While { condition, body, span }) = higher::While::hir(expr) {
874+
if let Some(higher::While {
875+
condition, body, span, ..
876+
}) = higher::While::hir(expr)
877+
{
875878
while_immutable_condition::check(cx, condition, body);
876879
while_float::check(cx, condition);
877880
missing_spin_loop::check(cx, condition, body);

clippy_lints/src/needless_continue.rs

Lines changed: 152 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use clippy_utils::diagnostics::span_lint_and_help;
2-
use clippy_utils::source::{indent_of, snippet, snippet_block};
3-
use rustc_ast::{Block, Label, ast};
4-
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
2+
use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_context};
3+
use clippy_utils::{higher, is_from_proc_macro};
4+
use rustc_ast::Label;
5+
use rustc_errors::Applicability;
6+
use rustc_hir::{Block, Expr, ExprKind, LoopSource, StmtKind};
7+
use rustc_lint::{LateContext, LateLintPass, LintContext};
58
use rustc_session::declare_lint_pass;
69
use rustc_span::Span;
710

@@ -127,9 +130,9 @@ declare_clippy_lint! {
127130

128131
declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]);
129132

130-
impl EarlyLintPass for NeedlessContinue {
131-
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
132-
if !expr.span.from_expansion() {
133+
impl<'tcx> LateLintPass<'tcx> for NeedlessContinue {
134+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
135+
if !expr.span.in_external_macro(cx.sess().source_map()) && !is_from_proc_macro(cx, expr) {
133136
check_and_warn(cx, expr);
134137
}
135138
}
@@ -188,19 +191,19 @@ impl EarlyLintPass for NeedlessContinue {
188191
///
189192
/// - The expression is a `continue` node.
190193
/// - The expression node is a block with the first statement being a `continue`.
191-
fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&Label>) -> bool {
194+
fn needless_continue_in_else(else_expr: &Expr<'_>, label: Option<&Label>) -> bool {
192195
match else_expr.kind {
193-
ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label),
194-
ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()),
196+
ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label),
197+
ExprKind::Continue(l) => compare_labels(label, l.label.as_ref()),
195198
_ => false,
196199
}
197200
}
198201

199-
fn is_first_block_stmt_continue(block: &Block, label: Option<&Label>) -> bool {
202+
fn is_first_block_stmt_continue(block: &Block<'_>, label: Option<&Label>) -> bool {
200203
block.stmts.first().is_some_and(|stmt| match stmt.kind {
201-
ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
202-
if let ast::ExprKind::Continue(ref l) = e.kind {
203-
compare_labels(label, l.as_ref())
204+
StmtKind::Semi(ref e) | StmtKind::Expr(ref e) => {
205+
if let ExprKind::Continue(ref l) = e.kind {
206+
compare_labels(label, l.label.as_ref())
204207
} else {
205208
false
206209
}
@@ -223,19 +226,33 @@ fn compare_labels(loop_label: Option<&Label>, continue_label: Option<&Label>) ->
223226

224227
/// If `expr` is a loop expression (while/while let/for/loop), calls `func` with
225228
/// the AST object representing the loop block of `expr`.
226-
fn with_loop_block<F>(expr: &ast::Expr, mut func: F)
229+
fn with_loop_block<'tcx, F>(expr: &Expr<'tcx>, mut func: F)
227230
where
228-
F: FnMut(&Block, Option<&Label>),
231+
F: FnMut(&Block<'tcx>, Option<&Label>),
229232
{
230-
if let ast::ExprKind::While(_, loop_block, label)
231-
| ast::ExprKind::ForLoop {
232-
body: loop_block,
233-
label,
234-
..
233+
if let Some(higher::ForLoop { body, label, .. }) = higher::ForLoop::hir(expr)
234+
&& let ExprKind::Block(block, _) = &body.kind
235+
{
236+
func(block, label.as_ref());
237+
return;
235238
}
236-
| ast::ExprKind::Loop(loop_block, label, ..) = &expr.kind
239+
240+
if let Some(higher::While { body, label, .. }) = higher::While::hir(expr)
241+
&& let ExprKind::Block(block, _) = &body.kind
237242
{
238-
func(loop_block, label.as_ref());
243+
func(block, label.as_ref());
244+
return;
245+
}
246+
247+
if let Some(higher::WhileLet { if_then, label, .. }) = higher::WhileLet::hir(expr)
248+
&& let ExprKind::Block(block, _) = &if_then.kind
249+
{
250+
func(block, label.as_ref());
251+
return;
252+
}
253+
254+
if let ExprKind::Loop(block, label, LoopSource::Loop, ..) = expr.kind {
255+
func(block, label.as_ref());
239256
}
240257
}
241258

@@ -247,17 +264,18 @@ where
247264
/// - The `if` condition expression,
248265
/// - The `then` block, and
249266
/// - The `else` expression.
250-
fn with_if_expr<F>(stmt: &ast::Stmt, mut func: F)
267+
fn with_if_expr<'tcx, F>(expr: &Expr<'tcx>, mut func: F)
251268
where
252-
F: FnMut(&ast::Expr, &ast::Expr, &Block, &ast::Expr),
269+
F: FnMut(&Expr<'tcx>, &Expr<'tcx>, &Block<'tcx>, &Expr<'tcx>),
253270
{
254-
match stmt.kind {
255-
ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
256-
if let ast::ExprKind::If(ref cond, ref if_block, Some(ref else_expr)) = e.kind {
257-
func(e, cond, if_block, else_expr);
258-
}
259-
},
260-
_ => {},
271+
if let Some(higher::If {
272+
cond,
273+
then,
274+
r#else: Some(r#else),
275+
}) = higher::If::hir(expr)
276+
&& let ExprKind::Block(then, _) = then.kind
277+
{
278+
func(expr, cond, then, r#else);
261279
}
262280
}
263281

@@ -269,20 +287,21 @@ enum LintType {
269287
}
270288

271289
/// Data we pass around for construction of help messages.
272-
struct LintData<'a> {
290+
#[derive(Debug)]
291+
struct LintData<'tcx> {
273292
/// The `if` expression encountered in the above loop.
274-
if_expr: &'a ast::Expr,
293+
if_expr: &'tcx Expr<'tcx>,
275294
/// The condition expression for the above `if`.
276-
if_cond: &'a ast::Expr,
295+
if_cond: &'tcx Expr<'tcx>,
277296
/// The `then` block of the `if` statement.
278-
if_block: &'a Block,
297+
if_block: &'tcx Block<'tcx>,
279298
/// The `else` block of the `if` statement.
280299
/// Note that we only work with `if` exprs that have an `else` branch.
281-
else_expr: &'a ast::Expr,
300+
else_expr: &'tcx Expr<'tcx>,
282301
/// The 0-based index of the `if` statement in the containing loop block.
283-
stmt_idx: usize,
302+
stmt_idx: Option<usize>,
284303
/// The statements of the loop block.
285-
loop_block: &'a Block,
304+
loop_block: &'tcx Block<'tcx>,
286305
}
287306

288307
const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant";
@@ -299,7 +318,7 @@ const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause";
299318

300319
const DROP_CONTINUE_EXPRESSION_MSG: &str = "consider dropping the `continue` expression";
301320

302-
fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) {
321+
fn emit_warning(cx: &LateContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) {
303322
// snip is the whole *help* message that appears after the warning.
304323
// message is the warning message.
305324
// expr is the expression which the lint warning message refers to.
@@ -325,8 +344,15 @@ fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: L
325344
);
326345
}
327346

328-
fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String {
329-
let cond_code = snippet(cx, data.if_cond.span, "..");
347+
fn suggestion_snippet_for_continue_inside_if(cx: &LateContext<'_>, data: &LintData<'_>) -> String {
348+
let mut applicability = Applicability::MachineApplicable;
349+
let (cond_code, _) = snippet_with_context(
350+
cx,
351+
data.if_cond.span,
352+
data.if_expr.span.ctxt(),
353+
"..",
354+
&mut applicability,
355+
);
330356

331357
let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span));
332358

@@ -339,7 +365,7 @@ fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintD
339365
)
340366
}
341367

342-
fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String {
368+
fn suggestion_snippet_for_continue_inside_else(cx: &LateContext<'_>, data: &LintData<'_>) -> String {
343369
let cond_code = snippet(cx, data.if_cond.span, "..");
344370

345371
// Region B
@@ -352,18 +378,32 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin
352378
let indent = span_of_first_expr_in_block(data.if_block)
353379
.and_then(|span| indent_of(cx, span))
354380
.unwrap_or(0);
355-
let to_annex = data.loop_block.stmts[data.stmt_idx + 1..]
356-
.iter()
357-
.map(|stmt| {
358-
let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span);
381+
let to_annex = if let Some(stmt_idx) = data.stmt_idx {
382+
let mut lines = data.loop_block.stmts[stmt_idx + 1..]
383+
.iter()
384+
.map(|stmt| {
385+
let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span);
386+
let snip = snippet_block(cx, span, "..", None);
387+
snip.lines()
388+
.map(|line| format!("{}{line}", " ".repeat(indent)))
389+
.collect::<Vec<_>>()
390+
.join("\n")
391+
})
392+
.collect::<Vec<_>>();
393+
if let Some(expr) = data.loop_block.expr {
394+
let span = expr.span;
359395
let snip = snippet_block(cx, span, "..", None);
360-
snip.lines()
396+
let expr_lines = snip
397+
.lines()
361398
.map(|line| format!("{}{line}", " ".repeat(indent)))
362399
.collect::<Vec<_>>()
363-
.join("\n")
364-
})
365-
.collect::<Vec<_>>()
366-
.join("\n");
400+
.join("\n");
401+
lines.push(expr_lines);
402+
}
403+
lines.join("\n")
404+
} else {
405+
"".to_string()
406+
};
367407

368408
let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0);
369409
format!(
@@ -373,46 +413,49 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin
373413
)
374414
}
375415

376-
fn check_last_stmt_in_expr<F>(inner_expr: &ast::Expr, func: &F)
416+
fn check_last_stmt_in_expr<F>(inner_expr: &Expr<'_>, func: &F)
377417
where
378418
F: Fn(Option<&Label>, Span),
379419
{
380420
match &inner_expr.kind {
381-
ast::ExprKind::Continue(continue_label) => {
382-
func(continue_label.as_ref(), inner_expr.span);
421+
ExprKind::Continue(continue_label) => {
422+
func(continue_label.label.as_ref(), inner_expr.span);
383423
},
384-
ast::ExprKind::If(_, then_block, else_block) => {
424+
ExprKind::If(_, then_block, else_block) if let ExprKind::Block(then_block, _) = then_block.kind => {
385425
check_last_stmt_in_block(then_block, func);
386426
if let Some(else_block) = else_block {
387427
check_last_stmt_in_expr(else_block, func);
388428
}
389429
},
390-
ast::ExprKind::Match(_, arms, _) => {
391-
for arm in arms {
392-
if let Some(expr) = &arm.body {
393-
check_last_stmt_in_expr(expr, func);
394-
}
430+
ExprKind::Match(_, arms, _) => {
431+
for arm in arms.iter() {
432+
check_last_stmt_in_expr(arm.body, func);
395433
}
396434
},
397-
ast::ExprKind::Block(b, _) => {
435+
ExprKind::Block(b, _) => {
398436
check_last_stmt_in_block(b, func);
399437
},
400438
_ => {},
401439
}
402440
}
403441

404-
fn check_last_stmt_in_block<F>(b: &Block, func: &F)
442+
fn check_last_stmt_in_block<F>(b: &Block<'_>, func: &F)
405443
where
406444
F: Fn(Option<&Label>, Span),
407445
{
446+
if let Some(expr) = b.expr {
447+
check_last_stmt_in_expr(expr, func);
448+
return;
449+
}
450+
408451
if let Some(last_stmt) = b.stmts.last()
409-
&& let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind
452+
&& let StmtKind::Expr(inner_expr) | StmtKind::Semi(inner_expr) = last_stmt.kind
410453
{
411454
check_last_stmt_in_expr(inner_expr, func);
412455
}
413456
}
414457

415-
fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
458+
fn check_and_warn(cx: &LateContext<'_>, expr: &Expr<'_>) {
416459
with_loop_block(expr, |loop_block, label| {
417460
let p = |continue_label: Option<&Label>, span: Span| {
418461
if compare_labels(label, continue_label) {
@@ -427,16 +470,51 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
427470
}
428471
};
429472

430-
let stmts = &loop_block.stmts;
473+
let stmts = loop_block.stmts;
431474
for (i, stmt) in stmts.iter().enumerate() {
432475
let mut maybe_emitted_in_if = false;
433-
with_if_expr(stmt, |if_expr, cond, then_block, else_expr| {
476+
if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind {
477+
with_if_expr(expr, |if_expr, cond, then_block, else_expr| {
478+
let data = &LintData {
479+
if_expr,
480+
if_cond: cond,
481+
if_block: then_block,
482+
else_expr,
483+
stmt_idx: Some(i),
484+
loop_block,
485+
};
486+
487+
maybe_emitted_in_if = true;
488+
if needless_continue_in_else(else_expr, label) {
489+
emit_warning(
490+
cx,
491+
data,
492+
DROP_ELSE_BLOCK_AND_MERGE_MSG,
493+
LintType::ContinueInsideElseBlock,
494+
);
495+
} else if is_first_block_stmt_continue(then_block, label) {
496+
emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock);
497+
} else {
498+
maybe_emitted_in_if = false;
499+
}
500+
});
501+
}
502+
503+
if i == stmts.len() - 1 && loop_block.expr.is_none() && !maybe_emitted_in_if {
504+
check_last_stmt_in_block(loop_block, &p);
505+
}
506+
}
507+
508+
if let Some(expr) = loop_block.expr {
509+
let mut maybe_emitted_in_if = false;
510+
511+
with_if_expr(expr, |if_expr, cond, then_block, else_expr| {
434512
let data = &LintData {
435513
if_expr,
436514
if_cond: cond,
437515
if_block: then_block,
438516
else_expr,
439-
stmt_idx: i,
517+
stmt_idx: None,
440518
loop_block,
441519
};
442520

@@ -455,7 +533,7 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
455533
}
456534
});
457535

458-
if i == stmts.len() - 1 && !maybe_emitted_in_if {
536+
if !maybe_emitted_in_if {
459537
check_last_stmt_in_block(loop_block, &p);
460538
}
461539
}
@@ -491,8 +569,12 @@ fn erode_from_back(s: &str) -> String {
491569
if ret.is_empty() { s.to_string() } else { ret }
492570
}
493571

494-
fn span_of_first_expr_in_block(block: &Block) -> Option<Span> {
495-
block.stmts.first().map(|stmt| stmt.span)
572+
fn span_of_first_expr_in_block(block: &Block<'_>) -> Option<Span> {
573+
block
574+
.stmts
575+
.first()
576+
.map(|stmt| stmt.span)
577+
.or(block.expr.map(|expr| expr.span))
496578
}
497579

498580
#[cfg(test)]

0 commit comments

Comments
 (0)