Skip to content

Commit 8e15985

Browse files
committed
Rewrite suggestion generation of needless_continue
1 parent 10cd166 commit 8e15985

File tree

2 files changed

+84
-166
lines changed

2 files changed

+84
-166
lines changed

clippy_lints/src/needless_continue.rs

Lines changed: 84 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@
3636
use rustc_lint::{EarlyContext, EarlyLintPass};
3737
use rustc_session::{declare_lint_pass, declare_tool_lint};
3838
use rustc_span::source_map::{original_sp, DUMMY_SP};
39-
use std::borrow::Cow;
39+
use rustc_span::Span;
4040
use syntax::ast;
4141

42-
use crate::utils::{snippet, snippet_block, span_lint_and_help, trim_multiline};
42+
use crate::utils::{indent_of, snippet, snippet_block, span_lint_and_help};
4343

4444
declare_clippy_lint! {
4545
/// **What it does:** The lint checks for `if`-statements appearing in loops
@@ -273,90 +273,93 @@ struct LintData<'a> {
273273
block_stmts: &'a [ast::Stmt],
274274
}
275275

276-
const MSG_REDUNDANT_ELSE_BLOCK: &str = "This `else` block is redundant.\n";
276+
const MSG_REDUNDANT_ELSE_BLOCK: &str = "this `else` block is redundant";
277277

278-
const MSG_ELSE_BLOCK_NOT_NEEDED: &str = "There is no need for an explicit `else` block for this `if` \
279-
expression\n";
278+
const MSG_ELSE_BLOCK_NOT_NEEDED: &str = "there is no need for an explicit `else` block for this `if` \
279+
expression";
280280

281-
const DROP_ELSE_BLOCK_AND_MERGE_MSG: &str = "Consider dropping the `else` clause and merging the code that \
282-
follows (in the loop) with the `if` block, like so:\n";
281+
const DROP_ELSE_BLOCK_AND_MERGE_MSG: &str = "consider dropping the `else` clause and merging the code that \
282+
follows (in the loop) with the `if` block";
283283

284-
const DROP_ELSE_BLOCK_MSG: &str = "Consider dropping the `else` clause, and moving out the code in the `else` \
285-
block, like so:\n";
284+
const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause";
286285

287286
fn emit_warning<'a>(ctx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, typ: LintType) {
288287
// snip is the whole *help* message that appears after the warning.
289288
// message is the warning message.
290289
// expr is the expression which the lint warning message refers to.
291290
let (snip, message, expr) = match typ {
292291
LintType::ContinueInsideElseBlock => (
293-
suggestion_snippet_for_continue_inside_else(ctx, data, header),
292+
suggestion_snippet_for_continue_inside_else(ctx, data),
294293
MSG_REDUNDANT_ELSE_BLOCK,
295294
data.else_expr,
296295
),
297296
LintType::ContinueInsideThenBlock => (
298-
suggestion_snippet_for_continue_inside_if(ctx, data, header),
297+
suggestion_snippet_for_continue_inside_if(ctx, data),
299298
MSG_ELSE_BLOCK_NOT_NEEDED,
300299
data.if_expr,
301300
),
302301
};
303-
span_lint_and_help(ctx, NEEDLESS_CONTINUE, expr.span, message, &snip);
302+
span_lint_and_help(
303+
ctx,
304+
NEEDLESS_CONTINUE,
305+
expr.span,
306+
message,
307+
&format!("{}\n{}", header, snip),
308+
);
304309
}
305310

306-
fn suggestion_snippet_for_continue_inside_if<'a>(
307-
ctx: &EarlyContext<'_>,
308-
data: &'a LintData<'_>,
309-
header: &str,
310-
) -> String {
311+
fn suggestion_snippet_for_continue_inside_if<'a>(ctx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
311312
let cond_code = snippet(ctx, data.if_cond.span, "..");
312313

313-
let if_code = format!("if {} {{\n continue;\n}}\n", cond_code);
314-
/* ^^^^--- Four spaces of indentation. */
314+
let continue_code = snippet_block(ctx, data.if_block.span, "..", Some(data.if_expr.span));
315315
// region B
316-
let else_code = snippet(ctx, data.else_expr.span, "..").into_owned();
317-
let else_code = erode_block(&else_code);
318-
let else_code = trim_multiline(Cow::from(else_code), false);
316+
let else_code = snippet_block(ctx, data.else_expr.span, "..", Some(data.if_expr.span));
319317

320-
let mut ret = String::from(header);
321-
ret.push_str(&if_code);
322-
ret.push_str(&else_code);
323-
ret.push_str("\n...");
324-
ret
318+
let indent_if = indent_of(ctx, data.if_expr.span).unwrap_or(0);
319+
format!(
320+
"{}if {} {} {}",
321+
" ".repeat(indent_if),
322+
cond_code,
323+
continue_code,
324+
else_code,
325+
)
325326
}
326327

