36
36
use rustc_lint:: { EarlyContext , EarlyLintPass } ;
37
37
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
38
38
use rustc_span:: source_map:: { original_sp, DUMMY_SP } ;
39
- use std :: borrow :: Cow ;
39
+ use rustc_span :: Span ;
40
40
use syntax:: ast;
41
41
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} ;
43
43
44
44
declare_clippy_lint ! {
45
45
/// **What it does:** The lint checks for `if`-statements appearing in loops
@@ -273,90 +273,93 @@ struct LintData<'a> {
273
273
block_stmts : & ' a [ ast:: Stmt ] ,
274
274
}
275
275
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" ;
277
277
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";
280
280
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";
283
283
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" ;
286
285
287
286
fn emit_warning < ' a > ( ctx : & EarlyContext < ' _ > , data : & ' a LintData < ' _ > , header : & str , typ : LintType ) {
288
287
// snip is the whole *help* message that appears after the warning.
289
288
// message is the warning message.
290
289
// expr is the expression which the lint warning message refers to.
291
290
let ( snip, message, expr) = match typ {
292
291
LintType :: ContinueInsideElseBlock => (
293
- suggestion_snippet_for_continue_inside_else ( ctx, data, header ) ,
292
+ suggestion_snippet_for_continue_inside_else ( ctx, data) ,
294
293
MSG_REDUNDANT_ELSE_BLOCK ,
295
294
data. else_expr ,
296
295
) ,
297
296
LintType :: ContinueInsideThenBlock => (
298
- suggestion_snippet_for_continue_inside_if ( ctx, data, header ) ,
297
+ suggestion_snippet_for_continue_inside_if ( ctx, data) ,
299
298
MSG_ELSE_BLOCK_NOT_NEEDED ,
300
299
data. if_expr ,
301
300
) ,
302
301
} ;
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
+ ) ;
304
309
}
305
310
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 {
311
312
let cond_code = snippet ( ctx, data. if_cond . span , ".." ) ;
312
313
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 ) ) ;
315
315
// 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 ) ) ;
319
317
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
+ )
325
326
}
326
327
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 {
332
329
let cond_code = snippet ( ctx, data. if_cond . span , ".." ) ;
333
- let mut if_code = format ! ( "if {} {{\n " , cond_code) ;
334
330
335
331
// 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 ) ) ) ;
341
333
342
334
// Region C
343
335
// These is the code in the loop block that follows the if/else construction
344
336
// we are complaining about. We want to pull all of this code into the
345
337
// `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 ) ;
346
341
let to_annex = data. block_stmts [ data. stmt_idx + 1 ..]
347
342
. iter ( )
348
343
. 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
+ } )
350
351
. collect :: < Vec < _ > > ( )
351
352
. join ( "\n " ) ;
352
353
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
+ )
360
363
}
361
364
362
365
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) {
406
409
/// NOTE: when there is no closing brace in `s`, `s` is _not_ preserved, i.e.,
407
410
/// an empty string will be returned in that case.
408
411
#[ must_use]
409
- pub fn erode_from_back ( s : & str ) -> String {
412
+ fn erode_from_back ( s : & str ) -> String {
410
413
let mut ret = String :: from ( s) ;
411
414
while ret. pop ( ) . map_or ( false , |c| c != '}' ) { }
412
415
while let Some ( c) = ret. pop ( ) {
@@ -418,38 +421,40 @@ pub fn erode_from_back(s: &str) -> String {
418
421
ret
419
422
}
420
423
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 )
446
426
}
447
427
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
+ }
455
460
}
0 commit comments