Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
b5bf09e57a
156 changed files with 2118 additions and 522 deletions
41
clippy_lints/src/methods/double_ended_iterator_last.rs
Normal file
41
clippy_lints/src/methods/double_ended_iterator_last.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
use super::DOUBLE_ENDED_ITERATOR_LAST;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Expr<'_>, call_span: Span) {
|
||||
let typeck = cx.typeck_results();
|
||||
|
||||
// if the "last" method is that of Iterator
|
||||
if is_trait_method(cx, expr, sym::Iterator)
|
||||
// if self implements DoubleEndedIterator
|
||||
&& let Some(deiter_id) = cx.tcx.get_diagnostic_item(sym::DoubleEndedIterator)
|
||||
&& let self_type = cx.typeck_results().expr_ty(self_expr)
|
||||
&& implements_trait(cx, self_type.peel_refs(), deiter_id, &[])
|
||||
// resolve the method definition
|
||||
&& let id = typeck.type_dependent_def_id(expr.hir_id).unwrap()
|
||||
&& let args = typeck.node_args(expr.hir_id)
|
||||
&& let Ok(Some(fn_def)) = Instance::try_resolve(cx.tcx, cx.typing_env(), id, args)
|
||||
// find the provided definition of Iterator::last
|
||||
&& let Some(item) = cx.tcx.get_diagnostic_item(sym::Iterator)
|
||||
&& let Some(last_def) = cx.tcx.provided_trait_methods(item).find(|m| m.name.as_str() == "last")
|
||||
// if the resolved method is the same as the provided definition
|
||||
&& fn_def.def_id() == last_def.def_id
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
DOUBLE_ENDED_ITERATOR_LAST,
|
||||
call_span,
|
||||
"called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator",
|
||||
"try",
|
||||
"next_back()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_trait_method, span_contains_comment};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -17,10 +17,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_
|
|||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
let closure_snippet = snippet_with_applicability(cx, map_arg.span, "..", &mut applicability);
|
||||
let span = expr.span.with_lo(map_span.lo());
|
||||
// If the methods are separated with comments, we don't apply suggestion automatically.
|
||||
if span_contains_comment(cx.tcx.sess.source_map(), span) {
|
||||
applicability = Applicability::Unspecified;
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_FLATTEN,
|
||||
expr.span.with_lo(map_span.lo()),
|
||||
span,
|
||||
format!("called `map(..).flatten()` on `{caller_ty_name}`"),
|
||||
format!("try replacing `map` with `{method_to_use}` and remove the `.flatten()`"),
|
||||
format!("{method_to_use}({closure_snippet})"),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_expr_untyped_identity_function, is_trait_method};
|
||||
use clippy_utils::{is_expr_untyped_identity_function, is_trait_method, path_to_local};
|
||||
use rustc_ast::BindingMode;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{self as hir, Node, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
|
|
@ -24,6 +25,16 @@ pub(super) fn check(
|
|||
&& is_expr_untyped_identity_function(cx, map_arg)
|
||||
&& let Some(sugg_span) = expr.span.trim_start(caller.span)
|
||||
{
|
||||
// If the result of `.map(identity)` is used as a mutable reference,
|
||||
// the caller must not be an immutable binding.
|
||||
if cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr()
|
||||
&& let Some(hir_id) = path_to_local(caller)
|
||||
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
|
||||
&& !matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_IDENTITY,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ mod clone_on_copy;
|
|||
mod clone_on_ref_ptr;
|
||||
mod cloned_instead_of_copied;
|
||||
mod collapsible_str_replace;
|
||||
mod double_ended_iterator_last;
|
||||
mod drain_collect;
|
||||
mod err_expect;
|
||||
mod expect_fun_call;
|
||||
|
|
@ -4284,6 +4285,32 @@ declare_clippy_lint! {
|
|||
"map of a trivial closure (not dependent on parameter) over a range"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Checks for `Iterator::last` being called on a `DoubleEndedIterator`, which can be replaced
|
||||
/// with `DoubleEndedIterator::next_back`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// `Iterator::last` is implemented by consuming the iterator, which is unnecessary if
|
||||
/// the iterator is a `DoubleEndedIterator`. Since Rust traits do not allow specialization,
|
||||
/// `Iterator::last` cannot be optimized for `DoubleEndedIterator`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let last_arg = "echo hello world".split(' ').last();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let last_arg = "echo hello world".split(' ').next_back();
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub DOUBLE_ENDED_ITERATOR_LAST,
|
||||
perf,
|
||||
"using `Iterator::last` on a `DoubleEndedIterator`"
|
||||
}
|
||||
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Msrv,
|
||||
|
|
@ -4449,6 +4476,7 @@ impl_lint_pass!(Methods => [
|
|||
MAP_ALL_ANY_IDENTITY,
|
||||
MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
|
||||
UNNECESSARY_MAP_OR,
|
||||
DOUBLE_ENDED_ITERATOR_LAST,
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
|
|
@ -4931,6 +4959,7 @@ impl Methods {
|
|||
false,
|
||||
);
|
||||
}
|
||||
double_ended_iterator_last::check(cx, expr, recv, call_span);
|
||||
},
|
||||
("len", []) => {
|
||||
if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use rustc_span::Span;
|
|||
use super::NEEDLESS_CHARACTER_ITERATION;
|
||||
use super::utils::get_last_chain_binding_hir_id;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::paths::CHAR_IS_ASCII;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::{match_def_path, path_to_local_id, peel_blocks};
|
||||
|
||||
|
|
@ -77,7 +78,7 @@ fn handle_expr(
|
|||
if revert != is_all
|
||||
&& let ExprKind::Path(path) = fn_path.kind
|
||||
&& let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id()
|
||||
&& match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"])
|
||||
&& match_def_path(cx, fn_def_id, &CHAR_IS_ASCII)
|
||||
&& path_to_local_id(peels_expr_ref(arg), first_param)
|
||||
&& let Some(snippet) = before_chars.get_source_text(cx)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -470,14 +470,14 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>(
|
|||
captured_ids: HirIdSet,
|
||||
) -> Option<Vec<IterFunction>> {
|
||||
let mut visitor = IterFunctionVisitor {
|
||||
uses: Vec::new(),
|
||||
target: id,
|
||||
seen_other: false,
|
||||
cx,
|
||||
current_mutably_captured_ids: HirIdSet::default(),
|
||||
illegal_mutable_capture_ids: captured_ids,
|
||||
current_mutably_captured_ids: HirIdSet::default(),
|
||||
cx,
|
||||
uses: Vec::new(),
|
||||
hir_id_uses_map: FxHashMap::default(),
|
||||
current_statement_hir_id: None,
|
||||
seen_other: false,
|
||||
target: id,
|
||||
};
|
||||
visitor.visit_block(block);
|
||||
if visitor.seen_other {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::paths::STDIN;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::for_each_local_use_after_expr;
|
||||
|
|
@ -33,7 +34,7 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool {
|
|||
|
||||
pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
|
||||
if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
|
||||
&& match_def_path(cx, recv_adt.did(), &["std", "io", "stdio", "Stdin"])
|
||||
&& match_def_path(cx, recv_adt.did(), &STDIN)
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind
|
||||
&& let Res::Local(local_id) = path.res
|
||||
{
|
||||
|
|
|
|||
|
|
@ -297,8 +297,8 @@ fn parse_iter_usage<'tcx>(
|
|||
{
|
||||
Some(IterUsage {
|
||||
kind: IterUsageKind::NextTuple,
|
||||
span: e.span,
|
||||
unwrap_kind: None,
|
||||
span: e.span,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -124,30 +124,30 @@ pub(super) fn check(
|
|||
match lit.node {
|
||||
ast::LitKind::Bool(false) => {
|
||||
check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, Replacement {
|
||||
method_name: "any",
|
||||
has_args: true,
|
||||
has_generic_return: false,
|
||||
method_name: "any",
|
||||
});
|
||||
},
|
||||
ast::LitKind::Bool(true) => {
|
||||
check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, Replacement {
|
||||
method_name: "all",
|
||||
has_args: true,
|
||||
has_generic_return: false,
|
||||
method_name: "all",
|
||||
});
|
||||
},
|
||||
ast::LitKind::Int(Pu128(0), _) => {
|
||||
check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, Replacement {
|
||||
method_name: "sum",
|
||||
has_args: false,
|
||||
has_generic_return: needs_turbofish(cx, expr),
|
||||
method_name: "sum",
|
||||
});
|
||||
},
|
||||
ast::LitKind::Int(Pu128(1), _) => {
|
||||
check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, Replacement {
|
||||
method_name: "product",
|
||||
has_args: false,
|
||||
has_generic_return: needs_turbofish(cx, expr),
|
||||
method_name: "product",
|
||||
});
|
||||
},
|
||||
_ => (),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ 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 clippy_utils::{get_parent_expr, is_from_proc_macro, path_to_local_id};
|
||||
use rustc_ast::LitKind::Bool;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
|
||||
|
|
@ -96,11 +96,25 @@ pub(super) fn check<'a>(
|
|||
Sugg::hir(cx, non_binding_location, "")
|
||||
)));
|
||||
|
||||
let binop = make_binop(op.node, &Sugg::hir(cx, recv, ".."), &inner_non_binding)
|
||||
.maybe_par()
|
||||
.into_string();
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let binop = make_binop(
|
||||
op.node,
|
||||
&Sugg::hir_with_applicability(cx, recv, "..", &mut app),
|
||||
&inner_non_binding,
|
||||
);
|
||||
|
||||
(binop, "a standard comparison", Applicability::MaybeIncorrect)
|
||||
let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) {
|
||||
match parent_expr.kind {
|
||||
ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Cast(..) => binop.maybe_par(),
|
||||
ExprKind::MethodCall(_, receiver, _, _) if receiver.hir_id == expr.hir_id => binop.maybe_par(),
|
||||
_ => binop,
|
||||
}
|
||||
} else {
|
||||
binop
|
||||
}
|
||||
.into_string();
|
||||
|
||||
(sugg, "a standard comparison", app)
|
||||
} else if !def_bool
|
||||
&& msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND)
|
||||
&& let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite())
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use clippy_utils::{is_trait_method, std_or_core};
|
|||
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;
|
||||
|
|
|
|||
|
|
@ -494,7 +494,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
|
|||
for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
|
||||
match node {
|
||||
Node::Stmt(_) => return true,
|
||||
Node::Block(..) => continue,
|
||||
Node::Block(..) => {},
|
||||
Node::Item(item) => {
|
||||
if let ItemKind::Fn { body: body_id, .. } = &item.kind
|
||||
&& let output_ty = return_ty(cx, item.owner_id)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue