@@ -26,7 +26,7 @@ use rustc_span::symbol::{sym, SymbolStr};
26
26
use crate :: consts:: { constant, Constant } ;
27
27
use crate :: utils:: usage:: mutated_variables;
28
28
use crate :: utils:: {
29
- get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, in_macro, is_copy,
29
+ get_arg_name, get_parent_expr, get_trait_def_id, has_iter_method, higher , implements_trait, in_macro, is_copy,
30
30
is_ctor_or_promotable_const_function, is_expn_of, is_type_diagnostic_item, iter_input_pats, last_path_segment,
31
31
match_def_path, match_qpath, match_trait_method, match_type, match_var, method_calls, method_chain_args, paths,
32
32
remove_blocks, return_ty, same_tys, single_segment_path, snippet, snippet_with_applicability,
@@ -1242,6 +1242,32 @@ declare_clippy_lint! {
1242
1242
"using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
1243
1243
}
1244
1244
1245
+ declare_clippy_lint ! {
1246
+ /// **What it does:** Checks for usage of `iter().next()` on a Slice or an Array
1247
+ ///
1248
+ /// **Why is this bad?** These can be shortened into `.get()`
1249
+ ///
1250
+ /// **Known problems:** None.
1251
+ ///
1252
+ /// **Example:**
1253
+ /// ```rust
1254
+ /// # let a = [1, 2, 3];
1255
+ /// # let b = vec![1, 2, 3];
1256
+ /// a[2..].iter().next();
1257
+ /// b.iter().next();
1258
+ /// ```
1259
+ /// should be written as:
1260
+ /// ```rust
1261
+ /// # let a = [1, 2, 3];
1262
+ /// # let b = vec![1, 2, 3];
1263
+ /// a.get(2);
1264
+ /// b.get(0);
1265
+ /// ```
1266
+ pub ITER_NEXT_SLICE ,
1267
+ style,
1268
+ "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
1269
+ }
1270
+
1245
1271
declare_lint_pass ! ( Methods => [
1246
1272
UNWRAP_USED ,
1247
1273
EXPECT_USED ,
@@ -1273,6 +1299,7 @@ declare_lint_pass!(Methods => [
1273
1299
FIND_MAP ,
1274
1300
MAP_FLATTEN ,
1275
1301
ITERATOR_STEP_BY_ZERO ,
1302
+ ITER_NEXT_SLICE ,
1276
1303
ITER_NTH ,
1277
1304
ITER_NTH_ZERO ,
1278
1305
ITER_SKIP_NEXT ,
@@ -1320,6 +1347,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
1320
1347
} ,
1321
1348
[ "next" , "filter" ] => lint_filter_next ( cx, expr, arg_lists[ 1 ] ) ,
1322
1349
[ "next" , "skip_while" ] => lint_skip_while_next ( cx, expr, arg_lists[ 1 ] ) ,
1350
+ [ "next" , "iter" ] => lint_iter_next ( cx, expr, arg_lists[ 1 ] ) ,
1323
1351
[ "map" , "filter" ] => lint_filter_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
1324
1352
[ "map" , "filter_map" ] => lint_filter_map_map ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
1325
1353
[ "next" , "filter_map" ] => lint_filter_map_next ( cx, expr, arg_lists[ 1 ] ) ,
@@ -2199,6 +2227,60 @@ fn lint_step_by<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &hir::Expr<'_>, args
2199
2227
}
2200
2228
}
2201
2229
2230
+ fn lint_iter_next < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx hir:: Expr < ' _ > , iter_args : & ' tcx [ hir:: Expr < ' _ > ] ) {
2231
+ let caller_expr = & iter_args[ 0 ] ;
2232
+
2233
+ // Skip lint if the `iter().next()` expression is a for loop argument,
2234
+ // since it is already covered by `&loops::ITER_NEXT_LOOP`
2235
+ let mut parent_expr_opt = get_parent_expr ( cx, expr) ;
2236
+ while let Some ( parent_expr) = parent_expr_opt {
2237
+ if higher:: for_loop ( parent_expr) . is_some ( ) {
2238
+ return ;
2239
+ }
2240
+ parent_expr_opt = get_parent_expr ( cx, parent_expr) ;
2241
+ }
2242
+
2243
+ if derefs_to_slice ( cx, caller_expr, cx. tables . expr_ty ( caller_expr) ) . is_some ( ) {
2244
+ // caller is a Slice
2245
+ if_chain ! {
2246
+ if let hir:: ExprKind :: Index ( ref caller_var, ref index_expr) = & caller_expr. kind;
2247
+ if let Some ( higher:: Range { start: Some ( start_expr) , end: None , limits: ast:: RangeLimits :: HalfOpen } )
2248
+ = higher:: range( cx, index_expr) ;
2249
+ if let hir:: ExprKind :: Lit ( ref start_lit) = & start_expr. kind;
2250
+ if let ast:: LitKind :: Int ( start_idx, _) = start_lit. node;
2251
+ then {
2252
+ let mut applicability = Applicability :: MachineApplicable ;
2253
+ span_lint_and_sugg(
2254
+ cx,
2255
+ ITER_NEXT_SLICE ,
2256
+ expr. span,
2257
+ "Using `.iter().next()` on a Slice without end index." ,
2258
+ "try calling" ,
2259
+ format!( "{}.get({})" , snippet_with_applicability( cx, caller_var. span, ".." , & mut applicability) , start_idx) ,
2260
+ applicability,
2261
+ ) ;
2262
+ }
2263
+ }
2264
+ } else if is_type_diagnostic_item ( cx, cx. tables . expr_ty ( caller_expr) , sym ! ( vec_type) )
2265
+ || matches ! ( & walk_ptrs_ty( cx. tables. expr_ty( caller_expr) ) . kind, ty:: Array ( _, _) )
2266
+ {
2267
+ // caller is a Vec or an Array
2268
+ let mut applicability = Applicability :: MachineApplicable ;
2269
+ span_lint_and_sugg (
2270
+ cx,
2271
+ ITER_NEXT_SLICE ,
2272
+ expr. span ,
2273
+ "Using `.iter().next()` on an array" ,
2274
+ "try calling" ,
2275
+ format ! (
2276
+ "{}.get(0)" ,
2277
+ snippet_with_applicability( cx, caller_expr. span, ".." , & mut applicability)
2278
+ ) ,
2279
+ applicability,
2280
+ ) ;
2281
+ }
2282
+ }
2283
+
2202
2284
fn lint_iter_nth < ' a , ' tcx > (
2203
2285
cx : & LateContext < ' a , ' tcx > ,
2204
2286
expr : & hir:: Expr < ' _ > ,
0 commit comments