@@ -4,9 +4,10 @@ use crate::utils::sugg::Sugg;
44use crate :: utils:: usage:: { is_unused, mutated_variables} ;
55use crate :: utils:: {
66 contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
7- is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method,
8- match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_with_applicability, snippet_with_macro_callsite,
9- span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq ,
7+ indent_of, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment,
8+ match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path, snippet,
9+ snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg,
10+ span_lint_and_then, sugg, SpanlessEq ,
1011} ;
1112use if_chain:: if_chain;
1213use rustc_ast:: ast;
@@ -452,6 +453,31 @@ declare_clippy_lint! {
452453 "the same item is pushed inside of a for loop"
453454}
454455
456+ declare_clippy_lint ! {
457+ /// **What it does:** Checks whether a for loop has a single element.
458+ ///
459+ /// **Why is this bad?** There is no reason to have a loop of a
460+ /// single element.
461+ /// **Known problems:** None
462+ ///
463+ /// **Example:**
464+ /// ```rust
465+ /// let item1 = 2;
466+ /// for item in &[item1] {
467+ /// println!("{}", item);
468+ /// }
469+ /// ```
470+ /// could be written as
471+ /// ```rust
472+ /// let item1 = 2;
473+ /// let item = &item1;
474+ /// println!("{}", item);
475+ /// ```
476+ pub SINGLE_ELEMENT_LOOP ,
477+ complexity,
478+ "there is no reason to have a single element loop"
479+ }
480+
455481declare_lint_pass ! ( Loops => [
456482 MANUAL_MEMCPY ,
457483 NEEDLESS_RANGE_LOOP ,
@@ -469,6 +495,7 @@ declare_lint_pass!(Loops => [
469495 MUT_RANGE_BOUND ,
470496 WHILE_IMMUTABLE_CONDITION ,
471497 SAME_ITEM_PUSH ,
498+ SINGLE_ELEMENT_LOOP ,
472499] ) ;
473500
474501impl < ' tcx > LateLintPass < ' tcx > for Loops {
@@ -777,6 +804,7 @@ fn check_for_loop<'tcx>(
777804 check_for_loop_arg ( cx, pat, arg, expr) ;
778805 check_for_loop_over_map_kv ( cx, pat, arg, body, expr) ;
779806 check_for_mut_range_bound ( cx, arg, body) ;
807+ check_for_single_element_loop ( cx, pat, arg, body, expr) ;
780808 detect_same_item_push ( cx, pat, arg, body, expr) ;
781809}
782810
@@ -1866,6 +1894,43 @@ fn check_for_loop_over_map_kv<'tcx>(
18661894 }
18671895}
18681896
1897+ fn check_for_single_element_loop < ' tcx > (
1898+ cx : & LateContext < ' tcx > ,
1899+ pat : & ' tcx Pat < ' _ > ,
1900+ arg : & ' tcx Expr < ' _ > ,
1901+ body : & ' tcx Expr < ' _ > ,
1902+ expr : & ' tcx Expr < ' _ > ,
1903+ ) {
1904+ if_chain ! {
1905+ if let ExprKind :: AddrOf ( BorrowKind :: Ref , _, ref arg_expr) = arg. kind;
1906+ if let PatKind :: Binding ( .., target, _) = pat. kind;
1907+ if let ExprKind :: Array ( ref arg_expr_list) = arg_expr. kind;
1908+ if let [ arg_expression] = arg_expr_list;
1909+ if let ExprKind :: Path ( ref list_item) = arg_expression. kind;
1910+ if let Some ( list_item_name) = single_segment_path( list_item) . map( |ps| ps. ident. name) ;
1911+ if let ExprKind :: Block ( ref block, _) = body. kind;
1912+ if block. stmts. len( ) != 0 ;
1913+
1914+ then {
1915+ let for_span = get_span_of_entire_for_loop( expr) ;
1916+ let mut block_str = snippet( cx, block. span, ".." ) . into_owned( ) ;
1917+ block_str. remove( 0 ) ;
1918+ block_str. pop( ) ;
1919+
1920+
1921+ span_lint_and_sugg(
1922+ cx,
1923+ SINGLE_ELEMENT_LOOP ,
1924+ for_span,
1925+ "for loop over a single element" ,
1926+ "try" ,
1927+ format!( "{{\n {}let {} = &{};{}}}" , " " . repeat( indent_of( cx, block. stmts[ 0 ] . span) . unwrap_or( 0 ) ) , target. name, list_item_name, block_str) ,
1928+ Applicability :: MachineApplicable
1929+ )
1930+ }
1931+ }
1932+ }
1933+
18691934struct MutatePairDelegate < ' a , ' tcx > {
18701935 cx : & ' a LateContext < ' tcx > ,
18711936 hir_id_low : Option < HirId > ,
0 commit comments