mod iter_next_slice;
mod iter_nth;
mod iter_nth_zero;
+mod iter_overeager_cloned;
mod iter_skip_next;
+mod iter_with_drain;
mod iterator_step_by_zero;
mod manual_saturating_arithmetic;
mod manual_str_repeat;
mod option_map_or_none;
mod option_map_unwrap_or;
mod or_fun_call;
+mod or_then_unwrap;
mod search_is_some;
mod single_char_add_str;
mod single_char_insert_string;
mod uninit_assumed_init;
mod unnecessary_filter_map;
mod unnecessary_fold;
+mod unnecessary_iter_cloned;
+mod unnecessary_join;
mod unnecessary_lazy_eval;
+mod unnecessary_to_owned;
mod unwrap_or_else_default;
mod unwrap_used;
mod useless_asref;
use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, TraitRef, Ty, TyS};
+use rustc_middle::ty::{self, TraitRef, Ty};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::symbol::Symbol;
use rustc_span::{sym, Span};
use rustc_typeck::hir_ty_to_ty;
"used `cloned` where `copied` could be used instead"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
+ ///
+ /// ### Why is this bad?
+ /// It's often inefficient to clone all elements of an iterator, when eventually, only some
+ /// of them will be consumed.
+ ///
+ /// ### Examples
+ /// ```rust
+ /// # let vec = vec!["string".to_string()];
+ ///
+ /// // Bad
+ /// vec.iter().cloned().take(10);
+ ///
+ /// // Good
+ /// vec.iter().take(10).cloned();
+ ///
+ /// // Bad
+ /// vec.iter().cloned().last();
+ ///
+ /// // Good
+ /// vec.iter().last().cloned();
+ ///
+ /// ```
+ /// ### Known Problems
+ /// This `lint` removes the side of effect of cloning items in the iterator.
+ /// A code that relies on that side-effect could fail.
+ ///
+ #[clippy::version = "1.59.0"]
+ pub ITER_OVEREAGER_CLONED,
+ perf,
+ "using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies"
+}
+
declare_clippy_lint! {
/// ### What it does
/// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
///
/// ### Why is this bad?
/// Readability, this can be written more concisely as
- /// `_.flat_map(_)`
+ /// `_.flat_map(_)` for `Iterator` or `_.and_then(_)` for `Option`
///
/// ### Example
/// ```rust
/// let vec = vec![vec![1]];
+ /// let opt = Some(5);
///
/// // Bad
/// vec.iter().map(|x| x.iter()).flatten();
+ /// opt.map(|x| Some(x * 2)).flatten();
///
/// // Good
/// vec.iter().flat_map(|x| x.iter());
+ /// opt.and_then(|x| Some(x * 2));
/// ```
#[clippy::version = "1.31.0"]
pub MAP_FLATTEN,
"using any `*or` method with a function call, which suggests `*or_else`"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `.or(…).unwrap()` calls to Options and Results.
+ ///
+ /// ### Why is this bad?
+ /// You should use `.unwrap_or(…)` instead for clarity.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let fallback = "fallback";
+ /// // Result
+ /// # type Error = &'static str;
+ /// # let result: Result<&str, Error> = Err("error");
+ /// let value = result.or::<Error>(Ok(fallback)).unwrap();
+ ///
+ /// // Option
+ /// # let option: Option<&str> = None;
+ /// let value = option.or(Some(fallback)).unwrap();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # let fallback = "fallback";
+ /// // Result
+ /// # let result: Result<&str, &str> = Err("error");
+ /// let value = result.unwrap_or(fallback);
+ ///
+ /// // Option
+ /// # let option: Option<&str> = None;
+ /// let value = option.unwrap_or(fallback);
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub OR_THEN_UNWRAP,
+ complexity,
+ "checks for `.or(…).unwrap()` calls to Options and Results."
+}
+
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
"using `.skip(x).next()` on an iterator"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for use of `.drain(..)` on `Vec` and `VecDeque` for iteration.
+ ///
+ /// ### Why is this bad?
+ /// `.into_iter()` is simpler with better performance.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::collections::HashSet;
+ /// let mut foo = vec![0, 1, 2, 3];
+ /// let bar: HashSet<usize> = foo.drain(..).collect();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # use std::collections::HashSet;
+ /// let foo = vec![0, 1, 2, 3];
+ /// let bar: HashSet<usize> = foo.into_iter().collect();
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub ITER_WITH_DRAIN,
+ nursery,
+ "replace `.drain(..)` with `.into_iter()`"
+}
+
declare_clippy_lint! {
/// ### What it does
/// Checks for use of `.get().unwrap()` (or
declare_clippy_lint! {
/// ### What it does
- /// Checks for `filter_map` calls which could be replaced by `filter` or `map`.
+ /// Checks for `filter_map` calls that could be replaced by `filter` or `map`.
/// More specifically it checks if the closure provided is only performing one of the
/// filter or map operations and suggests the appropriate option.
///
"using `filter_map` when a more succinct alternative exists"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `find_map` calls that could be replaced by `find` or `map`. More
+ /// specifically it checks if the closure provided is only performing one of the
+ /// find or map operations and suggests the appropriate option.
+ ///
+ /// ### Why is this bad?
+ /// Complexity. The intent is also clearer if only a single
+ /// operation is being performed.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let _ = (0..3).find_map(|x| if x > 2 { Some(x) } else { None });
+ ///
+ /// // As there is no transformation of the argument this could be written as:
+ /// let _ = (0..3).find(|&x| x > 2);
+ /// ```
+ ///
+ /// ```rust
+ /// let _ = (0..4).find_map(|x| Some(x + 1));
+ ///
+ /// // As there is no conditional check on the argument this could be written as:
+ /// let _ = (0..4).map(|x| x + 1).next();
+ /// ```
+ #[clippy::version = "1.61.0"]
+ pub UNNECESSARY_FIND_MAP,
+ complexity,
+ "using `find_map` when a more succinct alternative exists"
+}
+
declare_clippy_lint! {
/// ### What it does
/// Checks for `into_iter` calls on references which should be replaced by `iter`
"usages of `str::splitn` that can be replaced with `str::split`"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned)
+ /// and other `to_owned`-like functions.
+ ///
+ /// ### Why is this bad?
+ /// The unnecessary calls result in useless allocations.
+ ///
+ /// ### Known problems
+ /// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an
+ /// owned copy of a resource and the resource is later used mutably. See
+ /// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148).
+ ///
+ /// ### Example
+ /// ```rust
+ /// let path = std::path::Path::new("x");
+ /// foo(&path.to_string_lossy().to_string());
+ /// fn foo(s: &str) {}
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let path = std::path::Path::new("x");
+ /// foo(&path.to_string_lossy());
+ /// fn foo(s: &str) {}
+ /// ```
+ #[clippy::version = "1.58.0"]
+ pub UNNECESSARY_TO_OWNED,
+ perf,
+ "unnecessary calls to `to_owned`-like functions"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for use of `.collect::<Vec<String>>().join("")` on iterators.
+ ///
+ /// ### Why is this bad?
+ /// `.collect::<String>()` is more concise and usually more performant
+ ///
+ /// ### Example
+ /// ```rust
+ /// let vector = vec!["hello", "world"];
+ /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
+ /// println!("{}", output);
+ /// ```
+ /// The correct use would be:
+ /// ```rust
+ /// let vector = vec!["hello", "world"];
+ /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
+ /// println!("{}", output);
+ /// ```
+ /// ### Known problems
+ /// While `.collect::<String>()` is more performant in most cases, there are cases where
+ /// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
+ /// will prevent loop unrolling and will result in a negative performance impact.
+ #[clippy::version = "1.61.0"]
+ pub UNNECESSARY_JOIN,
+ pedantic,
+ "using `.collect::<Vec<String>>().join(\"\")` on an iterator"
+}
+
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Option<RustcVersion>,
OPTION_MAP_OR_NONE,
BIND_INSTEAD_OF_MAP,
OR_FUN_CALL,
+ OR_THEN_UNWRAP,
EXPECT_FUN_CALL,
CHARS_NEXT_CMP,
CHARS_LAST_CMP,
CLONE_ON_COPY,
CLONE_ON_REF_PTR,
CLONE_DOUBLE_REF,
+ ITER_OVEREAGER_CLONED,
CLONED_INSTEAD_OF_COPIED,
FLAT_MAP_OPTION,
INEFFICIENT_TO_STRING,
GET_UNWRAP,
STRING_EXTEND_CHARS,
ITER_CLONED_COLLECT,
+ ITER_WITH_DRAIN,
USELESS_ASREF,
UNNECESSARY_FOLD,
UNNECESSARY_FILTER_MAP,
+ UNNECESSARY_FIND_MAP,
INTO_ITER_ON_REF,
SUSPICIOUS_MAP,
UNINIT_ASSUMED_INIT,
MANUAL_STR_REPEAT,
EXTEND_WITH_DRAIN,
MANUAL_SPLIT_ONCE,
- NEEDLESS_SPLITN
+ NEEDLESS_SPLITN,
+ UNNECESSARY_TO_OWNED,
+ UNNECESSARY_JOIN,
]);
/// Extracts a method call name, args, and `Span` of the method name.
-fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(Symbol, &'tcx [hir::Expr<'tcx>], Span)> {
- if let ExprKind::MethodCall(path, span, args, _) = recv.kind {
+fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx str, &'tcx [hir::Expr<'tcx>], Span)> {
+ if let ExprKind::MethodCall(path, args, _) = recv.kind {
if !args.iter().any(|e| e.span.from_expansion()) {
- return Some((path.ident.name, args, span));
+ let name = path.ident.name.as_str();
+ return Some((name, args, path.ident.span));
}
}
None
}
-/// Same as `method_call` but the `Symbol` is dereferenced into a temporary `&str`
-macro_rules! method_call {
- ($expr:expr) => {
- method_call($expr)
- .as_ref()
- .map(|&(ref name, args, span)| (name.as_str(), args, span))
- };
-}
-
impl<'tcx> LateLintPass<'tcx> for Methods {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if expr.span.from_expansion() {
hir::ExprKind::Call(func, args) => {
from_iter_instead_of_collect::check(cx, expr, args, func);
},
- hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => {
- or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
- expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
+ hir::ExprKind::MethodCall(method_call, args, _) => {
+ let method_span = method_call.ident.span;
+ or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), args);
+ expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), args);
clone_on_copy::check(cx, expr, method_call.ident.name, args);
clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args);
inefficient_to_string::check(cx, expr, method_call.ident.name, args);
single_char_add_str::check(cx, expr, args);
- into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args);
+ into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args);
single_char_pattern::check(cx, expr, method_call.ident.name, args);
+ unnecessary_to_owned::check(cx, expr, method_call.ident.name, args);
},
hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
let mut info = BinaryExprInfo {
return;
}
let name = impl_item.ident.name.as_str();
- let parent = cx.tcx.hir().get_parent_did(impl_item.hir_id());
+ let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
let item = cx.tcx.hir().expect_item(parent);
let self_ty = cx.tcx.type_of(item.def_id);
let method_sig = cx.tcx.fn_sig(impl_item.def_id);
let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
- let first_arg_ty = &method_sig.inputs().iter().next();
+ let first_arg_ty = method_sig.inputs().iter().next();
// check conventions w.r.t. conversion method names and predicates
if let Some(first_arg_ty) = first_arg_ty;
if name == method_config.method_name &&
sig.decl.inputs.len() == method_config.param_count &&
method_config.output_type.matches(&sig.decl.output) &&
- method_config.self_kind.matches(cx, self_ty, first_arg_ty) &&
+ method_config.self_kind.matches(cx, self_ty, *first_arg_ty) &&
fn_header_equals(method_config.fn_header, sig.header) &&
method_config.lifetime_param_cond(impl_item)
{
{
wrong_self_convention::check(
cx,
- &name,
+ name,
self_ty,
- first_arg_ty,
+ *first_arg_ty,
first_arg.pat.span,
implements_trait,
false
// walk the return type and check for Self (this does not check associated types)
if let Some(self_adt) = self_ty.ty_adt_def() {
- if contains_adt_constructor(cx.tcx, ret_ty, self_adt) {
+ if contains_adt_constructor(ret_ty, self_adt) {
return;
}
- } else if contains_ty(cx.tcx, ret_ty, self_ty) {
+ } else if contains_ty(ret_ty, self_ty) {
return;
}
// one of the associated types must be Self
for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
+ let assoc_ty = match projection_predicate.term {
+ ty::Term::Ty(ty) => ty,
+ ty::Term::Const(_c) => continue,
+ };
// walk the associated type and check for Self
if let Some(self_adt) = self_ty.ty_adt_def() {
- if contains_adt_constructor(cx.tcx, projection_predicate.ty, self_adt) {
+ if contains_adt_constructor(assoc_ty, self_adt) {
return;
}
- } else if contains_ty(cx.tcx, projection_predicate.ty, self_ty) {
+ } else if contains_ty(assoc_ty, self_ty) {
return;
}
}
}
}
- if name == "new" && !TyS::same_type(ret_ty, self_ty) {
+ if name == "new" && ret_ty != self_ty {
span_lint(
cx,
NEW_RET_NO_SELF,
if let TraitItemKind::Fn(_, _) = item.kind;
let ret_ty = return_ty(cx, item.hir_id());
let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
- if !contains_ty(cx.tcx, ret_ty, self_ty);
+ if !contains_ty(ret_ty, self_ty);
then {
span_lint(
#[allow(clippy::too_many_lines)]
fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) {
- if let Some((name, [recv, args @ ..], span)) = method_call!(expr) {
+ if let Some((name, [recv, args @ ..], span)) = method_call(expr) {
match (name, args) {
("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
zst_offset::check(cx, expr, recv);
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
- ("collect", []) => match method_call!(recv) {
+ ("collect", []) => match method_call(recv) {
Some((name @ ("cloned" | "copied"), [recv2], _)) => {
iter_cloned_collect::check(cx, name, expr, recv2);
},
},
_ => {},
},
- ("count", []) => match method_call!(recv) {
- Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
- iter_count::check(cx, expr, recv2, name);
+ (name @ "count", args @ []) => match method_call(recv) {
+ Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
+ Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
+ iter_count::check(cx, expr, recv2, name2);
},
Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
_ => {},
},
- ("expect", [_]) => match method_call!(recv) {
+ ("drain", [arg]) => {
+ iter_with_drain::check(cx, expr, recv, span, arg);
+ },
+ ("expect", [_]) => match method_call(recv) {
Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
_ => expect_used::check(cx, expr, recv),
},
extend_with_drain::check(cx, expr, recv, arg);
},
("filter_map", [arg]) => {
- unnecessary_filter_map::check(cx, expr, arg);
+ unnecessary_filter_map::check(cx, expr, arg, name);
filter_map_identity::check(cx, expr, arg, span);
},
+ ("find_map", [arg]) => {
+ unnecessary_filter_map::check(cx, expr, arg, name);
+ },
("flat_map", [arg]) => {
flat_map_identity::check(cx, expr, arg, span);
flat_map_option::check(cx, expr, arg, span);
},
- ("flatten", []) => {
- if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
- map_flatten::check(cx, expr, recv, map_arg);
- }
+ (name @ "flatten", args @ []) => match method_call(recv) {
+ Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
+ Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
+ _ => {},
},
("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
("for_each", [_]) => {
- if let Some(("inspect", [_, _], span2)) = method_call!(recv) {
+ if let Some(("inspect", [_, _], span2)) = method_call(recv) {
inspect_for_each::check(cx, expr, span2);
}
},
("is_file", []) => filetype_is_file::check(cx, expr, recv),
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
+ ("join", [join_arg]) => {
+ if let Some(("collect", _, span)) = method_call(recv) {
+ unnecessary_join::check(cx, expr, recv, join_arg, span);
+ }
+ },
+ ("last", args @ []) | ("skip", args @ [_]) => {
+ if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
+ if let ("cloned", []) = (name2, args2) {
+ iter_overeager_cloned::check(cx, expr, recv2, name, args);
+ }
+ }
+ },
("map", [m_arg]) => {
- if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) {
+ if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
match (name, args) {
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv),
("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv),
map_identity::check(cx, expr, recv, m_arg, span);
},
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
- ("next", []) => {
- if let Some((name, [recv, args @ ..], _)) = method_call!(recv) {
- match (name, args) {
- ("filter", [arg]) => filter_next::check(cx, expr, recv, arg),
- ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv),
- ("iter", []) => iter_next_slice::check(cx, expr, recv),
- ("skip", [arg]) => iter_skip_next::check(cx, expr, recv, arg),
+ (name @ "next", args @ []) => {
+ if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
+ match (name2, args2) {
+ ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
+ ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
+ ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, msrv),
+ ("iter", []) => iter_next_slice::check(cx, expr, recv2),
+ ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
("skip_while", [_]) => skip_while_next::check(cx, expr),
_ => {},
}
}
},
- ("nth", [n_arg]) => match method_call!(recv) {
+ ("nth", args @ [n_arg]) => match method_call(recv) {
Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
+ Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
}
},
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
+ ("take", args @ [_arg]) => {
+ if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
+ if let ("cloned", []) = (name2, args2) {
+ iter_overeager_cloned::check(cx, expr, recv2, name, args);
+ }
+ }
+ },
("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
- implicit_clone::check(cx, name, expr, recv, span);
+ implicit_clone::check(cx, name, expr, recv);
},
- ("unwrap", []) => match method_call!(recv) {
- Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false),
- Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
- _ => unwrap_used::check(cx, expr, recv),
+ ("unwrap", []) => {
+ match method_call(recv) {
+ Some(("get", [recv, get_arg], _)) => {
+ get_unwrap::check(cx, expr, recv, get_arg, false);
+ },
+ Some(("get_mut", [recv, get_arg], _)) => {
+ get_unwrap::check(cx, expr, recv, get_arg, true);
+ },
+ Some(("or", [recv, or_arg], or_span)) => {
+ or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
+ },
+ _ => {},
+ }
+ unwrap_used::check(cx, expr, recv);
},
- ("unwrap_or", [u_arg]) => match method_call!(recv) {
+ ("unwrap_or", [u_arg]) => match method_call(recv) {
Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
},
},
_ => {},
},
- ("unwrap_or_else", [u_arg]) => match method_call!(recv) {
+ ("unwrap_or_else", [u_arg]) => match method_call(recv) {
Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {},
_ => {
unwrap_or_else_default::check(cx, expr, recv, u_arg);
}
fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
- if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) {
+ if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call(recv) {
search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
}
}
implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
}
+ fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
+ !matches_value(cx, parent_ty, ty)
+ && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty)
+ && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty)
+ }
+
match self {
Self::Value => matches_value(cx, parent_ty, ty),
Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
- Self::No => ty != parent_ty,
+ Self::No => matches_none(cx, parent_ty, ty),
}
}