@@ -5,8 +5,9 @@ use 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,
77 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 ,
8+ match_type, match_var, multispan_sugg, qpath_res, single_segment_path, snippet, snippet_with_applicability,
9+ snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg,
10+ 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 {
@@ -776,6 +803,7 @@ fn check_for_loop<'tcx>(
776803 check_for_loop_arg ( cx, pat, arg, expr) ;
777804 check_for_loop_over_map_kv ( cx, pat, arg, body, expr) ;
778805 check_for_mut_range_bound ( cx, arg, body) ;
806+ check_for_single_element_loop ( cx, pat, arg, expr) ;
779807 detect_same_item_push ( cx, pat, arg, body, expr) ;
780808}
781809
@@ -1865,6 +1893,36 @@ fn check_for_loop_over_map_kv<'tcx>(
18651893 }
18661894}
18671895
1896+ fn check_for_single_element_loop < ' tcx > (
1897+ cx : & LateContext < ' tcx > ,
1898+ pat : & ' tcx Pat < ' _ > ,
1899+ arg : & ' tcx Expr < ' _ > ,
1900+ expr : & ' tcx Expr < ' _ > ,
1901+ ) {
1902+ if_chain ! {
1903+ if let ExprKind :: AddrOf ( BorrowKind :: Ref , _, ref arg_expr) = arg. kind;
1904+ if let PatKind :: Binding ( .., target, _) = pat. kind;
1905+ if let ExprKind :: Array ( ref arg_expr_list) = arg_expr. kind;
1906+ if let [ arg_expression] = arg_expr_list;
1907+ if let ExprKind :: Path ( ref list_item) = arg_expression. kind;
1908+ if let Some ( list_item_name) = single_segment_path( list_item) . map( |ps| ps. ident. name) ;
1909+
1910+ then {
1911+ let for_span = get_span_of_entire_for_loop( expr) ;
1912+
1913+ span_lint_and_sugg(
1914+ cx,
1915+ SINGLE_ELEMENT_LOOP ,
1916+ for_span. with_hi( expr. span. hi( ) ) ,
1917+ "for loop over a single element" ,
1918+ "try" ,
1919+ format!( "let {} = &{};" , target. name, list_item_name) ,
1920+ Applicability :: MachineApplicable
1921+ )
1922+ }
1923+ }
1924+ }
1925+
18681926struct MutatePairDelegate < ' a , ' tcx > {
18691927 cx : & ' a LateContext < ' tcx > ,
18701928 hir_id_low : Option < HirId > ,
0 commit comments