Merge remote-tracking branch 'upstream/master' into rustup

This commit is contained in:
Philipp Krones 2024-11-14 18:27:35 +01:00
commit c166ee1fc8
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
101 changed files with 1201 additions and 308 deletions

View file

@ -16,7 +16,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) {
return;
}
if let Some(lint_list) = &attr.meta_item_list() {
if attr.ident().map_or(false, |ident| is_lint_level(ident.name, attr.id)) {
if attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id)) {
for lint in lint_list {
match item.kind {
ItemKind::Use(..) => {

View file

@ -50,7 +50,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
block
.expr
.as_ref()
.map_or(false, |e| is_relevant_expr(cx, typeck_results, e)),
.is_some_and(|e| is_relevant_expr(cx, typeck_results, e)),
|stmt| match &stmt.kind {
StmtKind::Let(_) => true,
StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
@ -60,7 +60,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
}
fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool {
if macro_backtrace(expr.span).last().map_or(false, |macro_call| {
if macro_backtrace(expr.span).last().is_some_and(|macro_call| {
is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable
}) {
return false;

View file

@ -60,8 +60,8 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -
trait_id,
)
})
.map_or(false, |assoc_item| {
let proj = Ty::new_projection_from_args(cx.tcx, assoc_item.def_id, cx.tcx.mk_args_trait(ty, []));
.is_some_and(|assoc_item| {
let proj = Ty::new_projection(cx.tcx, assoc_item.def_id, cx.tcx.mk_args_trait(ty, []));
let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj);
nty.is_bool()

View file

@ -667,5 +667,5 @@ fn implements_ord(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let ty = cx.typeck_results().expr_ty(expr);
cx.tcx
.get_diagnostic_item(sym::Ord)
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
.is_some_and(|id| implements_trait(cx, ty, id, &[]))
}

View file

@ -45,7 +45,7 @@ impl LateLintPass<'_> for BoxDefault {
// And that method is `new`
&& seg.ident.name == sym::new
// And the call is that of a `Box` method
&& path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box())
&& path_def_id(cx, ty).is_some_and(|id| Some(id) == cx.tcx.lang_items().owned_box())
// And the single argument to the call is another function call
// This is the `T::default()` (or default equivalent) of `Box::new(T::default())`
&& let ExprKind::Call(arg_path, _) = arg.kind
@ -83,9 +83,9 @@ fn is_plain_default(cx: &LateContext<'_>, arg_path: &Expr<'_>) -> bool {
}
fn is_local_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>, ref_expr: &Expr<'_>) -> bool {
macro_backtrace(expr.span).next().map_or(false, |call| {
cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id) && call.span.eq_ctxt(ref_expr.span)
})
macro_backtrace(expr.span)
.next()
.is_some_and(|call| cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id) && call.span.eq_ctxt(ref_expr.span))
}
#[derive(Default)]

View file

@ -159,8 +159,7 @@ pub(super) fn check<'tcx>(
// The same is true if the expression encompassing the cast expression is a unary
// expression or an addressof expression.
let needs_block = matches!(cast_expr.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..))
|| get_parent_expr(cx, expr)
.map_or(false, |e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..)));
|| get_parent_expr(cx, expr).is_some_and(|e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..)));
span_lint_and_sugg(
cx,

View file

@ -110,7 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
let is_ord = cx
.tcx
.get_diagnostic_item(sym::Ord)
.map_or(false, |id| implements_trait(cx, ty, id, &[]));
.is_some_and(|id| implements_trait(cx, ty, id, &[]));
if !is_ord {
return;

View file

@ -345,7 +345,7 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool {
let mut i = 0usize;
let mut res = true;
l.pat.each_binding_or_first(&mut |_, _, _, name| {
if names.get(i).map_or(false, |&(_, n)| n == name.name) {
if names.get(i).is_some_and(|&(_, n)| n == name.name) {
i += 1;
} else {
res = false;
@ -389,12 +389,10 @@ fn eq_stmts(
let new_bindings = &moved_bindings[old_count..];
blocks
.iter()
.all(|b| get_stmt(b).map_or(false, |s| eq_binding_names(s, new_bindings)))
.all(|b| get_stmt(b).is_some_and(|s| eq_binding_names(s, new_bindings)))
} else {
true
}) && blocks
.iter()
.all(|b| get_stmt(b).map_or(false, |s| eq.eq_stmt(s, stmt)))
}) && blocks.iter().all(|b| get_stmt(b).is_some_and(|s| eq.eq_stmt(s, stmt)))
}
#[expect(clippy::too_many_lines)]
@ -451,9 +449,7 @@ fn scan_block_for_eq<'tcx>(
// x + 50
let expr_hash_eq = if let Some(e) = block.expr {
let hash = hash_expr(cx, e);
blocks
.iter()
.all(|b| b.expr.map_or(false, |e| hash_expr(cx, e) == hash))
blocks.iter().all(|b| b.expr.is_some_and(|e| hash_expr(cx, e) == hash))
} else {
blocks.iter().all(|b| b.expr.is_none())
};
@ -514,7 +510,7 @@ fn scan_block_for_eq<'tcx>(
});
if let Some(e) = block.expr {
for block in blocks {
if block.expr.map_or(false, |expr| !eq.eq_expr(expr, e)) {
if block.expr.is_some_and(|expr| !eq.eq_expr(expr, e)) {
moved_locals.truncate(moved_locals_at_start);
return BlockEq {
start_end_eq,
@ -533,7 +529,7 @@ fn scan_block_for_eq<'tcx>(
}
fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbol)], if_expr: &Expr<'_>) -> bool {
get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| {
get_enclosing_block(cx, if_expr.hir_id).is_some_and(|block| {
let ignore_span = block.span.shrink_to_lo().to(if_expr.span);
symbols

View file

@ -481,6 +481,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::methods::UNNECESSARY_JOIN_INFO,
crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO,
crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO,
crate::methods::UNNECESSARY_MAP_OR_INFO,
crate::methods::UNNECESSARY_MIN_OR_MAX_INFO,
crate::methods::UNNECESSARY_RESULT_MAP_OR_ELSE_INFO,
crate::methods::UNNECESSARY_SORT_BY_INFO,

View file

@ -253,6 +253,6 @@ impl<'tcx> From<Ty<'tcx>> for ExplicitTyBound {
impl<'tcx> From<Option<Ty<'tcx>>> for ExplicitTyBound {
fn from(v: Option<Ty<'tcx>>) -> Self {
Self(v.map_or(false, Ty::is_numeric))
Self(v.is_some_and(Ty::is_numeric))
}
}

View file

@ -81,7 +81,7 @@ fn is_path_self(e: &Expr<'_>) -> bool {
fn contains_trait_object(ty: Ty<'_>) -> bool {
match ty.kind() {
ty::Ref(_, ty, _) => contains_trait_object(*ty),
ty::Adt(def, args) => def.is_box() && args[0].as_type().map_or(false, contains_trait_object),
ty::Adt(def, args) => def.is_box() && args[0].as_type().is_some_and(contains_trait_object),
ty::Dynamic(..) => true,
_ => false,
}

View file

@ -327,7 +327,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h
// there's a Copy impl for any instance of the adt.
if !is_copy(cx, ty) {
if ty_subs.non_erasable_generics().next().is_some() {
let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).is_some_and(|impls| {
impls.iter().any(|&id| {
matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _)
if ty_adt.did() == adt.did())

View file

@ -47,7 +47,7 @@ pub fn check(
),
_ => (),
}
if !headers.panics && panic_info.map_or(false, |el| !el.1) {
if !headers.panics && panic_info.is_some_and(|el| !el.1) {
span_lint_and_note(
cx,
MISSING_PANICS_DOC,

View file

@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
MEM_FORGET,
Cow::Owned(format!(
"usage of `mem::forget` on {}",
if arg_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) {
if arg_ty.ty_adt_def().is_some_and(|def| def.has_dtor(cx.tcx)) {
"`Drop` type"
} else {
"type with `Drop` fields"

View file

@ -196,7 +196,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc
{
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
if path_to_local(callee).map_or(false, |l| {
if path_to_local(callee).is_some_and(|l| {
// FIXME: Do we really need this `local_used_in` check?
// Isn't it checking something like... `callee(callee)`?
// If somehow this check is needed, add some test for it,

View file

@ -87,7 +87,7 @@ fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option<HirId> {
}
fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) {
if path_to_local(arg).map_or(false, |id| raw_ptrs.contains(&id)) {
if path_to_local(arg).is_some_and(|id| raw_ptrs.contains(&id)) {
span_lint(
cx,
NOT_UNSAFE_PTR_ARG_DEREF,

View file

@ -171,13 +171,13 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
if let ExprKind::Path(ref qpath) = path.kind {
cx.qpath_res(qpath, path.hir_id)
.opt_def_id()
.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id))
.is_some_and(|id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id))
.into()
} else {
Finite
}
},
ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
ExprKind::Struct(..) => higher::Range::hir(expr).is_some_and(|r| r.end.is_none()).into(),
_ => Finite,
}
}
@ -228,9 +228,7 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
let not_double_ended = cx
.tcx
.get_diagnostic_item(sym::DoubleEndedIterator)
.map_or(false, |id| {
!implements_trait(cx, cx.typeck_results().expr_ty(receiver), id, &[])
});
.is_some_and(|id| !implements_trait(cx, cx.typeck_results().expr_ty(receiver), id, &[]));
if not_double_ended {
return is_infinite(cx, receiver);
}

View file

@ -309,8 +309,8 @@ fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>
let item_name_chars = item_name.chars().count();
if count_match_start(item_name, name).char_count == item_name_chars
&& name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
&& name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
&& name.chars().nth(item_name_chars).is_some_and(|c| !c.is_lowercase())
&& name.chars().nth(item_name_chars + 1).is_some_and(|c| !c.is_numeric())
{
span_lint_hir(
cx,

View file

@ -75,7 +75,7 @@ fn check_sig(cx: &LateContext<'_>, name: Symbol, sig: &FnSig<'_>, fn_id: LocalDe
if cx
.tcx
.get_diagnostic_item(sym::Iterator)
.map_or(false, |iter_id| !implements_trait(cx, ret_ty, iter_id, &[]))
.is_some_and(|iter_id| !implements_trait(cx, ret_ty, iter_id, &[]))
{
span_lint(
cx,

View file

@ -56,7 +56,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
&& !item.span.from_expansion()
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
&& let ty::Array(element_type, cst) = ty.kind()
&& let Some((ty::ValTree::Leaf(element_count), _)) = cx.tcx.try_normalize_erasing_regions(ParamEnv::empty(), *cst).unwrap_or(*cst).try_to_valtree()
&& let Some((ty::ValTree::Leaf(element_count), _)) = cx.tcx
.try_normalize_erasing_regions(ParamEnv::empty(), *cst).unwrap_or(*cst).try_to_valtree()
&& let element_count = element_count.to_target_usize(cx.tcx)
&& let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
&& u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size)

View file

@ -94,6 +94,8 @@ impl LateLintPass<'_> for LargeIncludeFile {
// Currently, rustc limits the usage of macro at the top-level of attributes,
// so we don't need to recurse into each level.
&& let AttrKind::Normal(ref normal) = attr.kind
&& let Some(doc) = attr.doc_str()
&& doc.as_str().len() as u64 > self.max_file_size
&& let AttrArgs::Eq(_, AttrArgsEq::Hir(ref meta)) = normal.item.args
&& !attr.span.contains(meta.span)
// Since the `include_str` is already expanded at this point, we can only take the

View file

@ -622,7 +622,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let ty = &cx.typeck_results().expr_ty(expr).peel_refs();
match ty.kind() {
ty::Dynamic(tt, ..) => tt.principal().map_or(false, |principal| {
ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| {
let is_empty = sym!(is_empty);
cx.tcx
.associated_items(principal.def_id())

View file

@ -737,7 +737,7 @@ fn elision_suggestions(
suggestions.extend(
usages
.iter()
.filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id)))
.filter(|usage| named_lifetime(usage).is_some_and(|id| elidable_lts.contains(&id)))
.map(|usage| {
match cx.tcx.parent_hir_node(usage.hir_id) {
Node::Ty(Ty {

View file

@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(
.tcx
.lang_items()
.copy_trait()
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
.is_some_and(|id| implements_trait(cx, ty, id, &[]))
{
snippet.push_str(
&format!(

View file

@ -416,7 +416,7 @@ fn get_assignments<'a, 'tcx>(
.chain(*expr)
.filter(move |e| {
if let ExprKind::AssignOp(_, place, _) = e.kind {
path_to_local(place).map_or(false, |id| {
path_to_local(place).is_some_and(|id| {
!loop_counters
.iter()
// skip the first item which should be `StartKind::Range`

View file

@ -126,7 +126,7 @@ impl BreakAfterExprVisitor {
break_after_expr: false,
};
get_enclosing_block(cx, hir_id).map_or(false, |block| {
get_enclosing_block(cx, hir_id).is_some_and(|block| {
visitor.visit_block(block);
visitor.break_after_expr
})

View file

@ -50,7 +50,7 @@ pub(super) fn check<'tcx>(
.tcx
.lang_items()
.clone_trait()
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
.is_some_and(|id| implements_trait(cx, ty, id, &[]))
{
// Make sure that the push does not involve possibly mutating values
match pushed_item.kind {

View file

@ -256,9 +256,10 @@ fn is_conditional(expr: &Expr<'_>) -> bool {
/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
/// actual `Iterator` that the loop uses.
pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String {
let impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[])
});
let impls_iterator = cx
.tcx
.get_diagnostic_item(sym::Iterator)
.is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[]));
if impls_iterator {
format!(
"{}",

View file

@ -226,7 +226,7 @@ impl TypeClampability {
} else if cx
.tcx
.get_diagnostic_item(sym::Ord)
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
.is_some_and(|id| implements_trait(cx, ty, id, &[]))
{
Some(TypeClampability::Ord)
} else {

View file

@ -162,9 +162,9 @@ fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'t
..
}) = expr.kind
{
constant_length(cx, pattern).map_or(false, |length| *n == length)
constant_length(cx, pattern).is_some_and(|length| *n == length)
} else {
len_arg(cx, expr).map_or(false, |arg| eq_expr_value(cx, pattern, arg))
len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, pattern, arg))
}
}

View file

@ -74,7 +74,7 @@ where
&& b0 != b1
&& (first_guard.is_none() || iter.len() == 0)
&& first_attrs.is_empty()
&& iter.all(|arm| find_bool_lit(&arm.2.kind).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty())
&& iter.all(|arm| find_bool_lit(&arm.2.kind).is_some_and(|b| b == b0) && arm.3.is_none() && arm.0.is_empty())
{
if let Some(last_pat) = last_pat_opt {
if !is_wild(last_pat) {

View file

@ -447,7 +447,7 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte
.tcx
.lang_items()
.get(expected_lang_item)
.map_or(false, |expected_id| cx.tcx.parent(id) == expected_id),
.is_some_and(|expected_id| cx.tcx.parent(id) == expected_id),
Item::Diag(expected_ty, expected_variant) => {
let ty = cx.typeck_results().pat_ty(pat);

View file

@ -175,7 +175,7 @@ impl<'tcx> Visitor<'tcx> for PatVisitor<'tcx> {
if matches!(pat.kind, PatKind::Binding(..)) {
ControlFlow::Break(())
} else {
self.has_enum |= self.typeck.pat_ty(pat).ty_adt_def().map_or(false, AdtDef::is_enum);
self.has_enum |= self.typeck.pat_ty(pat).ty_adt_def().is_some_and(AdtDef::is_enum);
walk_pat(self, pat)
}
}

View file

@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt());
let mut applicability = Applicability::MachineApplicable;
let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability);
let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
let ret_prefix = if get_parent_expr(cx, expr).is_some_and(|e| matches!(e.kind, ExprKind::Ret(_))) {
"" // already returns
} else {
"return "

View file

@ -83,7 +83,7 @@ pub(super) fn check<'tcx>(
hir::ExprKind::MethodCall(..) => {
cx.typeck_results()
.type_dependent_def_id(arg.hir_id)
.map_or(false, |method_id| {
.is_some_and(|method_id| {
matches!(
cx.tcx.fn_sig(method_id).instantiate_identity().output().skip_binder().kind(),
ty::Ref(re, ..) if re.is_static()

View file

@ -32,9 +32,10 @@ pub(super) fn check<'tcx>(
filter_arg: &'tcx hir::Expr<'_>,
) {
// lint if caller of `.filter().next()` is an Iterator
let recv_impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[])
});
let recv_impls_iterator = cx
.tcx
.get_diagnostic_item(sym::Iterator)
.is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[]));
if recv_impls_iterator {
let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
`.find(..)` instead";

View file

@ -19,9 +19,7 @@ pub(super) fn check<'tcx>(
if cx
.tcx
.get_diagnostic_item(sym::DoubleEndedIterator)
.map_or(false, |double_ended_iterator| {
implements_trait(cx, rev_recv_ty, double_ended_iterator, &[])
})
.is_some_and(|double_ended_iterator| implements_trait(cx, rev_recv_ty, double_ended_iterator, &[]))
&& is_trait_method(cx, rev_call, sym::Iterator)
&& is_trait_method(cx, expr, sym::Iterator)
{

View file

@ -36,8 +36,8 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<RepeatKind> {
} else {
let ty = cx.typeck_results().expr_ty(e);
if is_type_lang_item(cx, ty, LangItem::String)
|| (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str))
|| (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).map_or(false, Ty::is_str))
|| (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).is_some_and(Ty::is_str))
|| (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).is_some_and(Ty::is_str))
{
Some(RepeatKind::String)
} else {

View file

@ -70,7 +70,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_
if ident_eq(name, obj) && method.ident.name == sym::clone
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id)
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
&& cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id)
&& cx.tcx.lang_items().clone_trait() == Some(trait_id)
// no autoderefs
&& !cx.typeck_results().expr_adjustments(obj).iter()
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))))

View file

@ -121,6 +121,7 @@ mod unnecessary_iter_cloned;
mod unnecessary_join;
mod unnecessary_lazy_eval;
mod unnecessary_literal_unwrap;
mod unnecessary_map_or;
mod unnecessary_min_or_max;
mod unnecessary_result_map_or_else;
mod unnecessary_sort_by;
@ -4099,6 +4100,33 @@ declare_clippy_lint! {
"is_empty() called on strings known at compile time"
}
declare_clippy_lint! {
/// ### What it does
/// Converts some constructs mapping an Enum value for equality comparison.
///
/// ### Why is this bad?
/// Calls such as `opt.map_or(false, |val| val == 5)` are needlessly long and cumbersome,
/// and can be reduced to, for example, `opt == Some(5)` assuming `opt` implements `PartialEq`.
/// This lint offers readability and conciseness improvements.
///
/// ### Example
/// ```no_run
/// pub fn a(x: Option<i32>) -> bool {
/// x.map_or(false, |n| n == 5)
/// }
/// ```
/// Use instead:
/// ```no_run
/// pub fn a(x: Option<i32>) -> bool {
/// x == Some(5)
/// }
/// ```
#[clippy::version = "1.75.0"]
pub UNNECESSARY_MAP_OR,
style,
"reduce unnecessary pattern matching for constructs that implement `PartialEq`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks if an iterator is used to check if a string is ascii.
@ -4417,6 +4445,7 @@ impl_lint_pass!(Methods => [
NEEDLESS_AS_BYTES,
MAP_ALL_ANY_IDENTITY,
MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
UNNECESSARY_MAP_OR,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@ -4950,6 +4979,7 @@ impl Methods {
option_map_or_none::check(cx, expr, recv, def, map);
manual_ok_or::check(cx, expr, recv, def, map);
option_map_or_err_ok::check(cx, expr, recv, def, map);
unnecessary_map_or::check(cx, expr, recv, def, map, &self.msrv);
},
("map_or_else", [def, map]) => {
result_map_or_else_none::check(cx, expr, recv, def, map);
@ -5343,7 +5373,7 @@ impl SelfKind {
boxed_ty == parent_ty
} else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
if let ty::Adt(_, args) = ty.kind() {
args.types().next().map_or(false, |t| t == parent_ty)
args.types().next() == Some(parent_ty)
} else {
false
}

View file

@ -193,7 +193,7 @@ fn check_collect_into_intoiterator<'tcx>(
/// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool`
fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool {
cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| {
cx.typeck_results().type_dependent_def_id(call_id).is_some_and(|id| {
let sig = cx.tcx.fn_sig(id).instantiate_identity().skip_binder();
sig.inputs().len() == 1 && sig.output().is_bool()
})

View file

@ -45,7 +45,7 @@ pub(super) fn check(
hir::ExprKind::Path(ref expr_qpath) => {
cx.qpath_res(expr_qpath, map_arg.hir_id)
.opt_def_id()
.map_or(false, |fun_def_id| {
.is_some_and(|fun_def_id| {
cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id)
|| cx.tcx.is_diagnostic_item(sym::deref_mut_method, fun_def_id)
|| deref_aliases

View file

@ -59,9 +59,7 @@ pub(super) fn check<'tcx>(
let output_ty = cx.tcx.fn_sig(def_id).instantiate(cx.tcx, args).skip_binder().output();
cx.tcx
.get_diagnostic_item(sym::Default)
.map_or(false, |default_trait_id| {
implements_trait(cx, output_ty, default_trait_id, &[])
})
.is_some_and(|default_trait_id| implements_trait(cx, output_ty, default_trait_id, &[]))
} else {
false
}

View file

@ -348,7 +348,7 @@ fn parse_iter_usage<'tcx>(
&& cx
.typeck_results()
.type_dependent_def_id(e.hir_id)
.map_or(false, |id| is_diag_item_method(cx, id, sym::Option)) =>
.is_some_and(|id| is_diag_item_method(cx, id, sym::Option)) =>
{
(Some(UnwrapKind::Unwrap), e.span)
},

View file

@ -0,0 +1,131 @@
use std::borrow::Cow;
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::source::snippet_opt;
use clippy_utils::sugg::{Sugg, make_binop};
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
use clippy_utils::visitors::is_local_used;
use clippy_utils::{is_from_proc_macro, path_to_local_id};
use rustc_ast::LitKind::Bool;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
use rustc_lint::LateContext;
use rustc_span::sym;
use super::UNNECESSARY_MAP_OR;
pub(super) enum Variant {
Ok,
Some,
}
impl Variant {
pub fn variant_name(&self) -> &'static str {
match self {
Variant::Ok => "Ok",
Variant::Some => "Some",
}
}
pub fn method_name(&self) -> &'static str {
match self {
Variant::Ok => "is_ok_and",
Variant::Some => "is_some_and",
}
}
}
pub(super) fn check<'a>(
cx: &LateContext<'a>,
expr: &Expr<'a>,
recv: &Expr<'_>,
def: &Expr<'_>,
map: &Expr<'_>,
msrv: &Msrv,
) {
let ExprKind::Lit(def_kind) = def.kind else {
return;
};
let recv_ty = cx.typeck_results().expr_ty(recv);
let Bool(def_bool) = def_kind.node else {
return;
};
let variant = match get_type_diagnostic_name(cx, recv_ty) {
Some(sym::Option) => Variant::Some,
Some(sym::Result) => Variant::Ok,
Some(_) | None => return,
};
let (sugg, method) = if let ExprKind::Closure(map_closure) = map.kind
&& let closure_body = cx.tcx.hir().body(map_closure.body)
&& let closure_body_value = closure_body.value.peel_blocks()
&& let ExprKind::Binary(op, l, r) = closure_body_value.kind
&& let Some(param) = closure_body.params.first()
&& let PatKind::Binding(_, hir_id, _, _) = param.pat.kind
// checking that map_or is one of the following:
// .map_or(false, |x| x == y)
// .map_or(false, |x| y == x) - swapped comparison
// .map_or(true, |x| x != y)
// .map_or(true, |x| y != x) - swapped comparison
&& ((BinOpKind::Eq == op.node && !def_bool) || (BinOpKind::Ne == op.node && def_bool))
&& let non_binding_location = if path_to_local_id(l, hir_id) { r } else { l }
&& switch_to_eager_eval(cx, non_binding_location)
// xor, because if its both then thats a strange edge case and
// we can just ignore it, since by default clippy will error on this
&& (path_to_local_id(l, hir_id) ^ path_to_local_id(r, hir_id))
&& !is_local_used(cx, non_binding_location, hir_id)
&& let typeck_results = cx.typeck_results()
&& typeck_results.expr_ty(l) == typeck_results.expr_ty(r)
&& let Some(partial_eq) = cx.tcx.get_diagnostic_item(sym::PartialEq)
&& implements_trait(cx, recv_ty, partial_eq, &[recv_ty.into()])
{
let wrap = variant.variant_name();
// we may need to add parens around the suggestion
// in case the parent expression has additional method calls,
// since for example `Some(5).map_or(false, |x| x == 5).then(|| 1)`
// being converted to `Some(5) == Some(5).then(|| 1)` isnt
// the same thing
let inner_non_binding = Sugg::NonParen(Cow::Owned(format!(
"{wrap}({})",
Sugg::hir(cx, non_binding_location, "")
)));
let binop = make_binop(op.node, &Sugg::hir(cx, recv, ".."), &inner_non_binding)
.maybe_par()
.into_string();
(binop, "a standard comparison")
} else if !def_bool
&& msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND)
&& let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite())
&& let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite())
{
let suggested_name = variant.method_name();
(
format!("{recv_callsite}.{suggested_name}({span_callsite})",),
suggested_name,
)
} else {
return;
};
if is_from_proc_macro(cx, expr) {
return;
}
span_lint_and_sugg(
cx,
UNNECESSARY_MAP_OR,
expr.span,
"this `map_or` is redundant",
format!("use {method} instead"),
sugg,
Applicability::MaybeIncorrect,
);
}

View file

@ -5,7 +5,8 @@ use clippy_utils::ty::implements_trait;
use rustc_errors::Applicability;
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, GenericArgKind};
use rustc_middle::ty;
use rustc_middle::ty::GenericArgKind;
use rustc_span::sym;
use rustc_span::symbol::Ident;
use std::iter;
@ -166,9 +167,10 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp
},
)) = &left_expr.kind
&& left_name == left_ident
&& cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
})
&& cx
.tcx
.get_diagnostic_item(sym::Ord)
.is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[]))
{
return Some(LintTrigger::Sort(SortDetection { vec_name }));
}

View file

@ -124,7 +124,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id)
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
// We check it's the `Clone` trait.
&& cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id)
&& cx.tcx.lang_items().clone_trait().is_some_and(|id| id == trait_id)
// no autoderefs
&& !cx.typeck_results().expr_adjustments(obj).iter()
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))))

View file

@ -427,7 +427,7 @@ impl MiscEarlyLints {
// See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
// FIXME: Find a better way to detect those cases.
let lit_snip = match snippet_opt(cx, span) {
Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip,
Some(snip) if snip.chars().next().is_some_and(|c| c.is_ascii_digit()) => snip,
_ => return,
};

View file

@ -96,10 +96,6 @@ impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> {
self.found = true;
return;
},
ExprKind::If(..) => {
self.found = true;
return;
},
ExprKind::Path(_) => {
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
if adj

View file

@ -134,9 +134,11 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
}
fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &Body<'_>) {
if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| {
local_def_id == cx.tcx.hir().body_owner_def_id(body.id())
}) {
if self
.possible_borrowers
.last()
.is_some_and(|&(local_def_id, _)| local_def_id == cx.tcx.hir().body_owner_def_id(body.id()))
{
self.possible_borrowers.pop();
}
}
@ -232,7 +234,7 @@ fn needless_borrow_count<'tcx>(
let mut check_reference_and_referent = |reference: &Expr<'tcx>, referent: &Expr<'tcx>| {
if let ExprKind::Field(base, _) = &referent.kind {
let base_ty = cx.typeck_results().expr_ty(base);
if drop_trait_def_id.map_or(false, |id| implements_trait(cx, base_ty, id, &[])) {
if drop_trait_def_id.is_some_and(|id| implements_trait(cx, base_ty, id, &[])) {
return false;
}
}

View file

@ -153,7 +153,7 @@ fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>)
}
fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool {
block.stmts.first().map_or(false, |stmt| match stmt.kind {
block.stmts.first().is_some_and(|stmt| match stmt.kind {
ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
if let ast::ExprKind::Continue(ref l) = e.kind {
compare_labels(label, l.as_ref())
@ -390,7 +390,7 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
#[must_use]
fn erode_from_back(s: &str) -> String {
let mut ret = s.to_string();
while ret.pop().map_or(false, |c| c != '}') {}
while ret.pop().is_some_and(|c| c != '}') {}
while let Some(c) = ret.pop() {
if !c.is_whitespace() {
ret.push(c);

View file

@ -88,7 +88,7 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace
|| span
.macro_backtrace()
.last()
.map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local))
.is_some_and(|e| e.macro_def_id.is_some_and(DefId::is_local))
};
let span_call_site = span.ctxt().outer_expn_data().call_site;
if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind

View file

@ -290,7 +290,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
Some((Node::Expr(parent), child_id)) => match parent.kind {
// Recursive call. Track which index the parameter is used in.
ExprKind::Call(callee, args)
if path_def_id(cx, callee).map_or(false, |id| {
if path_def_id(cx, callee).is_some_and(|id| {
id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(callee.hir_id))
}) =>
{
@ -300,7 +300,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
return;
},
ExprKind::MethodCall(_, receiver, args, _)
if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
if typeck.type_dependent_def_id(parent.hir_id).is_some_and(|id| {
id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(parent.hir_id))
}) =>
{

View file

@ -325,7 +325,7 @@ impl ArithmeticSideEffects {
fn should_skip_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) -> bool {
is_lint_allowed(cx, ARITHMETIC_SIDE_EFFECTS, expr.hir_id)
|| self.expr_span.is_some()
|| self.const_span.map_or(false, |sp| sp.contains(expr.span))
|| self.const_span.is_some_and(|sp| sp.contains(expr.span))
}
}

View file

@ -42,14 +42,12 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool)
if typeck
.type_dependent_def_id(expr.hir_id)
.and_then(|id| cx.tcx.trait_of_item(id))
.map_or(false, |id| {
matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))
}) =>
.is_some_and(|id| matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))) =>
{
(arg, arg.span)
},
ExprKind::Call(path, [arg])
if path_def_id(cx, path).map_or(false, |did| {
if path_def_id(cx, path).is_some_and(|did| {
if cx.tcx.is_diagnostic_item(sym::from_str_method, did) {
true
} else if cx.tcx.is_diagnostic_item(sym::from_fn, did) {

View file

@ -42,83 +42,43 @@ pub(crate) fn check<'tcx>(
match op {
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
let _ = check_op(
cx,
left,
0,
expr.span,
peeled_right_span,
needs_parenthesis(cx, expr, right),
right_is_coerced_to_value,
) || check_op(
cx,
right,
0,
expr.span,
peeled_left_span,
Parens::Unneeded,
left_is_coerced_to_value,
);
if is_redundant_op(cx, left, 0) {
let paren = needs_parenthesis(cx, expr, right);
span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value);
} else if is_redundant_op(cx, right, 0) {
let paren = needs_parenthesis(cx, expr, left);
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
}
},
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
let _ = check_op(
cx,
right,
0,
expr.span,
peeled_left_span,
Parens::Unneeded,
left_is_coerced_to_value,
);
if is_redundant_op(cx, right, 0) {
let paren = needs_parenthesis(cx, expr, left);
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
}
},
BinOpKind::Mul => {
let _ = check_op(
cx,
left,
1,
expr.span,
peeled_right_span,
needs_parenthesis(cx, expr, right),
right_is_coerced_to_value,
) || check_op(
cx,
right,
1,
expr.span,
peeled_left_span,
Parens::Unneeded,
left_is_coerced_to_value,
);
if is_redundant_op(cx, left, 1) {
let paren = needs_parenthesis(cx, expr, right);
span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value);
} else if is_redundant_op(cx, right, 1) {
let paren = needs_parenthesis(cx, expr, left);
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
}
},
BinOpKind::Div => {
let _ = check_op(
cx,
right,
1,
expr.span,
peeled_left_span,
Parens::Unneeded,
left_is_coerced_to_value,
);
if is_redundant_op(cx, right, 1) {
let paren = needs_parenthesis(cx, expr, left);
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
}
},
BinOpKind::BitAnd => {
let _ = check_op(
cx,
left,
-1,
expr.span,
peeled_right_span,
needs_parenthesis(cx, expr, right),
right_is_coerced_to_value,
) || check_op(
cx,
right,
-1,
expr.span,
peeled_left_span,
Parens::Unneeded,
left_is_coerced_to_value,
);
if is_redundant_op(cx, left, -1) {
let paren = needs_parenthesis(cx, expr, right);
span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value);
} else if is_redundant_op(cx, right, -1) {
let paren = needs_parenthesis(cx, expr, left);
span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
}
},
BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
_ => (),
@ -138,43 +98,70 @@ enum Parens {
Unneeded,
}
/// Checks if `left op right` needs parenthesis when reduced to `right`
/// Checks if a binary expression needs parenthesis when reduced to just its
/// right or left child.
///
/// e.g. `-(x + y + 0)` cannot be reduced to `-x + y`, as the behavior changes silently.
/// e.g. `1u64 + ((x + y + 0i32) as u64)` cannot be reduced to `1u64 + x + y as u64`, since
/// the the cast expression will not apply to the same expression.
/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced
/// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be
/// interpreted as a statement
/// interpreted as a statement. The same behavior happens for `match`, `loop`,
/// and blocks.
/// e.g. `2 * (0 + { a })` can be reduced to `2 * { a }` without the need for parenthesis,
/// but `1 * ({ a } + 4)` cannot be reduced to `{ a } + 4`, as a block at the start of a line
/// will be interpreted as a statement instead of an expression.
///
/// See #8724
fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> Parens {
match right.kind {
/// See #8724, #13470
fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, child: &Expr<'_>) -> Parens {
match child.kind {
ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => {
// ensure we're checking against the leftmost expression of `right`
//
// ~~~ `lhs`
// 0 + {4} * 2
// ~~~~~~~ `right`
return needs_parenthesis(cx, binary, lhs);
// For casts and binary expressions, we want to add parenthesis if
// the parent HIR node is an expression, or if the parent HIR node
// is a Block or Stmt, and the new left hand side would need
// parenthesis be treated as a statement rather than an expression.
if let Some((_, parent)) = cx.tcx.hir().parent_iter(binary.hir_id).next() {
match parent {
Node::Expr(_) => return Parens::Needed,
Node::Block(_) | Node::Stmt(_) => {
// ensure we're checking against the leftmost expression of `child`
//
// ~~~~~~~~~~~ `binary`
// ~~~ `lhs`
// 0 + {4} * 2
// ~~~~~~~ `child`
return needs_parenthesis(cx, binary, lhs);
},
_ => return Parens::Unneeded,
}
}
},
ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => {
// For if, match, block, and loop expressions, we want to add parenthesis if
// the closest ancestor node that is not an expression is a block or statement.
// This would mean that the rustfix suggestion will appear at the start of a line, which causes
// these expressions to be interpreted as statements if they do not have parenthesis.
let mut prev_id = binary.hir_id;
for (_, parent) in cx.tcx.hir().parent_iter(binary.hir_id) {
if let Node::Expr(expr) = parent
&& let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) | ExprKind::Unary(_, lhs) = expr.kind
&& lhs.hir_id == prev_id
{
// keep going until we find a node that encompasses left of `binary`
prev_id = expr.hir_id;
continue;
}
match parent {
Node::Block(_) | Node::Stmt(_) => return Parens::Needed,
_ => return Parens::Unneeded,
};
}
},
_ => {
return Parens::Unneeded;
},
ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => {},
_ => return Parens::Unneeded,
}
let mut prev_id = binary.hir_id;
for (_, node) in cx.tcx.hir().parent_iter(binary.hir_id) {
if let Node::Expr(expr) = node
&& let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) = expr.kind
&& lhs.hir_id == prev_id
{
// keep going until we find a node that encompasses left of `binary`
prev_id = expr.hir_id;
continue;
}
match node {
Node::Block(_) | Node::Stmt(_) => break,
_ => return Parens::Unneeded,
};
}
Parens::Needed
}
@ -199,7 +186,7 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
}
}
fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) -> bool {
fn is_redundant_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8) -> bool {
if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_simple(e).map(Constant::peel_refs) {
let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
@ -212,7 +199,6 @@ fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, pa
1 => v == 1,
_ => unreachable!(),
} {
span_ineffective_operation(cx, span, arg, parens, is_erased);
return true;
}
}

View file

@ -14,7 +14,7 @@ pub struct Context {
}
impl Context {
fn skip_expr(&mut self, e: &hir::Expr<'_>) -> bool {
self.expr_id.is_some() || self.const_span.map_or(false, |span| span.contains(e.span))
self.expr_id.is_some() || self.const_span.is_some_and(|span| span.contains(e.span))
}
pub fn check_binary<'tcx>(

View file

@ -192,7 +192,7 @@ impl PassByRefOrValue {
continue;
}
}
let value_type = if fn_body.and_then(|body| body.params.get(index)).map_or(false, is_self) {
let value_type = if fn_body.and_then(|body| body.params.get(index)).is_some_and(is_self) {
"self".into()
} else {
snippet(cx, decl_ty.span, "_").into()

View file

@ -727,9 +727,8 @@ fn get_ref_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutabili
fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let ExprKind::Call(pathexp, []) = expr.kind {
path_def_id(cx, pathexp).map_or(false, |id| {
matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))
})
path_def_id(cx, pathexp)
.is_some_and(|id| matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut)))
} else {
false
}

View file

@ -8,18 +8,18 @@ use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::{
eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor,
pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
span_contains_comment,
pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
span_contains_cfg, span_contains_comment,
};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
use rustc_hir::def::Res;
use rustc_hir::{
BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, Stmt,
StmtKind,
Arm, BindingMode, Block, Body, ByRef, Expr, ExprKind, FnRetTy, HirId, LetStmt, MatchSource, Mutability, Node, Pat,
PatKind, PathSegment, QPath, Stmt, StmtKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
use rustc_middle::ty::{self, Ty};
use rustc_session::impl_lint_pass;
use rustc_span::sym;
use rustc_span::symbol::Symbol;
@ -58,6 +58,9 @@ pub struct QuestionMark {
/// if it is greater than zero.
/// As for why we need this in the first place: <https://github.com/rust-lang/rust-clippy/issues/8628>
try_block_depth_stack: Vec<u32>,
/// Keeps track of the number of inferred return type closures we are inside, to avoid problems
/// with the `Err(x.into())` expansion being ambiguious.
inferred_ret_closure_stack: u16,
}
impl_lint_pass!(QuestionMark => [QUESTION_MARK, MANUAL_LET_ELSE]);
@ -68,6 +71,7 @@ impl QuestionMark {
msrv: conf.msrv.clone(),
matches_behaviour: conf.matches_for_let_else,
try_block_depth_stack: Vec::new(),
inferred_ret_closure_stack: 0,
}
}
}
@ -125,7 +129,7 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
cx.tcx
.lang_items()
.try_trait()
.map_or(false, |did| implements_trait(cx, init_ty, did, &[]))
.is_some_and(|did| implements_trait(cx, init_ty, did, &[]))
}
if let StmtKind::Let(LetStmt {
@ -271,6 +275,135 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex
}
}
#[derive(Clone, Copy, Debug)]
enum TryMode {
Result,
Option,
}
fn find_try_mode<'tcx>(cx: &LateContext<'tcx>, scrutinee: &Expr<'tcx>) -> Option<TryMode> {
let scrutinee_ty = cx.typeck_results().expr_ty_adjusted(scrutinee);
let ty::Adt(scrutinee_adt_def, _) = scrutinee_ty.kind() else {
return None;
};
match cx.tcx.get_diagnostic_name(scrutinee_adt_def.did())? {
sym::Result => Some(TryMode::Result),
sym::Option => Some(TryMode::Option),
_ => None,
}
}
// Check that `pat` is `{ctor_lang_item}(val)`, returning `val`.
fn extract_ctor_call<'a, 'tcx>(
cx: &LateContext<'tcx>,
expected_ctor: LangItem,
pat: &'a Pat<'tcx>,
) -> Option<&'a Pat<'tcx>> {
if let PatKind::TupleStruct(variant_path, [val_binding], _) = &pat.kind
&& is_res_lang_ctor(cx, cx.qpath_res(variant_path, pat.hir_id), expected_ctor)
{
Some(val_binding)
} else {
None
}
}
// Extracts the local ID of a plain `val` pattern.
fn extract_binding_pat(pat: &Pat<'_>) -> Option<HirId> {
if let PatKind::Binding(BindingMode::NONE, binding, _, None) = pat.kind {
Some(binding)
} else {
None
}
}
fn check_arm_is_some_or_ok<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Arm<'tcx>) -> bool {
let happy_ctor = match mode {
TryMode::Result => ResultOk,
TryMode::Option => OptionSome,
};
// Check for `Ok(val)` or `Some(val)`
if arm.guard.is_none()
&& let Some(val_binding) = extract_ctor_call(cx, happy_ctor, arm.pat)
// Extract out `val`
&& let Some(binding) = extract_binding_pat(val_binding)
// Check body is just `=> val`
&& path_to_local_id(peel_blocks(arm.body), binding)
{
true
} else {
false
}
}
fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Arm<'tcx>) -> bool {
if arm.guard.is_some() {
return false;
}
let arm_body = peel_blocks(arm.body);
match mode {
TryMode::Result => {
// Check that pat is Err(val)
if let Some(ok_pat) = extract_ctor_call(cx, ResultErr, arm.pat)
&& let Some(ok_val) = extract_binding_pat(ok_pat)
// check `=> return Err(...)`
&& let ExprKind::Ret(Some(wrapped_ret_expr)) = arm_body.kind
&& let ExprKind::Call(ok_ctor, [ret_expr]) = wrapped_ret_expr.kind
&& is_res_lang_ctor(cx, path_res(cx, ok_ctor), ResultErr)
// check `...` is `val` from binding
&& path_to_local_id(ret_expr, ok_val)
{
true
} else {
false
}
},
TryMode::Option => {
// Check the pat is `None`
if is_res_lang_ctor(cx, path_res(cx, arm.pat), OptionNone)
// Check `=> return None`
&& let ExprKind::Ret(Some(ret_expr)) = arm_body.kind
&& is_res_lang_ctor(cx, path_res(cx, ret_expr), OptionNone)
&& !ret_expr.span.from_expansion()
{
true
} else {
false
}
},
}
}
fn check_arms_are_try<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm1: &Arm<'tcx>, arm2: &Arm<'tcx>) -> bool {
(check_arm_is_some_or_ok(cx, mode, arm1) && check_arm_is_none_or_err(cx, mode, arm2))
|| (check_arm_is_some_or_ok(cx, mode, arm2) && check_arm_is_none_or_err(cx, mode, arm1))
}
fn check_if_try_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if let ExprKind::Match(scrutinee, [arm1, arm2], MatchSource::Normal | MatchSource::Postfix) = expr.kind
&& !expr.span.from_expansion()
&& let Some(mode) = find_try_mode(cx, scrutinee)
&& !span_contains_cfg(cx, expr.span)
&& check_arms_are_try(cx, mode, arm1, arm2)
{
let mut applicability = Applicability::MachineApplicable;
let snippet = snippet_with_applicability(cx, scrutinee.span.source_callsite(), "..", &mut applicability);
span_lint_and_sugg(
cx,
QUESTION_MARK,
expr.span,
"this `match` expression can be replaced with `?`",
"try instead",
snippet.into_owned() + "?",
applicability,
);
}
}
fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if let Some(higher::IfLet {
let_pat,
@ -339,6 +472,17 @@ fn is_try_block(cx: &LateContext<'_>, bl: &Block<'_>) -> bool {
}
}
fn is_inferred_ret_closure(expr: &Expr<'_>) -> bool {
let ExprKind::Closure(closure) = expr.kind else {
return false;
};
match closure.fn_decl.output {
FnRetTy::Return(ret_ty) => ret_ty.is_suggestable_infer_ty(),
FnRetTy::DefaultReturn(_) => true,
}
}
impl<'tcx> LateLintPass<'tcx> for QuestionMark {
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) {
@ -350,11 +494,27 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark {
}
self.check_manual_let_else(cx, stmt);
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if is_inferred_ret_closure(expr) {
self.inferred_ret_closure_stack += 1;
return;
}
if !self.inside_try_block() && !is_in_const_context(cx) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id)
{
check_is_none_or_err_and_early_return(cx, expr);
check_if_let_some_or_err_and_early_return(cx, expr);
if self.inferred_ret_closure_stack == 0 {
check_if_try_match(cx, expr);
}
}
}
fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if is_inferred_ret_closure(expr) {
self.inferred_ret_closure_stack -= 1;
}
}

View file

@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr));
let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed));
let parent_expr = get_parent_expr(cx, expr);
let needs_parens_for_prefix = parent_expr.map_or(false, |parent| parent.precedence().order() > PREC_PREFIX);
let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence().order() > PREC_PREFIX);
if expr_ty == indexed_ty {
if expr_ref_count > indexed_ref_count {
@ -107,7 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _),
..
})
) || cx.typeck_results().expr_adjustments(expr).first().map_or(false, |a| {
) || cx.typeck_results().expr_adjustments(expr).first().is_some_and(|a| {
matches!(
a.kind,
Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Mut { .. }))

View file

@ -412,9 +412,7 @@ impl<'tcx> IndexBinding<'_, 'tcx> {
}
Self::is_used_slice_indexed(lhs, idx_ident) || Self::is_used_slice_indexed(rhs, idx_ident)
},
ExprKind::Path(QPath::Resolved(_, path)) => {
path.segments.first().map_or(false, |idx| idx.ident == idx_ident)
},
ExprKind::Path(QPath::Resolved(_, path)) => path.segments.first().is_some_and(|idx| idx.ident == idx_ident),
_ => false,
}
}

View file

@ -51,8 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
format!("&{ltopt}({inner_snippet})")
},
TyKind::Path(qpath)
if get_bounds_if_impl_trait(cx, qpath, inner.hir_id)
.map_or(false, |bounds| bounds.len() > 1) =>
if get_bounds_if_impl_trait(cx, qpath, inner.hir_id).is_some_and(|bounds| bounds.len() > 1) =>
{
format!("&{ltopt}({inner_snippet})")
},

View file

@ -38,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) {
return;
}
if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer))
if (local.ty.is_some_and(|ty| !matches!(ty.kind, TyKind::Infer))
|| matches!(local.pat.kind, PatKind::Tuple([], ddpos) if ddpos.as_opt_usize().is_none()))
&& expr_needs_inferred_result(cx, init)
{

View file

@ -98,7 +98,7 @@ impl VecPushSearcher {
needs_mut |= cx.typeck_results().expr_ty_adjusted(last_place).ref_mutability()
== Some(Mutability::Mut)
|| get_parent_expr(cx, last_place)
.map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(_, Mutability::Mut, _)));
.is_some_and(|e| matches!(e.kind, ExprKind::AddrOf(_, Mutability::Mut, _)));
},
ExprKind::MethodCall(_, recv, ..)
if recv.hir_id == e.hir_id

View file

@ -295,7 +295,7 @@ impl<'tcx> LateLintPass<'tcx> for Write {
.opts
.crate_name
.as_ref()
.map_or(false, |crate_name| crate_name == "build_script_build");
.is_some_and(|crate_name| crate_name == "build_script_build");
let allowed_in_tests = self.allow_print_in_tests && is_in_test(cx.tcx, expr.hir_id);
match diag_name {