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

This commit is contained in:
Philipp Krones
2026-04-02 13:26:49 +02:00
48 changed files with 1972 additions and 409 deletions
+2
View File
@@ -6797,9 +6797,11 @@ Released 2018-09-13
[`manual_midpoint`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_midpoint
[`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
[`manual_noop_waker`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_noop_waker
[`manual_ok_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_err
[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
[`manual_option_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_option_as_slice
[`manual_option_zip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_option_zip
[`manual_pattern_char_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison
[`manual_pop_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pop_if
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
-1
View File
@@ -1,7 +1,6 @@
#![feature(
exit_status_error,
new_range,
new_range_api,
os_str_slice,
os_string_truncate,
pattern,
+52 -24
View File
@@ -1,7 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::numeric_literal::NumericLiteral;
use clippy_utils::res::MaybeResPath as _;
use clippy_utils::source::{SpanRangeExt, snippet_opt};
use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability};
use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::visitors::{Visitable, for_each_expr_without_closures};
use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, sym};
use rustc_ast::{LitFloatType, LitIntType, LitKind};
@@ -24,7 +25,8 @@ pub(super) fn check<'tcx>(
cast_from: Ty<'tcx>,
cast_to: Ty<'tcx>,
) -> bool {
let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
let mut app = Applicability::MachineApplicable;
let cast_str = snippet_with_applicability(cx, cast_expr.span, "_", &mut app);
if let ty::RawPtr(..) = cast_from.kind()
// check both mutability and type are the same
@@ -47,16 +49,23 @@ pub(super) fn check<'tcx>(
_ => {},
}
span_lint_and_sugg(
// Preserve parentheses around `expr` in case of cascaded casts
let surrounding =
if matches!(cast_expr.kind, ExprKind::Cast(..)) && has_enclosing_paren(snippet(cx, expr.span, "")) {
MaybeParenOrBlock::Paren
} else {
MaybeParenOrBlock::Nothing
};
emit_lint(
cx,
UNNECESSARY_CAST,
expr.span,
expr,
format!(
"casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)"
),
"try",
cast_str.clone(),
Applicability::MaybeIncorrect,
&cast_str,
surrounding,
app.max(Applicability::MaybeIncorrect),
);
}
@@ -143,12 +152,6 @@ pub(super) fn check<'tcx>(
}
if cast_from.kind() == cast_to.kind() && !expr.span.in_external_macro(cx.sess().source_map()) {
enum MaybeParenOrBlock {
Paren,
Block,
Nothing,
}
fn is_borrow_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
matches!(expr.kind, ExprKind::AddrOf(..))
|| cx
@@ -188,18 +191,13 @@ fn is_in_allowed_macro(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
_ => MaybeParenOrBlock::Nothing,
};
span_lint_and_sugg(
emit_lint(
cx,
UNNECESSARY_CAST,
expr.span,
expr,
format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"),
"try",
match surrounding {
MaybeParenOrBlock::Paren => format!("({cast_str})"),
MaybeParenOrBlock::Block => format!("{{ {cast_str} }}"),
MaybeParenOrBlock::Nothing => cast_str,
},
Applicability::MachineApplicable,
&cast_str,
surrounding,
app,
);
return true;
}
@@ -312,3 +310,33 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
})
.is_some()
}
#[derive(Clone, Copy)]
enum MaybeParenOrBlock {
Paren,
Block,
Nothing,
}
fn emit_lint(
cx: &LateContext<'_>,
expr: &Expr<'_>,
msg: String,
sugg: &str,
surrounding: MaybeParenOrBlock,
applicability: Applicability,
) {
span_lint_and_sugg(
cx,
UNNECESSARY_CAST,
expr.span,
msg,
"try",
match surrounding {
MaybeParenOrBlock::Paren => format!("({sugg})"),
MaybeParenOrBlock::Block => format!("{{ {sugg} }}"),
MaybeParenOrBlock::Nothing => sugg.to_string(),
},
applicability,
);
}
+7 -7
View File
@@ -2,7 +2,7 @@
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::msrvs::Msrv;
use clippy_utils::source::{HasSession, IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability};
use clippy_utils::{can_use_if_let_chains, span_contains_non_whitespace, sym, tokenize_with_text};
use clippy_utils::{can_use_if_let_chains, span_contains_cfg, span_contains_non_whitespace, sym, tokenize_with_text};
use rustc_ast::{BinOpKind, MetaItemInner};
use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind, StmtKind};
@@ -169,6 +169,11 @@ fn check_collapsible_if_if(&self, cx: &LateContext<'_>, expr: &Expr<'_>, check:
&& self.eligible_condition(cx, check_inner)
&& expr.span.eq_ctxt(inner.span)
&& self.check_significant_tokens_and_expect_attrs(cx, then, inner, sym::collapsible_if)
&& let then_closing_bracket = {
let end = then.span.shrink_to_hi();
end.with_lo(end.lo() - BytePos(1))
}
&& !span_contains_cfg(cx, inner.span.between(then_closing_bracket))
{
span_lint_hir_and_then(
cx,
@@ -178,12 +183,7 @@ fn check_collapsible_if_if(&self, cx: &LateContext<'_>, expr: &Expr<'_>, check:
"this `if` statement can be collapsed",
|diag| {
let then_open_bracket = then.span.split_at(1).0.with_leading_whitespace(cx).into_span();
let then_closing_bracket = {
let end = then.span.shrink_to_hi();
end.with_lo(end.lo() - BytePos(1))
.with_leading_whitespace(cx)
.into_span()
};
let then_closing_bracket = then_closing_bracket.with_leading_whitespace(cx).into_span();
let (paren_start, inner_if_span, paren_end) = peel_parens(cx, inner.span);
let inner_if = inner_if_span.split_at(2).0;
let mut sugg = vec![
+2
View File
@@ -311,6 +311,7 @@
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,
crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
crate::manual_noop_waker::MANUAL_NOOP_WAKER_INFO,
crate::manual_option_as_slice::MANUAL_OPTION_AS_SLICE_INFO,
crate::manual_pop_if::MANUAL_POP_IF_INFO,
crate::manual_range_patterns::MANUAL_RANGE_PATTERNS_INFO,
@@ -418,6 +419,7 @@
crate::methods::MANUAL_IS_VARIANT_AND_INFO,
crate::methods::MANUAL_NEXT_BACK_INFO,
crate::methods::MANUAL_OK_OR_INFO,
crate::methods::MANUAL_OPTION_ZIP_INFO,
crate::methods::MANUAL_REPEAT_N_INFO,
crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
crate::methods::MANUAL_SPLIT_ONCE_INFO,
+3 -1
View File
@@ -209,6 +209,7 @@
mod manual_let_else;
mod manual_main_separator_str;
mod manual_non_exhaustive;
mod manual_noop_waker;
mod manual_option_as_slice;
mod manual_pop_if;
mod manual_range_patterns;
@@ -864,7 +865,8 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
Box::new(move |tcx| Box::new(duration_suboptimal_units::DurationSuboptimalUnits::new(tcx, conf))),
Box::new(move |_| Box::new(manual_take::ManualTake::new(conf))),
Box::new(|_| Box::new(manual_checked_ops::ManualCheckedOps)),
Box::new(move |_| Box::new(manual_pop_if::ManualPopIf::new(conf))),
Box::new(move |tcx| Box::new(manual_pop_if::ManualPopIf::new(tcx, conf))),
Box::new(|_| Box::new(manual_noop_waker::ManualNoopWaker)),
// add late passes here, used by `cargo dev new_lint`
];
store.late_passes.extend(late_lints);
@@ -2,6 +2,7 @@
use super::{EXPLICIT_COUNTER_LOOP, IncrementVisitor, InitializeVisitor, make_iterator_snippet};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::Range;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::{EMPTY, Sugg};
use clippy_utils::{get_enclosing_block, is_integer_const, is_integer_literal_untyped};
@@ -83,6 +84,26 @@ pub(super) fn check<'tcx>(
snippet
});
// If the loop variable is unused and the range is `0..n`, suggest `(init..).take(n)`.
if pat_snippet == "_"
&& let Some(range) = Range::hir(cx, arg)
&& range.limits == RangeLimits::HalfOpen
&& range.start.is_some_and(|start| is_integer_const(cx, start, 0))
&& let Some(end) = range.end
{
let end = snippet_with_applicability(cx, end.span, "..", &mut applicability);
diag.span_suggestion(
span,
"consider using",
format!(
"{loop_label}for {name} in ({}).take({end})",
initializer.range(&EMPTY, RangeLimits::HalfOpen)
),
applicability,
);
return;
}
diag.span_suggestion(
span,
"consider using",
+2 -2
View File
@@ -385,8 +385,8 @@ fn get_offset<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>, starts: &[Start<'tcx>]
ExprKind::Binary(op, lhs, rhs) => match op.node {
BinOpKind::Add => {
let offset_opt = get_start(lhs, starts)
.and_then(|s| get_offset(cx, rhs, starts).map(|o| (s, o)))
.or_else(|| get_start(rhs, starts).and_then(|s| get_offset(cx, lhs, starts).map(|o| (s, o))));
.zip(get_offset(cx, rhs, starts))
.or_else(|| get_start(rhs, starts).zip(get_offset(cx, lhs, starts)));
offset_opt.map(|(s, o)| (s, Offset::positive(o)))
},
+9 -2
View File
@@ -91,6 +91,13 @@ enum CharRange {
impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if !matches!(
expr.kind,
ExprKind::Match(_, [_, ..], _) | ExprKind::MethodCall(_, _, [_], _)
) {
return;
}
if !self.msrv.meets(cx, msrvs::IS_ASCII_DIGIT) {
return;
}
@@ -99,8 +106,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
return;
}
let (arg, span, range) = if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro)
&& let ExprKind::Match(recv, [arm, ..], _) = expr.kind
let (arg, span, range) = if let ExprKind::Match(recv, [arm, ..], _) = expr.kind
&& let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro)
{
let recv = peel_ref_operators(cx, recv);
let range = check_pat(&arm.pat.kind);
+71
View File
@@ -0,0 +1,71 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::{is_empty_block, sym};
use rustc_hir::{ImplItemKind, Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
declare_clippy_lint! {
/// ### What it does
/// Checks for manual implementations of `std::task::Wake` that are empty.
///
/// ### Why is this bad?
/// `Waker::noop()` provides a more performant and cleaner way to create a
/// waker that does nothing, avoiding unnecessary `Arc` allocations and
/// reference count increments.
///
/// ### Example
/// ```rust
/// # use std::sync::Arc;
/// # use std::task::Wake;
/// struct MyWaker;
/// impl Wake for MyWaker {
/// fn wake(self: Arc<Self>) {}
/// fn wake_by_ref(self: &Arc<Self>) {}
/// }
/// ```
///
/// Use instead:
/// ```rust
/// use std::task::Waker;
/// let waker = Waker::noop();
/// ```
#[clippy::version = "1.96.0"]
pub MANUAL_NOOP_WAKER,
complexity,
"manual implementations of noop wakers can be simplified using Waker::noop()"
}
declare_lint_pass!(ManualNoopWaker => [MANUAL_NOOP_WAKER]);
impl<'tcx> LateLintPass<'tcx> for ManualNoopWaker {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Impl(imp) = item.kind
&& let Some(trait_ref) = imp.of_trait
&& let Some(trait_id) = trait_ref.trait_ref.trait_def_id()
&& cx.tcx.is_diagnostic_item(sym::Wake, trait_id)
{
for impl_item_ref in imp.items {
let impl_item = cx
.tcx
.hir_node_by_def_id(impl_item_ref.owner_id.def_id)
.expect_impl_item();
if let ImplItemKind::Fn(_, body_id) = &impl_item.kind {
let body = cx.tcx.hir_body(*body_id);
if !is_empty_block(body.value) {
return;
}
}
}
span_lint_and_help(
cx,
MANUAL_NOOP_WAKER,
trait_ref.trait_ref.path.span,
"manual implementation of a no-op waker",
None,
"use `std::task::Waker::noop()` instead",
);
}
}
}
+146 -89
View File
@@ -1,17 +1,19 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::MaybeDef;
use clippy_utils::source::snippet_with_context;
use clippy_utils::visitors::is_local_used;
use clippy_utils::{eq_expr_value, is_else_clause, is_lang_item_or_ctor, peel_blocks_with_stmt, sym};
use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used};
use clippy_utils::{eq_expr_value, is_else_clause, is_lang_item_or_ctor, span_contains_non_whitespace, sym};
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, PatKind, RustcVersion, StmtKind};
use rustc_errors::{Applicability, MultiSpan};
use rustc_hir::{BlockCheckMode, Expr, ExprKind, LangItem, PatKind, StmtKind, UnsafeSource};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TyCtxt;
use rustc_session::impl_lint_pass;
use rustc_span::{Span, Symbol};
use rustc_span::{BytePos, Span, Symbol};
use std::fmt;
use std::ops::ControlFlow;
declare_clippy_lint! {
/// ### What it does
@@ -21,11 +23,9 @@
/// Using `pop_if` is more concise and idiomatic.
///
/// ### Known issues
/// Currently, the lint does not handle the case where the
/// `if` condition is part of an `else if` branch.
///
/// The lint also does not handle the case where
/// the popped value is assigned and used.
/// When the popped value is assigned or used in an expression,
/// or when the `if` condition is part of an `else if` branch, the
/// lint will trigger but will not provide an automatic suggestion.
///
/// ### Examples
/// ```no_run
@@ -61,11 +61,24 @@
pub struct ManualPopIf {
msrv: Msrv,
binary_heap_pop_if_feature_enabled: bool,
}
impl ManualPopIf {
pub fn new(conf: &'static Conf) -> Self {
Self { msrv: conf.msrv }
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv,
binary_heap_pop_if_feature_enabled: tcx.features().enabled(sym::binary_heap_pop_if),
}
}
fn msrv_compatible(&self, cx: &LateContext<'_>, kind: ManualPopIfKind) -> bool {
match kind {
ManualPopIfKind::Vec => self.msrv.meets(cx, msrvs::VEC_POP_IF),
ManualPopIfKind::VecDequeBack => self.msrv.meets(cx, msrvs::VEC_DEQUE_POP_BACK_IF),
ManualPopIfKind::VecDequeFront => self.msrv.meets(cx, msrvs::VEC_DEQUE_POP_FRONT_IF),
ManualPopIfKind::BinaryHeap => self.binary_heap_pop_if_feature_enabled,
}
}
}
@@ -75,20 +88,22 @@ enum ManualPopIfKind {
Vec,
VecDequeBack,
VecDequeFront,
BinaryHeap,
}
impl ManualPopIfKind {
fn check_method(self) -> Symbol {
fn peek_method(self) -> Symbol {
match self {
ManualPopIfKind::Vec => sym::last,
ManualPopIfKind::VecDequeBack => sym::back,
ManualPopIfKind::VecDequeFront => sym::front,
ManualPopIfKind::BinaryHeap => sym::peek,
}
}
fn pop_method(self) -> Symbol {
match self {
ManualPopIfKind::Vec => sym::pop,
ManualPopIfKind::Vec | ManualPopIfKind::BinaryHeap => sym::pop,
ManualPopIfKind::VecDequeBack => sym::pop_back,
ManualPopIfKind::VecDequeFront => sym::pop_front,
}
@@ -96,7 +111,7 @@ fn pop_method(self) -> Symbol {
fn pop_if_method(self) -> Symbol {
match self {
ManualPopIfKind::Vec => sym::pop_if,
ManualPopIfKind::Vec | ManualPopIfKind::BinaryHeap => sym::pop_if,
ManualPopIfKind::VecDequeBack => sym::pop_back_if,
ManualPopIfKind::VecDequeFront => sym::pop_front_if,
}
@@ -107,14 +122,7 @@ fn is_diag_item(self, cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match self {
ManualPopIfKind::Vec => ty.is_diag_item(cx, sym::Vec),
ManualPopIfKind::VecDequeBack | ManualPopIfKind::VecDequeFront => ty.is_diag_item(cx, sym::VecDeque),
}
}
fn msrv(self) -> RustcVersion {
match self {
ManualPopIfKind::Vec => msrvs::VEC_POP_IF,
ManualPopIfKind::VecDequeBack => msrvs::VEC_DEQUE_POP_BACK_IF,
ManualPopIfKind::VecDequeFront => msrvs::VEC_DEQUE_POP_FRONT_IF,
ManualPopIfKind::BinaryHeap => ty.is_diag_item(cx, sym::BinaryHeap),
}
}
}
@@ -125,6 +133,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ManualPopIfKind::Vec => write!(f, "`Vec::pop_if`"),
ManualPopIfKind::VecDequeBack => write!(f, "`VecDeque::pop_back_if`"),
ManualPopIfKind::VecDequeFront => write!(f, "`VecDeque::pop_front_if`"),
ManualPopIfKind::BinaryHeap => write!(f, "`BinaryHeap::pop_if`"),
}
}
}
@@ -143,10 +152,18 @@ struct ManualPopIfPattern<'tcx> {
/// Span of the if expression (including the `if` keyword)
if_span: Span,
/// Span of the:
/// - check call (`vec.last().is_some_and(|x| *x > 5)`)
/// - pop+unwrap call (`vec.pop().unwrap()`)
spans: MultiSpan,
/// Whether we are able to provide a suggestion
suggestable: bool,
}
impl ManualPopIfPattern<'_> {
fn emit_lint(&self, cx: &LateContext<'_>) {
fn emit_lint(self, cx: &LateContext<'_>) {
let mut app = Applicability::MachineApplicable;
let ctxt = self.if_span.ctxt();
let collection_snippet = snippet_with_context(cx, self.collection_expr.span, ctxt, "..", &mut app).0;
@@ -154,36 +171,23 @@ fn emit_lint(&self, cx: &LateContext<'_>) {
let param_name = self.param_name;
let pop_if_method = self.kind.pop_if_method();
let suggestion = format!("{collection_snippet}.{pop_if_method}(|{param_name}| {predicate_snippet});");
span_lint_and_sugg(
span_lint_and_then(
cx,
MANUAL_POP_IF,
self.if_span,
self.spans,
format!("manual implementation of {}", self.kind),
"try",
suggestion,
app,
|diag| {
let sugg = format!("{collection_snippet}.{pop_if_method}(|{param_name}| {predicate_snippet});");
if self.suggestable {
diag.span_suggestion_verbose(self.if_span, "try", sugg, app);
} else {
diag.help(format!("try refactoring the code using `{sugg}`"));
}
},
);
}
}
fn pop_value_is_used(then_block: &Expr<'_>) -> bool {
let ExprKind::Block(block, _) = then_block.kind else {
return true;
};
if block.expr.is_some() {
return true;
}
match block.stmts {
[stmt] => !matches!(stmt.kind, StmtKind::Semi(_) | StmtKind::Item(_)),
[.., last] => matches!(last.kind, StmtKind::Expr(_)),
[] => false,
}
}
/// Checks for the pattern:
/// ```ignore
/// if vec.last().is_some_and(|x| *x > 5) {
@@ -197,21 +201,17 @@ fn check_is_some_and_pattern<'tcx>(
if_expr_span: Span,
kind: ManualPopIfKind,
) -> Option<ManualPopIfPattern<'tcx>> {
if pop_value_is_used(then_block) {
return None;
}
let check_method = kind.check_method();
let peek_method = kind.peek_method();
let pop_method = kind.pop_method();
if let ExprKind::MethodCall(path, receiver, [closure_arg], _) = cond.kind
&& path.ident.name == sym::is_some_and
&& let ExprKind::MethodCall(check_path, collection_expr, [], _) = receiver.kind
&& check_path.ident.name == check_method
&& check_path.ident.name == peek_method
&& kind.is_diag_item(cx, collection_expr)
&& let ExprKind::Closure(closure) = closure_arg.kind
&& let body = cx.tcx.hir_body(closure.body)
&& let Some((pop_collection, _pop_span)) = check_pop_unwrap(then_block, pop_method)
&& let Some((pop_collection, pop_span, suggestable)) = check_pop_unwrap(cx, then_block, pop_method)
&& eq_expr_value(cx, collection_expr, pop_collection)
&& let Some(param) = body.params.first()
&& let Some(ident) = param.pat.simple_ident()
@@ -222,6 +222,8 @@ fn check_is_some_and_pattern<'tcx>(
predicate: body.value,
param_name: ident.name,
if_span: if_expr_span,
spans: MultiSpan::from(vec![if_expr_span.with_hi(cond.span.hi()), pop_span]),
suggestable,
});
}
@@ -243,7 +245,7 @@ fn check_if_let_pattern<'tcx>(
if_expr_span: Span,
kind: ManualPopIfKind,
) -> Option<ManualPopIfPattern<'tcx>> {
let check_method = kind.check_method();
let peek_method = kind.peek_method();
let pop_method = kind.pop_method();
if let ExprKind::Let(let_expr) = cond.kind
@@ -255,7 +257,7 @@ fn check_if_let_pattern<'tcx>(
&& is_lang_item_or_ctor(cx, def_id, LangItem::OptionSome)
&& let PatKind::Binding(_, binding_id, binding_name, _) = binding_pat.kind
&& let ExprKind::MethodCall(path, collection_expr, [], _) = let_expr.init.kind
&& path.ident.name == check_method
&& path.ident.name == peek_method
&& kind.is_diag_item(cx, collection_expr)
&& let ExprKind::Block(block, _) = then_block.kind
{
@@ -271,8 +273,7 @@ fn check_if_let_pattern<'tcx>(
if let ExprKind::If(inner_cond, inner_then, None) = inner_if.kind
&& is_local_used(cx, inner_cond, binding_id)
&& !pop_value_is_used(inner_then)
&& let Some((pop_collection, _pop_span)) = check_pop_unwrap(inner_then, pop_method)
&& let Some((pop_collection, pop_span, suggestable)) = check_pop_unwrap(cx, inner_then, pop_method)
&& eq_expr_value(cx, collection_expr, pop_collection)
{
return Some(ManualPopIfPattern {
@@ -281,6 +282,12 @@ fn check_if_let_pattern<'tcx>(
predicate: inner_cond,
param_name: binding_name.name,
if_span: if_expr_span,
spans: MultiSpan::from(vec![
if_expr_span.with_hi(cond.span.hi()),
inner_if.span.with_hi(inner_cond.span.hi()),
pop_span,
]),
suggestable,
});
}
}
@@ -302,11 +309,7 @@ fn check_let_chain_pattern<'tcx>(
if_expr_span: Span,
kind: ManualPopIfKind,
) -> Option<ManualPopIfPattern<'tcx>> {
if pop_value_is_used(then_block) {
return None;
}
let check_method = kind.check_method();
let peek_method = kind.peek_method();
let pop_method = kind.pop_method();
if let ExprKind::Binary(op, left, right) = cond.kind
@@ -320,11 +323,10 @@ fn check_let_chain_pattern<'tcx>(
&& is_lang_item_or_ctor(cx, def_id, LangItem::OptionSome)
&& let PatKind::Binding(_, binding_id, binding_name, _) = binding_pat.kind
&& let ExprKind::MethodCall(path, collection_expr, [], _) = let_expr.init.kind
&& path.ident.name == check_method
&& path.ident.name == peek_method
&& kind.is_diag_item(cx, collection_expr)
&& is_local_used(cx, right, binding_id)
&& !pop_value_is_used(then_block)
&& let Some((pop_collection, _pop_span)) = check_pop_unwrap(then_block, pop_method)
&& let Some((pop_collection, pop_span, suggestable)) = check_pop_unwrap(cx, then_block, pop_method)
&& eq_expr_value(cx, collection_expr, pop_collection)
{
return Some(ManualPopIfPattern {
@@ -333,6 +335,8 @@ fn check_let_chain_pattern<'tcx>(
predicate: right,
param_name: binding_name.name,
if_span: if_expr_span,
spans: MultiSpan::from(vec![if_expr_span.with_hi(cond.span.hi()), pop_span]),
suggestable,
});
}
}
@@ -353,11 +357,7 @@ fn check_map_unwrap_or_pattern<'tcx>(
if_expr_span: Span,
kind: ManualPopIfKind,
) -> Option<ManualPopIfPattern<'tcx>> {
if pop_value_is_used(then_block) {
return None;
}
let check_method = kind.check_method();
let peek_method = kind.peek_method();
let pop_method = kind.pop_method();
if let ExprKind::MethodCall(unwrap_path, receiver, [default_arg], _) = cond.kind
@@ -366,12 +366,12 @@ fn check_map_unwrap_or_pattern<'tcx>(
&& let ExprKind::MethodCall(map_path, map_receiver, [closure_arg], _) = receiver.kind
&& map_path.ident.name == sym::map
&& let ExprKind::MethodCall(check_path, collection_expr, [], _) = map_receiver.kind
&& check_path.ident.name == check_method
&& check_path.ident.name == peek_method
&& kind.is_diag_item(cx, collection_expr)
&& let ExprKind::Closure(closure) = closure_arg.kind
&& let body = cx.tcx.hir_body(closure.body)
&& cx.typeck_results().expr_ty(body.value).is_bool()
&& let Some((pop_collection, _pop_span)) = check_pop_unwrap(then_block, pop_method)
&& let Some((pop_collection, pop_span, suggestable)) = check_pop_unwrap(cx, then_block, pop_method)
&& eq_expr_value(cx, collection_expr, pop_collection)
&& let Some(param) = body.params.first()
&& let Some(ident) = param.pat.simple_ident()
@@ -382,6 +382,8 @@ fn check_map_unwrap_or_pattern<'tcx>(
predicate: body.value,
param_name: ident.name,
if_span: if_expr_span,
spans: MultiSpan::from(vec![if_expr_span.with_hi(cond.span.hi()), pop_span]),
suggestable,
});
}
@@ -389,19 +391,72 @@ fn check_map_unwrap_or_pattern<'tcx>(
}
/// Checks for `collection.<pop_method>().unwrap()` or `collection.<pop_method>().expect(..)`
/// and returns the collection and the span of the peeled expr
fn check_pop_unwrap<'tcx>(expr: &'tcx Expr<'_>, pop_method: Symbol) -> Option<(&'tcx Expr<'tcx>, Span)> {
let inner_expr = peel_blocks_with_stmt(expr);
/// and returns the collection expression and the span of the pop+unwrap call.
/// If the pop+unwrap is the only statement in the block, the result is marked as
/// suggestable (we can provide an automatic fix).
fn check_pop_unwrap<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
pop_method: Symbol,
) -> Option<(&'tcx Expr<'tcx>, Span, bool)> {
let ExprKind::Block(block, _) = expr.kind else {
return None;
};
if let ExprKind::MethodCall(unwrap_path, receiver, _, _) = inner_expr.kind
&& matches!(unwrap_path.ident.name, sym::unwrap | sym::expect)
&& let ExprKind::MethodCall(pop_path, collection_expr, [], _) = receiver.kind
&& pop_path.ident.name == pop_method
let as_pop_unwrap = |expr: &Expr<'tcx>| -> Option<(&'tcx Expr<'tcx>, Span)> {
if let ExprKind::MethodCall(unwrap_path, receiver, _, _) = expr.kind
&& matches!(
unwrap_path.ident.name,
sym::unwrap | sym::unwrap_unchecked | sym::expect
)
&& let ExprKind::MethodCall(pop_path, collection_expr, [], _) = receiver.kind
&& pop_path.ident.name == pop_method
{
Some((collection_expr, expr.span))
} else {
None
}
};
// Peel through an `unsafe` block for `unwrap_unchecked`.
let peel_unsafe = |expr: &'tcx Expr<'tcx>| -> &'tcx Expr<'tcx> {
if let ExprKind::Block(block, _) = expr.kind
&& block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
&& block.stmts.is_empty()
&& let Some(inner) = block.expr
{
inner
} else {
expr
}
};
// Check for single statement with the pop unwrap (not in a macro or other expression)
// and that there are no comments or other text before or after the pop call.
if let [stmt] = block.stmts
&& block.expr.is_none()
&& let StmtKind::Semi(stmt_expr) | StmtKind::Expr(stmt_expr) = &stmt.kind
&& !stmt_expr.span.from_expansion()
&& let Some((collection_expr, span)) = as_pop_unwrap(peel_unsafe(stmt_expr))
{
return Some((collection_expr, inner_expr.span));
let span_before = block
.span
.with_lo(block.span.lo() + BytePos(1))
.with_hi(stmt_expr.span.lo());
let span_after = stmt.span.shrink_to_hi().with_hi(block.span.hi() - BytePos(1));
let suggestable = !span_contains_non_whitespace(cx, span_before, false)
&& !span_contains_non_whitespace(cx, span_after, false);
return Some((collection_expr, span, suggestable));
}
None
// Check if the pop unwrap is present at all
for_each_expr_without_closures(block, |expr| {
if let Some((collection_expr, span)) = as_pop_unwrap(expr) {
ControlFlow::Break((collection_expr, span, false))
} else {
ControlFlow::Continue(())
}
})
}
impl<'tcx> LateLintPass<'tcx> for ManualPopIf {
@@ -410,22 +465,24 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
return;
};
// Do not lint if we are in an else-if branch.
if is_else_clause(cx.tcx, expr) {
return;
}
let in_else_clause = is_else_clause(cx.tcx, expr);
for kind in [
ManualPopIfKind::Vec,
ManualPopIfKind::VecDequeBack,
ManualPopIfKind::VecDequeFront,
ManualPopIfKind::BinaryHeap,
] {
if let Some(pattern) = check_is_some_and_pattern(cx, cond, then_block, expr.span, kind)
if let Some(mut pattern) = check_is_some_and_pattern(cx, cond, then_block, expr.span, kind)
.or_else(|| check_if_let_pattern(cx, cond, then_block, expr.span, kind))
.or_else(|| check_let_chain_pattern(cx, cond, then_block, expr.span, kind))
.or_else(|| check_map_unwrap_or_pattern(cx, cond, then_block, expr.span, kind))
&& self.msrv.meets(cx, kind.msrv())
&& self.msrv_compatible(cx, kind)
{
if in_else_clause {
pattern.suggestable = false;
}
pattern.emit_lint(cx);
return;
}
+51 -1
View File
@@ -3,13 +3,17 @@
use clippy_utils::msrvs::Msrv;
use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet};
use clippy_utils::usage::mutated_variables;
use clippy_utils::visitors::is_local_used;
use clippy_utils::{SpanlessEq, get_ref_operators, is_unit_expr, peel_blocks_with_stmt, peel_ref_operators};
use rustc_ast::BorrowKind;
use rustc_errors::{Applicability, MultiSpan};
use rustc_hir::LangItem::OptionNone;
use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatExpr, PatExprKind, PatKind};
use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdSet, Pat, PatExpr, PatExprKind, PatKind};
use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use rustc_lint::LateContext;
use rustc_middle::mir::FakeReadCause;
use rustc_middle::ty;
use rustc_span::symbol::Ident;
use rustc_span::{BytePos, Span};
@@ -129,6 +133,7 @@ fn check_arm<'tcx>(
(None, Some(e)) | (Some(e), None) => is_unit_expr(e),
(Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b),
}
&& !pat_bindings_moved_or_mutated(cx, outer_pat, inner.cond)
{
span_lint_hir_and_then(
cx,
@@ -255,3 +260,48 @@ fn build_ref_method_chain(expr: Vec<&Expr<'_>>) -> Option<String> {
Some(req_method_calls)
}
/// Checks if any of the bindings in the `pat` are moved or mutated in the `expr`. It is invalid to
/// move or mutate bindings in `if` guards.
fn pat_bindings_moved_or_mutated<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
let mut delegate = MovedVarDelegate {
moved: HirIdSet::default(),
};
if ExprUseVisitor::for_clippy(cx, expr.hir_id.owner.def_id, &mut delegate)
.walk_expr(expr)
.is_err()
{
return true;
}
let mut candidates = delegate.moved;
if let Some(mutated) = mutated_variables(expr, cx) {
candidates.extend(mutated);
}
!pat.walk_short(|pat| {
if let PatKind::Binding(_, hir_id, ..) = pat.kind
&& candidates.contains(&hir_id)
{
return false;
}
true
})
}
struct MovedVarDelegate {
moved: HirIdSet,
}
impl<'tcx> Delegate<'tcx> for MovedVarDelegate {
fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
if let PlaceBase::Local(hir_id) = cmt.place.base {
self.moved.insert(hir_id);
}
}
fn use_cloned(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn borrow(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {}
fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
}
+2 -2
View File
@@ -247,11 +247,11 @@ fn hir(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, filter_param_id: HirId) -
}),
_ => None,
}
} else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some()
} else if let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind
// we know for a fact that the wildcard pattern is the second arm
&& let ExprKind::Match(scrutinee, [arm, _], _) = expr.kind
&& scrutinee.res_local_id() == Some(filter_param_id)
&& let PatKind::TupleStruct(QPath::Resolved(_, path), ..) = arm.pat.kind
&& matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some()
&& let Some(variant_def_id) = path.res.opt_def_id()
{
Some(OffendingFilterExpr::Matches { variant_def_id })
+7 -1
View File
@@ -48,14 +48,20 @@ pub(super) fn check<'tcx>(
if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind
&& let [local_ident] = path.segments
&& local_ident.ident.name == bound_ident.name
&& [sym::map, sym::flat_map].contains(&method_name)
{
let identity_map_equivalent = match method_name {
sym::map => "",
sym::flat_map => ".flatten()",
_ => unreachable!(),
};
span_lint_and_sugg(
cx,
ITER_KV_MAP,
expr.span,
format!("iterating on a map's {replacement_kind}s"),
"try",
format!("{recv_snippet}.{into_prefix}{replacement_kind}s()"),
format!("{recv_snippet}.{into_prefix}{replacement_kind}s(){identity_map_equivalent}"),
applicability,
);
} else {
@@ -0,0 +1,89 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::peel_blocks;
use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::visitors::for_each_expr_without_closures;
use rustc_errors::Applicability;
use rustc_hir::{self as hir, Expr, ExprKind, HirId, PatKind};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use std::ops::ControlFlow;
use super::MANUAL_OPTION_ZIP;
/// Checks for `a.and_then(|a| b.map(|b| (a, b)))` and suggests `a.zip(b)`.
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
recv: &'tcx Expr<'_>,
arg: &'tcx Expr<'_>,
msrv: Msrv,
) {
// Looking for: `a.and_then(|a| b.map(|b| (a, b)))`.
// `and_then(|a| ...)`
if let ExprKind::Closure(&hir::Closure { body: outer_body_id, .. }) = arg.kind
&& let hir::Body { params: [outer_param], value: outer_value, .. } = cx.tcx.hir_body(outer_body_id)
&& let PatKind::Binding(_, outer_param_id, _, None) = outer_param.pat.kind
&& cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option)
// `b.map(|b| ...)`
&& let ExprKind::MethodCall(method_path, map_recv, [map_arg], _) = peel_blocks(outer_value).kind
&& method_path.ident.name == sym::map
&& cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Option)
// `b` does not reference the outer closure parameter `a`.
&& for_each_expr_without_closures(map_recv, |e| {
if e.res_local_id() == Some(outer_param_id) {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}).is_none()
// `|b| (a, b)`
&& let ExprKind::Closure(&hir::Closure { body: inner_body_id, .. }) = map_arg.kind
&& let hir::Body { params: [inner_param], value: inner_value, .. } = cx.tcx.hir_body(inner_body_id)
&& let PatKind::Binding(_, inner_param_id, _, None) = inner_param.pat.kind
// `(a, b)` or `(b, a)` — tuple of outer and inner param in either order.
&& let ExprKind::Tup([first, second]) = peel_blocks(inner_value).kind
&& let Some((zip_recv, zip_arg)) = zip_operands(first, second, outer_param_id, inner_param_id, recv, map_recv)
// `Option.zip()` is available.
&& msrv.meets(cx, msrvs::OPTION_ZIP)
{
let mut applicability = Applicability::MachineApplicable;
let zip_recv_snip = snippet_with_applicability(cx, zip_recv.span, "_", &mut applicability);
let zip_arg_snip = snippet_with_applicability(cx, zip_arg.span, "_", &mut applicability);
let suggestion = format!("{zip_recv_snip}.zip({zip_arg_snip})");
span_lint_and_sugg(
cx,
MANUAL_OPTION_ZIP,
expr.span,
"manual implementation of `Option::zip`",
"use",
suggestion,
applicability,
);
}
}
/// Given the two tuple elements and the `and_then` receiver / `map` receiver, returns the
/// `(zip_receiver, zip_argument)` expressions for the `.zip()` suggestion.
///
/// For `(outer, inner)` order the zip is `recv.zip(map_recv)`.
/// For `(inner, outer)` (reversed) the zip is `map_recv.zip(recv)`.
/// Returns `None` if the tuple elements don't match either order.
fn zip_operands<'a>(
first: &Expr<'_>,
second: &Expr<'_>,
outer_param_id: HirId,
inner_param_id: HirId,
recv: &'a Expr<'a>,
map_recv: &'a Expr<'a>,
) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
if first.res_local_id() == Some(outer_param_id) && second.res_local_id() == Some(inner_param_id) {
Some((recv, map_recv))
} else if first.res_local_id() == Some(inner_param_id) && second.res_local_id() == Some(outer_param_id) {
Some((map_recv, recv))
} else {
None
}
}
+31
View File
@@ -63,6 +63,7 @@
mod manual_is_variant_and;
mod manual_next_back;
mod manual_ok_or;
mod manual_option_zip;
mod manual_repeat_n;
mod manual_saturating_arithmetic;
mod manual_str_repeat;
@@ -1950,6 +1951,34 @@
"finds patterns that can be encoded more concisely with `Option::ok_or`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `a.and_then(|a| b.map(|b| (a, b)))` which can be
/// more concisely expressed as `a.zip(b)`.
///
/// ### Why is this bad?
/// `Option::zip` is more concise and directly expresses the intent of
/// combining two `Option` values into a tuple.
///
/// ### Example
/// ```no_run
/// let a: Option<i32> = Some(1);
/// let b: Option<i32> = Some(2);
/// let _ = a.and_then(|x| b.map(|y| (x, y)));
/// ```
///
/// Use instead:
/// ```no_run
/// let a: Option<i32> = Some(1);
/// let b: Option<i32> = Some(2);
/// let _ = a.zip(b);
/// ```
#[clippy::version = "1.95.0"]
pub MANUAL_OPTION_ZIP,
complexity,
"manual reimplementation of `Option::zip`"
}
declare_clippy_lint! {
/// ### What it does
///
@@ -4814,6 +4843,7 @@
MANUAL_IS_VARIANT_AND,
MANUAL_NEXT_BACK,
MANUAL_OK_OR,
MANUAL_OPTION_ZIP,
MANUAL_REPEAT_N,
MANUAL_SATURATING_ARITHMETIC,
MANUAL_SPLIT_ONCE,
@@ -5115,6 +5145,7 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
}
},
(sym::and_then, [arg]) => {
manual_option_zip::check(cx, expr, recv, arg, self.msrv);
let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg);
let biom_result_linted = bind_instead_of_map::check_and_then_ok(cx, expr, recv, arg);
if !biom_option_linted && !biom_result_linted {
@@ -105,10 +105,6 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
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)
&& adj
+12 -2
View File
@@ -52,7 +52,11 @@
declare_clippy_lint! {
/// ### What it does
/// Checks for names that are very similar and thus confusing.
/// Checks for names that are very similar and thus confusing. In particular,
/// the lint checks for names with a single character change.
///
/// It does not warn about names that have a single additional character at
/// the beginning nor the end; only insertions in the middle are considered.
///
/// Note: this lint looks for similar names throughout each
/// scope. To allow it, you need to allow it on the scope
@@ -65,7 +69,13 @@
/// ### Example
/// ```ignore
/// let checked_exp = something;
/// let checked_expr = something_else;
/// let checked_eap = something_else;
/// ```
///
/// ### Example 2
/// ```ignore
/// let orange = val;
/// let ornange = val2;
/// ```
#[clippy::version = "pre 1.29.0"]
pub SIMILAR_NAMES,
+2 -2
View File
@@ -77,8 +77,8 @@ fn emit_lint(cx: &LateContext<'_>, span: Span, kind: &str, note: &'static str, s
/// Checks `vec![Vec::with_capacity(x); n]`
fn check_vec_macro(cx: &LateContext<'_>, expr: &Expr<'_>) {
if matching_root_macro_call(cx, expr.span, sym::vec_macro).is_some()
&& let Some(VecArgs::Repeat(repeat_expr, len_expr)) = VecArgs::hir(cx, expr)
if let Some(VecArgs::Repeat(repeat_expr, len_expr)) = VecArgs::hir(cx, expr)
&& matching_root_macro_call(cx, expr.span, sym::vec_macro).is_some()
&& fn_def_id(cx, repeat_expr).is_some_and(|did| cx.tcx.is_diagnostic_item(sym::vec_with_capacity, did))
&& !len_expr.span.from_expansion()
&& let Some(Constant::Int(2..)) = ConstEvalCtxt::new(cx).eval(expr_or_init(cx, len_expr))
+2 -2
View File
@@ -166,9 +166,9 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr<
},
ExprKind::Binary(op, _, _) if op.node == BinOpKind::Or => ControlFlow::Continue(Descend::Yes),
ExprKind::Match(match_value, [arm, _], _) => {
if matching_root_macro_call(cx, sub_expr.span, sym::matches_macro).is_none()
|| arm.guard.is_some()
if arm.guard.is_some()
|| match_value.res_local_id() != Some(binding)
|| matching_root_macro_call(cx, sub_expr.span, sym::matches_macro).is_none()
{
return ControlFlow::Break(());
}
+1 -15
View File
@@ -4,7 +4,7 @@
use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::expr_type_is_certain;
use clippy_utils::{is_expr_default, is_from_proc_macro};
use clippy_utils::{is_empty_block, is_expr_default, is_from_proc_macro};
use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind, MatchSource, Node, StmtKind};
use rustc_lint::LateContext;
@@ -205,20 +205,6 @@ fn is_block_with_no_expr(expr: &Expr<'_>) -> bool {
matches!(expr.kind, ExprKind::Block(Block { expr: None, .. }, _))
}
fn is_empty_block(expr: &Expr<'_>) -> bool {
matches!(
expr.kind,
ExprKind::Block(
Block {
stmts: [],
expr: None,
..
},
_,
)
)
}
fn fmt_stmts_and_call(
cx: &LateContext<'_>,
call_expr: &Expr<'_>,
+15
View File
@@ -311,6 +311,21 @@ pub fn as_some_expr<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Optio
}
}
/// Check if the given `Expr` is an empty block (i.e. `{}`) or not.
pub fn is_empty_block(expr: &Expr<'_>) -> bool {
matches!(
expr.kind,
ExprKind::Block(
Block {
stmts: [],
expr: None,
..
},
_,
)
)
}
/// Checks if `expr` is an empty block or an empty tuple.
pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
matches!(
+1 -1
View File
@@ -60,7 +60,7 @@ macro_rules! msrv_aliases {
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
1,50,0 { BOOL_THEN, CLAMP, SLICE_FILL }
1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN, SATURATING_SUB_CONST }
1,46,0 { CONST_IF_MATCH }
1,46,0 { CONST_IF_MATCH, OPTION_ZIP }
1,45,0 { STR_STRIP_PREFIX }
1,43,0 { LOG2_10, LOG10_2, NUMERIC_ASSOCIATED_CONSTANTS }
1,42,0 { MATCHES_MACRO, SLICE_PATTERNS, PTR_SLICE_RAW_PARTS }
+2
View File
@@ -133,6 +133,8 @@ macro_rules! $name {
macro_path: PathNS::Macro,
}
// Paths in the standard library missing a diagnostic item
// Paths in external crates
pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt);
pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt);
+2
View File
@@ -122,6 +122,7 @@ macro_rules! generate {
V6,
VecDeque,
Visitor,
Wake,
Waker,
Weak,
Wrapping,
@@ -141,6 +142,7 @@ macro_rules! generate {
assert_failed,
author,
back,
binary_heap_pop_if,
binaryheap_iter,
bool_then,
borrow,
+1 -1
View File
@@ -21,7 +21,7 @@ rayon = "1.5.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.85"
strip-ansi-escapes = "0.2.0"
tar = "0.4"
tar = "0.4.45"
toml = "0.9.7"
ureq = { version = "2.2", features = ["json"] }
walkdir = "2.3"
+16
View File
@@ -18,3 +18,19 @@ fn issue13365() {
}
//~^^^^ ERROR: this lint expectation is unfulfilled
}
#[allow(unexpected_cfgs)]
fn issue16715(o: Option<i32>) {
if let Some(x) = o {
if x > 0 {
println!("Positive: {}", x);
}
#[cfg(feature = "some_feature")]
{
if x % 2 == 0 {
println!("Even: {}", x);
}
}
}
}
+55
View File
@@ -389,3 +389,58 @@ fn foo<T, U>(t: T) -> U {
fn take<T>(t: T) {}
fn main() {}
fn issue16705(x: Option<String>) {
fn takes_ownership(s: String) -> bool {
true
}
fn borrows_mut(s: &mut str) -> bool {
true
}
let _ = match x {
Some(val) => {
if takes_ownership(val) {
return;
} else {
false
}
},
_ => false,
};
let mut x: Option<&mut str> = Some(&mut String::new());
let _ = match x {
Some(val) => {
if borrows_mut(val) {
return;
} else {
false
}
},
_ => false,
};
let mut x = Some(String::new());
let _ = match x {
Some(ref mut val) => {
if borrows_mut(val) {
return;
} else {
false
}
},
_ => false,
};
let _ = match &mut x {
Some(val) => {
if borrows_mut(val) {
return;
} else {
false
}
},
_ => false,
};
}
+31
View File
@@ -332,3 +332,34 @@ fn add_assign(&mut self, rhs: u8) {
priority += 1;
}
}
pub fn issue_16642() {
let mut base = 100;
const MAX: usize = 10;
for _ in 0..MAX {
//~^ explicit_counter_loop
base += 1;
}
let mut base = 100;
let nums = vec![1, 2, 3, 4];
for _ in nums {
//~^ explicit_counter_loop
base += 1;
}
// inclusive range: should not suggest .take()
let mut base = 100;
for _ in 0..=MAX {
//~^ explicit_counter_loop
base += 1;
}
// non-zero start: should not suggest .take(), falls through to zip
let mut base = 100;
for _ in 5..MAX {
//~^ explicit_counter_loop
base += 1;
}
}
+25 -1
View File
@@ -81,5 +81,29 @@ error: the variable `j` is used as a loop counter
LL | for item in &v {
| ^^^^^^^^^^^^^^ help: consider using: `for (j, item) in (s + 1..).zip(v.iter())`
error: aborting due to 13 previous errors
error: the variable `base` is used as a loop counter
--> tests/ui/explicit_counter_loop.rs:339:5
|
LL | for _ in 0..MAX {
| ^^^^^^^^^^^^^^^ help: consider using: `for base in (100..).take(MAX)`
error: the variable `base` is used as a loop counter
--> tests/ui/explicit_counter_loop.rs:347:5
|
LL | for _ in nums {
| ^^^^^^^^^^^^^ help: consider using: `for (base, _) in (100..).zip(nums.into_iter())`
error: the variable `base` is used as a loop counter
--> tests/ui/explicit_counter_loop.rs:354:5
|
LL | for _ in 0..=MAX {
| ^^^^^^^^^^^^^^^^ help: consider using: `for (base, _) in (100..).zip((0..=MAX))`
error: the variable `base` is used as a loop counter
--> tests/ui/explicit_counter_loop.rs:361:5
|
LL | for _ in 5..MAX {
| ^^^^^^^^^^^^^^^ help: consider using: `for (base, _) in (100..).zip((5..MAX))`
error: aborting due to 17 previous errors
+12
View File
@@ -231,3 +231,15 @@ fn issue16515() {
hash_map.into_values().filter_map(|v| (v > 0).then_some(1));
//~^ iter_kv_map
}
fn issue16742() {
let map: HashMap<u32, Vec<u32>> = HashMap::new();
map.values().flat_map(|v| v.iter().map(|i| *i + 1));
//~^ iter_kv_map
map.values().flatten();
//~^ iter_kv_map
let map: HashMap<u32, Vec<u32>> = HashMap::new();
map.into_values().flatten();
//~^ iter_kv_map
}
+12
View File
@@ -235,3 +235,15 @@ fn issue16515() {
hash_map.into_iter().filter_map(|(_, v)| (v > 0).then_some(1));
//~^ iter_kv_map
}
fn issue16742() {
let map: HashMap<u32, Vec<u32>> = HashMap::new();
map.iter().flat_map(|(_, v)| v.iter().map(|i| *i + 1));
//~^ iter_kv_map
map.iter().flat_map(|(_, v)| v);
//~^ iter_kv_map
let map: HashMap<u32, Vec<u32>> = HashMap::new();
map.into_iter().flat_map(|(_, v)| v);
//~^ iter_kv_map
}
+19 -1
View File
@@ -323,5 +323,23 @@ error: iterating on a map's values
LL | hash_map.into_iter().filter_map(|(_, v)| (v > 0).then_some(1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `hash_map.into_values().filter_map(|v| (v > 0).then_some(1))`
error: aborting due to 48 previous errors
error: iterating on a map's values
--> tests/ui/iter_kv_map.rs:241:5
|
LL | map.iter().flat_map(|(_, v)| v.iter().map(|i| *i + 1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().flat_map(|v| v.iter().map(|i| *i + 1))`
error: iterating on a map's values
--> tests/ui/iter_kv_map.rs:243:5
|
LL | map.iter().flat_map(|(_, v)| v);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().flatten()`
error: iterating on a map's values
--> tests/ui/iter_kv_map.rs:247:5
|
LL | map.into_iter().flat_map(|(_, v)| v);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.into_values().flatten()`
error: aborting due to 51 previous errors
+40
View File
@@ -0,0 +1,40 @@
#![warn(clippy::manual_noop_waker)]
use std::sync::Arc;
use std::task::Wake;
struct PartialWaker;
impl Wake for PartialWaker {
//~^ ERROR: manual implementation of a no-op waker
fn wake(self: Arc<Self>) {}
}
struct MyWakerPartial;
impl Wake for MyWakerPartial {
//~^ manual_noop_waker
fn wake(self: Arc<Self>) {}
// wake_by_ref not implemented, uses default
}
trait CustomWake {
fn wake(self);
}
impl CustomWake for () {
fn wake(self) {}
}
mod custom_module {
use std::sync::Arc;
// Custom Wake trait that should NOT trigger the lint
pub trait Wake {
fn wake(self: Arc<Self>);
fn wake_by_ref(self: &Arc<Self>);
}
pub struct CustomWaker;
impl Wake for CustomWaker {
fn wake(self: Arc<Self>) {}
fn wake_by_ref(self: &Arc<Self>) {}
}
}
+20
View File
@@ -0,0 +1,20 @@
error: manual implementation of a no-op waker
--> tests/ui/manual_noop_waker.rs:6:6
|
LL | impl Wake for PartialWaker {
| ^^^^
|
= help: use `std::task::Waker::noop()` instead
= note: `-D clippy::manual-noop-waker` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::manual_noop_waker)]`
error: manual implementation of a no-op waker
--> tests/ui/manual_noop_waker.rs:12:6
|
LL | impl Wake for MyWakerPartial {
| ^^^^
|
= help: use `std::task::Waker::noop()` instead
error: aborting due to 2 previous errors
+118
View File
@@ -0,0 +1,118 @@
#![warn(clippy::manual_option_zip)]
#![allow(clippy::bind_instead_of_map)]
fn main() {}
fn should_lint() {
// basic case
let a: Option<i32> = Some(1);
let b: Option<i32> = Some(2);
let _ = a.zip(b);
//~^ manual_option_zip
// different types
let a: Option<String> = Some(String::new());
let b: Option<i32> = Some(1);
let _ = a.zip(b);
//~^ manual_option_zip
// with None receiver
let b: Option<i32> = Some(2);
let _ = None::<i32>.zip(b);
//~^ manual_option_zip
// with function call as map receiver
let a: Option<i32> = Some(1);
let _ = a.zip(get_option());
//~^ manual_option_zip
// tuple order reversed: (inner, outer) instead of (outer, inner)
let a: Option<i32> = Some(1);
let b: Option<i32> = Some(2);
let _ = b.zip(a);
//~^ manual_option_zip
// closure bodies wrapped in blocks
let a: Option<i32> = Some(1);
let b: Option<i32> = Some(2);
#[rustfmt::skip]
let _ = a.zip(b);
//~^ manual_option_zip
#[rustfmt::skip]
let _ = a.zip(b);
//~^ manual_option_zip
#[rustfmt::skip]
let _ = a.zip(b);
//~^ manual_option_zip
}
fn should_not_lint() {
let a: Option<i32> = Some(1);
let b: Option<i32> = Some(2);
// tuple has more than 2 elements
let _ = a.and_then(|a| b.map(|b| (a, b, 1)));
// three-element tuple but with either `a` or `b` as the elements
let _ = a.and_then(|a| b.map(|b| (a, b, a)));
// inner closure body is not a simple tuple of the params
let _ = a.and_then(|a| b.map(|b| (a, b + 1)));
// map receiver uses the outer closure parameter
let _ = a.and_then(|a| a.checked_add(1).map(|b| (a, b)));
// .map receiver is not an Option type.
let _ = a.and_then(|a| NotOption(Some(1)).map(|b| (a, b)));
// .and_then receiver is not an Option type.
let _ = NotOption(Some(1)).and_then(|a| b.map(|b| (a, b)));
// closure body is not a map call
let a: Option<i32> = Some(1);
let _ = a.and_then(|a| Some((a, 1)));
// single-element tuple
let _ = a.and_then(|a| b.map(|_b| (a,)));
// the outer param used in the map receiver (cannot extract)
let opts: Vec<Option<i32>> = vec![Some(1), Some(2)];
let _ = a.and_then(|a| opts[a as usize].map(|b| (a, b)));
// extra statements in outer closure body
let _ = a.and_then(|a| {
let _x = 1;
b.map(|b| (a, b))
});
// extra statements in inner closure body
let _ = a.and_then(|a| {
b.map(|b| {
let _x = 1;
(a, b)
})
});
// n-ary zip where n > 2, which is out of scope for this lint (for now)
let c: Option<i32> = Some(3);
let _ = a.and_then(|a| b.and_then(|b| c.map(|c| (a, b, c))));
// not Option type (Result)
let a: Result<i32, &str> = Ok(1);
let b: Result<i32, &str> = Ok(2);
let _ = a.and_then(|a| b.map(|b| (a, b)));
}
fn get_option() -> Option<i32> {
Some(123)
}
struct NotOption(Option<i32>);
impl NotOption {
fn map<U>(self, f: impl FnOnce(i32) -> U) -> Option<U> {
self.0.map(f)
}
fn and_then<U>(self, f: impl FnOnce(i32) -> Option<U>) -> Option<U> {
self.0.and_then(f)
}
}
+118
View File
@@ -0,0 +1,118 @@
#![warn(clippy::manual_option_zip)]
#![allow(clippy::bind_instead_of_map)]
fn main() {}
fn should_lint() {
// basic case
let a: Option<i32> = Some(1);
let b: Option<i32> = Some(2);
let _ = a.and_then(|a| b.map(|b| (a, b)));
//~^ manual_option_zip
// different types
let a: Option<String> = Some(String::new());
let b: Option<i32> = Some(1);
let _ = a.and_then(|a| b.map(|b| (a, b)));
//~^ manual_option_zip
// with None receiver
let b: Option<i32> = Some(2);
let _ = None::<i32>.and_then(|a| b.map(|b| (a, b)));
//~^ manual_option_zip
// with function call as map receiver
let a: Option<i32> = Some(1);
let _ = a.and_then(|a| get_option().map(|b| (a, b)));
//~^ manual_option_zip
// tuple order reversed: (inner, outer) instead of (outer, inner)
let a: Option<i32> = Some(1);
let b: Option<i32> = Some(2);
let _ = a.and_then(|a| b.map(|b| (b, a)));
//~^ manual_option_zip
// closure bodies wrapped in blocks
let a: Option<i32> = Some(1);
let b: Option<i32> = Some(2);
#[rustfmt::skip]
let _ = a.and_then(|a| { b.map(|b| (a, b)) });
//~^ manual_option_zip
#[rustfmt::skip]
let _ = a.and_then(|a| b.map(|b| { (a, b) }));
//~^ manual_option_zip
#[rustfmt::skip]
let _ = a.and_then(|a| { b.map(|b| { (a, b) }) });
//~^ manual_option_zip
}
fn should_not_lint() {
let a: Option<i32> = Some(1);
let b: Option<i32> = Some(2);
// tuple has more than 2 elements
let _ = a.and_then(|a| b.map(|b| (a, b, 1)));
// three-element tuple but with either `a` or `b` as the elements
let _ = a.and_then(|a| b.map(|b| (a, b, a)));
// inner closure body is not a simple tuple of the params
let _ = a.and_then(|a| b.map(|b| (a, b + 1)));
// map receiver uses the outer closure parameter
let _ = a.and_then(|a| a.checked_add(1).map(|b| (a, b)));
// .map receiver is not an Option type.
let _ = a.and_then(|a| NotOption(Some(1)).map(|b| (a, b)));
// .and_then receiver is not an Option type.
let _ = NotOption(Some(1)).and_then(|a| b.map(|b| (a, b)));
// closure body is not a map call
let a: Option<i32> = Some(1);
let _ = a.and_then(|a| Some((a, 1)));
// single-element tuple
let _ = a.and_then(|a| b.map(|_b| (a,)));
// the outer param used in the map receiver (cannot extract)
let opts: Vec<Option<i32>> = vec![Some(1), Some(2)];
let _ = a.and_then(|a| opts[a as usize].map(|b| (a, b)));
// extra statements in outer closure body
let _ = a.and_then(|a| {
let _x = 1;
b.map(|b| (a, b))
});
// extra statements in inner closure body
let _ = a.and_then(|a| {
b.map(|b| {
let _x = 1;
(a, b)
})
});
// n-ary zip where n > 2, which is out of scope for this lint (for now)
let c: Option<i32> = Some(3);
let _ = a.and_then(|a| b.and_then(|b| c.map(|c| (a, b, c))));
// not Option type (Result)
let a: Result<i32, &str> = Ok(1);
let b: Result<i32, &str> = Ok(2);
let _ = a.and_then(|a| b.map(|b| (a, b)));
}
fn get_option() -> Option<i32> {
Some(123)
}
struct NotOption(Option<i32>);
impl NotOption {
fn map<U>(self, f: impl FnOnce(i32) -> U) -> Option<U> {
self.0.map(f)
}
fn and_then<U>(self, f: impl FnOnce(i32) -> Option<U>) -> Option<U> {
self.0.and_then(f)
}
}
+53
View File
@@ -0,0 +1,53 @@
error: manual implementation of `Option::zip`
--> tests/ui/manual_option_zip.rs:10:13
|
LL | let _ = a.and_then(|a| b.map(|b| (a, b)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)`
|
= note: `-D clippy::manual-option-zip` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::manual_option_zip)]`
error: manual implementation of `Option::zip`
--> tests/ui/manual_option_zip.rs:16:13
|
LL | let _ = a.and_then(|a| b.map(|b| (a, b)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)`
error: manual implementation of `Option::zip`
--> tests/ui/manual_option_zip.rs:21:13
|
LL | let _ = None::<i32>.and_then(|a| b.map(|b| (a, b)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `None::<i32>.zip(b)`
error: manual implementation of `Option::zip`
--> tests/ui/manual_option_zip.rs:26:13
|
LL | let _ = a.and_then(|a| get_option().map(|b| (a, b)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(get_option())`
error: manual implementation of `Option::zip`
--> tests/ui/manual_option_zip.rs:32:13
|
LL | let _ = a.and_then(|a| b.map(|b| (b, a)));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.zip(a)`
error: manual implementation of `Option::zip`
--> tests/ui/manual_option_zip.rs:39:13
|
LL | let _ = a.and_then(|a| { b.map(|b| (a, b)) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)`
error: manual implementation of `Option::zip`
--> tests/ui/manual_option_zip.rs:42:13
|
LL | let _ = a.and_then(|a| b.map(|b| { (a, b) }));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)`
error: manual implementation of `Option::zip`
--> tests/ui/manual_option_zip.rs:45:13
|
LL | let _ = a.and_then(|a| { b.map(|b| { (a, b) }) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)`
error: aborting due to 8 previous errors
+46 -49
View File
@@ -1,9 +1,12 @@
#![warn(clippy::manual_pop_if)]
#![allow(clippy::collapsible_if, clippy::redundant_closure)]
#![feature(binary_heap_pop_if)]
use std::collections::VecDeque;
use std::collections::{BinaryHeap, VecDeque};
use std::marker::PhantomData;
fn main() {}
// FakeVec has the same methods as Vec but isn't actually a Vec
struct FakeVec<T>(PhantomData<T>);
@@ -17,14 +20,24 @@ impl<T> FakeVec<T> {
}
}
fn is_some_and_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
fn is_some_and_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>, mut heap: BinaryHeap<i32>) {
//~v manual_pop_if
vec.pop_if(|x| *x > 2);
//~v manual_pop_if
vec.pop_if(|x| *x > 2);
//~v manual_pop_if
vec.pop_if(|x| *x > 2);
//~v manual_pop_if
deque.pop_back_if(|x| *x > 2);
//~v manual_pop_if
deque.pop_front_if(|x| *x > 2);
//~v manual_pop_if
heap.pop_if(|x| *x > 2);
}
fn is_some_and_pattern_negative(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
@@ -40,24 +53,6 @@ fn is_some_and_pattern_negative(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
fake_vec.pop().unwrap();
}
// Do not lint, else-if branch
if false {
// something
} else if vec.last().is_some_and(|x| *x > 2) {
vec.pop().unwrap();
}
// Do not lint, value used in let binding
if vec.last().is_some_and(|x| *x > 2) {
let _value = vec.pop().unwrap();
println!("Popped: {}", _value);
}
// Do not lint, value used in expression
if vec.last().is_some_and(|x| *x > 2) {
println!("Popped: {}", vec.pop().unwrap());
}
// Do not lint, else block
let _result = if vec.last().is_some_and(|x| *x > 2) {
vec.pop().unwrap()
@@ -66,14 +61,24 @@ fn is_some_and_pattern_negative(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
};
}
fn if_let_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
fn if_let_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>, mut heap: BinaryHeap<i32>) {
//~v manual_pop_if
vec.pop_if(|x| *x > 2);
//~v manual_pop_if
vec.pop_if(|x| *x > 2);
//~v manual_pop_if
vec.pop_if(|x| *x > 2);
//~v manual_pop_if
deque.pop_back_if(|x| *x > 2);
//~v manual_pop_if
deque.pop_front_if(|x| *x > 2);
//~v manual_pop_if
heap.pop_if(|x| *x > 2);
}
fn if_let_pattern_negative(mut vec: Vec<i32>) {
@@ -100,13 +105,6 @@ fn if_let_pattern_negative(mut vec: Vec<i32>) {
}
}
// Do not lint, value used in let binding
if let Some(x) = vec.last() {
if *x > 2 {
let _val = vec.pop().unwrap();
}
}
// Do not lint, else block
let _result = if let Some(x) = vec.last() {
if *x > 2 { vec.pop().unwrap() } else { 0 }
@@ -115,7 +113,9 @@ fn if_let_pattern_negative(mut vec: Vec<i32>) {
};
}
fn let_chain_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
fn let_chain_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>, mut heap: BinaryHeap<i32>) {
vec.pop_if(|x| *x > 2);
vec.pop_if(|x| *x > 2);
vec.pop_if(|x| *x > 2);
@@ -123,6 +123,8 @@ fn let_chain_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
deque.pop_back_if(|x| *x > 2);
deque.pop_front_if(|x| *x > 2);
heap.pop_if(|x| *x > 2);
}
fn let_chain_pattern_negative(mut vec: Vec<i32>) {
@@ -141,20 +143,6 @@ fn let_chain_pattern_negative(mut vec: Vec<i32>) {
vec.pop().unwrap();
}
// Do not lint, value used in let binding
if let Some(x) = vec.last()
&& *x > 2
{
let _val = vec.pop().unwrap();
}
// Do not lint, value used in expression
if let Some(x) = vec.last()
&& *x > 2
{
println!("Popped: {}", vec.pop().unwrap());
}
// Do not lint, else block
let _result = if let Some(x) = vec.last()
&& *x > 2
@@ -165,14 +153,24 @@ fn let_chain_pattern_negative(mut vec: Vec<i32>) {
};
}
fn map_unwrap_or_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
fn map_unwrap_or_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>, mut heap: BinaryHeap<i32>) {
//~v manual_pop_if
vec.pop_if(|x| *x > 2);
//~v manual_pop_if
vec.pop_if(|x| *x > 2);
//~v manual_pop_if
vec.pop_if(|x| *x > 2);
//~v manual_pop_if
deque.pop_back_if(|x| *x > 2);
//~v manual_pop_if
deque.pop_front_if(|x| *x > 2);
//~v manual_pop_if
heap.pop_if(|x| *x > 2);
}
fn map_unwrap_or_pattern_negative(mut vec: Vec<i32>) {
@@ -198,11 +196,6 @@ fn map_unwrap_or_pattern_negative(mut vec: Vec<i32>) {
vec.pop().unwrap();
}
// Do not lint, value used in let binding
if vec.last().map(|x| *x > 2).unwrap_or(false) {
let _val = vec.pop().unwrap();
}
// Do not lint, else block
let _result = if vec.last().map(|x| *x > 2).unwrap_or(false) {
vec.pop().unwrap()
@@ -213,6 +206,7 @@ fn map_unwrap_or_pattern_negative(mut vec: Vec<i32>) {
// this makes sure we do not expand vec![] in the suggestion
fn handle_macro_in_closure(mut vec: Vec<Vec<i32>>) {
//~v manual_pop_if
vec.pop_if(|e| *e == vec![1]);
}
@@ -236,12 +230,15 @@ fn msrv_too_low_vecdeque(mut deque: VecDeque<i32>) {
#[clippy::msrv = "1.86.0"]
fn msrv_high_enough_vec(mut vec: Vec<i32>) {
//~v manual_pop_if
vec.pop_if(|x| *x > 2);
}
#[clippy::msrv = "1.93.0"]
fn msrv_high_enough_vecdeque(mut deque: VecDeque<i32>) {
//~v manual_pop_if
deque.pop_back_if(|x| *x > 2);
//~v manual_pop_if
deque.pop_front_if(|x| *x > 2);
}
+70 -65
View File
@@ -1,9 +1,12 @@
#![warn(clippy::manual_pop_if)]
#![allow(clippy::collapsible_if, clippy::redundant_closure)]
#![feature(binary_heap_pop_if)]
use std::collections::VecDeque;
use std::collections::{BinaryHeap, VecDeque};
use std::marker::PhantomData;
fn main() {}
// FakeVec has the same methods as Vec but isn't actually a Vec
struct FakeVec<T>(PhantomData<T>);
@@ -17,26 +20,36 @@ fn pop(&mut self) -> Option<T> {
}
}
fn is_some_and_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
fn is_some_and_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>, mut heap: BinaryHeap<i32>) {
//~v manual_pop_if
if vec.last().is_some_and(|x| *x > 2) {
//~^ manual_pop_if
vec.pop().unwrap();
}
//~v manual_pop_if
if vec.last().is_some_and(|x| *x > 2) {
unsafe { vec.pop().unwrap_unchecked() };
}
//~v manual_pop_if
if vec.last().is_some_and(|x| *x > 2) {
//~^ manual_pop_if
vec.pop().expect("element");
}
//~v manual_pop_if
if deque.back().is_some_and(|x| *x > 2) {
//~^ manual_pop_if
deque.pop_back().unwrap();
}
//~v manual_pop_if
if deque.front().is_some_and(|x| *x > 2) {
//~^ manual_pop_if
deque.pop_front().unwrap();
}
//~v manual_pop_if
if heap.peek().is_some_and(|x| *x > 2) {
heap.pop().unwrap();
}
}
fn is_some_and_pattern_negative(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
@@ -52,24 +65,6 @@ fn is_some_and_pattern_negative(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
fake_vec.pop().unwrap();
}
// Do not lint, else-if branch
if false {
// something
} else if vec.last().is_some_and(|x| *x > 2) {
vec.pop().unwrap();
}
// Do not lint, value used in let binding
if vec.last().is_some_and(|x| *x > 2) {
let _value = vec.pop().unwrap();
println!("Popped: {}", _value);
}
// Do not lint, value used in expression
if vec.last().is_some_and(|x| *x > 2) {
println!("Popped: {}", vec.pop().unwrap());
}
// Do not lint, else block
let _result = if vec.last().is_some_and(|x| *x > 2) {
vec.pop().unwrap()
@@ -78,34 +73,48 @@ fn is_some_and_pattern_negative(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
};
}
fn if_let_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
fn if_let_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>, mut heap: BinaryHeap<i32>) {
//~v manual_pop_if
if let Some(x) = vec.last() {
//~^ manual_pop_if
if *x > 2 {
vec.pop().unwrap();
}
}
//~v manual_pop_if
if let Some(x) = vec.last() {
if *x > 2 {
unsafe { vec.pop().unwrap_unchecked() };
}
}
//~v manual_pop_if
if let Some(x) = vec.last() {
//~^ manual_pop_if
if *x > 2 {
vec.pop().expect("element");
}
}
//~v manual_pop_if
if let Some(x) = deque.back() {
//~^ manual_pop_if
if *x > 2 {
deque.pop_back().unwrap();
}
}
//~v manual_pop_if
if let Some(x) = deque.front() {
//~^ manual_pop_if
if *x > 2 {
deque.pop_front().unwrap();
}
}
//~v manual_pop_if
if let Some(x) = heap.peek() {
if *x > 2 {
heap.pop().unwrap();
}
}
}
fn if_let_pattern_negative(mut vec: Vec<i32>) {
@@ -132,13 +141,6 @@ fn if_let_pattern_negative(mut vec: Vec<i32>) {
}
}
// Do not lint, value used in let binding
if let Some(x) = vec.last() {
if *x > 2 {
let _val = vec.pop().unwrap();
}
}
// Do not lint, else block
let _result = if let Some(x) = vec.last() {
if *x > 2 { vec.pop().unwrap() } else { 0 }
@@ -147,13 +149,19 @@ fn if_let_pattern_negative(mut vec: Vec<i32>) {
};
}
fn let_chain_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
fn let_chain_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>, mut heap: BinaryHeap<i32>) {
if let Some(x) = vec.last() //~ manual_pop_if
&& *x > 2
{
vec.pop().unwrap();
}
if let Some(x) = vec.last() //~ manual_pop_if
&& *x > 2
{
unsafe { vec.pop().unwrap_unchecked() };
}
if let Some(x) = vec.last() //~ manual_pop_if
&& *x > 2
{
@@ -171,6 +179,12 @@ fn let_chain_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
{
deque.pop_front().unwrap();
}
if let Some(x) = heap.peek() //~ manual_pop_if
&& *x > 2
{
heap.pop().unwrap();
}
}
fn let_chain_pattern_negative(mut vec: Vec<i32>) {
@@ -189,20 +203,6 @@ fn let_chain_pattern_negative(mut vec: Vec<i32>) {
vec.pop().unwrap();
}
// Do not lint, value used in let binding
if let Some(x) = vec.last()
&& *x > 2
{
let _val = vec.pop().unwrap();
}
// Do not lint, value used in expression
if let Some(x) = vec.last()
&& *x > 2
{
println!("Popped: {}", vec.pop().unwrap());
}
// Do not lint, else block
let _result = if let Some(x) = vec.last()
&& *x > 2
@@ -213,26 +213,36 @@ fn let_chain_pattern_negative(mut vec: Vec<i32>) {
};
}
fn map_unwrap_or_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
fn map_unwrap_or_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>, mut heap: BinaryHeap<i32>) {
//~v manual_pop_if
if vec.last().map(|x| *x > 2).unwrap_or(false) {
//~^ manual_pop_if
vec.pop().unwrap();
}
//~v manual_pop_if
if vec.last().map(|x| *x > 2).unwrap_or(false) {
unsafe { vec.pop().unwrap_unchecked() };
}
//~v manual_pop_if
if vec.last().map(|x| *x > 2).unwrap_or(false) {
//~^ manual_pop_if
vec.pop().expect("element");
}
//~v manual_pop_if
if deque.back().map(|x| *x > 2).unwrap_or(false) {
//~^ manual_pop_if
deque.pop_back().unwrap();
}
//~v manual_pop_if
if deque.front().map(|x| *x > 2).unwrap_or(false) {
//~^ manual_pop_if
deque.pop_front().unwrap();
}
//~v manual_pop_if
if heap.peek().map(|x| *x > 2).unwrap_or(false) {
heap.pop().unwrap();
}
}
fn map_unwrap_or_pattern_negative(mut vec: Vec<i32>) {
@@ -258,11 +268,6 @@ fn map_unwrap_or_pattern_negative(mut vec: Vec<i32>) {
vec.pop().unwrap();
}
// Do not lint, value used in let binding
if vec.last().map(|x| *x > 2).unwrap_or(false) {
let _val = vec.pop().unwrap();
}
// Do not lint, else block
let _result = if vec.last().map(|x| *x > 2).unwrap_or(false) {
vec.pop().unwrap()
@@ -273,8 +278,8 @@ fn map_unwrap_or_pattern_negative(mut vec: Vec<i32>) {
// this makes sure we do not expand vec![] in the suggestion
fn handle_macro_in_closure(mut vec: Vec<Vec<i32>>) {
//~v manual_pop_if
if vec.last().is_some_and(|e| *e == vec![1]) {
//~^ manual_pop_if
vec.pop().unwrap();
}
}
@@ -299,21 +304,21 @@ fn msrv_too_low_vecdeque(mut deque: VecDeque<i32>) {
#[clippy::msrv = "1.86.0"]
fn msrv_high_enough_vec(mut vec: Vec<i32>) {
//~v manual_pop_if
if vec.last().is_some_and(|x| *x > 2) {
//~^ manual_pop_if
vec.pop().unwrap();
}
}
#[clippy::msrv = "1.93.0"]
fn msrv_high_enough_vecdeque(mut deque: VecDeque<i32>) {
//~v manual_pop_if
if deque.back().is_some_and(|x| *x > 2) {
//~^ manual_pop_if
deque.pop_back().unwrap();
}
//~v manual_pop_if
if deque.front().is_some_and(|x| *x > 2) {
//~^ manual_pop_if
deque.pop_front().unwrap();
}
}
+428 -125
View File
@@ -1,197 +1,500 @@
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if.rs:21:5
--> tests/ui/manual_pop_if.rs:25:5
|
LL | / if vec.last().is_some_and(|x| *x > 2) {
LL | |
LL | | vec.pop().unwrap();
LL | | }
| |_____^ help: try: `vec.pop_if(|x| *x > 2);`
LL | if vec.last().is_some_and(|x| *x > 2) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::manual-pop-if` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::manual_pop_if)]`
help: try
|
LL - if vec.last().is_some_and(|x| *x > 2) {
LL - vec.pop().unwrap();
LL - }
LL + vec.pop_if(|x| *x > 2);
|
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if.rs:26:5
--> tests/ui/manual_pop_if.rs:30:5
|
LL | if vec.last().is_some_and(|x| *x > 2) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | unsafe { vec.pop().unwrap_unchecked() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if vec.last().is_some_and(|x| *x > 2) {
LL - unsafe { vec.pop().unwrap_unchecked() };
LL - }
LL + vec.pop_if(|x| *x > 2);
|
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if.rs:35:5
|
LL | if vec.last().is_some_and(|x| *x > 2) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | vec.pop().expect("element");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if vec.last().is_some_and(|x| *x > 2) {
LL - vec.pop().expect("element");
LL - }
LL + vec.pop_if(|x| *x > 2);
|
LL | / if vec.last().is_some_and(|x| *x > 2) {
LL | |
LL | | vec.pop().expect("element");
LL | | }
| |_____^ help: try: `vec.pop_if(|x| *x > 2);`
error: manual implementation of `VecDeque::pop_back_if`
--> tests/ui/manual_pop_if.rs:31:5
--> tests/ui/manual_pop_if.rs:40:5
|
LL | if deque.back().is_some_and(|x| *x > 2) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | deque.pop_back().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if deque.back().is_some_and(|x| *x > 2) {
LL - deque.pop_back().unwrap();
LL - }
LL + deque.pop_back_if(|x| *x > 2);
|
LL | / if deque.back().is_some_and(|x| *x > 2) {
LL | |
LL | | deque.pop_back().unwrap();
LL | | }
| |_____^ help: try: `deque.pop_back_if(|x| *x > 2);`
error: manual implementation of `VecDeque::pop_front_if`
--> tests/ui/manual_pop_if.rs:36:5
--> tests/ui/manual_pop_if.rs:45:5
|
LL | if deque.front().is_some_and(|x| *x > 2) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | deque.pop_front().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if deque.front().is_some_and(|x| *x > 2) {
LL - deque.pop_front().unwrap();
LL - }
LL + deque.pop_front_if(|x| *x > 2);
|
error: manual implementation of `BinaryHeap::pop_if`
--> tests/ui/manual_pop_if.rs:50:5
|
LL | if heap.peek().is_some_and(|x| *x > 2) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | heap.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if heap.peek().is_some_and(|x| *x > 2) {
LL - heap.pop().unwrap();
LL - }
LL + heap.pop_if(|x| *x > 2);
|
LL | / if deque.front().is_some_and(|x| *x > 2) {
LL | |
LL | | deque.pop_front().unwrap();
LL | | }
| |_____^ help: try: `deque.pop_front_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if.rs:82:5
--> tests/ui/manual_pop_if.rs:78:5
|
LL | if let Some(x) = vec.last() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | if *x > 2 {
| ^^^^^^^^^
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if let Some(x) = vec.last() {
LL - if *x > 2 {
LL - vec.pop().unwrap();
LL - }
LL - }
LL + vec.pop_if(|x| *x > 2);
|
LL | / if let Some(x) = vec.last() {
LL | |
LL | | if *x > 2 {
LL | | vec.pop().unwrap();
LL | | }
LL | | }
| |_____^ help: try: `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if.rs:89:5
--> tests/ui/manual_pop_if.rs:85:5
|
LL | if let Some(x) = vec.last() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | if *x > 2 {
| ^^^^^^^^^
LL | unsafe { vec.pop().unwrap_unchecked() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if let Some(x) = vec.last() {
LL - if *x > 2 {
LL - unsafe { vec.pop().unwrap_unchecked() };
LL - }
LL - }
LL + vec.pop_if(|x| *x > 2);
|
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if.rs:92:5
|
LL | if let Some(x) = vec.last() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | if *x > 2 {
| ^^^^^^^^^
LL | vec.pop().expect("element");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if let Some(x) = vec.last() {
LL - if *x > 2 {
LL - vec.pop().expect("element");
LL - }
LL - }
LL + vec.pop_if(|x| *x > 2);
|
LL | / if let Some(x) = vec.last() {
LL | |
LL | | if *x > 2 {
LL | | vec.pop().expect("element");
LL | | }
LL | | }
| |_____^ help: try: `vec.pop_if(|x| *x > 2);`
error: manual implementation of `VecDeque::pop_back_if`
--> tests/ui/manual_pop_if.rs:96:5
--> tests/ui/manual_pop_if.rs:99:5
|
LL | if let Some(x) = deque.back() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | if *x > 2 {
| ^^^^^^^^^
LL | deque.pop_back().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if let Some(x) = deque.back() {
LL - if *x > 2 {
LL - deque.pop_back().unwrap();
LL - }
LL - }
LL + deque.pop_back_if(|x| *x > 2);
|
LL | / if let Some(x) = deque.back() {
LL | |
LL | | if *x > 2 {
LL | | deque.pop_back().unwrap();
LL | | }
LL | | }
| |_____^ help: try: `deque.pop_back_if(|x| *x > 2);`
error: manual implementation of `VecDeque::pop_front_if`
--> tests/ui/manual_pop_if.rs:103:5
--> tests/ui/manual_pop_if.rs:106:5
|
LL | if let Some(x) = deque.front() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | if *x > 2 {
| ^^^^^^^^^
LL | deque.pop_front().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if let Some(x) = deque.front() {
LL - if *x > 2 {
LL - deque.pop_front().unwrap();
LL - }
LL - }
LL + deque.pop_front_if(|x| *x > 2);
|
error: manual implementation of `BinaryHeap::pop_if`
--> tests/ui/manual_pop_if.rs:113:5
|
LL | if let Some(x) = heap.peek() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | if *x > 2 {
| ^^^^^^^^^
LL | heap.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if let Some(x) = heap.peek() {
LL - if *x > 2 {
LL - heap.pop().unwrap();
LL - }
LL - }
LL + heap.pop_if(|x| *x > 2);
|
LL | / if let Some(x) = deque.front() {
LL | |
LL | | if *x > 2 {
LL | | deque.pop_front().unwrap();
LL | | }
LL | | }
| |_____^ help: try: `deque.pop_front_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if.rs:151:5
--> tests/ui/manual_pop_if.rs:153:5
|
LL | / if let Some(x) = vec.last()
LL | | && *x > 2
LL | | {
LL | | vec.pop().unwrap();
LL | | }
| |_____^ help: try: `vec.pop_if(|x| *x > 2);`
| |_________________^
LL | {
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if let Some(x) = vec.last()
LL - && *x > 2
LL - {
LL - vec.pop().unwrap();
LL - }
LL + vec.pop_if(|x| *x > 2);
|
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if.rs:157:5
--> tests/ui/manual_pop_if.rs:159:5
|
LL | / if let Some(x) = vec.last()
LL | | && *x > 2
LL | | {
LL | | vec.pop().expect("element");
LL | | }
| |_____^ help: try: `vec.pop_if(|x| *x > 2);`
| |_________________^
LL | {
LL | unsafe { vec.pop().unwrap_unchecked() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if let Some(x) = vec.last()
LL - && *x > 2
LL - {
LL - unsafe { vec.pop().unwrap_unchecked() };
LL - }
LL + vec.pop_if(|x| *x > 2);
|
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if.rs:165:5
|
LL | / if let Some(x) = vec.last()
LL | | && *x > 2
| |_________________^
LL | {
LL | vec.pop().expect("element");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if let Some(x) = vec.last()
LL - && *x > 2
LL - {
LL - vec.pop().expect("element");
LL - }
LL + vec.pop_if(|x| *x > 2);
|
error: manual implementation of `VecDeque::pop_back_if`
--> tests/ui/manual_pop_if.rs:163:5
--> tests/ui/manual_pop_if.rs:171:5
|
LL | / if let Some(x) = deque.back()
LL | | && *x > 2
LL | | {
LL | | deque.pop_back().unwrap();
LL | | }
| |_____^ help: try: `deque.pop_back_if(|x| *x > 2);`
| |_________________^
LL | {
LL | deque.pop_back().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if let Some(x) = deque.back()
LL - && *x > 2
LL - {
LL - deque.pop_back().unwrap();
LL - }
LL + deque.pop_back_if(|x| *x > 2);
|
error: manual implementation of `VecDeque::pop_front_if`
--> tests/ui/manual_pop_if.rs:169:5
--> tests/ui/manual_pop_if.rs:177:5
|
LL | / if let Some(x) = deque.front()
LL | | && *x > 2
LL | | {
LL | | deque.pop_front().unwrap();
LL | | }
| |_____^ help: try: `deque.pop_front_if(|x| *x > 2);`
| |_________________^
LL | {
LL | deque.pop_front().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if let Some(x) = deque.front()
LL - && *x > 2
LL - {
LL - deque.pop_front().unwrap();
LL - }
LL + deque.pop_front_if(|x| *x > 2);
|
error: manual implementation of `BinaryHeap::pop_if`
--> tests/ui/manual_pop_if.rs:183:5
|
LL | / if let Some(x) = heap.peek()
LL | | && *x > 2
| |_________________^
LL | {
LL | heap.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if let Some(x) = heap.peek()
LL - && *x > 2
LL - {
LL - heap.pop().unwrap();
LL - }
LL + heap.pop_if(|x| *x > 2);
|
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if.rs:217:5
--> tests/ui/manual_pop_if.rs:218:5
|
LL | if vec.last().map(|x| *x > 2).unwrap_or(false) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if vec.last().map(|x| *x > 2).unwrap_or(false) {
LL - vec.pop().unwrap();
LL - }
LL + vec.pop_if(|x| *x > 2);
|
LL | / if vec.last().map(|x| *x > 2).unwrap_or(false) {
LL | |
LL | | vec.pop().unwrap();
LL | | }
| |_____^ help: try: `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if.rs:222:5
--> tests/ui/manual_pop_if.rs:223:5
|
LL | if vec.last().map(|x| *x > 2).unwrap_or(false) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | unsafe { vec.pop().unwrap_unchecked() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if vec.last().map(|x| *x > 2).unwrap_or(false) {
LL - unsafe { vec.pop().unwrap_unchecked() };
LL - }
LL + vec.pop_if(|x| *x > 2);
|
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if.rs:228:5
|
LL | if vec.last().map(|x| *x > 2).unwrap_or(false) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | vec.pop().expect("element");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if vec.last().map(|x| *x > 2).unwrap_or(false) {
LL - vec.pop().expect("element");
LL - }
LL + vec.pop_if(|x| *x > 2);
|
LL | / if vec.last().map(|x| *x > 2).unwrap_or(false) {
LL | |
LL | | vec.pop().expect("element");
LL | | }
| |_____^ help: try: `vec.pop_if(|x| *x > 2);`
error: manual implementation of `VecDeque::pop_back_if`
--> tests/ui/manual_pop_if.rs:227:5
--> tests/ui/manual_pop_if.rs:233:5
|
LL | if deque.back().map(|x| *x > 2).unwrap_or(false) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | deque.pop_back().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if deque.back().map(|x| *x > 2).unwrap_or(false) {
LL - deque.pop_back().unwrap();
LL - }
LL + deque.pop_back_if(|x| *x > 2);
|
LL | / if deque.back().map(|x| *x > 2).unwrap_or(false) {
LL | |
LL | | deque.pop_back().unwrap();
LL | | }
| |_____^ help: try: `deque.pop_back_if(|x| *x > 2);`
error: manual implementation of `VecDeque::pop_front_if`
--> tests/ui/manual_pop_if.rs:232:5
--> tests/ui/manual_pop_if.rs:238:5
|
LL | if deque.front().map(|x| *x > 2).unwrap_or(false) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | deque.pop_front().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if deque.front().map(|x| *x > 2).unwrap_or(false) {
LL - deque.pop_front().unwrap();
LL - }
LL + deque.pop_front_if(|x| *x > 2);
|
error: manual implementation of `BinaryHeap::pop_if`
--> tests/ui/manual_pop_if.rs:243:5
|
LL | if heap.peek().map(|x| *x > 2).unwrap_or(false) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | heap.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if heap.peek().map(|x| *x > 2).unwrap_or(false) {
LL - heap.pop().unwrap();
LL - }
LL + heap.pop_if(|x| *x > 2);
|
LL | / if deque.front().map(|x| *x > 2).unwrap_or(false) {
LL | |
LL | | deque.pop_front().unwrap();
LL | | }
| |_____^ help: try: `deque.pop_front_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if.rs:276:5
--> tests/ui/manual_pop_if.rs:282:5
|
LL | if vec.last().is_some_and(|e| *e == vec![1]) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if vec.last().is_some_and(|e| *e == vec![1]) {
LL - vec.pop().unwrap();
LL - }
LL + vec.pop_if(|e| *e == vec![1]);
|
LL | / if vec.last().is_some_and(|e| *e == vec![1]) {
LL | |
LL | | vec.pop().unwrap();
LL | | }
| |_____^ help: try: `vec.pop_if(|e| *e == vec![1]);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if.rs:302:5
--> tests/ui/manual_pop_if.rs:308:5
|
LL | if vec.last().is_some_and(|x| *x > 2) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if vec.last().is_some_and(|x| *x > 2) {
LL - vec.pop().unwrap();
LL - }
LL + vec.pop_if(|x| *x > 2);
|
LL | / if vec.last().is_some_and(|x| *x > 2) {
LL | |
LL | | vec.pop().unwrap();
LL | | }
| |_____^ help: try: `vec.pop_if(|x| *x > 2);`
error: manual implementation of `VecDeque::pop_back_if`
--> tests/ui/manual_pop_if.rs:310:5
--> tests/ui/manual_pop_if.rs:316:5
|
LL | if deque.back().is_some_and(|x| *x > 2) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | deque.pop_back().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if deque.back().is_some_and(|x| *x > 2) {
LL - deque.pop_back().unwrap();
LL - }
LL + deque.pop_back_if(|x| *x > 2);
|
LL | / if deque.back().is_some_and(|x| *x > 2) {
LL | |
LL | | deque.pop_back().unwrap();
LL | | }
| |_____^ help: try: `deque.pop_back_if(|x| *x > 2);`
error: manual implementation of `VecDeque::pop_front_if`
--> tests/ui/manual_pop_if.rs:315:5
--> tests/ui/manual_pop_if.rs:321:5
|
LL | if deque.front().is_some_and(|x| *x > 2) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | deque.pop_front().unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: try
|
LL - if deque.front().is_some_and(|x| *x > 2) {
LL - deque.pop_front().unwrap();
LL - }
LL + deque.pop_front_if(|x| *x > 2);
|
LL | / if deque.front().is_some_and(|x| *x > 2) {
LL | |
LL | | deque.pop_front().unwrap();
LL | | }
| |_____^ help: try: `deque.pop_front_if(|x| *x > 2);`
error: aborting due to 20 previous errors
error: aborting due to 28 previous errors
+128
View File
@@ -0,0 +1,128 @@
#![warn(clippy::manual_pop_if)]
#![allow(clippy::collapsible_if, clippy::redundant_closure)]
//@no-rustfix
fn main() {}
fn is_some_and_pattern(mut vec: Vec<i32>) {
if false {
// something
} else if vec.last().is_some_and(|x| *x > 2) {
vec.pop().unwrap();
}
//~^^^ manual_pop_if
//~v manual_pop_if
if vec.last().is_some_and(|x| *x > 2) {
let val = vec.pop().unwrap();
println!("Popped: {}", val);
}
//~v manual_pop_if
if vec.last().is_some_and(|x| *x > 2) {
println!("Popped: {}", vec.pop().unwrap());
}
//~v manual_pop_if
if vec.last().is_some_and(|x| *x > 2) {
// a comment before the pop
vec.pop().unwrap();
}
//~v manual_pop_if
if vec.last().is_some_and(|x| *x > 2) {
vec.pop().unwrap();
// a comment after the pop
}
}
fn if_let_pattern(mut vec: Vec<i32>) {
//~v manual_pop_if
if let Some(x) = vec.last() {
if *x > 2 {
let val = vec.pop().unwrap();
println!("Popped: {}", val);
}
}
//~v manual_pop_if
if let Some(x) = vec.last() {
if *x > 2 {
println!("Popped: {}", vec.pop().unwrap());
}
}
//~v manual_pop_if
if let Some(x) = vec.last() {
if *x > 2 {
// a comment before the pop
vec.pop().unwrap();
}
}
//~v manual_pop_if
if let Some(x) = vec.last() {
if *x > 2 {
vec.pop().unwrap();
// a comment after the pop
}
}
}
fn let_chain_pattern(mut vec: Vec<i32>) {
//~v manual_pop_if
if let Some(x) = vec.last()
&& *x > 2
{
let val = vec.pop().unwrap();
println!("Popped: {}", val);
}
//~v manual_pop_if
if let Some(x) = vec.last()
&& *x > 2
{
println!("Popped: {}", vec.pop().unwrap());
}
//~v manual_pop_if
if let Some(x) = vec.last()
&& *x > 2
{
// a comment before the pop
vec.pop().unwrap();
}
//~v manual_pop_if
if let Some(x) = vec.last()
&& *x > 2
{
vec.pop().unwrap();
// a comment after the pop
}
}
fn map_unwrap_or_pattern(mut vec: Vec<i32>) {
//~v manual_pop_if
if vec.last().map(|x| *x > 2).unwrap_or(false) {
let val = vec.pop().unwrap();
println!("Popped: {}", val);
}
//~v manual_pop_if
if vec.last().map(|x| *x > 2).unwrap_or(false) {
println!("Popped: {}", vec.pop().unwrap());
}
//~v manual_pop_if
if vec.last().map(|x| *x > 2).unwrap_or(false) {
// a comment before the pop
vec.pop().unwrap();
}
//~v manual_pop_if
if vec.last().map(|x| *x > 2).unwrap_or(false) {
vec.pop().unwrap();
// a comment after the pop
}
}
+193
View File
@@ -0,0 +1,193 @@
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:10:12
|
LL | } else if vec.last().is_some_and(|x| *x > 2) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
= note: `-D clippy::manual-pop-if` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::manual_pop_if)]`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:16:5
|
LL | if vec.last().is_some_and(|x| *x > 2) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let val = vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:22:5
|
LL | if vec.last().is_some_and(|x| *x > 2) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | println!("Popped: {}", vec.pop().unwrap());
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:27:5
|
LL | if vec.last().is_some_and(|x| *x > 2) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | // a comment before the pop
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:33:5
|
LL | if vec.last().is_some_and(|x| *x > 2) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:41:5
|
LL | if let Some(x) = vec.last() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | if *x > 2 {
| ^^^^^^^^^
LL | let val = vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:49:5
|
LL | if let Some(x) = vec.last() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | if *x > 2 {
| ^^^^^^^^^
LL | println!("Popped: {}", vec.pop().unwrap());
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:56:5
|
LL | if let Some(x) = vec.last() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | if *x > 2 {
| ^^^^^^^^^
LL | // a comment before the pop
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:64:5
|
LL | if let Some(x) = vec.last() {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | if *x > 2 {
| ^^^^^^^^^
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:74:5
|
LL | / if let Some(x) = vec.last()
LL | | && *x > 2
| |_________________^
LL | {
LL | let val = vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:82:5
|
LL | / if let Some(x) = vec.last()
LL | | && *x > 2
| |_________________^
LL | {
LL | println!("Popped: {}", vec.pop().unwrap());
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:89:5
|
LL | / if let Some(x) = vec.last()
LL | | && *x > 2
| |_________________^
...
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:97:5
|
LL | / if let Some(x) = vec.last()
LL | | && *x > 2
| |_________________^
LL | {
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:107:5
|
LL | if vec.last().map(|x| *x > 2).unwrap_or(false) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let val = vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:113:5
|
LL | if vec.last().map(|x| *x > 2).unwrap_or(false) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | println!("Popped: {}", vec.pop().unwrap());
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:118:5
|
LL | if vec.last().map(|x| *x > 2).unwrap_or(false) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | // a comment before the pop
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: manual implementation of `Vec::pop_if`
--> tests/ui/manual_pop_if_unfixable.rs:124:5
|
LL | if vec.last().map(|x| *x > 2).unwrap_or(false) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | vec.pop().unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= help: try refactoring the code using `vec.pop_if(|x| *x > 2);`
error: aborting due to 17 previous errors
+11 -1
View File
@@ -47,11 +47,18 @@ fn main() {
let bluby: i32;
//~^ similar_names
let cake: i32;
let orange: i32;
let ornange: i32;
//~^ similar_names
let cakes: i32;
let cake: i32;
let coke: i32;
//~^ similar_names
let wagon: i32;
let twagon: i32;
match 5 {
cheese @ 1 => {},
rabbit => panic!(),
@@ -93,6 +100,9 @@ fn main() {
// 3 letter names are allowed to be similar
let kta: i32;
let ktv: i32;
let checked_exp: i32;
let checked_expr: i32;
}
fn foo() {
+21 -9
View File
@@ -13,52 +13,64 @@ LL | let blubx: i32;
= help: to override `-D warnings` add `#[allow(clippy::similar_names)]`
error: binding's name is too similar to existing binding
--> tests/ui/similar_names.rs:52:9
--> tests/ui/similar_names.rs:51:9
|
LL | let ornange: i32;
| ^^^^^^^
|
note: existing binding defined here
--> tests/ui/similar_names.rs:50:9
|
LL | let orange: i32;
| ^^^^^^
error: binding's name is too similar to existing binding
--> tests/ui/similar_names.rs:56:9
|
LL | let coke: i32;
| ^^^^
|
note: existing binding defined here
--> tests/ui/similar_names.rs:50:9
--> tests/ui/similar_names.rs:55:9
|
LL | let cake: i32;
| ^^^^
error: binding's name is too similar to existing binding
--> tests/ui/similar_names.rs:71:9
--> tests/ui/similar_names.rs:78:9
|
LL | let xyzeabc: i32;
| ^^^^^^^
|
note: existing binding defined here
--> tests/ui/similar_names.rs:69:9
--> tests/ui/similar_names.rs:76:9
|
LL | let xyz1abc: i32;
| ^^^^^^^
error: binding's name is too similar to existing binding
--> tests/ui/similar_names.rs:76:9
--> tests/ui/similar_names.rs:83:9
|
LL | let parsee: i32;
| ^^^^^^
|
note: existing binding defined here
--> tests/ui/similar_names.rs:74:9
--> tests/ui/similar_names.rs:81:9
|
LL | let parser: i32;
| ^^^^^^
error: binding's name is too similar to existing binding
--> tests/ui/similar_names.rs:102:16
--> tests/ui/similar_names.rs:112:16
|
LL | bpple: sprang,
| ^^^^^^
|
note: existing binding defined here
--> tests/ui/similar_names.rs:101:16
--> tests/ui/similar_names.rs:111:16
|
LL | apple: spring,
| ^^^^^^
error: aborting due to 5 previous errors
error: aborting due to 6 previous errors
+8
View File
@@ -285,3 +285,11 @@ mod fixable {
//~^ unnecessary_cast
}
}
fn issue16475() -> *const u8 {
static NONE: Option<((), &'static u8)> = None;
unsafe {
*(&NONE as *const _ as *const _ as *const *const u8)
//~^ unnecessary_cast
}
}
+8
View File
@@ -285,3 +285,11 @@ fn issue_14640() {
//~^ unnecessary_cast
}
}
fn issue16475() -> *const u8 {
static NONE: Option<((), &'static u8)> = None;
unsafe {
*(&NONE as *const _ as *const _ as *const *const u8 as *const *const u8)
//~^ unnecessary_cast
}
}
+7 -1
View File
@@ -277,5 +277,11 @@ error: casting to the same type is unnecessary (`i64` -> `i64`)
LL | let _ = 5i32 as i64 as i64;
| ^^^^^^^^^^^^^^^^^^ help: try: `5i32 as i64`
error: aborting due to 46 previous errors
error: casting raw pointers to the same type and constness is unnecessary (`*const *const u8` -> `*const *const u8`)
--> tests/ui/unnecessary_cast.rs:292:10
|
LL | *(&NONE as *const _ as *const _ as *const *const u8 as *const *const u8)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&NONE as *const _ as *const _ as *const *const u8)`
error: aborting due to 47 previous errors