1- use crate :: utils:: { both, count_eq, eq_expr_value, in_macro, search_same, SpanlessEq , SpanlessHash } ;
21use crate :: utils:: {
3- first_line_of_span, get_enclosing_block, get_parent_expr, if_sequence, indent_of, parent_node_is_if_expr,
4- reindent_multiline, snippet, snippet_opt, span_lint_and_note, span_lint_and_then, ContainsName ,
2+ both, count_eq, eq_expr_value, first_line_of_span, get_enclosing_block, get_parent_expr, if_sequence, in_macro,
3+ indent_of, parent_node_is_if_expr, reindent_multiline, run_lints, search_same, snippet, snippet_opt,
4+ span_lint_and_note, span_lint_and_then, ContainsName , SpanlessEq , SpanlessHash ,
55} ;
6+ use if_chain:: if_chain;
67use rustc_data_structures:: fx:: FxHashSet ;
78use rustc_errors:: { Applicability , DiagnosticBuilder } ;
89use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
@@ -188,57 +189,15 @@ fn lint_same_then_else<'tcx>(
188189 return ;
189190 }
190191
191- let has_expr = blocks[ 0 ] . expr . is_some ( ) ;
192-
193192 // Check if each block has shared code
194- let mut start_eq = usize:: MAX ;
195- let mut end_eq = usize:: MAX ;
196- let mut expr_eq = true ;
197- for win in blocks. windows ( 2 ) {
198- let l_stmts = win[ 0 ] . stmts ;
199- let r_stmts = win[ 1 ] . stmts ;
200-
201- let mut evaluator = SpanlessEq :: new ( cx) ;
202- let current_start_eq = count_eq ( & mut l_stmts. iter ( ) , & mut r_stmts. iter ( ) , |l, r| evaluator. eq_stmt ( l, r) ) ;
203- let current_end_eq = count_eq ( & mut l_stmts. iter ( ) . rev ( ) , & mut r_stmts. iter ( ) . rev ( ) , |l, r| {
204- evaluator. eq_stmt ( l, r)
205- } ) ;
206- let block_expr_eq = both ( & win[ 0 ] . expr , & win[ 1 ] . expr , |l, r| evaluator. eq_expr ( l, r) ) ;
207-
208- // IF_SAME_THEN_ELSE
209- if block_expr_eq && l_stmts. len ( ) == r_stmts. len ( ) && l_stmts. len ( ) == current_start_eq {
210- span_lint_and_note (
211- cx,
212- IF_SAME_THEN_ELSE ,
213- win[ 0 ] . span ,
214- "this `if` has identical blocks" ,
215- Some ( win[ 1 ] . span ) ,
216- "same as this" ,
217- ) ;
218-
219- return ;
220- }
221-
222- start_eq = start_eq. min ( current_start_eq) ;
223- end_eq = end_eq. min ( current_end_eq) ;
224- expr_eq &= block_expr_eq;
225- }
193+ let has_expr = blocks[ 0 ] . expr . is_some ( ) ;
194+ let ( start_eq, mut end_eq, expr_eq) = scan_block_for_eq ( cx, blocks) ;
226195
227196 // SHARED_CODE_IN_IF_BLOCKS prerequisites
228197 if !has_unconditional_else || ( start_eq == 0 && end_eq == 0 && ( has_expr && !expr_eq) ) {
229198 return ;
230199 }
231200
232- if has_expr && !expr_eq {
233- end_eq = 0 ;
234- }
235-
236- // Check if the regions are overlapping. Set `end_eq` to prevent the overlap
237- let min_block_size = blocks. iter ( ) . map ( |x| x. stmts . len ( ) ) . min ( ) . unwrap ( ) ;
238- if ( start_eq + end_eq) > min_block_size {
239- end_eq = min_block_size - start_eq;
240- }
241-
242201 // Only the start is the same
243202 if start_eq != 0 && end_eq == 0 && ( !has_expr || !expr_eq) {
244203 let block = blocks[ 0 ] ;
@@ -302,14 +261,13 @@ fn lint_same_then_else<'tcx>(
302261 end_eq -= moved_start;
303262 }
304263
305- let end_linable = if let Some ( expr) = block. expr {
306- intravisit:: walk_expr ( & mut end_walker, expr) ;
307- end_walker. uses . iter ( ) . any ( |x| !block_defs. contains ( x) )
308- } else if end_eq == 0 {
309- false
310- } else {
311- true
312- } ;
264+ let end_linable = block. expr . map_or_else (
265+ || end_eq != 0 ,
266+ |expr| {
267+ intravisit:: walk_expr ( & mut end_walker, expr) ;
268+ end_walker. uses . iter ( ) . any ( |x| !block_defs. contains ( x) )
269+ } ,
270+ ) ;
313271
314272 if end_linable {
315273 end_walker. def_symbols . drain ( ) . for_each ( |x| {
@@ -329,13 +287,67 @@ fn lint_same_then_else<'tcx>(
329287 }
330288}
331289
290+ fn scan_block_for_eq ( cx : & LateContext < ' tcx > , blocks : & [ & Block < ' tcx > ] ) -> ( usize , usize , bool ) {
291+ let mut start_eq = usize:: MAX ;
292+ let mut end_eq = usize:: MAX ;
293+ let mut expr_eq = true ;
294+ for win in blocks. windows ( 2 ) {
295+ let l_stmts = win[ 0 ] . stmts ;
296+ let r_stmts = win[ 1 ] . stmts ;
297+
298+ let mut evaluator = SpanlessEq :: new ( cx) ;
299+ let current_start_eq = count_eq ( & mut l_stmts. iter ( ) , & mut r_stmts. iter ( ) , |l, r| evaluator. eq_stmt ( l, r) ) ;
300+ let current_end_eq = count_eq ( & mut l_stmts. iter ( ) . rev ( ) , & mut r_stmts. iter ( ) . rev ( ) , |l, r| {
301+ evaluator. eq_stmt ( l, r)
302+ } ) ;
303+ let block_expr_eq = both ( & win[ 0 ] . expr , & win[ 1 ] . expr , |l, r| evaluator. eq_expr ( l, r) ) ;
304+
305+ // IF_SAME_THEN_ELSE
306+ if_chain ! {
307+ if block_expr_eq;
308+ if l_stmts. len( ) == r_stmts. len( ) ;
309+ if l_stmts. len( ) == current_start_eq;
310+ if run_lints( cx, & [ IF_SAME_THEN_ELSE ] , win[ 0 ] . hir_id) ;
311+ if run_lints( cx, & [ IF_SAME_THEN_ELSE ] , win[ 1 ] . hir_id) ;
312+ then {
313+ span_lint_and_note(
314+ cx,
315+ IF_SAME_THEN_ELSE ,
316+ win[ 0 ] . span,
317+ "this `if` has identical blocks" ,
318+ Some ( win[ 1 ] . span) ,
319+ "same as this" ,
320+ ) ;
321+
322+ return ( 0 , 0 , false ) ;
323+ }
324+ }
325+
326+ start_eq = start_eq. min ( current_start_eq) ;
327+ end_eq = end_eq. min ( current_end_eq) ;
328+ expr_eq &= block_expr_eq;
329+ }
330+
331+ let has_expr = blocks[ 0 ] . expr . is_some ( ) ;
332+ if has_expr && !expr_eq {
333+ end_eq = 0 ;
334+ }
335+
336+ // Check if the regions are overlapping. Set `end_eq` to prevent the overlap
337+ let min_block_size = blocks. iter ( ) . map ( |x| x. stmts . len ( ) ) . min ( ) . unwrap ( ) ;
338+ if ( start_eq + end_eq) > min_block_size {
339+ end_eq = min_block_size - start_eq;
340+ }
341+
342+ ( start_eq, end_eq, expr_eq)
343+ }
344+
332345fn check_for_warn_of_moved_symbol (
333346 cx : & LateContext < ' tcx > ,
334347 symbols : & FxHashSet < Symbol > ,
335348 if_expr : & ' tcx Expr < ' _ > ,
336349) -> bool {
337- // Obs true as we include the current if block
338- if let Some ( block) = get_enclosing_block ( cx, if_expr. hir_id ) {
350+ get_enclosing_block ( cx, if_expr. hir_id ) . map_or ( false , |block| {
339351 let ignore_span = block. span . shrink_to_lo ( ) . to ( if_expr. span ) ;
340352
341353 symbols
@@ -360,9 +372,7 @@ fn check_for_warn_of_moved_symbol(
360372
361373 walker. result
362374 } )
363- } else {
364- false
365- }
375+ } )
366376}
367377
368378fn emit_shared_code_in_if_blocks_lint (
@@ -410,12 +420,10 @@ fn emit_shared_code_in_if_blocks_lint(
410420 block. stmts [ block. stmts . len ( ) - end_stmts] . span
411421 }
412422 . source_callsite ( ) ;
413- let moved_end = if let Some ( expr) = block. expr {
414- expr. span
415- } else {
416- block. stmts [ block. stmts . len ( ) - 1 ] . span
417- }
418- . source_callsite ( ) ;
423+ let moved_end = block
424+ . expr
425+ . map_or_else ( || block. stmts [ block. stmts . len ( ) - 1 ] . span , |expr| expr. span )
426+ . source_callsite ( ) ;
419427
420428 let moved_span = moved_start. to ( moved_end) ;
421429 let moved_snipped = reindent_multiline ( snippet ( cx, moved_span, "_" ) , true , None ) ;
@@ -488,7 +496,7 @@ fn emit_shared_code_in_if_blocks_lint(
488496 }
489497}
490498
491- /// This visitor collects HirIds and Symbols of defined symbols and HirIds of used values.
499+ /// This visitor collects `HirId`s and Symbols of defined symbols and `HirId`s of used values.
492500struct UsedValueFinderVisitor < ' a , ' tcx > {
493501 cx : & ' a LateContext < ' tcx > ,
494502
0 commit comments