@@ -515,11 +515,11 @@ declare_clippy_lint! {
515515}
516516
517517declare_clippy_lint ! {
518- /// **What it does:** Checks for an iterator search (such as `find()`,
518+ /// **What it does:** Checks for an iterator or string search (such as `find()`,
519519 /// `position()`, or `rposition()`) followed by a call to `is_some()`.
520520 ///
521521 /// **Why is this bad?** Readability, this can be written more concisely as
522- /// `_.any(_)`.
522+ /// `_.any(_)` or `_.contains(_)` .
523523 ///
524524 /// **Known problems:** None.
525525 ///
@@ -535,7 +535,7 @@ declare_clippy_lint! {
535535 /// ```
536536 pub SEARCH_IS_SOME ,
537537 complexity,
538- "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`"
538+ "using an iterator or string search followed by `is_some()`, which is more succinctly expressed as a call to `any()` or `contains ()`"
539539}
540540
541541declare_clippy_lint ! {
@@ -3041,6 +3041,7 @@ fn lint_flat_map_identity<'tcx>(
30413041}
30423042
30433043/// lint searching an Iterator followed by `is_some()`
3044+ /// or calling `find()` on a string followed by `is_some()`
30443045fn lint_search_is_some < ' tcx > (
30453046 cx : & LateContext < ' tcx > ,
30463047 expr : & ' tcx hir:: Expr < ' _ > ,
@@ -3052,10 +3053,10 @@ fn lint_search_is_some<'tcx>(
30523053 // lint if caller of search is an Iterator
30533054 if match_trait_method ( cx, & is_some_args[ 0 ] , & paths:: ITERATOR ) {
30543055 let msg = format ! (
3055- "called `is_some()` after searching an `Iterator` with {}. This is more succinctly \
3056- expressed by calling `any()`.",
3056+ "called `is_some()` after searching an `Iterator` with `{}`" ,
30573057 search_method
30583058 ) ;
3059+ let hint = "this is more succinctly expressed by calling `any()`" ;
30593060 let search_snippet = snippet ( cx, search_args[ 1 ] . span , ".." ) ;
30603061 if search_snippet. lines ( ) . count ( ) <= 1 {
30613062 // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()`
@@ -3083,15 +3084,44 @@ fn lint_search_is_some<'tcx>(
30833084 SEARCH_IS_SOME ,
30843085 method_span. with_hi ( expr. span . hi ( ) ) ,
30853086 & msg,
3086- "try this " ,
3087+ "use `any()` instead " ,
30873088 format ! (
30883089 "any({})" ,
30893090 any_search_snippet. as_ref( ) . map_or( & * search_snippet, String :: as_str)
30903091 ) ,
30913092 Applicability :: MachineApplicable ,
30923093 ) ;
30933094 } else {
3094- span_lint ( cx, SEARCH_IS_SOME , expr. span , & msg) ;
3095+ span_lint_and_help ( cx, SEARCH_IS_SOME , expr. span , & msg, None , hint) ;
3096+ }
3097+ }
3098+ // lint if `find()` is called by `String` or `&str`
3099+ else if search_method == "find" {
3100+ let is_string_or_str_slice = |e| {
3101+ let self_ty = cx. typeck_results ( ) . expr_ty ( e) . peel_refs ( ) ;
3102+ if is_type_diagnostic_item ( cx, self_ty, sym ! ( string_type) ) {
3103+ true
3104+ } else {
3105+ * self_ty. kind ( ) == ty:: Str
3106+ }
3107+ } ;
3108+ if_chain ! {
3109+ if is_string_or_str_slice( & search_args[ 0 ] ) ;
3110+ if is_string_or_str_slice( & search_args[ 1 ] ) ;
3111+ then {
3112+ let msg = "called `is_some()` after calling `find()` on a string" ;
3113+ let mut applicability = Applicability :: MachineApplicable ;
3114+ let find_arg = snippet_with_applicability( cx, search_args[ 1 ] . span, ".." , & mut applicability) ;
3115+ span_lint_and_sugg(
3116+ cx,
3117+ SEARCH_IS_SOME ,
3118+ method_span. with_hi( expr. span. hi( ) ) ,
3119+ msg,
3120+ "use `contains()` instead" ,
3121+ format!( "contains({})" , find_arg) ,
3122+ applicability,
3123+ ) ;
3124+ }
30953125 }
30963126 }
30973127}
0 commit comments