327-
fn suggestion_snippet_for_continue_inside_else<'a>(
328-
ctx: &EarlyContext<'_>,
329-
data: &'a LintData<'_>,
330-
header: &str,
331-
) -> String {
328+
fn suggestion_snippet_for_continue_inside_else<'a>(ctx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
332329
let cond_code = snippet(ctx, data.if_cond.span, "..");
333-
let mut if_code = format!("if {} {{\n", cond_code);
334330

335331
// Region B
336-
let block_code = &snippet(ctx, data.if_block.span, "..").into_owned();
337-
let block_code = erode_block(block_code);
338-
let block_code = trim_multiline(Cow::from(block_code), false);
339-
340-
if_code.push_str(&block_code);
332+
let block_code = erode_from_back(&snippet_block(ctx, data.if_block.span, "..", Some(data.if_expr.span)));
341333

342334
// Region C
343335
// These is the code in the loop block that follows the if/else construction
344336
// we are complaining about. We want to pull all of this code into the
345337
// `then` block of the `if` statement.
338+
let indent = span_of_first_expr_in_block(data.if_block)
339+
.and_then(|span| indent_of(ctx, span))
340+
.unwrap_or(0);
346341
let to_annex = data.block_stmts[data.stmt_idx + 1..]
347342
.iter()
348343
.map(|stmt| original_sp(stmt.span, DUMMY_SP))
349-
.map(|span| snippet_block(ctx, span, "..").into_owned())
344+
.map(|span| {
345+
let snip = snippet_block(ctx, span, "..", None).into_owned();
346+
snip.lines()
347+
.map(|line| format!("{}{}", " ".repeat(indent), line))
348+
.collect::<Vec<_>>()
349+
.join("\n")
350+
})
350351
.collect::<Vec<_>>()
351352
.join("\n");
352353

353-
let mut ret = String::from(header);
354-
355-
ret.push_str(&if_code);
356-
ret.push_str("\n// Merged code follows...");
357-
ret.push_str(&to_annex);
358-
ret.push_str("\n}\n");
359-
ret
354+
let indent_if = indent_of(ctx, data.if_expr.span).unwrap_or(0);
355+
format!(
356+
"{indent_if}if {} {}\n{indent}// merged code follows:\n{}\n{indent_if}}}",
357+
cond_code,
358+
block_code,
359+
to_annex,
360+
indent = " ".repeat(indent),
361+
indent_if = " ".repeat(indent_if),
362+
)
360363
}
361364

362365
fn check_and_warn<'a>(ctx: &EarlyContext<'_>, expr: &'a ast::Expr) {
@@ -406,7 +409,7 @@ fn check_and_warn<'a>(ctx: &EarlyContext<'_>, expr: &'a ast::Expr) {
406409
/// NOTE: when there is no closing brace in `s`, `s` is _not_ preserved, i.e.,
407410
/// an empty string will be returned in that case.
408411
#[must_use]
409-
pub fn erode_from_back(s: &str) -> String {
412+
fn erode_from_back(s: &str) -> String {
410413
let mut ret = String::from(s);
411414
while ret.pop().map_or(false, |c| c != '}') {}
412415
while let Some(c) = ret.pop() {
@@ -418,38 +421,40 @@ pub fn erode_from_back(s: &str) -> String {
418421
ret
419422
}
420423

421-
/// Eats at `s` from the front by first skipping all leading whitespace. Then,
422-
/// any number of opening braces are eaten, followed by any number of newlines.
423-
/// e.g., the string
424-
///
425-
/// ```ignore
426-
/// {
427-
/// something();
428-
/// inside_a_block();
429-
/// }
430-
/// ```
431-
///
432-
/// is transformed to
433-
///
434-
/// ```ignore
435-
/// something();
436-
/// inside_a_block();
437-
/// }
438-
/// ```
439-
#[must_use]
440-
pub fn erode_from_front(s: &str) -> String {
441-
s.chars()
442-
.skip_while(|c| c.is_whitespace())
443-
.skip_while(|c| *c == '{')
444-
.skip_while(|c| *c == '\n')
445-
.collect::<String>()
424+
fn span_of_first_expr_in_block(block: &ast::Block) -> Option<Span> {
425+
block.stmts.iter().next().map(|stmt| stmt.span)
446426
}
447427

448-
/// If `s` contains the code for a block, delimited by braces, this function
449-
/// tries to get the contents of the block. If there is no closing brace
450-
/// present,
451-
/// an empty string is returned.
452-
#[must_use]
453-
pub fn erode_block(s: &str) -> String {
454-
erode_from_back(&erode_from_front(s))
428+
#[cfg(test)]
429+
mod test {
430+
use super::erode_from_back;
431+
#[test]
432+
#[rustfmt::skip]
433+
fn test_erode_from_back() {
434+
let input = "\
435+
{
436+
let x = 5;
437+
let y = format!(\"{}\", 42);
438+
}";
439+
440+
let expected = "\
441+
{
442+
let x = 5;
443+
let y = format!(\"{}\", 42);";
444+
445+
let got = erode_from_back(input);
446+
assert_eq!(expected, got);
447+
}
448+
449+
#[test]
450+
#[rustfmt::skip]
451+
fn test_erode_from_back_no_brace() {
452+
let input = "\
453+
let x = 5;
454+
let y = something();
455+
";
456+
let expected = "";
457+
let got = erode_from_back(input);
458+
assert_eq!(expected, got);
459+
}
455460
}

tests/needless_continue_helpers.rs

Lines changed: 0 additions & 87 deletions
This file was deleted.

0 commit comments

Comments
 (0)