@@ -25,9 +25,11 @@ mod iter_nth_zero;
2525mod iterator_step_by_zero;
2626mod manual_saturating_arithmetic;
2727mod map_collect_result_unit;
28+ mod map_flatten;
2829mod ok_expect;
2930mod option_as_ref_deref;
3031mod option_map_unwrap_or;
32+ mod search_is_some;
3133mod single_char_insert_string;
3234mod single_char_pattern;
3335mod single_char_push_string;
@@ -1711,13 +1713,13 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
17111713 [ "flat_map" , "filter" ] => filter_flat_map:: check ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
17121714 [ "flat_map" , "filter_map" ] => filter_map_flat_map:: check ( cx, expr, arg_lists[ 1 ] , arg_lists[ 0 ] ) ,
17131715 [ "flat_map" , ..] => flat_map_identity:: check ( cx, expr, arg_lists[ 0 ] , method_spans[ 0 ] ) ,
1714- [ "flatten" , "map" ] => lint_map_flatten ( cx, expr, arg_lists[ 1 ] ) ,
1715- [ "is_some" , "find" ] => lint_search_is_some ( cx, expr, "find" , arg_lists[ 1 ] , arg_lists[ 0 ] , method_spans[ 1 ] ) ,
1716+ [ "flatten" , "map" ] => map_flatten :: check ( cx, expr, arg_lists[ 1 ] ) ,
1717+ [ "is_some" , "find" ] => search_is_some :: check ( cx, expr, "find" , arg_lists[ 1 ] , arg_lists[ 0 ] , method_spans[ 1 ] ) ,
17161718 [ "is_some" , "position" ] => {
1717- lint_search_is_some ( cx, expr, "position" , arg_lists[ 1 ] , arg_lists[ 0 ] , method_spans[ 1 ] )
1719+ search_is_some :: check ( cx, expr, "position" , arg_lists[ 1 ] , arg_lists[ 0 ] , method_spans[ 1 ] )
17181720 } ,
17191721 [ "is_some" , "rposition" ] => {
1720- lint_search_is_some ( cx, expr, "rposition" , arg_lists[ 1 ] , arg_lists[ 0 ] , method_spans[ 1 ] )
1722+ search_is_some :: check ( cx, expr, "rposition" , arg_lists[ 1 ] , arg_lists[ 0 ] , method_spans[ 1 ] )
17211723 } ,
17221724 [ "extend" , ..] => string_extend_chars:: check ( cx, expr, arg_lists[ 0 ] ) ,
17231725 [ "count" , "into_iter" ] => iter_count:: check ( cx, expr, & arg_lists[ 1 ] , "into_iter" ) ,
@@ -2566,59 +2568,6 @@ fn derefs_to_slice<'tcx>(
25662568 }
25672569}
25682570
2569- /// lint use of `map().flatten()` for `Iterators` and 'Options'
2570- fn lint_map_flatten < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' _ > , map_args : & ' tcx [ hir:: Expr < ' _ > ] ) {
2571- // lint if caller of `.map().flatten()` is an Iterator
2572- if match_trait_method ( cx, expr, & paths:: ITERATOR ) {
2573- let map_closure_ty = cx. typeck_results ( ) . expr_ty ( & map_args[ 1 ] ) ;
2574- let is_map_to_option = match map_closure_ty. kind ( ) {
2575- ty:: Closure ( _, _) | ty:: FnDef ( _, _) | ty:: FnPtr ( _) => {
2576- let map_closure_sig = match map_closure_ty. kind ( ) {
2577- ty:: Closure ( _, substs) => substs. as_closure ( ) . sig ( ) ,
2578- _ => map_closure_ty. fn_sig ( cx. tcx ) ,
2579- } ;
2580- let map_closure_return_ty = cx. tcx . erase_late_bound_regions ( map_closure_sig. output ( ) ) ;
2581- is_type_diagnostic_item ( cx, map_closure_return_ty, sym:: option_type)
2582- } ,
2583- _ => false ,
2584- } ;
2585-
2586- let method_to_use = if is_map_to_option {
2587- // `(...).map(...)` has type `impl Iterator<Item=Option<...>>
2588- "filter_map"
2589- } else {
2590- // `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>>
2591- "flat_map"
2592- } ;
2593- let func_snippet = snippet ( cx, map_args[ 1 ] . span , ".." ) ;
2594- let hint = format ! ( ".{0}({1})" , method_to_use, func_snippet) ;
2595- span_lint_and_sugg (
2596- cx,
2597- MAP_FLATTEN ,
2598- expr. span . with_lo ( map_args[ 0 ] . span . hi ( ) ) ,
2599- "called `map(..).flatten()` on an `Iterator`" ,
2600- & format ! ( "try using `{}` instead" , method_to_use) ,
2601- hint,
2602- Applicability :: MachineApplicable ,
2603- ) ;
2604- }
2605-
2606- // lint if caller of `.map().flatten()` is an Option
2607- if is_type_diagnostic_item ( cx, cx. typeck_results ( ) . expr_ty ( & map_args[ 0 ] ) , sym:: option_type) {
2608- let func_snippet = snippet ( cx, map_args[ 1 ] . span , ".." ) ;
2609- let hint = format ! ( ".and_then({})" , func_snippet) ;
2610- span_lint_and_sugg (
2611- cx,
2612- MAP_FLATTEN ,
2613- expr. span . with_lo ( map_args[ 0 ] . span . hi ( ) ) ,
2614- "called `map(..).flatten()` on an `Option`" ,
2615- "try using `and_then` instead" ,
2616- hint,
2617- Applicability :: MachineApplicable ,
2618- ) ;
2619- }
2620- }
2621-
26222571const MAP_UNWRAP_OR_MSRV : RustcVersion = RustcVersion :: new ( 1 , 41 , 0 ) ;
26232572
26242573/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
@@ -2756,93 +2705,6 @@ fn lint_map_or_none<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
27562705 ) ;
27572706}
27582707
2759- /// lint searching an Iterator followed by `is_some()`
2760- /// or calling `find()` on a string followed by `is_some()`
2761- fn lint_search_is_some < ' tcx > (
2762- cx : & LateContext < ' tcx > ,
2763- expr : & ' tcx hir:: Expr < ' _ > ,
2764- search_method : & str ,
2765- search_args : & ' tcx [ hir:: Expr < ' _ > ] ,
2766- is_some_args : & ' tcx [ hir:: Expr < ' _ > ] ,
2767- method_span : Span ,
2768- ) {
2769- // lint if caller of search is an Iterator
2770- if match_trait_method ( cx, & is_some_args[ 0 ] , & paths:: ITERATOR ) {
2771- let msg = format ! (
2772- "called `is_some()` after searching an `Iterator` with `{}`" ,
2773- search_method
2774- ) ;
2775- let hint = "this is more succinctly expressed by calling `any()`" ;
2776- let search_snippet = snippet ( cx, search_args[ 1 ] . span , ".." ) ;
2777- if search_snippet. lines ( ) . count ( ) <= 1 {
2778- // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()`
2779- // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()`
2780- let any_search_snippet = if_chain ! {
2781- if search_method == "find" ;
2782- if let hir:: ExprKind :: Closure ( _, _, body_id, ..) = search_args[ 1 ] . kind;
2783- let closure_body = cx. tcx. hir( ) . body( body_id) ;
2784- if let Some ( closure_arg) = closure_body. params. get( 0 ) ;
2785- then {
2786- if let hir:: PatKind :: Ref ( ..) = closure_arg. pat. kind {
2787- Some ( search_snippet. replacen( '&' , "" , 1 ) )
2788- } else if let PatKind :: Binding ( _, _, ident, _) = strip_pat_refs( & closure_arg. pat) . kind {
2789- let name = & * ident. name. as_str( ) ;
2790- Some ( search_snippet. replace( & format!( "*{}" , name) , name) )
2791- } else {
2792- None
2793- }
2794- } else {
2795- None
2796- }
2797- } ;
2798- // add note if not multi-line
2799- span_lint_and_sugg (
2800- cx,
2801- SEARCH_IS_SOME ,
2802- method_span. with_hi ( expr. span . hi ( ) ) ,
2803- & msg,
2804- "use `any()` instead" ,
2805- format ! (
2806- "any({})" ,
2807- any_search_snippet. as_ref( ) . map_or( & * search_snippet, String :: as_str)
2808- ) ,
2809- Applicability :: MachineApplicable ,
2810- ) ;
2811- } else {
2812- span_lint_and_help ( cx, SEARCH_IS_SOME , expr. span , & msg, None , hint) ;
2813- }
2814- }
2815- // lint if `find()` is called by `String` or `&str`
2816- else if search_method == "find" {
2817- let is_string_or_str_slice = |e| {
2818- let self_ty = cx. typeck_results ( ) . expr_ty ( e) . peel_refs ( ) ;
2819- if is_type_diagnostic_item ( cx, self_ty, sym:: string_type) {
2820- true
2821- } else {
2822- * self_ty. kind ( ) == ty:: Str
2823- }
2824- } ;
2825- if_chain ! {
2826- if is_string_or_str_slice( & search_args[ 0 ] ) ;
2827- if is_string_or_str_slice( & search_args[ 1 ] ) ;
2828- then {
2829- let msg = "called `is_some()` after calling `find()` on a string" ;
2830- let mut applicability = Applicability :: MachineApplicable ;
2831- let find_arg = snippet_with_applicability( cx, search_args[ 1 ] . span, ".." , & mut applicability) ;
2832- span_lint_and_sugg(
2833- cx,
2834- SEARCH_IS_SOME ,
2835- method_span. with_hi( expr. span. hi( ) ) ,
2836- msg,
2837- "use `contains()` instead" ,
2838- format!( "contains({})" , find_arg) ,
2839- applicability,
2840- ) ;
2841- }
2842- }
2843- }
2844- }
2845-
28462708/// Used for `lint_binary_expr_with_method_call`.
28472709#[ derive( Copy , Clone ) ]
28482710struct BinaryExprInfo < ' a > {
0 commit comments