}
declare_clippy_lint! {
- /// **What it does:** Checks for an iterator search (such as `find()`,
+ /// **What it does:** Checks for an iterator or string search (such as `find()`,
/// `position()`, or `rposition()`) followed by a call to `is_some()`.
///
/// **Why is this bad?** Readability, this can be written more concisely as
- /// `_.any(_)`.
+ /// `_.any(_)` or `_.contains(_)`.
///
/// **Known problems:** None.
///
/// ```
pub SEARCH_IS_SOME,
complexity,
- "using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`"
+ "using an iterator or string search followed by `is_some()`, which is more succinctly expressed as a call to `any()` or `contains()`"
}
declare_clippy_lint! {
}
/// lint searching an Iterator followed by `is_some()`
+/// or calling `find()` on a string followed by `is_some()`
fn lint_search_is_some<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
span_lint(cx, SEARCH_IS_SOME, expr.span, &msg);
}
}
+ // lint if `find()` is called by `String` or `&str`
+ else if search_method == "find" {
+ let is_string_or_str_slice = |e| {
+ let self_ty = cx.typeck_results().expr_ty(e).peel_refs();
+ if is_type_diagnostic_item(cx, self_ty, sym!(string_type)) {
+ true
+ } else {
+ *self_ty.kind() == ty::Str
+ }
+ };
+ if_chain! {
+ if is_string_or_str_slice(&search_args[0]);
+ if is_string_or_str_slice(&search_args[1]);
+ then {
+ let msg = "called `is_some()` after calling `find()` \
+ on a string. This is more succinctly expressed by calling \
+ `contains()`.".to_string();
+ let mut applicability = Applicability::MachineApplicable;
+ let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability);
+ span_lint_and_sugg(
+ cx,
+ SEARCH_IS_SOME,
+ method_span.with_hi(expr.span.hi()),
+ &msg,
+ "try this",
+ format!("contains({})", find_arg),
+ applicability,
+ );
+ }
+ }
+ }
}
/// Used for `lint_binary_expr_with_method_call`.
x < 0
}
).is_some();
-
- // Check that we don't lint if the caller is not an `Iterator`.
+
+ let s1 = String::from("hello world");
+ let s2 = String::from("world");
+ // Check caller `find()` is a &`static str case
+ let _ = "hello world".find("world").is_some();
+ let _ = "hello world".find(&s2).is_some();
+ let _ = "hello world".find(&s2[2..]).is_some();
+ // Check caller of `find()` is a String case
+ let _ = s1.find("world").is_some();
+ let _ = s1.find(&s2).is_some();
+ let _ = s1.find(&s2[2..]).is_some();
+ // Check caller of `find()` is a slice of String case
+ let _ = s1[2..].find("world").is_some();
+ let _ = s1[2..].find(&s2).is_some();
+ let _ = s1[2..].find(&s2[2..]).is_some();
+
+ // Check that we don't lint if `find()` is called with
+ // Pattern that is not a string
+ let _ = s1.find(|c: char| c == 'o' || c == 'l').is_some();
+
+ // Check that we don't lint if the caller is not an `Iterator` or string
let foo = IteratorFalsePositives { foo: 0 };
let _ = foo.find().is_some();
let _ = foo.position().is_some();