3636use rustc_lint:: { EarlyContext , EarlyLintPass } ;
3737use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
3838use rustc_span:: source_map:: { original_sp, DUMMY_SP } ;
39- use std :: borrow :: Cow ;
39+ use rustc_span :: Span ;
4040use 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
4444declare_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
287286fn 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
362365fn 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}
0 commit comments