Auto merge of #11747 - flip1995:rustup, r=flip1995
Rustup r? `@ghost` changelog: none
This commit is contained in:
commit
09ac14c901
167 changed files with 1834 additions and 1501 deletions
|
@ -82,33 +82,20 @@ fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg
|
||||||
(
|
(
|
||||||
Ok(LitKind::Byte(b'a') | LitKind::Char('a')),
|
Ok(LitKind::Byte(b'a') | LitKind::Char('a')),
|
||||||
Ok(LitKind::Byte(b'z') | LitKind::Char('z'))
|
Ok(LitKind::Byte(b'z') | LitKind::Char('z'))
|
||||||
)
|
) | (
|
||||||
| (
|
|
||||||
Ok(LitKind::Byte(b'A') | LitKind::Char('A')),
|
Ok(LitKind::Byte(b'A') | LitKind::Char('A')),
|
||||||
Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')),
|
Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')),
|
||||||
)
|
) | (
|
||||||
| (
|
|
||||||
Ok(LitKind::Byte(b'0') | LitKind::Char('0')),
|
Ok(LitKind::Byte(b'0') | LitKind::Char('0')),
|
||||||
Ok(LitKind::Byte(b'9') | LitKind::Char('9')),
|
Ok(LitKind::Byte(b'9') | LitKind::Char('9')),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
&& !in_external_macro(cx.sess(), span)
|
&& !in_external_macro(cx.sess(), span)
|
||||||
{
|
{
|
||||||
span_lint_and_then(
|
span_lint_and_then(cx, ALMOST_COMPLETE_RANGE, span, "almost complete ascii range", |diag| {
|
||||||
cx,
|
if let Some((span, sugg)) = sugg {
|
||||||
ALMOST_COMPLETE_RANGE,
|
diag.span_suggestion(span, "use an inclusive range", sugg, Applicability::MaybeIncorrect);
|
||||||
span,
|
|
||||||
"almost complete ascii range",
|
|
||||||
|diag| {
|
|
||||||
if let Some((span, sugg)) = sugg {
|
|
||||||
diag.span_suggestion(
|
|
||||||
span,
|
|
||||||
"use an inclusive range",
|
|
||||||
sugg,
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,19 +62,21 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync {
|
||||||
ARC_WITH_NON_SEND_SYNC,
|
ARC_WITH_NON_SEND_SYNC,
|
||||||
expr.span,
|
expr.span,
|
||||||
"usage of an `Arc` that is not `Send` or `Sync`",
|
"usage of an `Arc` that is not `Send` or `Sync`",
|
||||||
|diag| with_forced_trimmed_paths!({
|
|diag| {
|
||||||
if !is_send {
|
with_forced_trimmed_paths!({
|
||||||
diag.note(format!("the trait `Send` is not implemented for `{arg_ty}`"));
|
if !is_send {
|
||||||
}
|
diag.note(format!("the trait `Send` is not implemented for `{arg_ty}`"));
|
||||||
if !is_sync {
|
}
|
||||||
diag.note(format!("the trait `Sync` is not implemented for `{arg_ty}`"));
|
if !is_sync {
|
||||||
}
|
diag.note(format!("the trait `Sync` is not implemented for `{arg_ty}`"));
|
||||||
|
}
|
||||||
|
|
||||||
diag.note(format!("required for `{ty}` to implement `Send` and `Sync`"));
|
diag.note(format!("required for `{ty}` to implement `Send` and `Sync`"));
|
||||||
|
|
||||||
diag.help("consider using an `Rc` instead or wrapping the inner type with a `Mutex`");
|
diag.help("consider using an `Rc` instead or wrapping the inner type with a `Mutex`");
|
||||||
}
|
});
|
||||||
));
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let semicolon = if is_expr_final_block_expr(cx.tcx, e) {";"} else {""};
|
let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" };
|
||||||
let mut app = Applicability::MachineApplicable;
|
let mut app = Applicability::MachineApplicable;
|
||||||
match method_segment.ident.as_str() {
|
match method_segment.ident.as_str() {
|
||||||
"is_ok" if type_suitable_to_unwrap(cx, args.type_at(1)) => {
|
"is_ok" if type_suitable_to_unwrap(cx, args.type_at(1)) => {
|
||||||
|
@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
|
||||||
),
|
),
|
||||||
app,
|
app,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
"is_err" if type_suitable_to_unwrap(cx, args.type_at(0)) => {
|
"is_err" if type_suitable_to_unwrap(cx, args.type_at(0)) => {
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
|
@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
|
||||||
),
|
),
|
||||||
app,
|
app,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||||
use clippy_utils::source::snippet;
|
use clippy_utils::source::snippet;
|
||||||
use clippy_utils::ty::implements_trait;
|
use clippy_utils::ty::implements_trait;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{AsyncCoroutineKind, Body, BodyId, CoroutineKind, ExprKind, QPath};
|
use rustc_hir::{Body, BodyId, CoroutineKind, CoroutineSource, ExprKind, QPath};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]);
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
|
impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
|
||||||
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
|
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
|
||||||
use AsyncCoroutineKind::{Block, Closure};
|
use CoroutineSource::{Block, Closure};
|
||||||
// For functions, with explicitly defined types, don't warn.
|
// For functions, with explicitly defined types, don't warn.
|
||||||
// XXXkhuey maybe we should?
|
// XXXkhuey maybe we should?
|
||||||
if let Some(CoroutineKind::Async(Block | Closure)) = body.coroutine_kind {
|
if let Some(CoroutineKind::Async(Block | Closure)) = body.coroutine_kind {
|
||||||
|
|
|
@ -602,9 +602,26 @@ fn check_should_panic_reason(cx: &LateContext<'_>, attr: &Attribute) {
|
||||||
|
|
||||||
if let AttrArgs::Delimited(args) = &normal_attr.item.args
|
if let AttrArgs::Delimited(args) = &normal_attr.item.args
|
||||||
&& let mut tt_iter = args.tokens.trees()
|
&& let mut tt_iter = args.tokens.trees()
|
||||||
&& let Some(TokenTree::Token(Token { kind: TokenKind::Ident(sym::expected, _), .. }, _)) = tt_iter.next()
|
&& let Some(TokenTree::Token(
|
||||||
&& let Some(TokenTree::Token(Token { kind: TokenKind::Eq, .. }, _)) = tt_iter.next()
|
Token {
|
||||||
&& let Some(TokenTree::Token(Token { kind: TokenKind::Literal(_), .. }, _)) = tt_iter.next()
|
kind: TokenKind::Ident(sym::expected, _),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
_,
|
||||||
|
)) = tt_iter.next()
|
||||||
|
&& let Some(TokenTree::Token(
|
||||||
|
Token {
|
||||||
|
kind: TokenKind::Eq, ..
|
||||||
|
},
|
||||||
|
_,
|
||||||
|
)) = tt_iter.next()
|
||||||
|
&& let Some(TokenTree::Token(
|
||||||
|
Token {
|
||||||
|
kind: TokenKind::Literal(_),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
_,
|
||||||
|
)) = tt_iter.next()
|
||||||
{
|
{
|
||||||
// `#[should_panic(expected = "..")]` found, good
|
// `#[should_panic(expected = "..")]` found, good
|
||||||
return;
|
return;
|
||||||
|
@ -914,7 +931,9 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
|
||||||
fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
|
fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
|
||||||
for item in items {
|
for item in items {
|
||||||
if let NestedMetaItem::MetaItem(meta) = item {
|
if let NestedMetaItem::MetaItem(meta) = item {
|
||||||
if meta.has_name(sym!(features)) && let Some(val) = meta.value_str() {
|
if meta.has_name(sym!(features))
|
||||||
|
&& let Some(val) = meta.value_str()
|
||||||
|
{
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
MAYBE_MISUSED_CFG,
|
MAYBE_MISUSED_CFG,
|
||||||
|
@ -933,16 +952,16 @@ fn check_nested_misused_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_minimal_cfg_condition(cx: &EarlyContext<'_>, attr: &Attribute) {
|
fn check_minimal_cfg_condition(cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||||
if attr.has_name(sym::cfg) &&
|
if attr.has_name(sym::cfg)
|
||||||
let Some(items) = attr.meta_item_list()
|
&& let Some(items) = attr.meta_item_list()
|
||||||
{
|
{
|
||||||
check_nested_cfg(cx, &items);
|
check_nested_cfg(cx, &items);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_misused_cfg(cx: &EarlyContext<'_>, attr: &Attribute) {
|
fn check_misused_cfg(cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||||
if attr.has_name(sym::cfg) &&
|
if attr.has_name(sym::cfg)
|
||||||
let Some(items) = attr.meta_item_list()
|
&& let Some(items) = attr.meta_item_list()
|
||||||
{
|
{
|
||||||
check_nested_misused_cfg(cx, &items);
|
check_nested_misused_cfg(cx, &items);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::{match_def_path, paths};
|
use clippy_utils::{match_def_path, paths};
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::{AsyncCoroutineKind, Body, CoroutineKind};
|
use rustc_hir::{Body, CoroutineKind, CoroutineSource};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::mir::CoroutineLayout;
|
use rustc_middle::mir::CoroutineLayout;
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
|
@ -194,7 +194,7 @@ impl LateLintPass<'_> for AwaitHolding {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
|
fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
|
||||||
use AsyncCoroutineKind::{Block, Closure, Fn};
|
use CoroutineSource::{Block, Closure, Fn};
|
||||||
if let Some(CoroutineKind::Async(Block | Closure | Fn)) = body.coroutine_kind {
|
if let Some(CoroutineKind::Async(Block | Closure | Fn)) = body.coroutine_kind {
|
||||||
let def_id = cx.tcx.hir().body_owner_def_id(body.id());
|
let def_id = cx.tcx.hir().body_owner_def_id(body.id());
|
||||||
if let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(def_id) {
|
if let Some(coroutine_layout) = cx.tcx.mir_coroutine_witnesses(def_id) {
|
||||||
|
|
|
@ -55,7 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
|
fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
|
||||||
if let Some(If { cond, then, r#else: Some(r#else) }) = If::hir(expr)
|
if let Some(If {
|
||||||
|
cond,
|
||||||
|
then,
|
||||||
|
r#else: Some(r#else),
|
||||||
|
}) = If::hir(expr)
|
||||||
&& let Some(then_lit) = int_literal(then)
|
&& let Some(then_lit) = int_literal(then)
|
||||||
&& let Some(else_lit) = int_literal(r#else)
|
&& let Some(else_lit) = int_literal(r#else)
|
||||||
{
|
{
|
||||||
|
@ -90,19 +94,18 @@ fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>
|
||||||
let into_snippet = snippet.clone().maybe_par();
|
let into_snippet = snippet.clone().maybe_par();
|
||||||
let as_snippet = snippet.as_ty(ty);
|
let as_snippet = snippet.as_ty(ty);
|
||||||
|
|
||||||
span_lint_and_then(cx,
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
BOOL_TO_INT_WITH_IF,
|
BOOL_TO_INT_WITH_IF,
|
||||||
expr.span,
|
expr.span,
|
||||||
"boolean to int conversion using if",
|
"boolean to int conversion using if",
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(expr.span, "replace with from", suggestion, applicability);
|
||||||
expr.span,
|
diag.note(format!(
|
||||||
"replace with from",
|
"`{as_snippet}` or `{into_snippet}.into()` can also be valid options"
|
||||||
suggestion,
|
));
|
||||||
applicability,
|
},
|
||||||
);
|
);
|
||||||
diag.note(format!("`{as_snippet}` or `{into_snippet}.into()` can also be valid options"));
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +113,7 @@ fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>
|
||||||
fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hir::Expr<'tcx>> {
|
fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hir::Expr<'tcx>> {
|
||||||
if let ExprKind::Block(block, _) = expr.kind
|
if let ExprKind::Block(block, _) = expr.kind
|
||||||
&& let Block {
|
&& let Block {
|
||||||
stmts: [], // Shouldn't lint if statements with side effects
|
stmts: [], // Shouldn't lint if statements with side effects
|
||||||
expr: Some(expr),
|
expr: Some(expr),
|
||||||
..
|
..
|
||||||
} = block
|
} = block
|
||||||
|
|
|
@ -472,8 +472,9 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
|
||||||
self.bool_expr(e);
|
self.bool_expr(e);
|
||||||
},
|
},
|
||||||
ExprKind::Unary(UnOp::Not, inner) => {
|
ExprKind::Unary(UnOp::Not, inner) => {
|
||||||
if let ExprKind::Unary(UnOp::Not, ex) = inner.kind &&
|
if let ExprKind::Unary(UnOp::Not, ex) = inner.kind
|
||||||
!self.cx.typeck_results().node_types()[ex.hir_id].is_bool() {
|
&& !self.cx.typeck_results().node_types()[ex.hir_id].is_bool()
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() {
|
if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() {
|
||||||
|
@ -500,10 +501,10 @@ struct NotSimplificationVisitor<'a, 'tcx> {
|
||||||
|
|
||||||
impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
|
impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
|
||||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||||
if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind &&
|
if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
|
||||||
!inner.span.from_expansion() &&
|
&& !inner.span.from_expansion()
|
||||||
let Some(suggestion) = simplify_not(self.cx, inner)
|
&& let Some(suggestion) = simplify_not(self.cx, inner)
|
||||||
&& self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
|
&& self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
|
||||||
{
|
{
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
self.cx,
|
self.cx,
|
||||||
|
|
|
@ -61,9 +61,9 @@ impl LateLintPass<'_> for BoxDefault {
|
||||||
} else if let Some(arg_ty) = cx.typeck_results().expr_ty(arg).make_suggestable(cx.tcx, true) {
|
} else if let Some(arg_ty) = cx.typeck_results().expr_ty(arg).make_suggestable(cx.tcx, true) {
|
||||||
with_forced_trimmed_paths!(format!("Box::<{arg_ty}>::default()"))
|
with_forced_trimmed_paths!(format!("Box::<{arg_ty}>::default()"))
|
||||||
} else {
|
} else {
|
||||||
return
|
return;
|
||||||
},
|
},
|
||||||
Applicability::MachineApplicable
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +110,8 @@ fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
Node::Expr(Expr {
|
Node::Expr(Expr {
|
||||||
kind: ExprKind::Call(path, args),
|
kind: ExprKind::Call(path, args),
|
||||||
..
|
..
|
||||||
}) | Node::Block(Block {
|
})
|
||||||
|
| Node::Block(Block {
|
||||||
expr:
|
expr:
|
||||||
Some(Expr {
|
Some(Expr {
|
||||||
kind: ExprKind::Call(path, args),
|
kind: ExprKind::Call(path, args),
|
||||||
|
@ -119,10 +120,10 @@ fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
..
|
..
|
||||||
}),
|
}),
|
||||||
) => {
|
) => {
|
||||||
if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id) &&
|
if let Some(index) = args.iter().position(|arg| arg.hir_id == expr.hir_id)
|
||||||
let Some(sig) = expr_sig(cx, path) &&
|
&& let Some(sig) = expr_sig(cx, path)
|
||||||
let Some(input) = sig.input(index) &&
|
&& let Some(input) = sig.input(index)
|
||||||
!cx.typeck_results().expr_ty_adjusted(expr).boxed_ty().is_trait()
|
&& !cx.typeck_results().expr_ty_adjusted(expr).boxed_ty().is_trait()
|
||||||
{
|
{
|
||||||
input.no_bound_vars().is_some()
|
input.no_bound_vars().is_some()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -9,12 +9,19 @@ use rustc_middle::ty::{self, Ty, TypeAndMut};
|
||||||
use super::AS_PTR_CAST_MUT;
|
use super::AS_PTR_CAST_MUT;
|
||||||
|
|
||||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>) {
|
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>) {
|
||||||
if let ty::RawPtr(ptrty @ TypeAndMut { mutbl: Mutability::Mut, .. }) = cast_to.kind()
|
if let ty::RawPtr(
|
||||||
&& let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) =
|
ptrty @ TypeAndMut {
|
||||||
cx.typeck_results().node_type(cast_expr.hir_id).kind()
|
mutbl: Mutability::Mut, ..
|
||||||
|
},
|
||||||
|
) = cast_to.kind()
|
||||||
|
&& let ty::RawPtr(TypeAndMut {
|
||||||
|
mutbl: Mutability::Not, ..
|
||||||
|
}) = cx.typeck_results().node_type(cast_expr.hir_id).kind()
|
||||||
&& let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind
|
&& let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind
|
||||||
&& method_name.ident.name == rustc_span::sym::as_ptr
|
&& method_name.ident.name == rustc_span::sym::as_ptr
|
||||||
&& let Some(as_ptr_did) = cx.typeck_results().type_dependent_def_id(cast_expr.peel_blocks().hir_id)
|
&& let Some(as_ptr_did) = cx
|
||||||
|
.typeck_results()
|
||||||
|
.type_dependent_def_id(cast_expr.peel_blocks().hir_id)
|
||||||
&& let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity()
|
&& let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity()
|
||||||
&& let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next()
|
&& let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next()
|
||||||
&& let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind()
|
&& let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind()
|
||||||
|
@ -30,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
|
||||||
&format!("casting the result of `as_ptr` to *{ptrty}"),
|
&format!("casting the result of `as_ptr` to *{ptrty}"),
|
||||||
"replace with",
|
"replace with",
|
||||||
format!("{recv}.as_mut_ptr()"),
|
format!("{recv}.as_mut_ptr()"),
|
||||||
applicability
|
applicability,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||||
// There probably is no obvious reason to do this, just to be consistent with `as` cases.
|
// There probably is no obvious reason to do this, just to be consistent with `as` cases.
|
||||||
&& !is_hir_ty_cfg_dependant(cx, cast_to)
|
&& !is_hir_ty_cfg_dependant(cx, cast_to)
|
||||||
{
|
{
|
||||||
let (cast_from, cast_to) =
|
let (cast_from, cast_to) = (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
|
||||||
(cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
|
|
||||||
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
|
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,9 +80,9 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||||
cx.tcx.get_diagnostic_name(def_id),
|
cx.tcx.get_diagnostic_name(def_id),
|
||||||
Some(
|
Some(
|
||||||
sym::ptr_write_unaligned
|
sym::ptr_write_unaligned
|
||||||
| sym::ptr_read_unaligned
|
| sym::ptr_read_unaligned
|
||||||
| sym::intrinsics_unaligned_volatile_load
|
| sym::intrinsics_unaligned_volatile_load
|
||||||
| sym::intrinsics_unaligned_volatile_store
|
| sym::intrinsics_unaligned_volatile_store
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
|
@ -97,7 +97,9 @@ pub(super) fn check<'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
// skip cast of fn call that returns type alias
|
// skip cast of fn call that returns type alias
|
||||||
if let ExprKind::Cast(inner, ..) = expr.kind && is_cast_from_ty_alias(cx, inner, cast_from) {
|
if let ExprKind::Cast(inner, ..) = expr.kind
|
||||||
|
&& is_cast_from_ty_alias(cx, inner, cast_from)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,11 +191,10 @@ fn lint_unnecessary_cast(
|
||||||
let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr)
|
let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr)
|
||||||
&& let ExprKind::MethodCall(..) = parent_expr.kind
|
&& let ExprKind::MethodCall(..) = parent_expr.kind
|
||||||
&& literal_str.starts_with('-')
|
&& literal_str.starts_with('-')
|
||||||
{
|
{
|
||||||
format!("({literal_str}_{cast_to})")
|
format!("({literal_str}_{cast_to})")
|
||||||
|
} else {
|
||||||
} else {
|
format!("{literal_str}_{cast_to}")
|
||||||
format!("{literal_str}_{cast_to}")
|
|
||||||
};
|
};
|
||||||
|
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
|
@ -269,7 +270,9 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
|
||||||
&& let Some(parent) = get_parent_node(cx.tcx, hir_id)
|
&& let Some(parent) = get_parent_node(cx.tcx, hir_id)
|
||||||
&& let Node::Local(l) = parent
|
&& let Node::Local(l) = parent
|
||||||
{
|
{
|
||||||
if let Some(e) = l.init && is_cast_from_ty_alias(cx, e, cast_from) {
|
if let Some(e) = l.init
|
||||||
|
&& is_cast_from_ty_alias(cx, e, cast_from)
|
||||||
|
{
|
||||||
return ControlFlow::Break::<()>(());
|
return ControlFlow::Break::<()>(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,10 @@ fn make_sugg(
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
format!("std::iter::empty::<{}>()", snippet_with_context(cx, iter_ty.span, ctxt, "..", applicability).0)
|
format!(
|
||||||
|
"std::iter::empty::<{}>()",
|
||||||
|
snippet_with_context(cx, iter_ty.span, ctxt, "..", applicability).0
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
"std::iter::empty()".to_owned()
|
"std::iter::empty()".to_owned()
|
||||||
}
|
}
|
||||||
|
|
|
@ -353,23 +353,26 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
||||||
// priority.
|
// priority.
|
||||||
if let Some(fn_id) = typeck.type_dependent_def_id(hir_id)
|
if let Some(fn_id) = typeck.type_dependent_def_id(hir_id)
|
||||||
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
|
&& let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
|
||||||
&& let arg_ty
|
&& let arg_ty = cx
|
||||||
= cx.tcx.erase_regions(use_cx.adjustments.last().map_or(expr_ty, |a| a.target))
|
.tcx
|
||||||
|
.erase_regions(use_cx.adjustments.last().map_or(expr_ty, |a| a.target))
|
||||||
&& let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
|
&& let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
|
||||||
&& let args = cx
|
&& let args = cx
|
||||||
.typeck_results()
|
.typeck_results()
|
||||||
.node_args_opt(hir_id).map(|args| &args[1..]).unwrap_or_default()
|
.node_args_opt(hir_id)
|
||||||
&& let impl_ty = if cx.tcx.fn_sig(fn_id)
|
.map(|args| &args[1..])
|
||||||
.instantiate_identity()
|
.unwrap_or_default()
|
||||||
.skip_binder()
|
&& let impl_ty =
|
||||||
.inputs()[0].is_ref()
|
if cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder().inputs()[0]
|
||||||
{
|
.is_ref()
|
||||||
// Trait methods taking `&self`
|
{
|
||||||
sub_ty
|
// Trait methods taking `&self`
|
||||||
} else {
|
sub_ty
|
||||||
// Trait methods taking `self`
|
} else {
|
||||||
arg_ty
|
// Trait methods taking `self`
|
||||||
} && impl_ty.is_ref()
|
arg_ty
|
||||||
|
}
|
||||||
|
&& impl_ty.is_ref()
|
||||||
&& implements_trait(
|
&& implements_trait(
|
||||||
cx,
|
cx,
|
||||||
impl_ty,
|
impl_ty,
|
||||||
|
@ -414,9 +417,9 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
|
||||||
let (required_refs, msg) = if can_auto_borrow {
|
let (required_refs, msg) = if can_auto_borrow {
|
||||||
(1, if deref_count == 1 { borrow_msg } else { deref_msg })
|
(1, if deref_count == 1 { borrow_msg } else { deref_msg })
|
||||||
} else if let Some(&Adjustment {
|
} else if let Some(&Adjustment {
|
||||||
kind: Adjust::Borrow(AutoBorrow::Ref(_, mutability)),
|
kind: Adjust::Borrow(AutoBorrow::Ref(_, mutability)),
|
||||||
..
|
..
|
||||||
}) = next_adjust
|
}) = next_adjust
|
||||||
&& matches!(mutability, AutoBorrowMutability::Mut { .. })
|
&& matches!(mutability, AutoBorrowMutability::Mut { .. })
|
||||||
&& !stability.is_reborrow_stable()
|
&& !stability.is_reborrow_stable()
|
||||||
{
|
{
|
||||||
|
@ -705,9 +708,11 @@ fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> boo
|
||||||
{
|
{
|
||||||
match parent.kind {
|
match parent.kind {
|
||||||
ExprKind::Call(child, _) | ExprKind::MethodCall(_, child, _, _) | ExprKind::Index(child, _, _)
|
ExprKind::Call(child, _) | ExprKind::MethodCall(_, child, _, _) | ExprKind::Index(child, _, _)
|
||||||
if child.hir_id == e.hir_id => true,
|
if child.hir_id == e.hir_id =>
|
||||||
ExprKind::Match(.., MatchSource::TryDesugar(_) | MatchSource::AwaitDesugar)
|
{
|
||||||
| ExprKind::Field(_, _) => true,
|
true
|
||||||
|
},
|
||||||
|
ExprKind::Match(.., MatchSource::TryDesugar(_) | MatchSource::AwaitDesugar) | ExprKind::Field(_, _) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -569,9 +569,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||||
if let End(Heading(_, _, _)) = event {
|
if let End(Heading(_, _, _)) = event {
|
||||||
in_heading = false;
|
in_heading = false;
|
||||||
}
|
}
|
||||||
if ticks_unbalanced
|
if ticks_unbalanced && let Some(span) = fragments.span(cx, paragraph_range.clone()) {
|
||||||
&& let Some(span) = fragments.span(cx, paragraph_range.clone())
|
|
||||||
{
|
|
||||||
span_lint_and_help(
|
span_lint_and_help(
|
||||||
cx,
|
cx,
|
||||||
DOC_MARKDOWN,
|
DOC_MARKDOWN,
|
||||||
|
@ -617,8 +615,9 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
|
||||||
check_link_quotes(cx, trimmed_text, range.clone(), fragments);
|
check_link_quotes(cx, trimmed_text, range.clone(), fragments);
|
||||||
}
|
}
|
||||||
if let Some(link) = in_link.as_ref()
|
if let Some(link) = in_link.as_ref()
|
||||||
&& let Ok(url) = Url::parse(link)
|
&& let Ok(url) = Url::parse(link)
|
||||||
&& (url.scheme() == "https" || url.scheme() == "http") {
|
&& (url.scheme() == "https" || url.scheme() == "http")
|
||||||
|
{
|
||||||
// Don't check the text associated with external URLs
|
// Don't check the text associated with external URLs
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -716,7 +715,9 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, range: Range<u
|
||||||
// Because of the global session, we need to create a new session in a different thread with
|
// Because of the global session, we need to create a new session in a different thread with
|
||||||
// the edition we need.
|
// the edition we need.
|
||||||
let text = text.to_owned();
|
let text = text.to_owned();
|
||||||
if thread::spawn(move || has_needless_main(text, edition)).join().expect("thread::spawn failed")
|
if thread::spawn(move || has_needless_main(text, edition))
|
||||||
|
.join()
|
||||||
|
.expect("thread::spawn failed")
|
||||||
&& let Some(span) = fragments.span(cx, range.start..range.end - trailing_whitespace)
|
&& let Some(span) = fragments.span(cx, range.start..range.end - trailing_whitespace)
|
||||||
{
|
{
|
||||||
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
|
span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
|
||||||
|
|
|
@ -90,7 +90,8 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
|
||||||
let is_copy = is_copy(cx, arg_ty);
|
let is_copy = is_copy(cx, arg_ty);
|
||||||
let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
|
let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
|
||||||
let (lint, msg, note_span) = match fn_name {
|
let (lint, msg, note_span) = match fn_name {
|
||||||
// early return for uplifted lints: dropping_references, dropping_copy_types, forgetting_references, forgetting_copy_types
|
// early return for uplifted lints: dropping_references, dropping_copy_types, forgetting_references,
|
||||||
|
// forgetting_copy_types
|
||||||
sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => return,
|
sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => return,
|
||||||
sym::mem_forget if arg_ty.is_ref() => return,
|
sym::mem_forget if arg_ty.is_ref() => return,
|
||||||
sym::mem_drop if is_copy && !drop_is_single_call_in_arm => return,
|
sym::mem_drop if is_copy && !drop_is_single_call_in_arm => return,
|
||||||
|
@ -100,8 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
|
||||||
if !(arg_ty.needs_drop(cx.tcx, cx.param_env)
|
if !(arg_ty.needs_drop(cx.tcx, cx.param_env)
|
||||||
|| is_must_use_func_call(cx, arg)
|
|| is_must_use_func_call(cx, arg)
|
||||||
|| is_must_use_ty(cx, arg_ty)
|
|| is_must_use_ty(cx, arg_ty)
|
||||||
|| drop_is_single_call_in_arm
|
|| drop_is_single_call_in_arm) =>
|
||||||
) =>
|
|
||||||
{
|
{
|
||||||
(DROP_NON_DROP, DROP_NON_DROP_SUMMARY.into(), Some(arg.span))
|
(DROP_NON_DROP, DROP_NON_DROP_SUMMARY.into(), Some(arg.span))
|
||||||
},
|
},
|
||||||
|
@ -122,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
|
||||||
} else {
|
} else {
|
||||||
(FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY.into(), Some(arg.span))
|
(FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY.into(), Some(arg.span))
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
span_lint_and_note(
|
span_lint_and_note(
|
||||||
|
|
|
@ -35,7 +35,8 @@ impl EarlyLintPass for EmptyStructsWithBrackets {
|
||||||
|
|
||||||
if let ItemKind::Struct(var_data, _) = &item.kind
|
if let ItemKind::Struct(var_data, _) = &item.kind
|
||||||
&& has_brackets(var_data)
|
&& has_brackets(var_data)
|
||||||
&& has_no_fields(cx, var_data, span_after_ident) {
|
&& has_no_fields(cx, var_data, span_after_ident)
|
||||||
|
{
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
EMPTY_STRUCTS_WITH_BRACKETS,
|
EMPTY_STRUCTS_WITH_BRACKETS,
|
||||||
|
@ -46,8 +47,9 @@ impl EarlyLintPass for EmptyStructsWithBrackets {
|
||||||
span_after_ident,
|
span_after_ident,
|
||||||
"remove the brackets",
|
"remove the brackets",
|
||||||
";",
|
";",
|
||||||
Applicability::Unspecified);
|
Applicability::Unspecified,
|
||||||
},
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,7 +68,8 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||||
if !in_external_macro(cx.sess(), expr.span)
|
if !in_external_macro(cx.sess(), expr.span)
|
||||||
&& let ExprKind::Let(let_expr) = expr.kind
|
&& let ExprKind::Let(let_expr) = expr.kind
|
||||||
&& unary_pattern(let_expr.pat) {
|
&& unary_pattern(let_expr.pat)
|
||||||
|
{
|
||||||
let exp_ty = cx.typeck_results().expr_ty(let_expr.init);
|
let exp_ty = cx.typeck_results().expr_ty(let_expr.init);
|
||||||
let pat_ty = cx.typeck_results().pat_ty(let_expr.pat);
|
let pat_ty = cx.typeck_results().pat_ty(let_expr.pat);
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
@ -79,7 +80,9 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality {
|
||||||
"({})",
|
"({})",
|
||||||
snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0,
|
snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0,
|
||||||
),
|
),
|
||||||
_ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(),
|
_ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability)
|
||||||
|
.0
|
||||||
|
.to_string(),
|
||||||
};
|
};
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
|
|
|
@ -41,10 +41,11 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError {
|
||||||
};
|
};
|
||||||
|
|
||||||
match item.kind {
|
match item.kind {
|
||||||
ItemKind::TyAlias(..) if item.ident.name == sym::Error
|
ItemKind::TyAlias(..)
|
||||||
&& is_visible_outside_module(cx, item.owner_id.def_id)
|
if item.ident.name == sym::Error
|
||||||
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
|
&& is_visible_outside_module(cx, item.owner_id.def_id)
|
||||||
&& implements_trait(cx, ty, error_def_id, &[]) =>
|
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
|
||||||
|
&& implements_trait(cx, ty, error_def_id, &[]) =>
|
||||||
{
|
{
|
||||||
span_lint(
|
span_lint(
|
||||||
cx,
|
cx,
|
||||||
|
@ -53,13 +54,14 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError {
|
||||||
"exported type alias named `Error` that implements `Error`",
|
"exported type alias named `Error` that implements `Error`",
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
ItemKind::Impl(imp) if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_def_id())
|
ItemKind::Impl(imp)
|
||||||
&& error_def_id == trait_def_id
|
if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_def_id())
|
||||||
&& let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local)
|
&& error_def_id == trait_def_id
|
||||||
&& let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id)
|
&& let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local)
|
||||||
&& let Some(ident) = cx.tcx.opt_item_ident(def_id.to_def_id())
|
&& let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id)
|
||||||
&& ident.name == sym::Error
|
&& let Some(ident) = cx.tcx.opt_item_ident(def_id.to_def_id())
|
||||||
&& is_visible_outside_module(cx, def_id) =>
|
&& ident.name == sym::Error
|
||||||
|
&& is_visible_outside_module(cx, def_id) =>
|
||||||
{
|
{
|
||||||
span_lint_hir_and_then(
|
span_lint_hir_and_then(
|
||||||
cx,
|
cx,
|
||||||
|
@ -69,9 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError {
|
||||||
"exported type named `Error` that implements `Error`",
|
"exported type named `Error` that implements `Error`",
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.span_note(item.span, "`Error` was implemented here");
|
diag.span_note(item.span, "`Error` was implemented here");
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,19 +119,21 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
||||||
|
|
||||||
match body.value.kind {
|
match body.value.kind {
|
||||||
ExprKind::Call(callee, args)
|
ExprKind::Call(callee, args)
|
||||||
if matches!(callee.kind, ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..))) =>
|
if matches!(
|
||||||
|
callee.kind,
|
||||||
|
ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..))
|
||||||
|
) =>
|
||||||
{
|
{
|
||||||
let callee_ty = typeck.expr_ty(callee).peel_refs();
|
let callee_ty = typeck.expr_ty(callee).peel_refs();
|
||||||
if matches!(
|
if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
|
||||||
type_diagnostic_name(cx, callee_ty),
|
|| !check_inputs(typeck, body.params, None, args)
|
||||||
Some(sym::Arc | sym::Rc)
|
{
|
||||||
) || !check_inputs(typeck, body.params, None, args) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let callee_ty_adjusted = typeck.expr_adjustments(callee).last().map_or(
|
let callee_ty_adjusted = typeck
|
||||||
callee_ty,
|
.expr_adjustments(callee)
|
||||||
|a| a.target.peel_refs(),
|
.last()
|
||||||
);
|
.map_or(callee_ty, |a| a.target.peel_refs());
|
||||||
|
|
||||||
let sig = match callee_ty_adjusted.kind() {
|
let sig = match callee_ty_adjusted.kind() {
|
||||||
ty::FnDef(def, _) => cx.tcx.fn_sig(def).skip_binder().skip_binder(),
|
ty::FnDef(def, _) => cx.tcx.fn_sig(def).skip_binder().skip_binder(),
|
||||||
|
@ -160,36 +162,26 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
||||||
// For now ignore all callee types which reference a type parameter.
|
// For now ignore all callee types which reference a type parameter.
|
||||||
&& !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_)))
|
&& !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_)))
|
||||||
{
|
{
|
||||||
span_lint_and_then(
|
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
|
||||||
cx,
|
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
|
||||||
REDUNDANT_CLOSURE,
|
if let Ok((ClosureKind::FnMut, _)) = cx.tcx.infer_ctxt().build().type_implements_fn_trait(
|
||||||
expr.span,
|
cx.param_env,
|
||||||
"redundant closure",
|
Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
|
||||||
|diag| {
|
ImplPolarity::Positive,
|
||||||
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
|
) && path_to_local(callee).map_or(false, |l| {
|
||||||
if let Ok((ClosureKind::FnMut, _))
|
local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
|
||||||
= cx.tcx.infer_ctxt().build().type_implements_fn_trait(
|
}) {
|
||||||
cx.param_env,
|
// Mutable closure is used after current expr; we cannot consume it.
|
||||||
Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
|
snippet = format!("&mut {snippet}");
|
||||||
ImplPolarity::Positive,
|
|
||||||
) && path_to_local(callee)
|
|
||||||
.map_or(
|
|
||||||
false,
|
|
||||||
|l| local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr),
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Mutable closure is used after current expr; we cannot consume it.
|
|
||||||
snippet = format!("&mut {snippet}");
|
|
||||||
}
|
|
||||||
diag.span_suggestion(
|
|
||||||
expr.span,
|
|
||||||
"replace the closure with the function itself",
|
|
||||||
snippet,
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
diag.span_suggestion(
|
||||||
|
expr.span,
|
||||||
|
"replace the closure with the function itself",
|
||||||
|
snippet,
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
|
ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
|
||||||
|
|
|
@ -157,7 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools {
|
||||||
// functions with a body are already checked by `check_fn`
|
// functions with a body are already checked by `check_fn`
|
||||||
if let TraitItemKind::Fn(fn_sig, TraitFn::Required(_)) = &trait_item.kind
|
if let TraitItemKind::Fn(fn_sig, TraitFn::Required(_)) = &trait_item.kind
|
||||||
&& fn_sig.header.abi == Abi::Rust
|
&& fn_sig.header.abi == Abi::Rust
|
||||||
{
|
{
|
||||||
self.check_fn_sig(cx, fn_sig.decl, fn_sig.span);
|
self.check_fn_sig(cx, fn_sig.decl, fn_sig.span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,11 +174,8 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools {
|
||||||
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
|
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
|
||||||
if let Some(fn_header) = fn_kind.header()
|
if let Some(fn_header) = fn_kind.header()
|
||||||
&& fn_header.abi == Abi::Rust
|
&& fn_header.abi == Abi::Rust
|
||||||
&& get_parent_as_impl(cx.tcx, hir_id)
|
&& get_parent_as_impl(cx.tcx, hir_id).map_or(true, |impl_item| impl_item.of_trait.is_none())
|
||||||
.map_or(true,
|
{
|
||||||
|impl_item| impl_item.of_trait.is_none()
|
|
||||||
)
|
|
||||||
{
|
|
||||||
self.check_fn_sig(cx, fn_decl, span);
|
self.check_fn_sig(cx, fn_decl, span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,9 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
||||||
Some(sym::io_stderr) => ("stderr", "e"),
|
Some(sym::io_stderr) => ("stderr", "e"),
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root()) else { return; };
|
let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
// ordering is important here, since `writeln!` uses `write!` internally
|
// ordering is important here, since `writeln!` uses `write!` internally
|
||||||
let calling_macro = if is_expn_of(write_call.span, "writeln").is_some() {
|
let calling_macro = if is_expn_of(write_call.span, "writeln").is_some() {
|
||||||
|
@ -78,18 +80,11 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
||||||
macro_name.replace("write", "print"),
|
macro_name.replace("write", "print"),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(format!("{dest_name}().write_fmt(...)"), "print".into())
|
||||||
format!("{dest_name}().write_fmt(...)"),
|
|
||||||
"print".into(),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let inputs_snippet = snippet_with_applicability(
|
let inputs_snippet =
|
||||||
cx,
|
snippet_with_applicability(cx, format_args_inputs_span(&format_args), "..", &mut applicability);
|
||||||
format_args_inputs_span(&format_args),
|
|
||||||
"..",
|
|
||||||
&mut applicability,
|
|
||||||
);
|
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
EXPLICIT_WRITE,
|
EXPLICIT_WRITE,
|
||||||
|
|
|
@ -177,20 +177,22 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|(idx, param)| {
|
.map(|(idx, param)| {
|
||||||
if let Some(next) = explicit_params.get(idx + 1) && end != Some(next.def_id) {
|
if let Some(next) = explicit_params.get(idx + 1)
|
||||||
// Extend the current span forward, up until the next param in the list.
|
&& end != Some(next.def_id)
|
||||||
param.span.until(next.span)
|
{
|
||||||
} else {
|
// Extend the current span forward, up until the next param in the list.
|
||||||
// Extend the current span back to include the comma following the previous
|
param.span.until(next.span)
|
||||||
// param. If the span of the next param in the list has already been
|
} else {
|
||||||
// extended, we continue the chain. This is why we're iterating in reverse.
|
// Extend the current span back to include the comma following the previous
|
||||||
end = Some(param.def_id);
|
// param. If the span of the next param in the list has already been
|
||||||
|
// extended, we continue the chain. This is why we're iterating in reverse.
|
||||||
|
end = Some(param.def_id);
|
||||||
|
|
||||||
// idx will never be 0, else we'd be removing the entire list of generics
|
// idx will never be 0, else we'd be removing the entire list of generics
|
||||||
let prev = explicit_params[idx - 1];
|
let prev = explicit_params[idx - 1];
|
||||||
let prev_span = self.get_bound_span(prev);
|
let prev_span = self.get_bound_span(prev);
|
||||||
self.get_bound_span(param).with_lo(prev_span.hi())
|
self.get_bound_span(param).with_lo(prev_span.hi())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
|
@ -54,7 +54,9 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
||||||
([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
|
([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
|
||||||
([], [_]) => {
|
([], [_]) => {
|
||||||
// Simulate macro expansion, converting {{ and }} to { and }.
|
// Simulate macro expansion, converting {{ and }} to { and }.
|
||||||
let Some(snippet) = snippet_opt(cx, format_args.span) else { return };
|
let Some(snippet) = snippet_opt(cx, format_args.span) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let s_expand = snippet.replace("{{", "{").replace("}}", "}");
|
let s_expand = snippet.replace("{{", "{").replace("}}", "}");
|
||||||
let sugg = format!("{s_expand}.to_string()");
|
let sugg = format!("{s_expand}.to_string()");
|
||||||
span_useless_format(cx, call_site, sugg, applicability);
|
span_useless_format(cx, call_site, sugg, applicability);
|
||||||
|
@ -76,13 +78,14 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
let sugg = if is_new_string {
|
let sugg = if is_new_string {
|
||||||
snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability).0.into_owned()
|
snippet_with_context(cx, value.span, call_site.ctxt(), "..", &mut applicability)
|
||||||
|
.0
|
||||||
|
.into_owned()
|
||||||
} else {
|
} else {
|
||||||
let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "<arg>", &mut applicability);
|
let sugg = Sugg::hir_with_context(cx, value, call_site.ctxt(), "<arg>", &mut applicability);
|
||||||
format!("{}.to_string()", sugg.maybe_par())
|
format!("{}.to_string()", sugg.maybe_par())
|
||||||
};
|
};
|
||||||
span_useless_format(cx, call_site, sugg, applicability);
|
span_useless_format(cx, call_site, sugg, applicability);
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
|
|
|
@ -370,7 +370,7 @@ fn check_one_arg(
|
||||||
};
|
};
|
||||||
fixes.push((pos_span, replacement));
|
fixes.push((pos_span, replacement));
|
||||||
fixes.push((arg_span, String::new()));
|
fixes.push((arg_span, String::new()));
|
||||||
true // successful inlining, continue checking
|
true // successful inlining, continue checking
|
||||||
} else {
|
} else {
|
||||||
// Do not continue inlining (return false) in case
|
// Do not continue inlining (return false) in case
|
||||||
// * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
|
// * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
|
||||||
|
|
|
@ -58,7 +58,7 @@ fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||||
arms.iter().any(|arm| is_format(cx, arm.body))
|
arms.iter().any(|arm| is_format(cx, arm.body))
|
||||||
},
|
},
|
||||||
Some(higher::IfLetOrMatch::IfLet(_, _, then, r#else)) => {
|
Some(higher::IfLetOrMatch::IfLet(_, _, then, r#else)) => {
|
||||||
is_format(cx, then) ||r#else.is_some_and(|e| is_format(cx, e))
|
is_format(cx, then) || r#else.is_some_and(|e| is_format(cx, e))
|
||||||
},
|
},
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
@ -69,17 +69,15 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
let arg = match expr.kind {
|
let arg = match expr.kind {
|
||||||
ExprKind::MethodCall(_, _, [arg], _) => {
|
ExprKind::MethodCall(_, _, [arg], _) => {
|
||||||
if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) &&
|
if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
|
||||||
match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
|
&& match_def_path(cx, fn_def_id, &paths::PUSH_STR)
|
||||||
|
{
|
||||||
arg
|
arg
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ExprKind::AssignOp(op, left, arg)
|
|
||||||
if op.node == BinOpKind::Add && is_string(cx, left) => {
|
|
||||||
arg
|
|
||||||
},
|
},
|
||||||
|
ExprKind::AssignOp(op, left, arg) if op.node == BinOpKind::Add && is_string(cx, left) => arg,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
if is_format(cx, arg) {
|
if is_format(cx, arg) {
|
||||||
|
|
|
@ -88,7 +88,8 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
|
||||||
cx.tcx.sess.source_map().guess_head_span(item.span),
|
cx.tcx.sess.source_map().guess_head_span(item.span),
|
||||||
"an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true",
|
"an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true",
|
||||||
|diag| {
|
|diag| {
|
||||||
// If the target type is likely foreign mention the orphan rules as it's a common source of confusion
|
// If the target type is likely foreign mention the orphan rules as it's a common source of
|
||||||
|
// confusion
|
||||||
if path_def_id(cx, target_ty.peel_refs()).map_or(true, |id| !id.is_local()) {
|
if path_def_id(cx, target_ty.peel_refs()).map_or(true, |id| !id.is_local()) {
|
||||||
diag.help(
|
diag.help(
|
||||||
"`impl From<Local> for Foreign` is allowed by the orphan rules, for more information see\n\
|
"`impl From<Local> for Foreign` is allowed by the orphan rules, for more information see\n\
|
||||||
|
@ -96,7 +97,10 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let message = format!("replace the `Into` implementation with `From<{}>`", middle_trait_ref.self_ty());
|
let message = format!(
|
||||||
|
"replace the `Into` implementation with `From<{}>`",
|
||||||
|
middle_trait_ref.self_ty()
|
||||||
|
);
|
||||||
if let Some(suggestions) = convert_to_from(cx, into_trait_seg, target_ty, self_ty, impl_item_ref) {
|
if let Some(suggestions) = convert_to_from(cx, into_trait_seg, target_ty, self_ty, impl_item_ref) {
|
||||||
diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable);
|
diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -40,14 +40,22 @@ declare_lint_pass!(FromRawWithVoidPtr => [FROM_RAW_WITH_VOID_PTR]);
|
||||||
impl LateLintPass<'_> for FromRawWithVoidPtr {
|
impl LateLintPass<'_> for FromRawWithVoidPtr {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||||
if let ExprKind::Call(box_from_raw, [arg]) = expr.kind
|
if let ExprKind::Call(box_from_raw, [arg]) = expr.kind
|
||||||
&& let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind
|
&& let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind
|
||||||
&& seg.ident.name == sym!(from_raw)
|
&& seg.ident.name == sym!(from_raw)
|
||||||
&& let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id))
|
&& let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id))
|
||||||
&& let arg_kind = cx.typeck_results().expr_ty(arg).kind()
|
&& let arg_kind = cx.typeck_results().expr_ty(arg).kind()
|
||||||
&& let RawPtr(TypeAndMut { ty, .. }) = arg_kind
|
&& let RawPtr(TypeAndMut { ty, .. }) = arg_kind
|
||||||
&& is_c_void(cx, *ty) {
|
&& is_c_void(cx, *ty)
|
||||||
|
{
|
||||||
let msg = format!("creating a `{type_str}` from a void raw pointer");
|
let msg = format!("creating a `{type_str}` from a void raw pointer");
|
||||||
span_lint_and_help(cx, FROM_RAW_WITH_VOID_PTR, expr.span, &msg, Some(arg.span), "cast this to a pointer of the appropriate type");
|
span_lint_and_help(
|
||||||
|
cx,
|
||||||
|
FROM_RAW_WITH_VOID_PTR,
|
||||||
|
expr.span,
|
||||||
|
&msg,
|
||||||
|
Some(arg.span),
|
||||||
|
"cast this to a pointer of the appropriate type",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,9 +118,10 @@ fn check_needless_must_use(
|
||||||
if sig.header.is_async() {
|
if sig.header.is_async() {
|
||||||
let infcx = cx.tcx.infer_ctxt().build();
|
let infcx = cx.tcx.infer_ctxt().build();
|
||||||
if let Some(future_ty) = infcx.get_impl_future_output_ty(return_ty(cx, item_id))
|
if let Some(future_ty) = infcx.get_impl_future_output_ty(return_ty(cx, item_id))
|
||||||
&& !is_must_use_ty(cx, future_ty) {
|
&& !is_must_use_ty(cx, future_ty)
|
||||||
return;
|
{
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
span_lint_and_help(
|
span_lint_and_help(
|
||||||
|
|
|
@ -21,7 +21,9 @@ fn result_err_ty<'tcx>(
|
||||||
) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> {
|
) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> {
|
||||||
if !in_external_macro(cx.sess(), item_span)
|
if !in_external_macro(cx.sess(), item_span)
|
||||||
&& let hir::FnRetTy::Return(hir_ty) = decl.output
|
&& let hir::FnRetTy::Return(hir_ty) = decl.output
|
||||||
&& let ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).instantiate_identity().output())
|
&& let ty = cx
|
||||||
|
.tcx
|
||||||
|
.erase_late_bound_regions(cx.tcx.fn_sig(id).instantiate_identity().output())
|
||||||
&& is_type_diagnostic_item(cx, ty, sym::Result)
|
&& is_type_diagnostic_item(cx, ty, sym::Result)
|
||||||
&& let ty::Adt(_, args) = ty.kind()
|
&& let ty::Adt(_, args) = ty.kind()
|
||||||
{
|
{
|
||||||
|
|
|
@ -76,7 +76,11 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
||||||
|
|
||||||
let ctxt = expr.span.ctxt();
|
let ctxt = expr.span.ctxt();
|
||||||
|
|
||||||
if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr)
|
if let Some(higher::If {
|
||||||
|
cond,
|
||||||
|
then,
|
||||||
|
r#else: Some(els),
|
||||||
|
}) = higher::If::hir(expr)
|
||||||
&& let ExprKind::Block(then_block, _) = then.kind
|
&& let ExprKind::Block(then_block, _) = then.kind
|
||||||
&& let Some(then_expr) = then_block.expr
|
&& let Some(then_expr) = then_block.expr
|
||||||
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
|
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
|
||||||
|
@ -86,7 +90,9 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
||||||
&& !contains_return(then_block.stmts)
|
&& !contains_return(then_block.stmts)
|
||||||
{
|
{
|
||||||
let mut app = Applicability::Unspecified;
|
let mut app = Applicability::Unspecified;
|
||||||
let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app).maybe_par().to_string();
|
let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app)
|
||||||
|
.maybe_par()
|
||||||
|
.to_string();
|
||||||
let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0;
|
let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0;
|
||||||
let mut method_body = if then_block.stmts.is_empty() {
|
let mut method_body = if then_block.stmts.is_empty() {
|
||||||
arg_snip.into_owned()
|
arg_snip.into_owned()
|
||||||
|
@ -100,9 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
||||||
"then"
|
"then"
|
||||||
};
|
};
|
||||||
|
|
||||||
let help = format!(
|
let help =
|
||||||
"consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`",
|
format!("consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`",);
|
||||||
);
|
|
||||||
span_lint_and_help(
|
span_lint_and_help(
|
||||||
cx,
|
cx,
|
||||||
IF_THEN_SOME_ELSE_NONE,
|
IF_THEN_SOME_ELSE_NONE,
|
||||||
|
|
|
@ -230,19 +230,24 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
|
||||||
// Example:
|
// Example:
|
||||||
// `impl Deref<Target = i32> + DerefMut<Target = u32>` is not allowed.
|
// `impl Deref<Target = i32> + DerefMut<Target = u32>` is not allowed.
|
||||||
// `DerefMut::Target` needs to match `Deref::Target`.
|
// `DerefMut::Target` needs to match `Deref::Target`.
|
||||||
let implied_bounds: Vec<_> = opaque_ty.bounds.iter().filter_map(|bound| {
|
let implied_bounds: Vec<_> = opaque_ty
|
||||||
if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
|
.bounds
|
||||||
&& let [.., path] = poly_trait.trait_ref.path.segments
|
.iter()
|
||||||
&& poly_trait.bound_generic_params.is_empty()
|
.filter_map(|bound| {
|
||||||
&& let Some(trait_def_id) = path.res.opt_def_id()
|
if let GenericBound::Trait(poly_trait, TraitBoundModifier::None) = bound
|
||||||
&& let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
|
&& let [.., path] = poly_trait.trait_ref.path.segments
|
||||||
&& !predicates.is_empty() // If the trait has no supertrait, there is nothing to add.
|
&& poly_trait.bound_generic_params.is_empty()
|
||||||
{
|
&& let Some(trait_def_id) = path.res.opt_def_id()
|
||||||
Some((bound.span(), path, predicates, trait_def_id))
|
&& let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
|
||||||
} else {
|
&& !predicates.is_empty()
|
||||||
None
|
// If the trait has no supertrait, there is nothing to add.
|
||||||
}
|
{
|
||||||
}).collect();
|
Some((bound.span(), path, predicates, trait_def_id))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec.
|
// Lint all bounds in the `impl Trait` type that are also in the `implied_bounds` vec.
|
||||||
// This involves some extra logic when generic arguments are present, since
|
// This involves some extra logic when generic arguments are present, since
|
||||||
|
@ -253,30 +258,31 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
|
||||||
&& let implied_args = path.args.map_or([].as_slice(), |a| a.args)
|
&& let implied_args = path.args.map_or([].as_slice(), |a| a.args)
|
||||||
&& let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings)
|
&& let implied_bindings = path.args.map_or([].as_slice(), |a| a.bindings)
|
||||||
&& let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id()
|
&& let Some(def_id) = poly_trait.trait_ref.path.res.opt_def_id()
|
||||||
&& let Some((implied_by_span, implied_by_args, implied_by_bindings)) = implied_bounds
|
&& let Some((implied_by_span, implied_by_args, implied_by_bindings)) =
|
||||||
.iter()
|
implied_bounds
|
||||||
.find_map(|&(span, implied_by_path, preds, implied_by_def_id)| {
|
.iter()
|
||||||
let implied_by_args = implied_by_path.args.map_or([].as_slice(), |a| a.args);
|
.find_map(|&(span, implied_by_path, preds, implied_by_def_id)| {
|
||||||
let implied_by_bindings = implied_by_path.args.map_or([].as_slice(), |a| a.bindings);
|
let implied_by_args = implied_by_path.args.map_or([].as_slice(), |a| a.args);
|
||||||
|
let implied_by_bindings = implied_by_path.args.map_or([].as_slice(), |a| a.bindings);
|
||||||
|
|
||||||
preds.iter().find_map(|(clause, _)| {
|
preds.iter().find_map(|(clause, _)| {
|
||||||
if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
|
if let ClauseKind::Trait(tr) = clause.kind().skip_binder()
|
||||||
&& tr.def_id() == def_id
|
&& tr.def_id() == def_id
|
||||||
&& is_same_generics(
|
&& is_same_generics(
|
||||||
cx.tcx,
|
cx.tcx,
|
||||||
tr.trait_ref.args,
|
tr.trait_ref.args,
|
||||||
implied_by_args,
|
implied_by_args,
|
||||||
implied_args,
|
implied_args,
|
||||||
implied_by_def_id,
|
implied_by_def_id,
|
||||||
def_id,
|
def_id,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Some((span, implied_by_args, implied_by_bindings))
|
Some((span, implied_by_args, implied_by_bindings))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
{
|
{
|
||||||
emit_lint(
|
emit_lint(
|
||||||
cx,
|
cx,
|
||||||
|
@ -286,7 +292,7 @@ fn check(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
|
||||||
implied_bindings,
|
implied_bindings,
|
||||||
implied_by_bindings,
|
implied_by_bindings,
|
||||||
implied_by_args,
|
implied_by_args,
|
||||||
implied_by_span
|
implied_by_span,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,9 +74,7 @@ impl LateLintPass<'_> for ItemsAfterTestModule {
|
||||||
|
|
||||||
if let Some(last) = after.last()
|
if let Some(last) = after.last()
|
||||||
&& after.iter().all(|&item| {
|
&& after.iter().all(|&item| {
|
||||||
!matches!(item.kind, ItemKind::Mod(_))
|
!matches!(item.kind, ItemKind::Mod(_)) && !item.span.from_expansion() && !is_from_proc_macro(cx, item)
|
||||||
&& !item.span.from_expansion()
|
|
||||||
&& !is_from_proc_macro(cx, item)
|
|
||||||
})
|
})
|
||||||
&& !fulfill_or_allowed(cx, ITEMS_AFTER_TEST_MODULE, after.iter().map(|item| item.hir_id()))
|
&& !fulfill_or_allowed(cx, ITEMS_AFTER_TEST_MODULE, after.iter().map(|item| item.hir_id()))
|
||||||
{
|
{
|
||||||
|
@ -99,10 +97,7 @@ impl LateLintPass<'_> for ItemsAfterTestModule {
|
||||||
{
|
{
|
||||||
diag.multipart_suggestion_with_style(
|
diag.multipart_suggestion_with_style(
|
||||||
"move the items to before the test module was defined",
|
"move the items to before the test module was defined",
|
||||||
vec![
|
vec![(prev.span.shrink_to_hi(), items), (items_span, String::new())],
|
||||||
(prev.span.shrink_to_hi(), items),
|
|
||||||
(items_span, String::new())
|
|
||||||
],
|
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
SuggestionStyle::HideCodeAlways,
|
SuggestionStyle::HideCodeAlways,
|
||||||
);
|
);
|
||||||
|
|
|
@ -155,17 +155,18 @@ impl LateLintPass<'_> for IterWithoutIntoIter {
|
||||||
if let ItemKind::Impl(imp) = item.kind
|
if let ItemKind::Impl(imp) = item.kind
|
||||||
&& let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind
|
&& let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind
|
||||||
&& let Some(trait_ref) = imp.of_trait
|
&& let Some(trait_ref) = imp.of_trait
|
||||||
&& trait_ref.trait_def_id().is_some_and(|did| cx.tcx.is_diagnostic_item(sym::IntoIterator, did))
|
&& trait_ref
|
||||||
|
.trait_def_id()
|
||||||
|
.is_some_and(|did| cx.tcx.is_diagnostic_item(sym::IntoIterator, did))
|
||||||
&& let &ty::Ref(_, ty, mtbl) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind()
|
&& let &ty::Ref(_, ty, mtbl) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind()
|
||||||
&& let expected_method_name = match mtbl {
|
&& let expected_method_name = match mtbl {
|
||||||
Mutability::Mut => sym::iter_mut,
|
Mutability::Mut => sym::iter_mut,
|
||||||
Mutability::Not => sym::iter,
|
Mutability::Not => sym::iter,
|
||||||
}
|
}
|
||||||
&& !deref_chain(cx, ty)
|
&& !deref_chain(cx, ty).any(|ty| {
|
||||||
.any(|ty| {
|
// We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method
|
||||||
// We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method
|
ty.peel_refs().is_slice() || adt_has_inherent_method(cx, ty, expected_method_name)
|
||||||
ty.peel_refs().is_slice() || adt_has_inherent_method(cx, ty, expected_method_name)
|
})
|
||||||
})
|
|
||||||
&& let Some(iter_assoc_span) = imp.items.iter().find_map(|item| {
|
&& let Some(iter_assoc_span) = imp.items.iter().find_map(|item| {
|
||||||
if item.ident.name == sym!(IntoIter) {
|
if item.ident.name == sym!(IntoIter) {
|
||||||
Some(cx.tcx.hir().impl_item(item.id).expect_type().span)
|
Some(cx.tcx.hir().impl_item(item.id).expect_type().span)
|
||||||
|
@ -185,7 +186,7 @@ impl LateLintPass<'_> for IterWithoutIntoIter {
|
||||||
// to avoid name ambiguities, as there might be an inherent into_iter method
|
// to avoid name ambiguities, as there might be an inherent into_iter method
|
||||||
// that we don't want to call.
|
// that we don't want to call.
|
||||||
let sugg = format!(
|
let sugg = format!(
|
||||||
"
|
"
|
||||||
impl {self_ty_without_ref} {{
|
impl {self_ty_without_ref} {{
|
||||||
fn {expected_method_name}({ref_self}self) -> {iter_ty} {{
|
fn {expected_method_name}({ref_self}self) -> {iter_ty} {{
|
||||||
<{ref_self}Self as IntoIterator>::into_iter(self)
|
<{ref_self}Self as IntoIterator>::into_iter(self)
|
||||||
|
@ -203,9 +204,9 @@ impl {self_ty_without_ref} {{
|
||||||
sugg,
|
sugg,
|
||||||
// Just like iter_without_into_iter, this suggestion is on a best effort basis
|
// Just like iter_without_into_iter, this suggestion is on a best effort basis
|
||||||
// and requires potentially adding lifetimes or moving them around.
|
// and requires potentially adding lifetimes or moving them around.
|
||||||
Applicability::Unspecified
|
Applicability::Unspecified,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,7 +242,7 @@ impl {self_ty_without_ref} {{
|
||||||
cx.tcx,
|
cx.tcx,
|
||||||
cx.param_env,
|
cx.param_env,
|
||||||
iterator_did,
|
iterator_did,
|
||||||
sym!(Item),
|
sym::Item,
|
||||||
[ret_ty],
|
[ret_ty],
|
||||||
)
|
)
|
||||||
// Only lint if the `IntoIterator` impl doesn't actually exist
|
// Only lint if the `IntoIterator` impl doesn't actually exist
|
||||||
|
@ -254,18 +255,22 @@ impl {self_ty_without_ref} {{
|
||||||
cx,
|
cx,
|
||||||
ITER_WITHOUT_INTO_ITER,
|
ITER_WITHOUT_INTO_ITER,
|
||||||
item.span,
|
item.span,
|
||||||
&format!("`{}` method without an `IntoIterator` impl for `{self_ty_snippet}`", item.ident),
|
&format!(
|
||||||
|
"`{}` method without an `IntoIterator` impl for `{self_ty_snippet}`",
|
||||||
|
item.ident
|
||||||
|
),
|
||||||
|diag| {
|
|diag| {
|
||||||
// Get the lower span of the `impl` block, and insert the suggestion right before it:
|
// Get the lower span of the `impl` block, and insert the suggestion right before it:
|
||||||
// impl X {
|
// impl X {
|
||||||
// ^ fn iter(&self) -> impl IntoIterator { ... }
|
// ^ fn iter(&self) -> impl IntoIterator { ... }
|
||||||
// }
|
// }
|
||||||
let span_behind_impl = cx.tcx
|
let span_behind_impl = cx
|
||||||
|
.tcx
|
||||||
.def_span(cx.tcx.hir().parent_id(item.hir_id()).owner.def_id)
|
.def_span(cx.tcx.hir().parent_id(item.hir_id()).owner.def_id)
|
||||||
.shrink_to_lo();
|
.shrink_to_lo();
|
||||||
|
|
||||||
let sugg = format!(
|
let sugg = format!(
|
||||||
"
|
"
|
||||||
impl IntoIterator for {self_ty_snippet} {{
|
impl IntoIterator for {self_ty_snippet} {{
|
||||||
type IntoIter = {ret_ty};
|
type IntoIter = {ret_ty};
|
||||||
type Item = {iter_ty};
|
type Item = {iter_ty};
|
||||||
|
@ -283,7 +288,8 @@ impl IntoIterator for {self_ty_snippet} {{
|
||||||
// such as adding some lifetimes in the associated types, or importing types.
|
// such as adding some lifetimes in the associated types, or importing types.
|
||||||
Applicability::Unspecified,
|
Applicability::Unspecified,
|
||||||
);
|
);
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,27 +39,35 @@ impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
|
||||||
impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
|
impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||||
if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
|
if let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
|
||||||
&& let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
|
&& let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
|
||||||
&& let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
|
&& let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
|
||||||
&& let Ok(element_count) = element_count.try_to_target_usize(cx.tcx)
|
&& let Ok(element_count) = element_count.try_to_target_usize(cx.tcx)
|
||||||
&& let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
|
&& let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
|
||||||
&& !cx.tcx.hir().parent_iter(expr.hir_id)
|
&& !cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, node)| {
|
||||||
.any(|(_, node)| matches!(node, Node::Item(Item { kind: ItemKind::Static(..), .. })))
|
matches!(
|
||||||
&& self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size) {
|
node,
|
||||||
span_lint_and_help(
|
Node::Item(Item {
|
||||||
cx,
|
kind: ItemKind::Static(..),
|
||||||
LARGE_STACK_ARRAYS,
|
..
|
||||||
expr.span,
|
})
|
||||||
&format!(
|
)
|
||||||
"allocating a local array larger than {} bytes",
|
})
|
||||||
self.maximum_allowed_size
|
&& self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size)
|
||||||
),
|
{
|
||||||
None,
|
span_lint_and_help(
|
||||||
&format!(
|
cx,
|
||||||
"consider allocating on the heap with `vec!{}.into_boxed_slice()`",
|
LARGE_STACK_ARRAYS,
|
||||||
snippet(cx, expr.span, "[...]")
|
expr.span,
|
||||||
),
|
&format!(
|
||||||
);
|
"allocating a local array larger than {} bytes",
|
||||||
}
|
self.maximum_allowed_size
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
&format!(
|
||||||
|
"consider allocating on the heap with `vec!{}.into_boxed_slice()`",
|
||||||
|
snippet(cx, expr.span, "[...]")
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,8 +181,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
|
|
||||||
let lit1 = peel_ref_operators(cx, lt.init);
|
let lit1 = peel_ref_operators(cx, lt.init);
|
||||||
let lit_str =
|
let lit_str = Sugg::hir_with_context(cx, lit1, lt.span.ctxt(), "_", &mut applicability).maybe_par();
|
||||||
Sugg::hir_with_context(cx, lit1, lt.span.ctxt(), "_", &mut applicability).maybe_par();
|
|
||||||
|
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
|
@ -288,18 +287,26 @@ enum LenOutput {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
|
fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
|
||||||
if let ty::Alias(_, alias_ty) = ty.kind() &&
|
if let ty::Alias(_, alias_ty) = ty.kind()
|
||||||
let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(alias_ty.def_id) &&
|
&& let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(alias_ty.def_id)
|
||||||
let Item { kind: ItemKind::OpaqueTy(opaque), .. } = item &&
|
&& let Item {
|
||||||
opaque.bounds.len() == 1 &&
|
kind: ItemKind::OpaqueTy(opaque),
|
||||||
let GenericBound::LangItemTrait(LangItem::Future, _, _, generic_args) = &opaque.bounds[0] &&
|
..
|
||||||
generic_args.bindings.len() == 1 &&
|
} = item
|
||||||
let TypeBindingKind::Equality {
|
&& opaque.bounds.len() == 1
|
||||||
term: rustc_hir::Term::Ty(rustc_hir::Ty {kind: TyKind::Path(QPath::Resolved(_, path)), .. }),
|
&& let GenericBound::LangItemTrait(LangItem::Future, _, _, generic_args) = &opaque.bounds[0]
|
||||||
} = &generic_args.bindings[0].kind &&
|
&& generic_args.bindings.len() == 1
|
||||||
path.segments.len() == 1 {
|
&& let TypeBindingKind::Equality {
|
||||||
return Some(&path.segments[0]);
|
term:
|
||||||
}
|
rustc_hir::Term::Ty(rustc_hir::Ty {
|
||||||
|
kind: TyKind::Path(QPath::Resolved(_, path)),
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
} = &generic_args.bindings[0].kind
|
||||||
|
&& path.segments.len() == 1
|
||||||
|
{
|
||||||
|
return Some(&path.segments[0]);
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,14 +159,15 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
||||||
binding or dropping explicitly with `std::mem::drop`",
|
binding or dropping explicitly with `std::mem::drop`",
|
||||||
);
|
);
|
||||||
} else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
|
} else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
|
||||||
&& implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) {
|
&& implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[])
|
||||||
|
{
|
||||||
span_lint_and_help(
|
span_lint_and_help(
|
||||||
cx,
|
cx,
|
||||||
LET_UNDERSCORE_FUTURE,
|
LET_UNDERSCORE_FUTURE,
|
||||||
local.span,
|
local.span,
|
||||||
"non-binding `let` on a future",
|
"non-binding `let` on a future",
|
||||||
None,
|
None,
|
||||||
"consider awaiting the future or dropping explicitly with `std::mem::drop`"
|
"consider awaiting the future or dropping explicitly with `std::mem::drop`",
|
||||||
);
|
);
|
||||||
} else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
|
} else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
|
||||||
span_lint_and_help(
|
span_lint_and_help(
|
||||||
|
@ -203,17 +204,17 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
span_lint_and_help(
|
span_lint_and_help(
|
||||||
cx,
|
cx,
|
||||||
LET_UNDERSCORE_UNTYPED,
|
LET_UNDERSCORE_UNTYPED,
|
||||||
local.span,
|
local.span,
|
||||||
"non-binding `let` without a type annotation",
|
"non-binding `let` without a type annotation",
|
||||||
Some(
|
Some(Span::new(
|
||||||
Span::new(local.pat.span.hi(),
|
local.pat.span.hi(),
|
||||||
local.pat.span.hi() + BytePos(1),
|
local.pat.span.hi() + BytePos(1),
|
||||||
local.pat.span.ctxt(),
|
local.pat.span.ctxt(),
|
||||||
local.pat.span.parent()
|
local.pat.span.parent(),
|
||||||
)),
|
)),
|
||||||
"consider adding a type annotation",
|
"consider adding a type annotation",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -517,9 +517,11 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
|
||||||
|
|
||||||
fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>) {
|
fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>) {
|
||||||
let trait_ref = &poly_tref.trait_ref;
|
let trait_ref = &poly_tref.trait_ref;
|
||||||
if let Some(id) = trait_ref.trait_def_id() && lang_items::FN_TRAITS.iter().any(|&item| {
|
if let Some(id) = trait_ref.trait_def_id()
|
||||||
self.cx.tcx.lang_items().get(item) == Some(id)
|
&& lang_items::FN_TRAITS
|
||||||
}) {
|
.iter()
|
||||||
|
.any(|&item| self.cx.tcx.lang_items().get(item) == Some(id))
|
||||||
|
{
|
||||||
let mut sub_visitor = RefVisitor::new(self.cx);
|
let mut sub_visitor = RefVisitor::new(self.cx);
|
||||||
sub_visitor.visit_trait_ref(trait_ref);
|
sub_visitor.visit_trait_ref(trait_ref);
|
||||||
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
|
self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
|
||||||
|
|
|
@ -59,41 +59,56 @@ declare_lint_pass!(LinesFilterMapOk => [LINES_FILTER_MAP_OK]);
|
||||||
|
|
||||||
impl LateLintPass<'_> for LinesFilterMapOk {
|
impl LateLintPass<'_> for LinesFilterMapOk {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||||
if let ExprKind::MethodCall(fm_method, fm_receiver, [fm_arg], fm_span) = expr.kind &&
|
if let ExprKind::MethodCall(fm_method, fm_receiver, [fm_arg], fm_span) = expr.kind
|
||||||
is_trait_method(cx, expr, sym::Iterator) &&
|
&& is_trait_method(cx, expr, sym::Iterator)
|
||||||
(fm_method.ident.as_str() == "filter_map" || fm_method.ident.as_str() == "flat_map") &&
|
&& (fm_method.ident.as_str() == "filter_map" || fm_method.ident.as_str() == "flat_map")
|
||||||
is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines)
|
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(fm_receiver), sym::IoLines)
|
||||||
{
|
{
|
||||||
let lint = match &fm_arg.kind {
|
let lint = match &fm_arg.kind {
|
||||||
// Detect `Result::ok`
|
// Detect `Result::ok`
|
||||||
ExprKind::Path(qpath) =>
|
ExprKind::Path(qpath) => cx
|
||||||
cx.qpath_res(qpath, fm_arg.hir_id).opt_def_id().map(|did|
|
.qpath_res(qpath, fm_arg.hir_id)
|
||||||
match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD)).unwrap_or_default(),
|
.opt_def_id()
|
||||||
|
.map(|did| match_def_path(cx, did, &paths::CORE_RESULT_OK_METHOD))
|
||||||
|
.unwrap_or_default(),
|
||||||
// Detect `|x| x.ok()`
|
// Detect `|x| x.ok()`
|
||||||
ExprKind::Closure(Closure { body, .. }) =>
|
ExprKind::Closure(Closure { body, .. }) => {
|
||||||
if let Body { params: [param], value, .. } = cx.tcx.hir().body(*body) &&
|
if let Body {
|
||||||
let ExprKind::MethodCall(method, receiver, [], _) = value.kind &&
|
params: [param], value, ..
|
||||||
path_to_local_id(receiver, param.pat.hir_id) &&
|
} = cx.tcx.hir().body(*body)
|
||||||
let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id)
|
&& let ExprKind::MethodCall(method, receiver, [], _) = value.kind
|
||||||
|
&& path_to_local_id(receiver, param.pat.hir_id)
|
||||||
|
&& let Some(method_did) = cx.typeck_results().type_dependent_def_id(value.hir_id)
|
||||||
{
|
{
|
||||||
is_diag_item_method(cx, method_did, sym::Result) && method.ident.as_str() == "ok"
|
is_diag_item_method(cx, method_did, sym::Result) && method.ident.as_str() == "ok"
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
if lint {
|
if lint {
|
||||||
span_lint_and_then(cx,
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
LINES_FILTER_MAP_OK,
|
LINES_FILTER_MAP_OK,
|
||||||
fm_span,
|
fm_span,
|
||||||
&format!("`{}()` will run forever if the iterator repeatedly produces an `Err`", fm_method.ident),
|
&format!(
|
||||||
|
"`{}()` will run forever if the iterator repeatedly produces an `Err`",
|
||||||
|
fm_method.ident
|
||||||
|
),
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.span_note(
|
diag.span_note(
|
||||||
fm_receiver.span,
|
fm_receiver.span,
|
||||||
"this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error");
|
"this expression returning a `std::io::Lines` may produce an infinite number of `Err` in case of a read error");
|
||||||
diag.span_suggestion(fm_span, "replace with", "map_while(Result::ok)", Applicability::MaybeIncorrect);
|
diag.span_suggestion(
|
||||||
});
|
fm_span,
|
||||||
}
|
"replace with",
|
||||||
|
"map_while(Result::ok)",
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,9 @@ fn is_ref_iterable<'tcx>(
|
||||||
let typeck = cx.typeck_results();
|
let typeck = cx.typeck_results();
|
||||||
if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
|
if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
|
||||||
&& let Some(fn_id) = typeck.type_dependent_def_id(call_expr.hir_id)
|
&& let Some(fn_id) = typeck.type_dependent_def_id(call_expr.hir_id)
|
||||||
&& let sig = cx.tcx.liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder())
|
&& let sig = cx
|
||||||
|
.tcx
|
||||||
|
.liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder())
|
||||||
&& let &[req_self_ty, req_res_ty] = &**sig.inputs_and_output
|
&& let &[req_self_ty, req_res_ty] = &**sig.inputs_and_output
|
||||||
&& let param_env = cx.tcx.param_env(fn_id)
|
&& let param_env = cx.tcx.param_env(fn_id)
|
||||||
&& implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, &[])
|
&& implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, &[])
|
||||||
|
@ -131,8 +133,9 @@ fn is_ref_iterable<'tcx>(
|
||||||
return Some((AdjustKind::None, self_ty));
|
return Some((AdjustKind::None, self_ty));
|
||||||
}
|
}
|
||||||
|
|
||||||
let res_ty = cx.tcx.erase_regions(EarlyBinder::bind(req_res_ty)
|
let res_ty = cx
|
||||||
.instantiate(cx.tcx, typeck.node_args(call_expr.hir_id)));
|
.tcx
|
||||||
|
.erase_regions(EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id)));
|
||||||
let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() {
|
let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() {
|
||||||
Some(mutbl)
|
Some(mutbl)
|
||||||
} else {
|
} else {
|
||||||
|
@ -157,7 +160,7 @@ fn is_ref_iterable<'tcx>(
|
||||||
let self_ty = if mutbl.is_mut() {
|
let self_ty = if mutbl.is_mut() {
|
||||||
self_ty
|
self_ty
|
||||||
} else {
|
} else {
|
||||||
Ty::new_ref(cx.tcx,region, TypeAndMut { ty, mutbl })
|
Ty::new_ref(cx.tcx, region, TypeAndMut { ty, mutbl })
|
||||||
};
|
};
|
||||||
if implements_trait(cx, self_ty, trait_id, &[])
|
if implements_trait(cx, self_ty, trait_id, &[])
|
||||||
&& let Some(ty) =
|
&& let Some(ty) =
|
||||||
|
@ -172,10 +175,7 @@ fn is_ref_iterable<'tcx>(
|
||||||
&& !self_ty.is_ref()
|
&& !self_ty.is_ref()
|
||||||
{
|
{
|
||||||
// Attempt to borrow
|
// Attempt to borrow
|
||||||
let self_ty = Ty::new_ref(cx.tcx,cx.tcx.lifetimes.re_erased, TypeAndMut {
|
let self_ty = Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, TypeAndMut { ty: self_ty, mutbl });
|
||||||
ty: self_ty,
|
|
||||||
mutbl,
|
|
||||||
});
|
|
||||||
if implements_trait(cx, self_ty, trait_id, &[])
|
if implements_trait(cx, self_ty, trait_id, &[])
|
||||||
&& let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
|
&& let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
|
||||||
&& ty == res_ty
|
&& ty == res_ty
|
||||||
|
@ -187,12 +187,14 @@ fn is_ref_iterable<'tcx>(
|
||||||
match adjustments {
|
match adjustments {
|
||||||
[] => Some((AdjustKind::None, self_ty)),
|
[] => Some((AdjustKind::None, self_ty)),
|
||||||
&[
|
&[
|
||||||
Adjustment { kind: Adjust::Deref(_), ..},
|
Adjustment {
|
||||||
|
kind: Adjust::Deref(_), ..
|
||||||
|
},
|
||||||
Adjustment {
|
Adjustment {
|
||||||
kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)),
|
kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)),
|
||||||
target,
|
target,
|
||||||
},
|
},
|
||||||
..
|
..,
|
||||||
] => {
|
] => {
|
||||||
if enforce_iter_loop_reborrow
|
if enforce_iter_loop_reborrow
|
||||||
&& target != self_ty
|
&& target != self_ty
|
||||||
|
@ -205,8 +207,14 @@ fn is_ref_iterable<'tcx>(
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
&[Adjustment { kind: Adjust::Deref(_), target }, ..] => {
|
&[
|
||||||
|
Adjustment {
|
||||||
|
kind: Adjust::Deref(_),
|
||||||
|
target,
|
||||||
|
},
|
||||||
|
..,
|
||||||
|
] => {
|
||||||
if is_copy(cx, target)
|
if is_copy(cx, target)
|
||||||
&& implements_trait(cx, target, trait_id, &[])
|
&& implements_trait(cx, target, trait_id, &[])
|
||||||
&& let Some(ty) =
|
&& let Some(ty) =
|
||||||
|
@ -217,13 +225,13 @@ fn is_ref_iterable<'tcx>(
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
&[
|
&[
|
||||||
Adjustment {
|
Adjustment {
|
||||||
kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)),
|
kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl)),
|
||||||
target,
|
target,
|
||||||
},
|
},
|
||||||
..
|
..,
|
||||||
] => {
|
] => {
|
||||||
if self_ty.is_ref()
|
if self_ty.is_ref()
|
||||||
&& implements_trait(cx, target, trait_id, &[])
|
&& implements_trait(cx, target, trait_id, &[])
|
||||||
|
@ -235,7 +243,7 @@ fn is_ref_iterable<'tcx>(
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -271,9 +271,9 @@ fn never_loop_expr<'tcx>(
|
||||||
NeverLoopResult::Normal
|
NeverLoopResult::Normal
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if let NeverLoopResult::Diverging = result &&
|
if let NeverLoopResult::Diverging = result
|
||||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) &&
|
&& let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||||
let Some(sym::todo_macro) = cx.tcx.get_diagnostic_name(macro_call.def_id)
|
&& let Some(sym::todo_macro) = cx.tcx.get_diagnostic_name(macro_call.def_id)
|
||||||
{
|
{
|
||||||
// We return MayContinueMainLoop here because we treat `todo!()`
|
// We return MayContinueMainLoop here because we treat `todo!()`
|
||||||
// as potentially containing any code, including a continue of the main loop.
|
// as potentially containing any code, including a continue of the main loop.
|
||||||
|
|
|
@ -64,7 +64,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert {
|
||||||
};
|
};
|
||||||
let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par();
|
let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par();
|
||||||
let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});");
|
let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});");
|
||||||
// we show to the user the suggestion without the comments, but when applying the fix, include the comments in the block
|
// we show to the user the suggestion without the comments, but when applying the fix, include the
|
||||||
|
// comments in the block
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
MANUAL_ASSERT,
|
MANUAL_ASSERT,
|
||||||
|
@ -77,16 +78,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualAssert {
|
||||||
expr.span.shrink_to_lo(),
|
expr.span.shrink_to_lo(),
|
||||||
"add comments back",
|
"add comments back",
|
||||||
comments,
|
comments,
|
||||||
applicability
|
applicability,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(expr.span, "try instead", sugg, applicability);
|
||||||
expr.span,
|
},
|
||||||
"try instead",
|
|
||||||
sugg,
|
|
||||||
applicability
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::intravisit::FnKind;
|
use rustc_hir::intravisit::FnKind;
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
AsyncCoroutineKind, Block, Body, Closure, CoroutineKind, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, GenericBound,
|
Block, Body, Closure, CoroutineKind, CoroutineSource, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, GenericBound,
|
||||||
ImplItem, Item, ItemKind, LifetimeName, Node, Term, TraitRef, Ty, TyKind, TypeBindingKind,
|
ImplItem, Item, ItemKind, LifetimeName, Node, Term, TraitRef, Ty, TyKind, TypeBindingKind,
|
||||||
};
|
};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
@ -188,7 +188,7 @@ fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>)
|
||||||
..
|
..
|
||||||
} = block_expr;
|
} = block_expr;
|
||||||
let closure_body = cx.tcx.hir().body(body);
|
let closure_body = cx.tcx.hir().body(body);
|
||||||
if closure_body.coroutine_kind == Some(CoroutineKind::Async(AsyncCoroutineKind::Block));
|
if closure_body.coroutine_kind == Some(CoroutineKind::Async(CoroutineSource::Block));
|
||||||
then {
|
then {
|
||||||
return Some(closure_body);
|
return Some(closure_body);
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,11 +225,11 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx
|
||||||
then,
|
then,
|
||||||
r#else: Some(else_if),
|
r#else: Some(else_if),
|
||||||
}) = If::hir(expr)
|
}) = If::hir(expr)
|
||||||
&& let Some(If {
|
&& let Some(If {
|
||||||
cond: else_if_cond,
|
cond: else_if_cond,
|
||||||
then: else_if_then,
|
then: else_if_then,
|
||||||
r#else: Some(else_body),
|
r#else: Some(else_body),
|
||||||
}) = If::hir(peel_blocks(else_if))
|
}) = If::hir(peel_blocks(else_if))
|
||||||
{
|
{
|
||||||
let params = is_clamp_meta_pattern(
|
let params = is_clamp_meta_pattern(
|
||||||
cx,
|
cx,
|
||||||
|
@ -275,7 +275,12 @@ fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> O
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(ClampSuggestion {
|
Some(ClampSuggestion {
|
||||||
params: InputMinMax { input, min, max, is_float },
|
params: InputMinMax {
|
||||||
|
input,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
is_float,
|
||||||
|
},
|
||||||
span: expr.span,
|
span: expr.span,
|
||||||
make_assignment: None,
|
make_assignment: None,
|
||||||
hir_with_ignore_attr: None,
|
hir_with_ignore_attr: None,
|
||||||
|
@ -346,11 +351,16 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)
|
||||||
("max", "min") => (inner_arg, outer_arg),
|
("max", "min") => (inner_arg, outer_arg),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(ClampSuggestion {
|
Some(ClampSuggestion {
|
||||||
params: InputMinMax { input, min, max, is_float },
|
params: InputMinMax {
|
||||||
|
input,
|
||||||
|
min,
|
||||||
|
max,
|
||||||
|
is_float,
|
||||||
|
},
|
||||||
span,
|
span,
|
||||||
make_assignment: None,
|
make_assignment: None,
|
||||||
hir_with_ignore_attr: None,
|
hir_with_ignore_attr: None,
|
||||||
|
@ -384,7 +394,8 @@ fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opt
|
||||||
// Find possible min/max branches
|
// Find possible min/max branches
|
||||||
let minmax_values = |a: &'tcx Arm<'tcx>| {
|
let minmax_values = |a: &'tcx Arm<'tcx>| {
|
||||||
if let PatKind::Binding(_, var_hir_id, _, None) = &a.pat.kind
|
if let PatKind::Binding(_, var_hir_id, _, None) = &a.pat.kind
|
||||||
&& let Some(Guard::If(e)) = a.guard {
|
&& let Some(Guard::If(e)) = a.guard
|
||||||
|
{
|
||||||
Some((e, var_hir_id, a.body))
|
Some((e, var_hir_id, a.body))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -441,18 +452,20 @@ fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) ->
|
||||||
.filter_map(|(maybe_set_first, maybe_set_second)| {
|
.filter_map(|(maybe_set_first, maybe_set_second)| {
|
||||||
if let StmtKind::Expr(first_expr) = *maybe_set_first
|
if let StmtKind::Expr(first_expr) = *maybe_set_first
|
||||||
&& let StmtKind::Expr(second_expr) = *maybe_set_second
|
&& let StmtKind::Expr(second_expr) = *maybe_set_second
|
||||||
&& let Some(If { cond: first_cond, then: first_then, r#else: None }) = If::hir(first_expr)
|
&& let Some(If {
|
||||||
&& let Some(If { cond: second_cond, then: second_then, r#else: None }) = If::hir(second_expr)
|
cond: first_cond,
|
||||||
&& let ExprKind::Assign(
|
then: first_then,
|
||||||
maybe_input_first_path,
|
r#else: None,
|
||||||
maybe_min_max_first,
|
}) = If::hir(first_expr)
|
||||||
_
|
&& let Some(If {
|
||||||
) = peel_blocks_with_stmt(first_then).kind
|
cond: second_cond,
|
||||||
&& let ExprKind::Assign(
|
then: second_then,
|
||||||
maybe_input_second_path,
|
r#else: None,
|
||||||
maybe_min_max_second,
|
}) = If::hir(second_expr)
|
||||||
_
|
&& let ExprKind::Assign(maybe_input_first_path, maybe_min_max_first, _) =
|
||||||
) = peel_blocks_with_stmt(second_then).kind
|
peel_blocks_with_stmt(first_then).kind
|
||||||
|
&& let ExprKind::Assign(maybe_input_second_path, maybe_min_max_second, _) =
|
||||||
|
peel_blocks_with_stmt(second_then).kind
|
||||||
&& eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path)
|
&& eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path)
|
||||||
&& let Some(first_bin) = BinaryOp::new(first_cond)
|
&& let Some(first_bin) = BinaryOp::new(first_cond)
|
||||||
&& let Some(second_bin) = BinaryOp::new(second_cond)
|
&& let Some(second_bin) = BinaryOp::new(second_cond)
|
||||||
|
@ -462,7 +475,7 @@ fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) ->
|
||||||
&second_bin,
|
&second_bin,
|
||||||
maybe_min_max_first,
|
maybe_min_max_first,
|
||||||
maybe_min_max_second,
|
maybe_min_max_second,
|
||||||
None
|
None,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Some(ClampSuggestion {
|
Some(ClampSuggestion {
|
||||||
|
@ -505,16 +518,9 @@ fn is_if_elseif_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) ->
|
||||||
then: else_if_then,
|
then: else_if_then,
|
||||||
r#else: None,
|
r#else: None,
|
||||||
}) = If::hir(peel_blocks(else_if))
|
}) = If::hir(peel_blocks(else_if))
|
||||||
&& let ExprKind::Assign(
|
&& let ExprKind::Assign(maybe_input_first_path, maybe_min_max_first, _) = peel_blocks_with_stmt(then).kind
|
||||||
maybe_input_first_path,
|
&& let ExprKind::Assign(maybe_input_second_path, maybe_min_max_second, _) =
|
||||||
maybe_min_max_first,
|
peel_blocks_with_stmt(else_if_then).kind
|
||||||
_
|
|
||||||
) = peel_blocks_with_stmt(then).kind
|
|
||||||
&& let ExprKind::Assign(
|
|
||||||
maybe_input_second_path,
|
|
||||||
maybe_min_max_second,
|
|
||||||
_
|
|
||||||
) = peel_blocks_with_stmt(else_if_then).kind
|
|
||||||
{
|
{
|
||||||
let params = is_clamp_meta_pattern(
|
let params = is_clamp_meta_pattern(
|
||||||
cx,
|
cx,
|
||||||
|
|
|
@ -114,46 +114,40 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
span_lint_and_then(
|
span_lint_and_then(cx, variant.lint(), expr.span, variant.msg(), |diag| {
|
||||||
cx,
|
match variant {
|
||||||
variant.lint(),
|
Variant::ManualIsInfinite => {
|
||||||
expr.span,
|
diag.span_suggestion(
|
||||||
variant.msg(),
|
expr.span,
|
||||||
|diag| {
|
"use the dedicated method instead",
|
||||||
match variant {
|
format!("{local_snippet}.is_infinite()"),
|
||||||
Variant::ManualIsInfinite => {
|
Applicability::MachineApplicable,
|
||||||
diag.span_suggestion(
|
);
|
||||||
expr.span,
|
},
|
||||||
"use the dedicated method instead",
|
Variant::ManualIsFinite => {
|
||||||
format!("{local_snippet}.is_infinite()"),
|
// TODO: There's probably some better way to do this, i.e., create
|
||||||
Applicability::MachineApplicable,
|
// multiple suggestions with notes between each of them
|
||||||
);
|
diag.span_suggestion_verbose(
|
||||||
},
|
expr.span,
|
||||||
Variant::ManualIsFinite => {
|
"use the dedicated method instead",
|
||||||
// TODO: There's probably some better way to do this, i.e., create
|
format!("{local_snippet}.is_finite()"),
|
||||||
// multiple suggestions with notes between each of them
|
Applicability::MaybeIncorrect,
|
||||||
diag.span_suggestion_verbose(
|
)
|
||||||
expr.span,
|
.span_suggestion_verbose(
|
||||||
"use the dedicated method instead",
|
expr.span,
|
||||||
format!("{local_snippet}.is_finite()"),
|
"this will alter how it handles NaN; if that is a problem, use instead",
|
||||||
Applicability::MaybeIncorrect,
|
format!("{local_snippet}.is_finite() || {local_snippet}.is_nan()"),
|
||||||
)
|
Applicability::MaybeIncorrect,
|
||||||
.span_suggestion_verbose(
|
)
|
||||||
expr.span,
|
.span_suggestion_verbose(
|
||||||
"this will alter how it handles NaN; if that is a problem, use instead",
|
expr.span,
|
||||||
format!("{local_snippet}.is_finite() || {local_snippet}.is_nan()"),
|
"or, for conciseness",
|
||||||
Applicability::MaybeIncorrect,
|
format!("!{local_snippet}.is_infinite()"),
|
||||||
)
|
Applicability::MaybeIncorrect,
|
||||||
.span_suggestion_verbose(
|
);
|
||||||
expr.span,
|
},
|
||||||
"or, for conciseness",
|
}
|
||||||
format!("!{local_snippet}.is_infinite()"),
|
});
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,12 +117,11 @@ impl LateLintPass<'_> for ManualHashOne {
|
||||||
finish_expr.span,
|
finish_expr.span,
|
||||||
// `needless_borrows_for_generic_args` will take care of
|
// `needless_borrows_for_generic_args` will take care of
|
||||||
// removing the `&` when it isn't needed
|
// removing the `&` when it isn't needed
|
||||||
format!("{build_hasher}.hash_one(&{hashed_value})")
|
format!("{build_hasher}.hash_one(&{hashed_value})"),
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -98,15 +98,20 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(macro_call) = root_macro_call(expr.span)
|
if let Some(macro_call) = root_macro_call(expr.span)
|
||||||
&& is_matches_macro(cx, macro_call.def_id) {
|
&& is_matches_macro(cx, macro_call.def_id)
|
||||||
|
{
|
||||||
if let ExprKind::Match(recv, [arm, ..], _) = expr.kind {
|
if let ExprKind::Match(recv, [arm, ..], _) = expr.kind {
|
||||||
let range = check_pat(&arm.pat.kind);
|
let range = check_pat(&arm.pat.kind);
|
||||||
check_is_ascii(cx, macro_call.span, recv, &range);
|
check_is_ascii(cx, macro_call.span, recv, &range);
|
||||||
}
|
}
|
||||||
} else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind
|
} else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind
|
||||||
&& path.ident.name == sym!(contains)
|
&& path.ident.name == sym!(contains)
|
||||||
&& let Some(higher::Range { start: Some(start), end: Some(end), limits: RangeLimits::Closed })
|
&& let Some(higher::Range {
|
||||||
= higher::Range::hir(receiver) {
|
start: Some(start),
|
||||||
|
end: Some(end),
|
||||||
|
limits: RangeLimits::Closed,
|
||||||
|
}) = higher::Range::hir(receiver)
|
||||||
|
{
|
||||||
let range = check_range(start, end);
|
let range = check_range(start, end);
|
||||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, e) = arg.kind {
|
if let ExprKind::AddrOf(BorrowKind::Ref, _, e) = arg.kind {
|
||||||
check_is_ascii(cx, expr.span, e, &range);
|
check_is_ascii(cx, expr.span, e, &range);
|
||||||
|
@ -168,7 +173,8 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
|
||||||
|
|
||||||
fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
|
fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
|
||||||
if let ExprKind::Lit(start_lit) = &start.kind
|
if let ExprKind::Lit(start_lit) = &start.kind
|
||||||
&& let ExprKind::Lit(end_lit) = &end.kind {
|
&& let ExprKind::Lit(end_lit) = &end.kind
|
||||||
|
{
|
||||||
match (&start_lit.node, &end_lit.node) {
|
match (&start_lit.node, &end_lit.node) {
|
||||||
(Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar,
|
(Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar,
|
||||||
(Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar,
|
(Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar,
|
||||||
|
|
|
@ -56,21 +56,20 @@ impl<'tcx> QuestionMark {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let StmtKind::Local(local) = stmt.kind &&
|
if let StmtKind::Local(local) = stmt.kind
|
||||||
let Some(init) = local.init &&
|
&& let Some(init) = local.init
|
||||||
local.els.is_none() &&
|
&& local.els.is_none()
|
||||||
local.ty.is_none() &&
|
&& local.ty.is_none()
|
||||||
init.span.eq_ctxt(stmt.span) &&
|
&& init.span.eq_ctxt(stmt.span)
|
||||||
let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init)
|
&& let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init)
|
||||||
{
|
{
|
||||||
match if_let_or_match {
|
match if_let_or_match {
|
||||||
IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => {
|
IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => {
|
||||||
if
|
if let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then)
|
||||||
let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then) &&
|
&& let Some(if_else) = if_else
|
||||||
let Some(if_else) = if_else &&
|
&& expr_diverges(cx, if_else)
|
||||||
expr_diverges(cx, if_else) &&
|
&& let qm_allowed = is_lint_allowed(cx, QUESTION_MARK, stmt.hir_id)
|
||||||
let qm_allowed = is_lint_allowed(cx, QUESTION_MARK, stmt.hir_id) &&
|
&& (qm_allowed || pat_and_expr_can_be_question_mark(cx, let_pat, if_else).is_none())
|
||||||
(qm_allowed || pat_and_expr_can_be_question_mark(cx, let_pat, if_else).is_none())
|
|
||||||
{
|
{
|
||||||
emit_manual_let_else(cx, stmt.span, if_let_expr, &ident_map, let_pat, if_else);
|
emit_manual_let_else(cx, stmt.span, if_let_expr, &ident_map, let_pat, if_else);
|
||||||
}
|
}
|
||||||
|
@ -96,7 +95,9 @@ impl<'tcx> QuestionMark {
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types));
|
.find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types));
|
||||||
let Some((idx, diverging_arm)) = diverging_arm_opt else { return; };
|
let Some((idx, diverging_arm)) = diverging_arm_opt else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
// If the non-diverging arm is the first one, its pattern can be reused in a let/else statement.
|
// If the non-diverging arm is the first one, its pattern can be reused in a let/else statement.
|
||||||
// However, if it arrives in second position, its pattern may cover some cases already covered
|
// However, if it arrives in second position, its pattern may cover some cases already covered
|
||||||
// by the diverging one.
|
// by the diverging one.
|
||||||
|
@ -106,7 +107,7 @@ impl<'tcx> QuestionMark {
|
||||||
}
|
}
|
||||||
let pat_arm = &arms[1 - idx];
|
let pat_arm = &arms[1 - idx];
|
||||||
let Some(ident_map) = expr_simple_identity_map(local.pat, pat_arm.pat, pat_arm.body) else {
|
let Some(ident_map) = expr_simple_identity_map(local.pat, pat_arm.pat, pat_arm.body) else {
|
||||||
return
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
emit_manual_let_else(cx, stmt.span, match_expr, &ident_map, pat_arm.pat, diverging_arm.body);
|
emit_manual_let_else(cx, stmt.span, match_expr, &ident_map, pat_arm.pat, diverging_arm.body);
|
||||||
|
@ -217,8 +218,8 @@ fn replace_in_pattern(
|
||||||
let fields = fields
|
let fields = fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|fld| {
|
.map(|fld| {
|
||||||
if let PatKind::Binding(_, _, name, None) = fld.pat.kind &&
|
if let PatKind::Binding(_, _, name, None) = fld.pat.kind
|
||||||
let Some(pat_to_put) = ident_map.get(&name.name)
|
&& let Some(pat_to_put) = ident_map.get(&name.name)
|
||||||
{
|
{
|
||||||
let (sn_fld_name, _) = snippet_with_context(cx, fld.ident.span, span.ctxt(), "", app);
|
let (sn_fld_name, _) = snippet_with_context(cx, fld.ident.span, span.ctxt(), "", app);
|
||||||
let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app);
|
let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app);
|
||||||
|
@ -464,8 +465,8 @@ fn expr_simple_identity_map<'a, 'hir>(
|
||||||
}
|
}
|
||||||
let mut ident_map = FxHashMap::default();
|
let mut ident_map = FxHashMap::default();
|
||||||
for (sub_pat, path) in sub_pats.iter().zip(paths.iter()) {
|
for (sub_pat, path) in sub_pats.iter().zip(paths.iter()) {
|
||||||
if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind &&
|
if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind
|
||||||
let [path_seg] = path.segments
|
&& let [path_seg] = path.segments
|
||||||
{
|
{
|
||||||
let ident = path_seg.ident;
|
let ident = path_seg.ident;
|
||||||
if !pat_bindings.remove(&ident) {
|
if !pat_bindings.remove(&ident) {
|
||||||
|
|
|
@ -47,27 +47,27 @@ impl_lint_pass!(ManualMainSeparatorStr => [MANUAL_MAIN_SEPARATOR_STR]);
|
||||||
|
|
||||||
impl LateLintPass<'_> for ManualMainSeparatorStr {
|
impl LateLintPass<'_> for ManualMainSeparatorStr {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||||
if self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR) &&
|
if self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR)
|
||||||
let (target, _) = peel_hir_expr_refs(expr) &&
|
&& let (target, _) = peel_hir_expr_refs(expr)
|
||||||
is_trait_method(cx, target, sym::ToString) &&
|
&& is_trait_method(cx, target, sym::ToString)
|
||||||
let ExprKind::MethodCall(path, receiver, &[], _) = target.kind &&
|
&& let ExprKind::MethodCall(path, receiver, &[], _) = target.kind
|
||||||
path.ident.name == sym::to_string &&
|
&& path.ident.name == sym::to_string
|
||||||
let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind &&
|
&& let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind
|
||||||
let Res::Def(DefKind::Const, receiver_def_id) = path.res &&
|
&& let Res::Def(DefKind::Const, receiver_def_id) = path.res
|
||||||
match_def_path(cx, receiver_def_id, &paths::PATH_MAIN_SEPARATOR) &&
|
&& match_def_path(cx, receiver_def_id, &paths::PATH_MAIN_SEPARATOR)
|
||||||
let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind() &&
|
&& let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind()
|
||||||
ty.is_str()
|
&& ty.is_str()
|
||||||
{
|
{
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
MANUAL_MAIN_SEPARATOR_STR,
|
MANUAL_MAIN_SEPARATOR_STR,
|
||||||
expr.span,
|
expr.span,
|
||||||
"taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`",
|
"taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`",
|
||||||
"replace with",
|
"replace with",
|
||||||
"std::path::MAIN_SEPARATOR_STR".to_owned(),
|
"std::path::MAIN_SEPARATOR_STR".to_owned(),
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extract_msrv_attr!(LateContext);
|
extract_msrv_attr!(LateContext);
|
||||||
|
|
|
@ -138,7 +138,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
diag.span_help(field.span, "remove this field");
|
diag.span_help(field.span, "remove this field");
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,30 +76,31 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
|
||||||
// Also ensures the const is nonzero since zero can't be a divisor
|
// Also ensures the const is nonzero since zero can't be a divisor
|
||||||
&& const1 == const2 && const2 == const3
|
&& const1 == const2 && const2 == const3
|
||||||
&& let Some(hir_id) = path_to_local(expr3)
|
&& let Some(hir_id) = path_to_local(expr3)
|
||||||
&& let Some(Node::Pat(_)) = cx.tcx.hir().find(hir_id) {
|
&& let Some(Node::Pat(_)) = cx.tcx.hir().find(hir_id)
|
||||||
// Apply only to params or locals with annotated types
|
{
|
||||||
match cx.tcx.hir().find_parent(hir_id) {
|
// Apply only to params or locals with annotated types
|
||||||
Some(Node::Param(..)) => (),
|
match cx.tcx.hir().find_parent(hir_id) {
|
||||||
Some(Node::Local(local)) => {
|
Some(Node::Param(..)) => (),
|
||||||
let Some(ty) = local.ty else { return };
|
Some(Node::Local(local)) => {
|
||||||
if matches!(ty.kind, TyKind::Infer) {
|
let Some(ty) = local.ty else { return };
|
||||||
return;
|
if matches!(ty.kind, TyKind::Infer) {
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
_ => return,
|
},
|
||||||
};
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
let mut app = Applicability::MachineApplicable;
|
let mut app = Applicability::MachineApplicable;
|
||||||
let rem_of = snippet_with_context(cx, expr3.span, ctxt, "_", &mut app).0;
|
let rem_of = snippet_with_context(cx, expr3.span, ctxt, "_", &mut app).0;
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
MANUAL_REM_EUCLID,
|
MANUAL_REM_EUCLID,
|
||||||
expr.span,
|
expr.span,
|
||||||
"manual `rem_euclid` implementation",
|
"manual `rem_euclid` implementation",
|
||||||
"consider using",
|
"consider using",
|
||||||
format!("{rem_of}.rem_euclid({const1})"),
|
format!("{rem_of}.rem_euclid({const1})"),
|
||||||
app,
|
app,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,8 @@ fn check_into_iter(
|
||||||
&& let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id)
|
&& let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id)
|
||||||
&& Some(into_iter_def_id) == cx.tcx.lang_items().into_iter_fn()
|
&& Some(into_iter_def_id) == cx.tcx.lang_items().into_iter_fn()
|
||||||
&& match_acceptable_type(cx, left_expr, msrv)
|
&& match_acceptable_type(cx, left_expr, msrv)
|
||||||
&& SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) {
|
&& SpanlessEq::new(cx).eq_expr(left_expr, struct_expr)
|
||||||
|
{
|
||||||
suggest(cx, parent_expr, left_expr, target_expr);
|
suggest(cx, parent_expr, left_expr, target_expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +121,8 @@ fn check_iter(
|
||||||
&& let Some(iter_expr_def_id) = cx.typeck_results().type_dependent_def_id(iter_expr.hir_id)
|
&& let Some(iter_expr_def_id) = cx.typeck_results().type_dependent_def_id(iter_expr.hir_id)
|
||||||
&& match_acceptable_def_path(cx, iter_expr_def_id)
|
&& match_acceptable_def_path(cx, iter_expr_def_id)
|
||||||
&& match_acceptable_type(cx, left_expr, msrv)
|
&& match_acceptable_type(cx, left_expr, msrv)
|
||||||
&& SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) {
|
&& SpanlessEq::new(cx).eq_expr(left_expr, struct_expr)
|
||||||
|
{
|
||||||
suggest(cx, parent_expr, left_expr, filter_expr);
|
suggest(cx, parent_expr, left_expr, filter_expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,33 +146,35 @@ fn check_to_owned(
|
||||||
&& match_def_path(cx, chars_expr_def_id, &paths::STR_CHARS)
|
&& match_def_path(cx, chars_expr_def_id, &paths::STR_CHARS)
|
||||||
&& let ty = cx.typeck_results().expr_ty(str_expr).peel_refs()
|
&& let ty = cx.typeck_results().expr_ty(str_expr).peel_refs()
|
||||||
&& is_type_lang_item(cx, ty, hir::LangItem::String)
|
&& is_type_lang_item(cx, ty, hir::LangItem::String)
|
||||||
&& SpanlessEq::new(cx).eq_expr(left_expr, str_expr) {
|
&& SpanlessEq::new(cx).eq_expr(left_expr, str_expr)
|
||||||
|
{
|
||||||
suggest(cx, parent_expr, left_expr, filter_expr);
|
suggest(cx, parent_expr, left_expr, filter_expr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, filter_expr: &hir::Expr<'_>) {
|
fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, filter_expr: &hir::Expr<'_>) {
|
||||||
if let hir::ExprKind::MethodCall(_, _, [closure], _) = filter_expr.kind
|
if let hir::ExprKind::MethodCall(_, _, [closure], _) = filter_expr.kind
|
||||||
&& let hir::ExprKind::Closure(&hir::Closure { body, ..}) = closure.kind
|
&& let hir::ExprKind::Closure(&hir::Closure { body, .. }) = closure.kind
|
||||||
&& let filter_body = cx.tcx.hir().body(body)
|
&& let filter_body = cx.tcx.hir().body(body)
|
||||||
&& let [filter_params] = filter_body.params
|
&& let [filter_params] = filter_body.params
|
||||||
&& let Some(sugg) = match filter_params.pat.kind {
|
&& let Some(sugg) = match filter_params.pat.kind {
|
||||||
hir::PatKind::Binding(_, _, filter_param_ident, None) => {
|
hir::PatKind::Binding(_, _, filter_param_ident, None) => Some(format!(
|
||||||
Some(format!("{}.retain(|{filter_param_ident}| {})", snippet(cx, left_expr.span, ".."), snippet(cx, filter_body.value.span, "..")))
|
"{}.retain(|{filter_param_ident}| {})",
|
||||||
|
snippet(cx, left_expr.span, ".."),
|
||||||
|
snippet(cx, filter_body.value.span, "..")
|
||||||
|
)),
|
||||||
|
hir::PatKind::Tuple([key_pat, value_pat], _) => make_sugg(cx, key_pat, value_pat, left_expr, filter_body),
|
||||||
|
hir::PatKind::Ref(pat, _) => match pat.kind {
|
||||||
|
hir::PatKind::Binding(_, _, filter_param_ident, None) => Some(format!(
|
||||||
|
"{}.retain(|{filter_param_ident}| {})",
|
||||||
|
snippet(cx, left_expr.span, ".."),
|
||||||
|
snippet(cx, filter_body.value.span, "..")
|
||||||
|
)),
|
||||||
|
_ => None,
|
||||||
},
|
},
|
||||||
hir::PatKind::Tuple([key_pat, value_pat], _) => {
|
_ => None,
|
||||||
make_sugg(cx, key_pat, value_pat, left_expr, filter_body)
|
}
|
||||||
},
|
{
|
||||||
hir::PatKind::Ref(pat, _) => {
|
|
||||||
match pat.kind {
|
|
||||||
hir::PatKind::Binding(_, _, filter_param_ident, None) => {
|
|
||||||
Some(format!("{}.retain(|{filter_param_ident}| {})", snippet(cx, left_expr.span, ".."), snippet(cx, filter_body.value.span, "..")))
|
|
||||||
},
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => None
|
|
||||||
} {
|
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
MANUAL_RETAIN,
|
MANUAL_RETAIN,
|
||||||
|
@ -178,7 +182,7 @@ fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::E
|
||||||
"this expression can be written more simply using `.retain()`",
|
"this expression can be written more simply using `.retain()`",
|
||||||
"consider calling `.retain()` instead",
|
"consider calling `.retain()` instead",
|
||||||
sugg,
|
sugg,
|
||||||
Applicability::MachineApplicable
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,9 +65,9 @@ impl LateLintPass<'_> for ManualStringNew {
|
||||||
|
|
||||||
/// Checks if an expression's kind corresponds to an empty &str.
|
/// Checks if an expression's kind corresponds to an empty &str.
|
||||||
fn is_expr_kind_empty_str(expr_kind: &ExprKind<'_>) -> bool {
|
fn is_expr_kind_empty_str(expr_kind: &ExprKind<'_>) -> bool {
|
||||||
if let ExprKind::Lit(lit) = expr_kind &&
|
if let ExprKind::Lit(lit) = expr_kind
|
||||||
let LitKind::Str(value, _) = lit.node &&
|
&& let LitKind::Str(value, _) = lit.node
|
||||||
value == symbol::kw::Empty
|
&& value == symbol::kw::Empty
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -110,23 +110,22 @@ fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_
|
||||||
if let ExprKind::Path(qpath) = &func.kind {
|
if let ExprKind::Path(qpath) = &func.kind {
|
||||||
if let QPath::TypeRelative(_, _) = qpath {
|
if let QPath::TypeRelative(_, _) = qpath {
|
||||||
// String::from(...) or String::try_from(...)
|
// String::from(...) or String::try_from(...)
|
||||||
if let QPath::TypeRelative(ty, path_seg) = qpath &&
|
if let QPath::TypeRelative(ty, path_seg) = qpath
|
||||||
[sym::from, sym::try_from].contains(&path_seg.ident.name) &&
|
&& [sym::from, sym::try_from].contains(&path_seg.ident.name)
|
||||||
let TyKind::Path(qpath) = &ty.kind &&
|
&& let TyKind::Path(qpath) = &ty.kind
|
||||||
let QPath::Resolved(_, path) = qpath &&
|
&& let QPath::Resolved(_, path) = qpath
|
||||||
let [path_seg] = path.segments &&
|
&& let [path_seg] = path.segments
|
||||||
path_seg.ident.name == sym::String &&
|
&& path_seg.ident.name == sym::String
|
||||||
is_expr_kind_empty_str(arg_kind)
|
&& is_expr_kind_empty_str(arg_kind)
|
||||||
{
|
{
|
||||||
warn_then_suggest(cx, span);
|
warn_then_suggest(cx, span);
|
||||||
}
|
}
|
||||||
} else if let QPath::Resolved(_, path) = qpath {
|
} else if let QPath::Resolved(_, path) = qpath {
|
||||||
// From::from(...) or TryFrom::try_from(...)
|
// From::from(...) or TryFrom::try_from(...)
|
||||||
if let [path_seg1, path_seg2] = path.segments &&
|
if let [path_seg1, path_seg2] = path.segments
|
||||||
is_expr_kind_empty_str(arg_kind) && (
|
&& is_expr_kind_empty_str(arg_kind)
|
||||||
(path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) ||
|
&& ((path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from)
|
||||||
(path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)
|
|| (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from))
|
||||||
)
|
|
||||||
{
|
{
|
||||||
warn_then_suggest(cx, span);
|
warn_then_suggest(cx, span);
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,12 +99,20 @@ pub(super) fn check_match<'tcx>(
|
||||||
) {
|
) {
|
||||||
let ty = cx.typeck_results().expr_ty(expr);
|
let ty = cx.typeck_results().expr_ty(expr);
|
||||||
if is_type_diagnostic_item(cx, ty, sym::Option)
|
if is_type_diagnostic_item(cx, ty, sym::Option)
|
||||||
&& let [first_arm, second_arm] = arms
|
&& let [first_arm, second_arm] = arms
|
||||||
&& first_arm.guard.is_none()
|
&& first_arm.guard.is_none()
|
||||||
&& second_arm.guard.is_none()
|
&& second_arm.guard.is_none()
|
||||||
{
|
{
|
||||||
check(cx, expr, scrutinee, first_arm.pat, first_arm.body, Some(second_arm.pat), second_arm.body);
|
check(
|
||||||
}
|
cx,
|
||||||
|
expr,
|
||||||
|
scrutinee,
|
||||||
|
first_arm.pat,
|
||||||
|
first_arm.body,
|
||||||
|
Some(second_arm.pat),
|
||||||
|
second_arm.body,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn check_if_let<'tcx>(
|
pub(super) fn check_if_let<'tcx>(
|
||||||
|
|
|
@ -27,8 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
|
||||||
arm,
|
arm,
|
||||||
Arm {
|
Arm {
|
||||||
pat: Pat {
|
pat: Pat {
|
||||||
kind: PatKind::Wild,
|
kind: PatKind::Wild, ..
|
||||||
..
|
|
||||||
},
|
},
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
|
@ -42,14 +41,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
|
||||||
(PatKind::Ref(..), None) | (_, Some(_)) => continue,
|
(PatKind::Ref(..), None) | (_, Some(_)) => continue,
|
||||||
_ => arm.pat.span,
|
_ => arm.pat.span,
|
||||||
};
|
};
|
||||||
emit_redundant_guards(
|
emit_redundant_guards(cx, outer_arm, if_expr.span, pat_span, &binding, arm.guard);
|
||||||
cx,
|
|
||||||
outer_arm,
|
|
||||||
if_expr.span,
|
|
||||||
pat_span,
|
|
||||||
&binding,
|
|
||||||
arm.guard,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// `Some(x) if let Some(2) = x`
|
// `Some(x) if let Some(2) = x`
|
||||||
else if let Guard::IfLet(let_expr) = guard
|
else if let Guard::IfLet(let_expr) = guard
|
||||||
|
@ -60,14 +52,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
|
||||||
(PatKind::Ref(..), None) | (_, Some(_)) => continue,
|
(PatKind::Ref(..), None) | (_, Some(_)) => continue,
|
||||||
_ => let_expr.pat.span,
|
_ => let_expr.pat.span,
|
||||||
};
|
};
|
||||||
emit_redundant_guards(
|
emit_redundant_guards(cx, outer_arm, let_expr.span, pat_span, &binding, None);
|
||||||
cx,
|
|
||||||
outer_arm,
|
|
||||||
let_expr.span,
|
|
||||||
pat_span,
|
|
||||||
&binding,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
// `Some(x) if x == Some(2)`
|
// `Some(x) if x == Some(2)`
|
||||||
// `Some(x) if Some(2) == x`
|
// `Some(x) if Some(2) == x`
|
||||||
|
@ -93,14 +78,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
|
||||||
(ExprKind::AddrOf(..), None) | (_, Some(_)) => continue,
|
(ExprKind::AddrOf(..), None) | (_, Some(_)) => continue,
|
||||||
_ => pat.span,
|
_ => pat.span,
|
||||||
};
|
};
|
||||||
emit_redundant_guards(
|
emit_redundant_guards(cx, outer_arm, if_expr.span, pat_span, &binding, None);
|
||||||
cx,
|
|
||||||
outer_arm,
|
|
||||||
if_expr.span,
|
|
||||||
pat_span,
|
|
||||||
&binding,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +94,9 @@ fn get_pat_binding<'tcx>(
|
||||||
guard_expr: &Expr<'_>,
|
guard_expr: &Expr<'_>,
|
||||||
outer_arm: &Arm<'tcx>,
|
outer_arm: &Arm<'tcx>,
|
||||||
) -> Option<PatBindingInfo> {
|
) -> Option<PatBindingInfo> {
|
||||||
if let Some(local) = path_to_local(guard_expr) && !is_local_used(cx, outer_arm.body, local) {
|
if let Some(local) = path_to_local(guard_expr)
|
||||||
|
&& !is_local_used(cx, outer_arm.body, local)
|
||||||
|
{
|
||||||
let mut span = None;
|
let mut span = None;
|
||||||
let mut byref_ident = None;
|
let mut byref_ident = None;
|
||||||
let mut multiple_bindings = false;
|
let mut multiple_bindings = false;
|
||||||
|
@ -223,10 +203,7 @@ fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Ctor(..), ..),
|
Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Ctor(..), ..),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
ExprKind::AddrOf(..)
|
ExprKind::AddrOf(..) | ExprKind::Array(..) | ExprKind::Tup(..) | ExprKind::Struct(..) => true,
|
||||||
| ExprKind::Array(..)
|
|
||||||
| ExprKind::Tup(..)
|
|
||||||
| ExprKind::Struct(..) => true,
|
|
||||||
ExprKind::Lit(lit) if !matches!(lit.node, LitKind::Float(..)) => true,
|
ExprKind::Lit(lit) if !matches!(lit.node, LitKind::Float(..)) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
} {
|
} {
|
||||||
|
|
|
@ -20,8 +20,7 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
|
||||||
if let Some(ff) = get_source_text(cx, span)
|
if let Some(ff) = get_source_text(cx, span)
|
||||||
&& let Some(text) = ff.as_str()
|
&& let Some(text) = ff.as_str()
|
||||||
{
|
{
|
||||||
text.as_bytes().windows(2)
|
text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")
|
||||||
.any(|w| w == b"//" || w == b"/*")
|
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E
|
||||||
let n = snippet_with_applicability(cx, n_arg.span, "..", &mut applicability);
|
let n = snippet_with_applicability(cx, n_arg.span, "..", &mut applicability);
|
||||||
|
|
||||||
if let Some(parent) = clippy_utils::get_parent_expr(cx, expr)
|
if let Some(parent) = clippy_utils::get_parent_expr(cx, expr)
|
||||||
&& let Some((name, _, _, _, _)) = method_call(parent)
|
&& let Some((name, _, _, _, _)) = method_call(parent)
|
||||||
&& name == "unwrap" {
|
&& name == "unwrap"
|
||||||
|
{
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
BYTES_NTH,
|
BYTES_NTH,
|
||||||
|
@ -33,7 +34,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E
|
||||||
&format!("called `.bytes().nth().unwrap()` on a `{caller_type}`"),
|
&format!("called `.bytes().nth().unwrap()` on a `{caller_type}`"),
|
||||||
"try",
|
"try",
|
||||||
format!("{receiver}.as_bytes()[{n}]",),
|
format!("{receiver}.as_bytes()[{n}]",),
|
||||||
applicability
|
applicability,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
|
@ -42,8 +43,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E
|
||||||
expr.span,
|
expr.span,
|
||||||
&format!("called `.bytes().nth()` on a `{caller_type}`"),
|
&format!("called `.bytes().nth()` on a `{caller_type}`"),
|
||||||
"try",
|
"try",
|
||||||
format!("{receiver}.as_bytes().get({n}).copied()"),
|
format!("{receiver}.as_bytes().get({n}).copied()"),
|
||||||
applicability
|
applicability,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,7 +159,7 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
|
||||||
OffendingFilterExpr::IsSome { .. } => CalledMethod::OptionIsSome,
|
OffendingFilterExpr::IsSome { .. } => CalledMethod::OptionIsSome,
|
||||||
OffendingFilterExpr::IsOk { .. } => CalledMethod::ResultIsOk,
|
OffendingFilterExpr::IsOk { .. } => CalledMethod::ResultIsOk,
|
||||||
OffendingFilterExpr::Matches { .. } => unreachable!("only IsSome and IsOk can get here"),
|
OffendingFilterExpr::Matches { .. } => unreachable!("only IsSome and IsOk can get here"),
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -189,7 +189,8 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
|
||||||
// scrutinee variant_span variant_ident else_
|
// scrutinee variant_span variant_ident else_
|
||||||
let (scrutinee, else_, variant_ident, variant_span) =
|
let (scrutinee, else_, variant_ident, variant_span) =
|
||||||
match higher::IfLetOrMatch::parse(cx, map_body.value) {
|
match higher::IfLetOrMatch::parse(cx, map_body.value) {
|
||||||
// For `if let` we want to check that the variant matching arm references the local created by its pattern
|
// For `if let` we want to check that the variant matching arm references the local created by
|
||||||
|
// its pattern
|
||||||
Some(higher::IfLetOrMatch::IfLet(sc, pat, then, Some(else_)))
|
Some(higher::IfLetOrMatch::IfLet(sc, pat, then, Some(else_)))
|
||||||
if let Some((ident, span)) = expr_uses_local(pat, then) =>
|
if let Some((ident, span)) = expr_uses_local(pat, then) =>
|
||||||
{
|
{
|
||||||
|
@ -211,7 +212,10 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
|
||||||
&& let Some(mac) = root_macro_call(else_.peel_blocks().span)
|
&& let Some(mac) = root_macro_call(else_.peel_blocks().span)
|
||||||
&& (is_panic(cx, mac.def_id) || cx.tcx.opt_item_name(mac.def_id) == Some(sym::unreachable))
|
&& (is_panic(cx, mac.def_id) || cx.tcx.opt_item_name(mac.def_id) == Some(sym::unreachable))
|
||||||
{
|
{
|
||||||
Some(CheckResult::PatternMatching { variant_span, variant_ident })
|
Some(CheckResult::PatternMatching {
|
||||||
|
variant_span,
|
||||||
|
variant_ident,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -228,18 +232,20 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
|
||||||
// .filter(|x| effect(x).is_some()).map(|x| effect(x).unwrap())
|
// .filter(|x| effect(x).is_some()).map(|x| effect(x).unwrap())
|
||||||
// vs.
|
// vs.
|
||||||
// .filter_map(|x| effect(x))
|
// .filter_map(|x| effect(x))
|
||||||
//
|
//
|
||||||
// the latter only calls `effect` once
|
// the latter only calls `effect` once
|
||||||
let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span);
|
let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span);
|
||||||
|
|
||||||
if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did())
|
if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name == sym!(is_some) {
|
||||||
&& path.ident.name == sym!(is_some)
|
Some(Self::IsSome {
|
||||||
{
|
receiver,
|
||||||
Some(Self::IsSome { receiver, side_effect_expr_span })
|
side_effect_expr_span,
|
||||||
} else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did())
|
})
|
||||||
&& path.ident.name == sym!(is_ok)
|
} else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name == sym!(is_ok) {
|
||||||
{
|
Some(Self::IsOk {
|
||||||
Some(Self::IsOk { receiver, side_effect_expr_span })
|
receiver,
|
||||||
|
side_effect_expr_span,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
|
||||||
"use `filter` then `map` instead",
|
"use `filter` then `map` instead",
|
||||||
format!(
|
format!(
|
||||||
"filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})",
|
"filter(|&{param_snippet}| {derefs}{filter}).map(|{param_snippet}| {map})",
|
||||||
derefs="*".repeat(needed_derefs)
|
derefs = "*".repeat(needed_derefs)
|
||||||
),
|
),
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
|
|
|
@ -24,10 +24,16 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, map_arg: &Expr<'_>, m
|
||||||
&& let Some(mac) = root_macro_call_first_node(cx, value)
|
&& let Some(mac) = root_macro_call_first_node(cx, value)
|
||||||
&& is_format_macro(cx, mac.def_id)
|
&& is_format_macro(cx, mac.def_id)
|
||||||
{
|
{
|
||||||
span_lint_and_then(cx, FORMAT_COLLECT, expr.span, "use of `format!` to build up a string from an iterator", |diag| {
|
span_lint_and_then(
|
||||||
diag.span_help(map_span, "call `fold` instead")
|
cx,
|
||||||
.span_help(value.span.source_callsite(), "... and use the `write!` macro here")
|
FORMAT_COLLECT,
|
||||||
.note("this can be written more efficiently by appending to a `String` directly");
|
expr.span,
|
||||||
});
|
"use of `format!` to build up a string from an iterator",
|
||||||
|
|diag| {
|
||||||
|
diag.span_help(map_span, "call `fold` instead")
|
||||||
|
.span_help(value.span.source_callsite(), "... and use the `write!` macro here")
|
||||||
|
.note("this can be written more efficiently by appending to a `String` directly");
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,9 +43,9 @@ pub(super) fn check<'tcx>(
|
||||||
// by not requiring an explicit reference
|
// by not requiring an explicit reference
|
||||||
let needs_ref = if let Some(parent) = get_parent_expr(cx, expr)
|
let needs_ref = if let Some(parent) = get_parent_expr(cx, expr)
|
||||||
&& let hir::ExprKind::Unary(hir::UnOp::Deref, _)
|
&& let hir::ExprKind::Unary(hir::UnOp::Deref, _)
|
||||||
| hir::ExprKind::MethodCall(..)
|
| hir::ExprKind::MethodCall(..)
|
||||||
| hir::ExprKind::Field(..)
|
| hir::ExprKind::Field(..)
|
||||||
| hir::ExprKind::Index(..) = parent.kind
|
| hir::ExprKind::Index(..) = parent.kind
|
||||||
{
|
{
|
||||||
if let hir::ExprKind::Unary(hir::UnOp::Deref, _) = parent.kind {
|
if let hir::ExprKind::Unary(hir::UnOp::Deref, _) = parent.kind {
|
||||||
// if the user explicitly dereferences the result, we can adjust
|
// if the user explicitly dereferences the result, we can adjust
|
||||||
|
|
|
@ -51,7 +51,7 @@ fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) ->
|
||||||
Some(0)
|
Some(0)
|
||||||
} else if cx.tcx.is_diagnostic_item(sym::IterOnce, did) {
|
} else if cx.tcx.is_diagnostic_item(sym::IterOnce, did) {
|
||||||
Some(1)
|
Some(1)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,9 +60,15 @@ pub(super) fn check<'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Op::NeedlessMove(_, expr) = op {
|
if let Op::NeedlessMove(_, expr) = op {
|
||||||
let rustc_hir::ExprKind::Closure(closure) = expr.kind else { return } ;
|
let rustc_hir::ExprKind::Closure(closure) = expr.kind else {
|
||||||
let body @ Body { params: [p], .. } = cx.tcx.hir().body(closure.body) else { return };
|
return;
|
||||||
let mut delegate = MoveDelegate {used_move : HirIdSet::default()};
|
};
|
||||||
|
let body @ Body { params: [p], .. } = cx.tcx.hir().body(closure.body) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut delegate = MoveDelegate {
|
||||||
|
used_move: HirIdSet::default(),
|
||||||
|
};
|
||||||
let infcx = cx.tcx.infer_ctxt().build();
|
let infcx = cx.tcx.infer_ctxt().build();
|
||||||
|
|
||||||
ExprUseVisitor::new(
|
ExprUseVisitor::new(
|
||||||
|
@ -77,7 +83,7 @@ pub(super) fn check<'tcx>(
|
||||||
let mut to_be_discarded = false;
|
let mut to_be_discarded = false;
|
||||||
|
|
||||||
p.pat.walk(|it| {
|
p.pat.walk(|it| {
|
||||||
if delegate.used_move.contains(&it.hir_id){
|
if delegate.used_move.contains(&it.hir_id) {
|
||||||
to_be_discarded = true;
|
to_be_discarded = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -87,8 +93,8 @@ pub(super) fn check<'tcx>(
|
||||||
| PatKind::Ref(_, Mutability::Mut) => {
|
| PatKind::Ref(_, Mutability::Mut) => {
|
||||||
to_be_discarded = true;
|
to_be_discarded = true;
|
||||||
false
|
false
|
||||||
}
|
},
|
||||||
_ => { true }
|
_ => true,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -99,46 +105,42 @@ pub(super) fn check<'tcx>(
|
||||||
|
|
||||||
let (lint, msg, trailing_clone) = match op {
|
let (lint, msg, trailing_clone) = match op {
|
||||||
Op::RmCloned | Op::NeedlessMove(_, _) => (REDUNDANT_CLONE, "unneeded cloning of iterator items", ""),
|
Op::RmCloned | Op::NeedlessMove(_, _) => (REDUNDANT_CLONE, "unneeded cloning of iterator items", ""),
|
||||||
Op::LaterCloned | Op::FixClosure(_, _) => (ITER_OVEREAGER_CLONED, "unnecessarily eager cloning of iterator items", ".cloned()"),
|
Op::LaterCloned | Op::FixClosure(_, _) => (
|
||||||
|
ITER_OVEREAGER_CLONED,
|
||||||
|
"unnecessarily eager cloning of iterator items",
|
||||||
|
".cloned()",
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
span_lint_and_then(
|
span_lint_and_then(cx, lint, expr.span, msg, |diag| match op {
|
||||||
cx,
|
Op::RmCloned | Op::LaterCloned => {
|
||||||
lint,
|
let method_span = expr.span.with_lo(cloned_call.span.hi());
|
||||||
expr.span,
|
if let Some(mut snip) = snippet_opt(cx, method_span) {
|
||||||
msg,
|
snip.push_str(trailing_clone);
|
||||||
|diag| {
|
let replace_span = expr.span.with_lo(cloned_recv.span.hi());
|
||||||
match op {
|
diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable);
|
||||||
Op::RmCloned | Op::LaterCloned => {
|
|
||||||
let method_span = expr.span.with_lo(cloned_call.span.hi());
|
|
||||||
if let Some(mut snip) = snippet_opt(cx, method_span) {
|
|
||||||
snip.push_str(trailing_clone);
|
|
||||||
let replace_span = expr.span.with_lo(cloned_recv.span.hi());
|
|
||||||
diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Op::FixClosure(name, predicate_expr) => {
|
|
||||||
if let Some(predicate) = snippet_opt(cx, predicate_expr.span) {
|
|
||||||
let new_closure = if let ExprKind::Closure(_) = predicate_expr.kind {
|
|
||||||
predicate.replacen('|', "|&", 1)
|
|
||||||
} else {
|
|
||||||
format!("|&x| {predicate}(x)")
|
|
||||||
};
|
|
||||||
let snip = format!(".{name}({new_closure}).cloned()" );
|
|
||||||
let replace_span = expr.span.with_lo(cloned_recv.span.hi());
|
|
||||||
diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Op::NeedlessMove(_, _) => {
|
|
||||||
let method_span = expr.span.with_lo(cloned_call.span.hi());
|
|
||||||
if let Some(snip) = snippet_opt(cx, method_span) {
|
|
||||||
let replace_span = expr.span.with_lo(cloned_recv.span.hi());
|
|
||||||
diag.span_suggestion(replace_span, "try", snip, Applicability::MaybeIncorrect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
);
|
Op::FixClosure(name, predicate_expr) => {
|
||||||
|
if let Some(predicate) = snippet_opt(cx, predicate_expr.span) {
|
||||||
|
let new_closure = if let ExprKind::Closure(_) = predicate_expr.kind {
|
||||||
|
predicate.replacen('|', "|&", 1)
|
||||||
|
} else {
|
||||||
|
format!("|&x| {predicate}(x)")
|
||||||
|
};
|
||||||
|
let snip = format!(".{name}({new_closure}).cloned()");
|
||||||
|
let replace_span = expr.span.with_lo(cloned_recv.span.hi());
|
||||||
|
diag.span_suggestion(replace_span, "try", snip, Applicability::MachineApplicable);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Op::NeedlessMove(_, _) => {
|
||||||
|
let method_span = expr.span.with_lo(cloned_call.span.hi());
|
||||||
|
if let Some(snip) = snippet_opt(cx, method_span) {
|
||||||
|
let replace_span = expr.span.with_lo(cloned_recv.span.hi());
|
||||||
|
diag.span_suggestion(replace_span, "try", snip, Applicability::MaybeIncorrect);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ pub(super) fn check<'tcx>(
|
||||||
fold_span,
|
fold_span,
|
||||||
"usage of `Iterator::fold` on a type that implements `Try`",
|
"usage of `Iterator::fold` on a type that implements `Try`",
|
||||||
"use `try_fold` instead",
|
"use `try_fold` instead",
|
||||||
format!("try_fold({init_snip}, {args_snip} ...)", ),
|
format!("try_fold({init_snip}, {args_snip} ...)",),
|
||||||
Applicability::HasPlaceholders,
|
Applicability::HasPlaceholders,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3932,21 +3932,21 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||||
}
|
}
|
||||||
|
|
||||||
if sig.decl.implicit_self.has_implicit_self()
|
if sig.decl.implicit_self.has_implicit_self()
|
||||||
&& !(self.avoid_breaking_exported_api
|
&& !(self.avoid_breaking_exported_api
|
||||||
&& cx.effective_visibilities.is_exported(impl_item.owner_id.def_id))
|
&& cx.effective_visibilities.is_exported(impl_item.owner_id.def_id))
|
||||||
&& let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next()
|
&& let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next()
|
||||||
&& let Some(first_arg_ty) = first_arg_ty_opt
|
&& let Some(first_arg_ty) = first_arg_ty_opt
|
||||||
{
|
{
|
||||||
wrong_self_convention::check(
|
wrong_self_convention::check(
|
||||||
cx,
|
cx,
|
||||||
name,
|
name,
|
||||||
self_ty,
|
self_ty,
|
||||||
first_arg_ty,
|
first_arg_ty,
|
||||||
first_arg.pat.span,
|
first_arg.pat.span,
|
||||||
implements_trait,
|
implements_trait,
|
||||||
false
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this impl block implements a trait, lint in trait definition instead
|
// if this impl block implements a trait, lint in trait definition instead
|
||||||
|
@ -4031,10 +4031,16 @@ impl Methods {
|
||||||
},
|
},
|
||||||
("all", [arg]) => {
|
("all", [arg]) => {
|
||||||
if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
|
if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
|
||||||
iter_overeager_cloned::check(cx, expr, recv, recv2,
|
iter_overeager_cloned::check(
|
||||||
iter_overeager_cloned::Op::NeedlessMove(name, arg), false);
|
cx,
|
||||||
|
expr,
|
||||||
|
recv,
|
||||||
|
recv2,
|
||||||
|
iter_overeager_cloned::Op::NeedlessMove(name, arg),
|
||||||
|
false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
("and_then", [arg]) => {
|
("and_then", [arg]) => {
|
||||||
let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
|
let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
|
||||||
let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
|
let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
|
||||||
|
@ -4042,24 +4048,35 @@ impl Methods {
|
||||||
unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
|
unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
("any", [arg]) => {
|
("any", [arg]) => match method_call(recv) {
|
||||||
match method_call(recv) {
|
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||||
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::NeedlessMove(name, arg), false),
|
cx,
|
||||||
Some(("chars", recv, _, _, _)) if let ExprKind::Closure(arg) = arg.kind
|
expr,
|
||||||
&& let body = cx.tcx.hir().body(arg.body)
|
recv,
|
||||||
&& let [param] = body.params => {
|
recv2,
|
||||||
string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
|
iter_overeager_cloned::Op::NeedlessMove(name, arg),
|
||||||
}
|
false,
|
||||||
_ => {}
|
),
|
||||||
}
|
Some(("chars", recv, _, _, _))
|
||||||
}
|
if let ExprKind::Closure(arg) = arg.kind
|
||||||
|
&& let body = cx.tcx.hir().body(arg.body)
|
||||||
|
&& let [param] = body.params =>
|
||||||
|
{
|
||||||
|
string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
},
|
||||||
("arg", [arg]) => {
|
("arg", [arg]) => {
|
||||||
suspicious_command_arg_space::check(cx, recv, arg, span);
|
suspicious_command_arg_space::check(cx, recv, arg, span);
|
||||||
}
|
},
|
||||||
("as_deref" | "as_deref_mut", []) => {
|
("as_deref" | "as_deref_mut", []) => {
|
||||||
needless_option_as_deref::check(cx, expr, recv, name);
|
needless_option_as_deref::check(cx, expr, recv, name);
|
||||||
},
|
},
|
||||||
("as_bytes" | "is_empty", []) => if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); },
|
("as_bytes" | "is_empty", []) => {
|
||||||
|
if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) {
|
||||||
|
redundant_as_str::check(cx, expr, recv, as_str_span, span);
|
||||||
|
}
|
||||||
|
},
|
||||||
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
|
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
|
||||||
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
|
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
|
||||||
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
|
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
|
||||||
|
@ -4081,12 +4098,14 @@ impl Methods {
|
||||||
},
|
},
|
||||||
Some(("drain", recv, args, ..)) => {
|
Some(("drain", recv, args, ..)) => {
|
||||||
drain_collect::check(cx, args, expr, recv);
|
drain_collect::check(cx, args, expr, recv);
|
||||||
}
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) {
|
("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) {
|
||||||
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::RmCloned , false),
|
Some(("cloned", recv2, [], _, _)) => {
|
||||||
|
iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::RmCloned, false);
|
||||||
|
},
|
||||||
Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _, _)) => {
|
Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _, _)) => {
|
||||||
iter_count::check(cx, expr, recv2, name2);
|
iter_count::check(cx, expr, recv2, name2);
|
||||||
},
|
},
|
||||||
|
@ -4114,7 +4133,9 @@ impl Methods {
|
||||||
("expect", [_]) => {
|
("expect", [_]) => {
|
||||||
match method_call(recv) {
|
match method_call(recv) {
|
||||||
Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
|
Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
|
||||||
Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv),
|
Some(("err", recv, [], err_span, _)) => {
|
||||||
|
err_expect::check(cx, expr, recv, span, err_span, &self.msrv);
|
||||||
|
},
|
||||||
_ => unwrap_expect_used::check(
|
_ => unwrap_expect_used::check(
|
||||||
cx,
|
cx,
|
||||||
expr,
|
expr,
|
||||||
|
@ -4141,13 +4162,19 @@ impl Methods {
|
||||||
string_extend_chars::check(cx, expr, recv, arg);
|
string_extend_chars::check(cx, expr, recv, arg);
|
||||||
extend_with_drain::check(cx, expr, recv, arg);
|
extend_with_drain::check(cx, expr, recv, arg);
|
||||||
},
|
},
|
||||||
(name @ ( "filter" | "find" ) , [arg]) => {
|
(name @ ("filter" | "find"), [arg]) => {
|
||||||
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
|
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
|
||||||
// if `arg` has side-effect, the semantic will change
|
// if `arg` has side-effect, the semantic will change
|
||||||
iter_overeager_cloned::check(cx, expr, recv, recv2,
|
iter_overeager_cloned::check(
|
||||||
iter_overeager_cloned::Op::FixClosure(name, arg), false);
|
cx,
|
||||||
|
expr,
|
||||||
|
recv,
|
||||||
|
recv2,
|
||||||
|
iter_overeager_cloned::Op::FixClosure(name, arg),
|
||||||
|
false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
("filter_map", [arg]) => {
|
("filter_map", [arg]) => {
|
||||||
unnecessary_filter_map::check(cx, expr, arg, name);
|
unnecessary_filter_map::check(cx, expr, arg, name);
|
||||||
filter_map_bool_then::check(cx, expr, arg, call_span);
|
filter_map_bool_then::check(cx, expr, arg, call_span);
|
||||||
|
@ -4161,20 +4188,34 @@ impl Methods {
|
||||||
flat_map_option::check(cx, expr, arg, span);
|
flat_map_option::check(cx, expr, arg, span);
|
||||||
},
|
},
|
||||||
("flatten", []) => match method_call(recv) {
|
("flatten", []) => match method_call(recv) {
|
||||||
Some(("map", recv, [map_arg], map_span, _)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
|
Some(("map", recv, [map_arg], map_span, _)) => {
|
||||||
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::LaterCloned , true),
|
map_flatten::check(cx, expr, recv, map_arg, map_span);
|
||||||
|
},
|
||||||
|
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||||
|
cx,
|
||||||
|
expr,
|
||||||
|
recv,
|
||||||
|
recv2,
|
||||||
|
iter_overeager_cloned::Op::LaterCloned,
|
||||||
|
true,
|
||||||
|
),
|
||||||
_ => {},
|
_ => {},
|
||||||
},
|
},
|
||||||
("fold", [init, acc]) => {
|
("fold", [init, acc]) => {
|
||||||
manual_try_fold::check(cx, expr, init, acc, call_span, &self.msrv);
|
manual_try_fold::check(cx, expr, init, acc, call_span, &self.msrv);
|
||||||
unnecessary_fold::check(cx, expr, init, acc, span);
|
unnecessary_fold::check(cx, expr, init, acc, span);
|
||||||
},
|
},
|
||||||
("for_each", [arg]) => {
|
("for_each", [arg]) => match method_call(recv) {
|
||||||
match method_call(recv) {
|
Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2),
|
||||||
Some(("inspect", _, [_], span2, _)) => inspect_for_each::check(cx, expr, span2),
|
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||||
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::NeedlessMove(name, arg), false),
|
cx,
|
||||||
_ => {}
|
expr,
|
||||||
}
|
recv,
|
||||||
|
recv2,
|
||||||
|
iter_overeager_cloned::Op::NeedlessMove(name, arg),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
_ => {},
|
||||||
},
|
},
|
||||||
("get", [arg]) => {
|
("get", [arg]) => {
|
||||||
get_first::check(cx, expr, recv, arg);
|
get_first::check(cx, expr, recv, arg);
|
||||||
|
@ -4198,8 +4239,14 @@ impl Methods {
|
||||||
},
|
},
|
||||||
("last", []) => {
|
("last", []) => {
|
||||||
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
|
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
|
||||||
iter_overeager_cloned::check(cx, expr, recv, recv2,
|
iter_overeager_cloned::check(
|
||||||
iter_overeager_cloned::Op::LaterCloned , false);
|
cx,
|
||||||
|
expr,
|
||||||
|
recv,
|
||||||
|
recv2,
|
||||||
|
iter_overeager_cloned::Op::LaterCloned,
|
||||||
|
false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
("lock", []) => {
|
("lock", []) => {
|
||||||
|
@ -4209,14 +4256,23 @@ impl Methods {
|
||||||
if name == "map" {
|
if name == "map" {
|
||||||
map_clone::check(cx, expr, recv, m_arg, &self.msrv);
|
map_clone::check(cx, expr, recv, m_arg, &self.msrv);
|
||||||
match method_call(recv) {
|
match method_call(recv) {
|
||||||
Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => iter_kv_map::check(cx, map_name, expr, recv2, m_arg),
|
Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => {
|
||||||
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::NeedlessMove(name, m_arg), false),
|
iter_kv_map::check(cx, map_name, expr, recv2, m_arg);
|
||||||
_ => {}
|
},
|
||||||
|
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||||
|
cx,
|
||||||
|
expr,
|
||||||
|
recv,
|
||||||
|
recv2,
|
||||||
|
iter_overeager_cloned::Op::NeedlessMove(name, m_arg),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
map_err_ignore::check(cx, expr, m_arg);
|
map_err_ignore::check(cx, expr, m_arg);
|
||||||
}
|
}
|
||||||
if let Some((name, recv2, args, span2,_)) = method_call(recv) {
|
if let Some((name, recv2, args, span2, _)) = method_call(recv) {
|
||||||
match (name, args) {
|
match (name, args) {
|
||||||
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv),
|
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv),
|
||||||
("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv),
|
("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv),
|
||||||
|
@ -4238,20 +4294,34 @@ impl Methods {
|
||||||
("next", []) => {
|
("next", []) => {
|
||||||
if let Some((name2, recv2, args2, _, _)) = method_call(recv) {
|
if let Some((name2, recv2, args2, _, _)) = method_call(recv) {
|
||||||
match (name2, args2) {
|
match (name2, args2) {
|
||||||
("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::LaterCloned, false),
|
("cloned", []) => iter_overeager_cloned::check(
|
||||||
|
cx,
|
||||||
|
expr,
|
||||||
|
recv,
|
||||||
|
recv2,
|
||||||
|
iter_overeager_cloned::Op::LaterCloned,
|
||||||
|
false,
|
||||||
|
),
|
||||||
("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
|
("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
|
||||||
("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, &self.msrv),
|
("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, &self.msrv),
|
||||||
("iter", []) => iter_next_slice::check(cx, expr, recv2),
|
("iter", []) => iter_next_slice::check(cx, expr, recv2),
|
||||||
("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
|
("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
|
||||||
("skip_while", [_]) => skip_while_next::check(cx, expr),
|
("skip_while", [_]) => skip_while_next::check(cx, expr),
|
||||||
("rev", [])=> manual_next_back::check(cx, expr, recv, recv2),
|
("rev", []) => manual_next_back::check(cx, expr, recv, recv2),
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
("nth", [n_arg]) => match method_call(recv) {
|
("nth", [n_arg]) => match method_call(recv) {
|
||||||
Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg),
|
Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg),
|
||||||
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, iter_overeager_cloned::Op::LaterCloned , false),
|
Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(
|
||||||
|
cx,
|
||||||
|
expr,
|
||||||
|
recv,
|
||||||
|
recv2,
|
||||||
|
iter_overeager_cloned::Op::LaterCloned,
|
||||||
|
false,
|
||||||
|
),
|
||||||
Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
|
Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
|
||||||
Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
|
Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
|
||||||
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
|
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
|
||||||
|
@ -4276,7 +4346,7 @@ impl Methods {
|
||||||
},
|
},
|
||||||
("read_line", [arg]) => {
|
("read_line", [arg]) => {
|
||||||
read_line_without_trim::check(cx, expr, recv, arg);
|
read_line_without_trim::check(cx, expr, recv, arg);
|
||||||
}
|
},
|
||||||
("repeat", [arg]) => {
|
("repeat", [arg]) => {
|
||||||
repeat_once::check(cx, expr, recv, arg);
|
repeat_once::check(cx, expr, recv, arg);
|
||||||
},
|
},
|
||||||
|
@ -4307,10 +4377,16 @@ impl Methods {
|
||||||
iter_out_of_bounds::check_skip(cx, expr, recv, arg);
|
iter_out_of_bounds::check_skip(cx, expr, recv, arg);
|
||||||
|
|
||||||
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
|
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
|
||||||
iter_overeager_cloned::check(cx, expr, recv, recv2,
|
iter_overeager_cloned::check(
|
||||||
iter_overeager_cloned::Op::LaterCloned , false);
|
cx,
|
||||||
|
expr,
|
||||||
|
recv,
|
||||||
|
recv2,
|
||||||
|
iter_overeager_cloned::Op::LaterCloned,
|
||||||
|
false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
("sort", []) => {
|
("sort", []) => {
|
||||||
stable_sort_primitive::check(cx, expr, recv);
|
stable_sort_primitive::check(cx, expr, recv);
|
||||||
},
|
},
|
||||||
|
@ -4335,8 +4411,14 @@ impl Methods {
|
||||||
("take", [arg]) => {
|
("take", [arg]) => {
|
||||||
iter_out_of_bounds::check_take(cx, expr, recv, arg);
|
iter_out_of_bounds::check_take(cx, expr, recv, arg);
|
||||||
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
|
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
|
||||||
iter_overeager_cloned::check(cx, expr, recv, recv2,
|
iter_overeager_cloned::check(
|
||||||
iter_overeager_cloned::Op::LaterCloned, false);
|
cx,
|
||||||
|
expr,
|
||||||
|
recv,
|
||||||
|
recv2,
|
||||||
|
iter_overeager_cloned::Op::LaterCloned,
|
||||||
|
false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
("take", []) => needless_option_take::check(cx, expr, recv),
|
("take", []) => needless_option_take::check(cx, expr, recv),
|
||||||
|
@ -4348,7 +4430,7 @@ impl Methods {
|
||||||
},
|
},
|
||||||
("try_into", []) if is_trait_method(cx, expr, sym::TryInto) => {
|
("try_into", []) if is_trait_method(cx, expr, sym::TryInto) => {
|
||||||
unnecessary_fallible_conversions::check_method(cx, expr);
|
unnecessary_fallible_conversions::check_method(cx, expr);
|
||||||
}
|
},
|
||||||
("to_owned", []) => {
|
("to_owned", []) => {
|
||||||
if !suspicious_to_owned::check(cx, expr, recv) {
|
if !suspicious_to_owned::check(cx, expr, recv) {
|
||||||
implicit_clone::check(cx, name, expr, recv);
|
implicit_clone::check(cx, name, expr, recv);
|
||||||
|
@ -4359,7 +4441,7 @@ impl Methods {
|
||||||
},
|
},
|
||||||
("type_id", []) => {
|
("type_id", []) => {
|
||||||
type_id_on_box::check(cx, recv, expr.span);
|
type_id_on_box::check(cx, recv, expr.span);
|
||||||
}
|
},
|
||||||
("unwrap", []) => {
|
("unwrap", []) => {
|
||||||
match method_call(recv) {
|
match method_call(recv) {
|
||||||
Some(("get", recv, [get_arg], _, _)) => {
|
Some(("get", recv, [get_arg], _, _)) => {
|
||||||
|
@ -4411,7 +4493,7 @@ impl Methods {
|
||||||
},
|
},
|
||||||
("unwrap_or_default" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => {
|
("unwrap_or_default" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => {
|
||||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||||
}
|
},
|
||||||
("unwrap_or_else", [u_arg]) => {
|
("unwrap_or_else", [u_arg]) => {
|
||||||
match method_call(recv) {
|
match method_call(recv) {
|
||||||
Some(("map", recv, [map_arg], _, _))
|
Some(("map", recv, [map_arg], _, _))
|
||||||
|
@ -4424,10 +4506,10 @@ impl Methods {
|
||||||
},
|
},
|
||||||
("wake", []) => {
|
("wake", []) => {
|
||||||
waker_clone_wake::check(cx, expr, recv);
|
waker_clone_wake::check(cx, expr, recv);
|
||||||
}
|
},
|
||||||
("write", []) => {
|
("write", []) => {
|
||||||
readonly_write_lock::check(cx, expr, recv);
|
readonly_write_lock::check(cx, expr, recv);
|
||||||
}
|
},
|
||||||
("zip", [arg]) => {
|
("zip", [arg]) => {
|
||||||
if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind
|
if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind
|
||||||
&& name.ident.name == sym::iter
|
&& name.ident.name == sym::iter
|
||||||
|
|
|
@ -17,7 +17,7 @@ use rustc_lint::LateContext;
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
use rustc_middle::ty::{self, AssocKind, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty};
|
use rustc_middle::ty::{self, AssocKind, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty};
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::{sym, Span, Symbol};
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
|
const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
|
||||||
|
|
||||||
|
@ -87,21 +87,17 @@ pub(super) fn check<'tcx>(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Node::Local(l) => {
|
Node::Local(l) => {
|
||||||
if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None)
|
if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None) = l.pat.kind
|
||||||
= l.pat.kind
|
|
||||||
&& let ty = cx.typeck_results().expr_ty(collect_expr)
|
&& let ty = cx.typeck_results().expr_ty(collect_expr)
|
||||||
&& [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList].into_iter()
|
&& [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList]
|
||||||
|
.into_iter()
|
||||||
.any(|item| is_type_diagnostic_item(cx, ty, item))
|
.any(|item| is_type_diagnostic_item(cx, ty, item))
|
||||||
&& let iter_ty = cx.typeck_results().expr_ty(iter_expr)
|
&& let iter_ty = cx.typeck_results().expr_ty(iter_expr)
|
||||||
&& let Some(block) = get_enclosing_block(cx, l.hir_id)
|
&& let Some(block) = get_enclosing_block(cx, l.hir_id)
|
||||||
&& let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty))
|
&& let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty))
|
||||||
&& let [iter_call] = &*iter_calls
|
&& let [iter_call] = &*iter_calls
|
||||||
{
|
{
|
||||||
let mut used_count_visitor = UsedCountVisitor {
|
let mut used_count_visitor = UsedCountVisitor { cx, id, count: 0 };
|
||||||
cx,
|
|
||||||
id,
|
|
||||||
count: 0,
|
|
||||||
};
|
|
||||||
walk_block(&mut used_count_visitor, block);
|
walk_block(&mut used_count_visitor, block);
|
||||||
if used_count_visitor.count > 1 {
|
if used_count_visitor.count > 1 {
|
||||||
return;
|
return;
|
||||||
|
@ -117,13 +113,11 @@ pub(super) fn check<'tcx>(
|
||||||
span,
|
span,
|
||||||
NEEDLESS_COLLECT_MSG,
|
NEEDLESS_COLLECT_MSG,
|
||||||
|diag| {
|
|diag| {
|
||||||
let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx));
|
let iter_replacement =
|
||||||
|
format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx));
|
||||||
diag.multipart_suggestion(
|
diag.multipart_suggestion(
|
||||||
iter_call.get_suggestion_text(),
|
iter_call.get_suggestion_text(),
|
||||||
vec![
|
vec![(l.span, String::new()), (iter_call.span, iter_replacement)],
|
||||||
(l.span, String::new()),
|
|
||||||
(iter_call.span, iter_replacement)
|
|
||||||
],
|
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -175,11 +169,12 @@ fn check_collect_into_intoiterator<'tcx>(
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|p| {
|
.filter_map(|p| {
|
||||||
if let ClauseKind::Trait(t) = p.kind().skip_binder()
|
if let ClauseKind::Trait(t) = p.kind().skip_binder()
|
||||||
&& cx.tcx.is_diagnostic_item(sym::IntoIterator,t.trait_ref.def_id) {
|
&& cx.tcx.is_diagnostic_item(sym::IntoIterator, t.trait_ref.def_id)
|
||||||
Some(t.self_ty())
|
{
|
||||||
} else {
|
Some(t.self_ty())
|
||||||
None
|
} else {
|
||||||
}
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.any(|ty| ty == inputs[arg_idx])
|
.any(|ty| ty == inputs[arg_idx])
|
||||||
{
|
{
|
||||||
|
@ -207,14 +202,13 @@ fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool {
|
||||||
|
|
||||||
/// Checks if `<iter_ty as Iterator>::Item` is the same as `<collect_ty as IntoIter>::Item`
|
/// Checks if `<iter_ty as Iterator>::Item` is the same as `<collect_ty as IntoIter>::Item`
|
||||||
fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: Ty<'tcx>) -> bool {
|
fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: Ty<'tcx>) -> bool {
|
||||||
let item = Symbol::intern("Item");
|
|
||||||
if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
|
if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
|
||||||
&& let Some(into_iter_trait) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
|
&& let Some(into_iter_trait) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
|
||||||
&& let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iter_trait, item, [iter_ty])
|
&& let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iter_trait, sym::Item, [iter_ty])
|
||||||
&& let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, item, [collect_ty])
|
&& let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, sym::Item, [collect_ty])
|
||||||
&& let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions(
|
&& let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions(
|
||||||
cx.param_env,
|
cx.param_env,
|
||||||
Ty::new_projection(cx.tcx,into_iter_item_proj.def_id, into_iter_item_proj.args)
|
Ty::new_projection(cx.tcx, into_iter_item_proj.def_id, into_iter_item_proj.args),
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
iter_item_ty == into_iter_item_ty
|
iter_item_ty == into_iter_item_ty
|
||||||
|
@ -233,11 +227,14 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -
|
||||||
&& let [_, search_ty] = *sig.skip_binder().inputs()
|
&& let [_, search_ty] = *sig.skip_binder().inputs()
|
||||||
&& let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.erase_late_bound_regions(sig.rebind(search_ty)).kind()
|
&& let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.erase_late_bound_regions(sig.rebind(search_ty)).kind()
|
||||||
&& let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
|
&& let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
|
||||||
&& let Some(iter_item) = cx.tcx
|
&& let Some(iter_item) = cx.tcx.associated_items(iter_trait).find_by_name_and_kind(
|
||||||
.associated_items(iter_trait)
|
cx.tcx,
|
||||||
.find_by_name_and_kind(cx.tcx, Ident::with_dummy_span(Symbol::intern("Item")), AssocKind::Type, iter_trait)
|
Ident::with_dummy_span(sym::Item),
|
||||||
|
AssocKind::Type,
|
||||||
|
iter_trait,
|
||||||
|
)
|
||||||
&& let args = cx.tcx.mk_args(&[GenericArg::from(typeck.expr_ty_adjusted(iter_expr))])
|
&& let args = cx.tcx.mk_args(&[GenericArg::from(typeck.expr_ty_adjusted(iter_expr))])
|
||||||
&& let proj_ty = Ty::new_projection(cx.tcx,iter_item.def_id, args)
|
&& let proj_ty = Ty::new_projection(cx.tcx, iter_item.def_id, args)
|
||||||
&& let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty)
|
&& let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty)
|
||||||
{
|
{
|
||||||
item_ty == EarlyBinder::bind(search_ty).instantiate(cx.tcx, cx.typeck_results().node_args(call_id))
|
item_ty == EarlyBinder::bind(search_ty).instantiate(cx.tcx, cx.typeck_results().node_args(call_id))
|
||||||
|
|
|
@ -235,10 +235,10 @@ fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>)
|
||||||
let body = cx.tcx.hir().body(body);
|
let body = cx.tcx.hir().body(body);
|
||||||
|
|
||||||
if body.params.is_empty()
|
if body.params.is_empty()
|
||||||
&& let hir::Expr{ kind, .. } = &body.value
|
&& let hir::Expr { kind, .. } = &body.value
|
||||||
&& let hir::ExprKind::MethodCall(hir::PathSegment {ident, ..}, self_arg, _, _) = kind
|
&& let hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, self_arg, _, _) = kind
|
||||||
&& ident.name == sym::to_string
|
&& ident.name == sym::to_string
|
||||||
&& let hir::Expr{ kind, .. } = self_arg
|
&& let hir::Expr { kind, .. } = self_arg
|
||||||
&& let hir::ExprKind::Lit(lit) = kind
|
&& let hir::ExprKind::Lit(lit) = kind
|
||||||
&& let ast::LitKind::Str(symbol::kw::Empty, _) = lit.node
|
&& let ast::LitKind::Str(symbol::kw::Empty, _) = lit.node
|
||||||
{
|
{
|
||||||
|
|
|
@ -46,7 +46,7 @@ pub(super) fn check(
|
||||||
"this looks like a failed attempt at checking for the file extension",
|
"this looks like a failed attempt at checking for the file extension",
|
||||||
"try",
|
"try",
|
||||||
sugg,
|
sugg,
|
||||||
Applicability::MaybeIncorrect
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,9 +46,12 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<
|
||||||
span,
|
span,
|
||||||
"calling `.parse()` without trimming the trailing newline character",
|
"calling `.parse()` without trimming the trailing newline character",
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.span_note(call.span, "call to `.read_line()` here, \
|
diag.span_note(
|
||||||
|
call.span,
|
||||||
|
"call to `.read_line()` here, \
|
||||||
which leaves a trailing newline character in the buffer, \
|
which leaves a trailing newline character in the buffer, \
|
||||||
which in turn will cause `.parse()` to fail");
|
which in turn will cause `.parse()` to fail",
|
||||||
|
);
|
||||||
|
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(
|
||||||
expr.span,
|
expr.span,
|
||||||
|
@ -56,7 +59,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<
|
||||||
format!("{local_snippet}.trim_end()"),
|
format!("{local_snippet}.trim_end()"),
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,13 +26,18 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver
|
||||||
&& let parent = cx.tcx.hir().get_parent(unwrap_call_expr.hir_id)
|
&& let parent = cx.tcx.hir().get_parent(unwrap_call_expr.hir_id)
|
||||||
&& let Node::Local(local) = parent
|
&& let Node::Local(local) = parent
|
||||||
&& let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id)
|
&& let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id)
|
||||||
&& let Some((local, _)) = mir.local_decls.iter_enumerated().find(|(_, decl)| {
|
&& let Some((local, _)) = mir
|
||||||
local.span.contains(decl.source_info.span)
|
.local_decls
|
||||||
})
|
.iter_enumerated()
|
||||||
&& let Some(usages) = visit_local_usage(&[local], mir, Location {
|
.find(|(_, decl)| local.span.contains(decl.source_info.span))
|
||||||
block: START_BLOCK,
|
&& let Some(usages) = visit_local_usage(
|
||||||
statement_index: 0,
|
&[local],
|
||||||
})
|
mir,
|
||||||
|
Location {
|
||||||
|
block: START_BLOCK,
|
||||||
|
statement_index: 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
&& let [usage] = usages.as_slice()
|
&& let [usage] = usages.as_slice()
|
||||||
{
|
{
|
||||||
let writer_never_mutated = usage.local_consume_or_mutate_locs.is_empty();
|
let writer_never_mutated = usage.local_consume_or_mutate_locs.is_empty();
|
||||||
|
@ -45,7 +50,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver
|
||||||
"this write lock is used only for reading",
|
"this write lock is used only for reading",
|
||||||
"consider using a read lock instead",
|
"consider using a read lock instead",
|
||||||
format!("{}.read()", snippet(cx, receiver.span, "<receiver>")),
|
format!("{}.read()", snippet(cx, receiver.span, "<receiver>")),
|
||||||
Applicability::MaybeIncorrect // write lock might be intentional for enforcing exclusiveness
|
Applicability::MaybeIncorrect, // write lock might be intentional for enforcing exclusiveness
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,15 +33,17 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||||
if let ExprKind::Call(f, args) = expr.kind &&
|
if let ExprKind::Call(f, args) = expr.kind
|
||||||
let ExprKind::Path(ref path) = f.kind &&
|
&& let ExprKind::Path(ref path) = f.kind
|
||||||
let Some(def_id) = cx.qpath_res(path, f.hir_id).opt_def_id() &&
|
&& let Some(def_id) = cx.qpath_res(path, f.hir_id).opt_def_id()
|
||||||
match_def_path(cx, def_id, &paths::STD_IO_SEEK_FROM_CURRENT) {
|
&& match_def_path(cx, def_id, &paths::STD_IO_SEEK_FROM_CURRENT)
|
||||||
|
{
|
||||||
// check if argument of `SeekFrom::Current` is `0`
|
// check if argument of `SeekFrom::Current` is `0`
|
||||||
if args.len() == 1 &&
|
if args.len() == 1
|
||||||
let ExprKind::Lit(lit) = args[0].kind &&
|
&& let ExprKind::Lit(lit) = args[0].kind
|
||||||
let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node {
|
&& let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node
|
||||||
return true
|
{
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,15 +23,15 @@ pub(super) fn check<'tcx>(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(seek_trait_id) = cx.tcx.get_diagnostic_item(sym::IoSeek) &&
|
if let Some(seek_trait_id) = cx.tcx.get_diagnostic_item(sym::IoSeek)
|
||||||
implements_trait(cx, ty, seek_trait_id, &[]) &&
|
&& implements_trait(cx, ty, seek_trait_id, &[])
|
||||||
let ExprKind::Call(func, args1) = arg.kind &&
|
&& let ExprKind::Call(func, args1) = arg.kind
|
||||||
let ExprKind::Path(ref path) = func.kind &&
|
&& let ExprKind::Path(ref path) = func.kind
|
||||||
let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() &&
|
&& let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
|
||||||
match_def_path(cx, def_id, &paths::STD_IO_SEEKFROM_START) &&
|
&& match_def_path(cx, def_id, &paths::STD_IO_SEEKFROM_START)
|
||||||
args1.len() == 1 &&
|
&& args1.len() == 1
|
||||||
let ExprKind::Lit(lit) = args1[0].kind &&
|
&& let ExprKind::Lit(lit) = args1[0].kind
|
||||||
let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node
|
&& let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node
|
||||||
{
|
{
|
||||||
let method_call_span = expr.span.with_lo(name_span.lo());
|
let method_call_span = expr.span.with_lo(name_span.lo());
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
|
|
|
@ -133,13 +133,11 @@ fn check_manual_split_once_indirect(
|
||||||
&& let PatKind::Binding(BindingAnnotation::MUT, iter_binding_id, iter_ident, None) = local.pat.kind
|
&& let PatKind::Binding(BindingAnnotation::MUT, iter_binding_id, iter_ident, None) = local.pat.kind
|
||||||
&& let (iter_stmt_id, Node::Stmt(_)) = parents.next()?
|
&& let (iter_stmt_id, Node::Stmt(_)) = parents.next()?
|
||||||
&& let (_, Node::Block(enclosing_block)) = parents.next()?
|
&& let (_, Node::Block(enclosing_block)) = parents.next()?
|
||||||
|
|
||||||
&& let mut stmts = enclosing_block
|
&& let mut stmts = enclosing_block
|
||||||
.stmts
|
.stmts
|
||||||
.iter()
|
.iter()
|
||||||
.skip_while(|stmt| stmt.hir_id != iter_stmt_id)
|
.skip_while(|stmt| stmt.hir_id != iter_stmt_id)
|
||||||
.skip(1)
|
.skip(1)
|
||||||
|
|
||||||
&& let first = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
|
&& let first = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
|
||||||
&& let second = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
|
&& let second = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
|
||||||
&& first.unwrap_kind == second.unwrap_kind
|
&& first.unwrap_kind == second.unwrap_kind
|
||||||
|
@ -173,18 +171,8 @@ fn check_manual_split_once_indirect(
|
||||||
);
|
);
|
||||||
|
|
||||||
let remove_msg = format!("remove the `{iter_ident}` usages");
|
let remove_msg = format!("remove the `{iter_ident}` usages");
|
||||||
diag.span_suggestion(
|
diag.span_suggestion(first.span, remove_msg.clone(), "", app);
|
||||||
first.span,
|
diag.span_suggestion(second.span, remove_msg, "", app);
|
||||||
remove_msg.clone(),
|
|
||||||
"",
|
|
||||||
app,
|
|
||||||
);
|
|
||||||
diag.span_suggestion(
|
|
||||||
second.span,
|
|
||||||
remove_msg,
|
|
||||||
"",
|
|
||||||
app,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ pub(super) fn check<'tcx>(
|
||||||
format!("matches!({scrutinee_snip}, {pat_snip})"),
|
format!("matches!({scrutinee_snip}, {pat_snip})"),
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,13 +25,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx hir::Expr<'_>, arg
|
||||||
|diag: &mut Diagnostic| {
|
|diag: &mut Diagnostic| {
|
||||||
diag.multipart_suggestion_verbose(
|
diag.multipart_suggestion_verbose(
|
||||||
"consider splitting the argument",
|
"consider splitting the argument",
|
||||||
vec![
|
vec![(span, "args".to_string()), (arg.span, format!("[{arg1:?}, {arg2:?}]"))],
|
||||||
(span, "args".to_string()),
|
|
||||||
(arg.span, format!("[{arg1:?}, {arg2:?}]")),
|
|
||||||
],
|
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,11 +44,11 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span)
|
||||||
|
|
||||||
diag.note(
|
diag.note(
|
||||||
"this returns the type id of the literal type `Box<dyn Any>` instead of the \
|
"this returns the type id of the literal type `Box<dyn Any>` instead of the \
|
||||||
type id of the boxed value, which is most likely not what you want"
|
type id of the boxed value, which is most likely not what you want",
|
||||||
)
|
)
|
||||||
.note(
|
.note(
|
||||||
"if this is intentional, use `TypeId::of::<Box<dyn Any>>()` instead, \
|
"if this is intentional, use `TypeId::of::<Box<dyn Any>>()` instead, \
|
||||||
which makes it more clear"
|
which makes it more clear",
|
||||||
)
|
)
|
||||||
.span_suggestion(
|
.span_suggestion(
|
||||||
receiver.span,
|
receiver.span,
|
||||||
|
|
|
@ -43,16 +43,15 @@ fn check<'tcx>(
|
||||||
// what `<T as TryFrom<U>>::Error` is: it's always `Infallible`
|
// what `<T as TryFrom<U>>::Error` is: it's always `Infallible`
|
||||||
&& implements_trait(cx, self_ty, from_into_trait, &[other_ty])
|
&& implements_trait(cx, self_ty, from_into_trait, &[other_ty])
|
||||||
{
|
{
|
||||||
let parent_unwrap_call = get_parent_expr(cx, expr)
|
let parent_unwrap_call = get_parent_expr(cx, expr).and_then(|parent| {
|
||||||
.and_then(|parent| {
|
if let ExprKind::MethodCall(path, .., span) = parent.kind
|
||||||
if let ExprKind::MethodCall(path, .., span) = parent.kind
|
&& let sym::unwrap | sym::expect = path.ident.name
|
||||||
&& let sym::unwrap | sym::expect = path.ident.name
|
{
|
||||||
{
|
Some(span)
|
||||||
Some(span)
|
} else {
|
||||||
} else {
|
None
|
||||||
None
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
let (sugg, span, applicability) = match kind {
|
let (sugg, span, applicability) = match kind {
|
||||||
FunctionKind::TryIntoMethod if let Some(unwrap_span) = parent_unwrap_call => {
|
FunctionKind::TryIntoMethod if let Some(unwrap_span) = parent_unwrap_call => {
|
||||||
|
@ -63,8 +62,12 @@ fn check<'tcx>(
|
||||||
// `try_into().unwrap()` specifically can be trivially replaced with just `into()`,
|
// `try_into().unwrap()` specifically can be trivially replaced with just `into()`,
|
||||||
// so that can be machine-applicable
|
// so that can be machine-applicable
|
||||||
|
|
||||||
("into()", primary_span.with_hi(unwrap_span.hi()), Applicability::MachineApplicable)
|
(
|
||||||
}
|
"into()",
|
||||||
|
primary_span.with_hi(unwrap_span.hi()),
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
)
|
||||||
|
},
|
||||||
FunctionKind::TryFromFunction => ("From::from", primary_span, Applicability::Unspecified),
|
FunctionKind::TryFromFunction => ("From::from", primary_span, Applicability::Unspecified),
|
||||||
FunctionKind::TryIntoFunction => ("Into::into", primary_span, Applicability::Unspecified),
|
FunctionKind::TryIntoFunction => ("Into::into", primary_span, Applicability::Unspecified),
|
||||||
FunctionKind::TryIntoMethod => ("into", primary_span, Applicability::Unspecified),
|
FunctionKind::TryIntoMethod => ("into", primary_span, Applicability::Unspecified),
|
||||||
|
@ -77,7 +80,7 @@ fn check<'tcx>(
|
||||||
"use of a fallible conversion when an infallible one could be used",
|
"use of a fallible conversion when an infallible one could be used",
|
||||||
"use",
|
"use",
|
||||||
sugg.into(),
|
sugg.into(),
|
||||||
applicability
|
applicability,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ pub(super) fn check(
|
||||||
{
|
{
|
||||||
suggs.extend([
|
suggs.extend([
|
||||||
(block.span.shrink_to_lo().to(expr.span.shrink_to_lo()), String::new()),
|
(block.span.shrink_to_lo().to(expr.span.shrink_to_lo()), String::new()),
|
||||||
(expr.span.shrink_to_hi().to(block.span.shrink_to_hi()), String::new())
|
(expr.span.shrink_to_hi().to(block.span.shrink_to_hi()), String::new()),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
Some(suggs)
|
Some(suggs)
|
||||||
|
|
|
@ -382,10 +382,10 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
|
||||||
Node::Block(..) => continue,
|
Node::Block(..) => continue,
|
||||||
Node::Item(item) => {
|
Node::Item(item) => {
|
||||||
if let ItemKind::Fn(_, _, body_id) = &item.kind
|
if let ItemKind::Fn(_, _, body_id) = &item.kind
|
||||||
&& let output_ty = return_ty(cx, item.owner_id)
|
&& let output_ty = return_ty(cx, item.owner_id)
|
||||||
&& let inherited = Inherited::new(cx.tcx, item.owner_id.def_id)
|
&& let inherited = Inherited::new(cx.tcx, item.owner_id.def_id)
|
||||||
&& let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.owner_id.def_id)
|
&& let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.owner_id.def_id)
|
||||||
&& fn_ctxt.can_coerce(ty, output_ty)
|
&& fn_ctxt.can_coerce(ty, output_ty)
|
||||||
{
|
{
|
||||||
if has_lifetime(output_ty) && has_lifetime(ty) {
|
if has_lifetime(output_ty) && has_lifetime(ty) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -393,12 +393,15 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
|
||||||
let body = cx.tcx.hir().body(*body_id);
|
let body = cx.tcx.hir().body(*body_id);
|
||||||
let body_expr = &body.value;
|
let body_expr = &body.value;
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
return find_all_ret_expressions(cx, body_expr, |_| { count += 1; count <= 1 });
|
return find_all_ret_expressions(cx, body_expr, |_| {
|
||||||
|
count += 1;
|
||||||
|
count <= 1
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
Node::Expr(parent_expr) => {
|
Node::Expr(parent_expr) => {
|
||||||
if let Some((callee_def_id, call_generic_args, recv, call_args))
|
if let Some((callee_def_id, call_generic_args, recv, call_args)) =
|
||||||
= get_callee_generic_args_and_args(cx, parent_expr)
|
get_callee_generic_args_and_args(cx, parent_expr)
|
||||||
{
|
{
|
||||||
// FIXME: the `instantiate_identity()` below seems incorrect, since we eventually
|
// FIXME: the `instantiate_identity()` below seems incorrect, since we eventually
|
||||||
// call `tcx.try_instantiate_and_normalize_erasing_regions` further down
|
// call `tcx.try_instantiate_and_normalize_erasing_regions` further down
|
||||||
|
@ -420,40 +423,49 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut trait_predicates = cx.tcx.param_env(callee_def_id)
|
let mut trait_predicates =
|
||||||
.caller_bounds().iter().filter(|predicate| {
|
cx.tcx
|
||||||
if let ClauseKind::Trait(trait_predicate)
|
.param_env(callee_def_id)
|
||||||
= predicate.kind().skip_binder()
|
.caller_bounds()
|
||||||
&& trait_predicate.trait_ref.self_ty() == *param_ty
|
.iter()
|
||||||
{
|
.filter(|predicate| {
|
||||||
true
|
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
||||||
} else {
|
&& trait_predicate.trait_ref.self_ty() == *param_ty
|
||||||
false
|
{
|
||||||
}
|
true
|
||||||
});
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let new_subst = cx.tcx.mk_args_from_iter(
|
let new_subst = cx
|
||||||
call_generic_args.iter()
|
.tcx
|
||||||
.enumerate()
|
.mk_args_from_iter(call_generic_args.iter().enumerate().map(|(i, t)| {
|
||||||
.map(|(i, t)|
|
if i == (*param_index as usize) {
|
||||||
if i == (*param_index as usize) {
|
GenericArg::from(ty)
|
||||||
GenericArg::from(ty)
|
} else {
|
||||||
} else {
|
t
|
||||||
t
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if trait_predicates.any(|predicate| {
|
if trait_predicates.any(|predicate| {
|
||||||
let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, new_subst);
|
let predicate = EarlyBinder::bind(predicate).instantiate(cx.tcx, new_subst);
|
||||||
let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
|
let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
|
||||||
!cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation)
|
!cx.tcx
|
||||||
|
.infer_ctxt()
|
||||||
|
.build()
|
||||||
|
.predicate_must_hold_modulo_regions(&obligation)
|
||||||
}) {
|
}) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let output_ty = fn_sig.output();
|
let output_ty = fn_sig.output();
|
||||||
if output_ty.contains(*param_ty) {
|
if output_ty.contains(*param_ty) {
|
||||||
if let Ok(new_ty) = cx.tcx.try_instantiate_and_normalize_erasing_regions(
|
if let Ok(new_ty) = cx.tcx.try_instantiate_and_normalize_erasing_regions(
|
||||||
new_subst, cx.param_env, EarlyBinder::bind(output_ty)) {
|
new_subst,
|
||||||
|
cx.param_env,
|
||||||
|
EarlyBinder::bind(output_ty),
|
||||||
|
) {
|
||||||
expr = parent_expr;
|
expr = parent_expr;
|
||||||
ty = new_ty;
|
ty = new_ty;
|
||||||
continue;
|
continue;
|
||||||
|
@ -515,10 +527,11 @@ fn is_to_string_on_string_like<'a>(
|
||||||
&& let GenericArgKind::Type(ty) = generic_arg.unpack()
|
&& let GenericArgKind::Type(ty) = generic_arg.unpack()
|
||||||
&& let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
|
&& let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
|
||||||
&& let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef)
|
&& let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef)
|
||||||
&& (cx.get_associated_type(ty, deref_trait_id, "Target") == Some(cx.tcx.types.str_) ||
|
&& (cx.get_associated_type(ty, deref_trait_id, "Target") == Some(cx.tcx.types.str_)
|
||||||
implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) {
|
|| implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()]))
|
||||||
true
|
{
|
||||||
} else {
|
true
|
||||||
false
|
} else {
|
||||||
}
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,13 +107,17 @@ impl Visitor<'_> for IdentVisitor<'_, '_> {
|
||||||
|
|
||||||
let str = ident.as_str();
|
let str = ident.as_str();
|
||||||
if conf.is_ident_too_short(cx, str, ident.span) {
|
if conf.is_ident_too_short(cx, str, ident.span) {
|
||||||
if let Node::Item(item) = node && let ItemKind::Use(..) = item.kind {
|
if let Node::Item(item) = node
|
||||||
|
&& let ItemKind::Use(..) = item.kind
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// `struct Awa<T>(T)`
|
// `struct Awa<T>(T)`
|
||||||
// ^
|
// ^
|
||||||
if let Node::PathSegment(path) = node {
|
if let Node::PathSegment(path) = node {
|
||||||
if let Res::Def(def_kind, ..) = path.res && let DefKind::TyParam = def_kind {
|
if let Res::Def(def_kind, ..) = path.res
|
||||||
|
&& let DefKind::TyParam = def_kind
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if matches!(path.res, Res::PrimTy(..)) || path.res.opt_def_id().is_some_and(|def_id| !def_id.is_local())
|
if matches!(path.res, Res::PrimTy(..)) || path.res.opt_def_id().is_some_and(|def_id| !def_id.is_local())
|
||||||
|
|
|
@ -263,7 +263,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
|
||||||
),
|
),
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.span_note(definition_span, format!("`{name}` is defined here"));
|
diag.span_note(definition_span, format!("`{name}` is defined here"));
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -200,9 +200,13 @@ impl<'hir> IndexEntry<'hir> {
|
||||||
/// E.g. for `5` this returns `Some(5)`, for `..5` this returns `Some(4)`,
|
/// E.g. for `5` this returns `Some(5)`, for `..5` this returns `Some(4)`,
|
||||||
/// for `..=5` this returns `Some(5)`
|
/// for `..=5` this returns `Some(5)`
|
||||||
fn upper_index_expr(expr: &Expr<'_>) -> Option<usize> {
|
fn upper_index_expr(expr: &Expr<'_>) -> Option<usize> {
|
||||||
if let ExprKind::Lit(lit) = &expr.kind && let LitKind::Int(index, _) = lit.node {
|
if let ExprKind::Lit(lit) = &expr.kind
|
||||||
|
&& let LitKind::Int(index, _) = lit.node
|
||||||
|
{
|
||||||
Some(index as usize)
|
Some(index as usize)
|
||||||
} else if let Some(higher::Range { end: Some(end), limits, .. }) = higher::Range::hir(expr)
|
} else if let Some(higher::Range {
|
||||||
|
end: Some(end), limits, ..
|
||||||
|
}) = higher::Range::hir(expr)
|
||||||
&& let ExprKind::Lit(lit) = &end.kind
|
&& let ExprKind::Lit(lit) = &end.kind
|
||||||
&& let LitKind::Int(index @ 1.., _) = lit.node
|
&& let LitKind::Int(index @ 1.., _) = lit.node
|
||||||
{
|
{
|
||||||
|
@ -228,7 +232,12 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Unh
|
||||||
|
|
||||||
if let Some(entry) = entry {
|
if let Some(entry) = entry {
|
||||||
match entry {
|
match entry {
|
||||||
IndexEntry::StrayAssert { asserted_len, comparison, assert_span, slice } => {
|
IndexEntry::StrayAssert {
|
||||||
|
asserted_len,
|
||||||
|
comparison,
|
||||||
|
assert_span,
|
||||||
|
slice,
|
||||||
|
} => {
|
||||||
*entry = IndexEntry::AssertWithIndex {
|
*entry = IndexEntry::AssertWithIndex {
|
||||||
highest_index: index,
|
highest_index: index,
|
||||||
asserted_len: *asserted_len,
|
asserted_len: *asserted_len,
|
||||||
|
@ -238,8 +247,12 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Unh
|
||||||
comparison: *comparison,
|
comparison: *comparison,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
IndexEntry::IndexWithoutAssert { highest_index, indexes, .. }
|
IndexEntry::IndexWithoutAssert {
|
||||||
| IndexEntry::AssertWithIndex { highest_index, indexes, .. } => {
|
highest_index, indexes, ..
|
||||||
|
}
|
||||||
|
| IndexEntry::AssertWithIndex {
|
||||||
|
highest_index, indexes, ..
|
||||||
|
} => {
|
||||||
indexes.push(expr.span);
|
indexes.push(expr.span);
|
||||||
*highest_index = (*highest_index).max(index);
|
*highest_index = (*highest_index).max(index);
|
||||||
},
|
},
|
||||||
|
|
|
@ -207,9 +207,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some((lhs_a, a)) = fetch_assign(then) &&
|
if let Some((lhs_a, a)) = fetch_assign(then)
|
||||||
let Some((lhs_b, b)) = fetch_assign(r#else) &&
|
&& let Some((lhs_b, b)) = fetch_assign(r#else)
|
||||||
SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b)
|
&& SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b)
|
||||||
{
|
{
|
||||||
let mut applicability = Applicability::MachineApplicable;
|
let mut applicability = Applicability::MachineApplicable;
|
||||||
let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability);
|
let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability);
|
||||||
|
@ -226,7 +226,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
|
||||||
"this if-then-else expression assigns a bool literal",
|
"this if-then-else expression assigns a bool literal",
|
||||||
"you can reduce it to",
|
"you can reduce it to",
|
||||||
sugg,
|
sugg,
|
||||||
applicability
|
applicability,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,18 +87,24 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
|
||||||
&& let ty::Param(ty) = *ty.value.skip_binder().kind()
|
&& let ty::Param(ty) = *ty.value.skip_binder().kind()
|
||||||
&& let Some((hir_id, fn_id, i)) = match use_cx.node {
|
&& let Some((hir_id, fn_id, i)) = match use_cx.node {
|
||||||
ExprUseNode::MethodArg(_, _, 0) => None,
|
ExprUseNode::MethodArg(_, _, 0) => None,
|
||||||
ExprUseNode::MethodArg(hir_id, None, i) => {
|
ExprUseNode::MethodArg(hir_id, None, i) => cx
|
||||||
cx.typeck_results().type_dependent_def_id(hir_id).map(|id| (hir_id, id, i))
|
.typeck_results()
|
||||||
},
|
.type_dependent_def_id(hir_id)
|
||||||
ExprUseNode::FnArg(&Expr { kind: ExprKind::Path(ref p), hir_id, .. }, i)
|
.map(|id| (hir_id, id, i)),
|
||||||
if !path_has_args(p) => match cx.typeck_results().qpath_res(p, hir_id) {
|
ExprUseNode::FnArg(
|
||||||
Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => {
|
&Expr {
|
||||||
Some((hir_id, id, i))
|
kind: ExprKind::Path(ref p),
|
||||||
|
hir_id,
|
||||||
|
..
|
||||||
},
|
},
|
||||||
|
i,
|
||||||
|
) if !path_has_args(p) => match cx.typeck_results().qpath_res(p, hir_id) {
|
||||||
|
Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) => Some((hir_id, id, i)),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
} && let count = needless_borrow_count(
|
}
|
||||||
|
&& let count = needless_borrow_count(
|
||||||
cx,
|
cx,
|
||||||
&mut self.possible_borrowers,
|
&mut self.possible_borrowers,
|
||||||
fn_id,
|
fn_id,
|
||||||
|
@ -107,7 +113,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
|
||||||
ty,
|
ty,
|
||||||
expr,
|
expr,
|
||||||
&self.msrv,
|
&self.msrv,
|
||||||
) && count != 0
|
)
|
||||||
|
&& count != 0
|
||||||
{
|
{
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
|
@ -119,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
|
||||||
let snip_span = peel_n_hir_expr_refs(expr, count).0.span;
|
let snip_span = peel_n_hir_expr_refs(expr, count).0.span;
|
||||||
let snip = snippet_with_context(cx, snip_span, expr.span.ctxt(), "..", &mut app).0;
|
let snip = snippet_with_context(cx, snip_span, expr.span.ctxt(), "..", &mut app).0;
|
||||||
diag.span_suggestion(expr.span, "change this to", snip.into_owned(), app);
|
diag.span_suggestion(expr.span, "change this to", snip.into_owned(), app);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,7 +252,9 @@ fn needless_borrow_count<'tcx>(
|
||||||
|
|
||||||
predicates.iter().all(|predicate| {
|
predicates.iter().all(|predicate| {
|
||||||
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
||||||
&& cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
|
&& cx
|
||||||
|
.tcx
|
||||||
|
.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
|
||||||
&& let ty::Param(param_ty) = trait_predicate.self_ty().kind()
|
&& let ty::Param(param_ty) = trait_predicate.self_ty().kind()
|
||||||
&& let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack()
|
&& let GenericArgKind::Type(ty) = args_with_referent_ty[param_ty.index as usize].unpack()
|
||||||
&& ty.is_array()
|
&& ty.is_array()
|
||||||
|
@ -308,13 +317,13 @@ fn is_mixed_projection_predicate<'tcx>(
|
||||||
match projection_ty.self_ty().kind() {
|
match projection_ty.self_ty().kind() {
|
||||||
ty::Alias(ty::Projection, inner_projection_ty) => {
|
ty::Alias(ty::Projection, inner_projection_ty) => {
|
||||||
projection_ty = *inner_projection_ty;
|
projection_ty = *inner_projection_ty;
|
||||||
}
|
},
|
||||||
ty::Param(param_ty) => {
|
ty::Param(param_ty) => {
|
||||||
return (param_ty.index as usize) >= generics.parent_count;
|
return (param_ty.index as usize) >= generics.parent_count;
|
||||||
}
|
},
|
||||||
_ => {
|
_ => {
|
||||||
return false;
|
return false;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -46,9 +46,9 @@ fn snippet_enclosed_in_parenthesis(snippet: &str) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) {
|
fn check_for_parens(cx: &LateContext<'_>, e: &Expr<'_>, is_start: bool) {
|
||||||
if is_start &&
|
if is_start
|
||||||
let ExprKind::Lit(literal) = e.kind &&
|
&& let ExprKind::Lit(literal) = e.kind
|
||||||
let ast::LitKind::Float(_sym, ast::LitFloatType::Unsuffixed) = literal.node
|
&& let ast::LitKind::Float(_sym, ast::LitFloatType::Unsuffixed) = literal.node
|
||||||
{
|
{
|
||||||
// don't check floating point literals on the start expression of a range
|
// don't check floating point literals on the start expression of a range
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -225,7 +225,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
|
||||||
if let PatKind::Binding(_, canonical_id, ..) = arg.pat.kind
|
if let PatKind::Binding(_, canonical_id, ..) = arg.pat.kind
|
||||||
&& !mutably_used_vars.contains(&canonical_id)
|
&& !mutably_used_vars.contains(&canonical_id)
|
||||||
{
|
{
|
||||||
self.fn_def_ids_to_maybe_unused_mut.entry(fn_def_id).or_default().push(input);
|
self.fn_def_ids_to_maybe_unused_mut
|
||||||
|
.entry(fn_def_id)
|
||||||
|
.or_default()
|
||||||
|
.push(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -520,7 +523,11 @@ impl<'tcx> Visitor<'tcx> for FnNeedsMutVisitor<'_, 'tcx> {
|
||||||
// #11182; do not lint if mutability is required elsewhere
|
// #11182; do not lint if mutability is required elsewhere
|
||||||
if let Node::Expr(expr) = cx.tcx.hir().get(hir_id)
|
if let Node::Expr(expr) = cx.tcx.hir().get(hir_id)
|
||||||
&& let Some(parent) = get_parent_node(cx.tcx, expr.hir_id)
|
&& let Some(parent) = get_parent_node(cx.tcx, expr.hir_id)
|
||||||
&& let ty::FnDef(def_id, _) = cx.tcx.typeck(cx.tcx.hir().enclosing_body_owner(hir_id)).expr_ty(expr).kind()
|
&& let ty::FnDef(def_id, _) = cx
|
||||||
|
.tcx
|
||||||
|
.typeck(cx.tcx.hir().enclosing_body_owner(hir_id))
|
||||||
|
.expr_ty(expr)
|
||||||
|
.kind()
|
||||||
&& let Some(def_id) = def_id.as_local()
|
&& let Some(def_id) = def_id.as_local()
|
||||||
{
|
{
|
||||||
if let Node::Expr(e) = parent
|
if let Node::Expr(e) = parent
|
||||||
|
|
|
@ -4,7 +4,7 @@ use clippy_utils::source::snippet;
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::{AsyncCoroutineKind, Block, Body, CoroutineKind, Expr, ExprKind, LangItem, MatchSource, QPath};
|
use rustc_hir::{Block, Body, CoroutineKind, CoroutineSource, Expr, ExprKind, LangItem, MatchSource, QPath};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ impl LateLintPass<'_> for NeedlessQuestionMark {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
|
fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
|
||||||
if let Some(CoroutineKind::Async(AsyncCoroutineKind::Fn)) = body.coroutine_kind {
|
if let Some(CoroutineKind::Async(CoroutineSource::Fn)) = body.coroutine_kind {
|
||||||
if let ExprKind::Block(
|
if let ExprKind::Block(
|
||||||
Block {
|
Block {
|
||||||
expr:
|
expr:
|
||||||
|
|
|
@ -103,11 +103,16 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||||
&& final_stmt.hir_id == stmt.hir_id
|
&& final_stmt.hir_id == stmt.hir_id
|
||||||
{
|
{
|
||||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||||
let mut ret_ty = cx.tcx.fn_sig(item.owner_id).instantiate_identity().output().skip_binder();
|
let mut ret_ty = cx
|
||||||
|
.tcx
|
||||||
|
.fn_sig(item.owner_id)
|
||||||
|
.instantiate_identity()
|
||||||
|
.output()
|
||||||
|
.skip_binder();
|
||||||
|
|
||||||
// Remove `impl Future<Output = T>` to get `T`
|
// Remove `impl Future<Output = T>` to get `T`
|
||||||
if cx.tcx.ty_is_opaque_future(ret_ty) &&
|
if cx.tcx.ty_is_opaque_future(ret_ty)
|
||||||
let Some(true_ret_ty) = cx.tcx.infer_ctxt().build().get_impl_future_output_ty(ret_ty)
|
&& let Some(true_ret_ty) = cx.tcx.infer_ctxt().build().get_impl_future_output_ty(ret_ty)
|
||||||
{
|
{
|
||||||
ret_ty = true_ret_ty;
|
ret_ty = true_ret_ty;
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,8 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
|
||||||
&& let Some((fn_attrs, _)) = snippet.split_once("fn")
|
&& let Some((fn_attrs, _)) = snippet.split_once("fn")
|
||||||
&& !fn_attrs.contains("extern")
|
&& !fn_attrs.contains("extern")
|
||||||
{
|
{
|
||||||
let sugg_span = fn_sig.span
|
let sugg_span = fn_sig
|
||||||
|
.span
|
||||||
.with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len()))
|
.with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len()))
|
||||||
.shrink_to_lo();
|
.shrink_to_lo();
|
||||||
|
|
||||||
|
|
|
@ -131,12 +131,7 @@ impl LateLintPass<'_> for NonCanonicalImpls {
|
||||||
|
|
||||||
if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id)
|
if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id)
|
||||||
&& let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy)
|
&& let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy)
|
||||||
&& implements_trait(
|
&& implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[])
|
||||||
cx,
|
|
||||||
trait_impl.self_ty(),
|
|
||||||
copy_def_id,
|
|
||||||
&[],
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if impl_item.ident.name == sym::clone {
|
if impl_item.ident.name == sym::clone {
|
||||||
if block.stmts.is_empty()
|
if block.stmts.is_empty()
|
||||||
|
@ -144,7 +139,8 @@ impl LateLintPass<'_> for NonCanonicalImpls {
|
||||||
&& let ExprKind::Unary(UnOp::Deref, deref) = expr.kind
|
&& let ExprKind::Unary(UnOp::Deref, deref) = expr.kind
|
||||||
&& let ExprKind::Path(qpath) = deref.kind
|
&& let ExprKind::Path(qpath) = deref.kind
|
||||||
&& last_path_segment(&qpath).ident.name == kw::SelfLower
|
&& last_path_segment(&qpath).ident.name == kw::SelfLower
|
||||||
{} else {
|
{
|
||||||
|
} else {
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
NON_CANONICAL_CLONE_IMPL,
|
NON_CANONICAL_CLONE_IMPL,
|
||||||
|
@ -197,10 +193,13 @@ impl LateLintPass<'_> for NonCanonicalImpls {
|
||||||
&& is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome)
|
&& is_res_lang_ctor(cx, cx.qpath_res(some_path, *some_hir_id), LangItem::OptionSome)
|
||||||
// Fix #11178, allow `Self::cmp(self, ..)` too
|
// Fix #11178, allow `Self::cmp(self, ..)` too
|
||||||
&& self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id, &mut needs_fully_qualified)
|
&& self_cmp_call(cx, cmp_expr, impl_item.owner_id.def_id, &mut needs_fully_qualified)
|
||||||
{} else {
|
{
|
||||||
|
} else {
|
||||||
// If `Self` and `Rhs` are not the same type, bail. This makes creating a valid
|
// If `Self` and `Rhs` are not the same type, bail. This makes creating a valid
|
||||||
// suggestion tons more complex.
|
// suggestion tons more complex.
|
||||||
if let [lhs, rhs, ..] = trait_impl.args.as_slice() && lhs != rhs {
|
if let [lhs, rhs, ..] = trait_impl.args.as_slice()
|
||||||
|
&& lhs != rhs
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,12 +237,8 @@ impl LateLintPass<'_> for NonCanonicalImpls {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
diag.multipart_suggestion(
|
diag.multipart_suggestion("change this to", suggs, Applicability::Unspecified);
|
||||||
"change this to",
|
},
|
||||||
suggs,
|
|
||||||
Applicability::Unspecified,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,7 +244,10 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
|
||||||
})) => {
|
})) => {
|
||||||
#[allow(trivial_casts)]
|
#[allow(trivial_casts)]
|
||||||
if let Some(Node::Item(item)) = get_parent_node(cx.tcx, owner_id.into())
|
if let Some(Node::Item(item)) = get_parent_node(cx.tcx, owner_id.into())
|
||||||
&& let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::instantiate_identity)
|
&& let Some(trait_ref) = cx
|
||||||
|
.tcx
|
||||||
|
.impl_trait_ref(item.owner_id)
|
||||||
|
.map(EarlyBinder::instantiate_identity)
|
||||||
&& let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id
|
&& let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id
|
||||||
{
|
{
|
||||||
(
|
(
|
||||||
|
@ -288,8 +291,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
|
||||||
// Recursive call. Track which index the parameter is used in.
|
// Recursive call. Track which index the parameter is used in.
|
||||||
ExprKind::Call(callee, args)
|
ExprKind::Call(callee, args)
|
||||||
if path_def_id(cx, callee).map_or(false, |id| {
|
if path_def_id(cx, callee).map_or(false, |id| {
|
||||||
id == param.fn_id
|
id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(callee.hir_id))
|
||||||
&& has_matching_args(param.fn_kind, typeck.node_args(callee.hir_id))
|
|
||||||
}) =>
|
}) =>
|
||||||
{
|
{
|
||||||
if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
|
if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
|
||||||
|
@ -299,8 +301,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
|
||||||
},
|
},
|
||||||
ExprKind::MethodCall(_, receiver, args, _)
|
ExprKind::MethodCall(_, receiver, args, _)
|
||||||
if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
|
if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
|
||||||
id == param.fn_id
|
id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(parent.hir_id))
|
||||||
&& has_matching_args(param.fn_kind, typeck.node_args(parent.hir_id))
|
|
||||||
}) =>
|
}) =>
|
||||||
{
|
{
|
||||||
if let Some(idx) = iter::once(receiver).chain(args).position(|arg| arg.hir_id == child_id) {
|
if let Some(idx) = iter::once(receiver).chain(args).position(|arg| arg.hir_id == child_id) {
|
||||||
|
@ -336,8 +337,8 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
|
||||||
// Only allow field accesses without auto-deref
|
// Only allow field accesses without auto-deref
|
||||||
ExprKind::Field(..) if typeck.adjustments().get(child_id).is_none() => {
|
ExprKind::Field(..) if typeck.adjustments().get(child_id).is_none() => {
|
||||||
e = parent;
|
e = parent;
|
||||||
continue
|
continue;
|
||||||
}
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
|
|
|
@ -71,7 +71,7 @@ impl ArithmeticSideEffects {
|
||||||
rhs_has_allowed_ty || rhs_from_specific.contains("*")
|
rhs_has_allowed_ty || rhs_from_specific.contains("*")
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
true
|
true
|
||||||
} else if let Some(rhs_from_glob) = self.allowed_binary.get("*") {
|
} else if let Some(rhs_from_glob) = self.allowed_binary.get("*") {
|
||||||
rhs_from_glob.contains(rhs_ty_string_elem)
|
rhs_from_glob.contains(rhs_ty_string_elem)
|
||||||
} else {
|
} else {
|
||||||
|
@ -144,8 +144,10 @@ impl ArithmeticSideEffects {
|
||||||
/// like `i32::MAX` or constant references like `N` from `const N: i32 = 1;`,
|
/// like `i32::MAX` or constant references like `N` from `const N: i32 = 1;`,
|
||||||
fn literal_integer(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<u128> {
|
fn literal_integer(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<u128> {
|
||||||
let actual = peel_hir_expr_unary(expr).0;
|
let actual = peel_hir_expr_unary(expr).0;
|
||||||
if let hir::ExprKind::Lit(lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node {
|
if let hir::ExprKind::Lit(lit) = actual.kind
|
||||||
return Some(n)
|
&& let ast::LitKind::Int(n, _) = lit.node
|
||||||
|
{
|
||||||
|
return Some(n);
|
||||||
}
|
}
|
||||||
if let Some(Constant::Int(n)) = constant(cx, cx.typeck_results(), expr) {
|
if let Some(Constant::Int(n)) = constant(cx, cx.typeck_results(), expr) {
|
||||||
return Some(n);
|
return Some(n);
|
||||||
|
@ -317,7 +319,9 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
|
||||||
let body_owner_kind = cx.tcx.hir().body_owner_kind(body_owner_def_id);
|
let body_owner_kind = cx.tcx.hir().body_owner_kind(body_owner_def_id);
|
||||||
if let hir::BodyOwnerKind::Const { .. } | hir::BodyOwnerKind::Static(_) = body_owner_kind {
|
if let hir::BodyOwnerKind::Const { .. } | hir::BodyOwnerKind::Static(_) = body_owner_kind {
|
||||||
let body_span = cx.tcx.hir().span_with_body(body_owner);
|
let body_span = cx.tcx.hir().span_with_body(body_owner);
|
||||||
if let Some(span) = self.const_span && span.contains(body_span) {
|
if let Some(span) = self.const_span
|
||||||
|
&& span.contains(body_span)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.const_span = Some(body_span);
|
self.const_span = Some(body_span);
|
||||||
|
@ -327,7 +331,9 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
|
||||||
fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
|
fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
|
||||||
let body_owner = cx.tcx.hir().body_owner(body.id());
|
let body_owner = cx.tcx.hir().body_owner(body.id());
|
||||||
let body_span = cx.tcx.hir().span(body_owner);
|
let body_span = cx.tcx.hir().span(body_owner);
|
||||||
if let Some(span) = self.const_span && span.contains(body_span) {
|
if let Some(span) = self.const_span
|
||||||
|
&& span.contains(body_span)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.const_span = None;
|
self.const_span = None;
|
||||||
|
|
|
@ -8,13 +8,14 @@ use rustc_lint::LateContext;
|
||||||
use super::EQ_OP;
|
use super::EQ_OP;
|
||||||
|
|
||||||
pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||||
if let Some((macro_call, macro_name))
|
if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| {
|
||||||
= first_node_macro_backtrace(cx, e).find_map(|macro_call| {
|
let name = cx.tcx.item_name(macro_call.def_id);
|
||||||
let name = cx.tcx.item_name(macro_call.def_id);
|
matches!(
|
||||||
matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne")
|
name.as_str(),
|
||||||
.then(|| (macro_call, name))
|
"assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne"
|
||||||
})
|
)
|
||||||
&& let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn)
|
.then(|| (macro_call, name))
|
||||||
|
}) && let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn)
|
||||||
&& eq_expr_value(cx, lhs, rhs)
|
&& eq_expr_value(cx, lhs, rhs)
|
||||||
&& macro_call.is_local()
|
&& macro_call.is_local()
|
||||||
&& !is_in_test_function(cx.tcx, e.hir_id)
|
&& !is_in_test_function(cx.tcx, e.hir_id)
|
||||||
|
@ -42,7 +43,9 @@ pub(crate) fn check<'tcx>(
|
||||||
e.span,
|
e.span,
|
||||||
&format!("equal expressions as operands to `{}`", op.as_str()),
|
&format!("equal expressions as operands to `{}`", op.as_str()),
|
||||||
|diag| {
|
|diag| {
|
||||||
if let BinOpKind::Ne = op && cx.typeck_results().expr_ty(left).is_floating_point() {
|
if let BinOpKind::Ne = op
|
||||||
|
&& cx.typeck_results().expr_ty(left).is_floating_point()
|
||||||
|
{
|
||||||
diag.note("if you intended to check if the operand is NaN, use `.is_nan()` instead");
|
diag.note("if you intended to check if the operand is NaN, use `.is_nan()` instead");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -46,16 +46,19 @@ impl EarlyLintPass for OptionEnvUnwrap {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind &&
|
if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind
|
||||||
matches!(seg.ident.name, sym::expect | sym::unwrap) {
|
&& matches!(seg.ident.name, sym::expect | sym::unwrap)
|
||||||
if let ExprKind::Call(caller, _) = &receiver.kind &&
|
{
|
||||||
|
if let ExprKind::Call(caller, _) = &receiver.kind &&
|
||||||
// If it exists, it will be ::core::option::Option::Some("<env var>").unwrap() (A method call in the HIR)
|
// If it exists, it will be ::core::option::Option::Some("<env var>").unwrap() (A method call in the HIR)
|
||||||
is_direct_expn_of(caller.span, "option_env").is_some() {
|
is_direct_expn_of(caller.span, "option_env").is_some()
|
||||||
lint(cx, expr.span);
|
{
|
||||||
} else if let ExprKind::Path(_, caller) = &receiver.kind && // If it doesn't exist, it will be ::core::option::Option::None::<&'static str>.unwrap() (A path in the HIR)
|
lint(cx, expr.span);
|
||||||
is_direct_expn_of(caller.span, "option_env").is_some() {
|
} else if let ExprKind::Path(_, caller) = &receiver.kind && // If it doesn't exist, it will be ::core::option::Option::None::<&'static str>.unwrap() (A path in the HIR)
|
||||||
lint(cx, expr.span);
|
is_direct_expn_of(caller.span, "option_env").is_some()
|
||||||
}
|
{
|
||||||
}
|
lint(cx, expr.span);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,7 +240,7 @@ fn try_convert_match<'tcx>(
|
||||||
if let [first_arm, second_arm] = arms
|
if let [first_arm, second_arm] = arms
|
||||||
&& first_arm.guard.is_none()
|
&& first_arm.guard.is_none()
|
||||||
&& second_arm.guard.is_none()
|
&& second_arm.guard.is_none()
|
||||||
{
|
{
|
||||||
return if is_none_or_err_arm(cx, second_arm) {
|
return if is_none_or_err_arm(cx, second_arm) {
|
||||||
Some((first_arm.pat, first_arm.body, second_arm.body))
|
Some((first_arm.pat, first_arm.body, second_arm.body))
|
||||||
} else if is_none_or_err_arm(cx, first_arm) {
|
} else if is_none_or_err_arm(cx, first_arm) {
|
||||||
|
|
|
@ -208,7 +208,10 @@ impl<'tcx> PassByRefOrValue {
|
||||||
cx,
|
cx,
|
||||||
TRIVIALLY_COPY_PASS_BY_REF,
|
TRIVIALLY_COPY_PASS_BY_REF,
|
||||||
input.span,
|
input.span,
|
||||||
&format!("this argument ({size} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", self.ref_min_size),
|
&format!(
|
||||||
|
"this argument ({size} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)",
|
||||||
|
self.ref_min_size
|
||||||
|
),
|
||||||
"consider passing by value instead",
|
"consider passing by value instead",
|
||||||
value_type,
|
value_type,
|
||||||
Applicability::Unspecified,
|
Applicability::Unspecified,
|
||||||
|
|
|
@ -43,9 +43,11 @@ impl<'tcx> LateLintPass<'tcx> for PermissionsSetReadonlyFalse {
|
||||||
"call to `set_readonly` with argument `false`",
|
"call to `set_readonly` with argument `false`",
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.note("on Unix platforms this results in the file being world writable");
|
diag.note("on Unix platforms this results in the file being world writable");
|
||||||
diag.help("you can set the desired permissions using `PermissionsExt`. For more information, see\n\
|
diag.help(
|
||||||
https://doc.rust-lang.org/std/os/unix/fs/trait.PermissionsExt.html");
|
"you can set the desired permissions using `PermissionsExt`. For more information, see\n\
|
||||||
}
|
https://doc.rust-lang.org/std/os/unix/fs/trait.PermissionsExt.html",
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -289,10 +289,7 @@ fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||||
| sym::ptr_write_volatile
|
| sym::ptr_write_volatile
|
||||||
| sym::slice_from_raw_parts
|
| sym::slice_from_raw_parts
|
||||||
| sym::slice_from_raw_parts_mut => &[0],
|
| sym::slice_from_raw_parts_mut => &[0],
|
||||||
sym::ptr_copy
|
sym::ptr_copy | sym::ptr_copy_nonoverlapping | sym::ptr_swap | sym::ptr_swap_nonoverlapping => &[0, 1],
|
||||||
| sym::ptr_copy_nonoverlapping
|
|
||||||
| sym::ptr_swap
|
|
||||||
| sym::ptr_swap_nonoverlapping => &[0, 1],
|
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -416,7 +413,6 @@ impl<'tcx> DerefTy<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::too_many_lines)]
|
|
||||||
fn check_fn_args<'cx, 'tcx: 'cx>(
|
fn check_fn_args<'cx, 'tcx: 'cx>(
|
||||||
cx: &'cx LateContext<'tcx>,
|
cx: &'cx LateContext<'tcx>,
|
||||||
fn_sig: ty::FnSig<'tcx>,
|
fn_sig: ty::FnSig<'tcx>,
|
||||||
|
@ -438,104 +434,93 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
|
||||||
&& let [.., name] = path.segments
|
&& let [.., name] = path.segments
|
||||||
&& cx.tcx.item_name(adt.did()) == name.ident.name
|
&& cx.tcx.item_name(adt.did()) == name.ident.name
|
||||||
{
|
{
|
||||||
let emission_id = params.get(i).map_or(hir_ty.hir_id, |param| param.hir_id);
|
let emission_id = params.get(i).map_or(hir_ty.hir_id, |param| param.hir_id);
|
||||||
let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
|
let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
|
||||||
Some(sym::Vec) => (
|
Some(sym::Vec) => (
|
||||||
[("clone", ".to_owned()")].as_slice(),
|
[("clone", ".to_owned()")].as_slice(),
|
||||||
DerefTy::Slice(
|
DerefTy::Slice(
|
||||||
name.args
|
name.args.and_then(|args| args.args.first()).and_then(|arg| {
|
||||||
.and_then(|args| args.args.first())
|
if let GenericArg::Type(ty) = arg {
|
||||||
.and_then(|arg| if let GenericArg::Type(ty) = arg {
|
Some(ty.span)
|
||||||
Some(ty.span)
|
} else {
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}),
|
|
||||||
args.type_at(0),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
_ if Some(adt.did()) == cx.tcx.lang_items().string() => (
|
|
||||||
[("clone", ".to_owned()"), ("as_str", "")].as_slice(),
|
|
||||||
DerefTy::Str,
|
|
||||||
),
|
|
||||||
Some(sym::PathBuf) => (
|
|
||||||
[("clone", ".to_path_buf()"), ("as_path", "")].as_slice(),
|
|
||||||
DerefTy::Path,
|
|
||||||
),
|
|
||||||
Some(sym::Cow) if mutability == Mutability::Not => {
|
|
||||||
if let Some((lifetime, ty)) = name.args
|
|
||||||
.and_then(|args| {
|
|
||||||
if let [GenericArg::Lifetime(lifetime), ty] = args.args {
|
|
||||||
return Some((lifetime, ty));
|
|
||||||
}
|
|
||||||
None
|
None
|
||||||
})
|
|
||||||
{
|
|
||||||
if !lifetime.is_anonymous()
|
|
||||||
&& fn_sig.output()
|
|
||||||
.walk()
|
|
||||||
.filter_map(|arg| {
|
|
||||||
arg.as_region().and_then(|lifetime| {
|
|
||||||
match lifetime.kind() {
|
|
||||||
ty::ReEarlyBound(r) => Some(r.def_id),
|
|
||||||
ty::ReLateBound(_, r) => r.kind.get_id(),
|
|
||||||
ty::ReFree(r) => r.bound_region.get_id(),
|
|
||||||
ty::ReStatic
|
|
||||||
| ty::ReVar(_)
|
|
||||||
| ty::RePlaceholder(_)
|
|
||||||
| ty::ReErased
|
|
||||||
| ty::ReError(_) => None,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.any(|def_id| {
|
|
||||||
matches!(
|
|
||||||
lifetime.res,
|
|
||||||
LifetimeName::Param(param_def_id) if def_id
|
|
||||||
.as_local()
|
|
||||||
.is_some_and(|def_id| def_id == param_def_id),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
{
|
|
||||||
// `&Cow<'a, T>` when the return type uses 'a is okay
|
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
let ty_name =
|
args.type_at(0),
|
||||||
snippet_opt(cx, ty.span()).unwrap_or_else(|| args.type_at(1).to_string());
|
),
|
||||||
|
),
|
||||||
span_lint_hir_and_then(
|
_ if Some(adt.did()) == cx.tcx.lang_items().string() => {
|
||||||
cx,
|
([("clone", ".to_owned()"), ("as_str", "")].as_slice(), DerefTy::Str)
|
||||||
PTR_ARG,
|
},
|
||||||
emission_id,
|
Some(sym::PathBuf) => ([("clone", ".to_path_buf()"), ("as_path", "")].as_slice(), DerefTy::Path),
|
||||||
hir_ty.span,
|
Some(sym::Cow) if mutability == Mutability::Not => {
|
||||||
"using a reference to `Cow` is not recommended",
|
if let Some((lifetime, ty)) = name.args.and_then(|args| {
|
||||||
|diag| {
|
if let [GenericArg::Lifetime(lifetime), ty] = args.args {
|
||||||
diag.span_suggestion(
|
return Some((lifetime, ty));
|
||||||
hir_ty.span,
|
|
||||||
"change this to",
|
|
||||||
format!("&{}{ty_name}", mutability.prefix_str()),
|
|
||||||
Applicability::Unspecified,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return None;
|
None
|
||||||
},
|
}) {
|
||||||
_ => return None,
|
if !lifetime.is_anonymous()
|
||||||
};
|
&& fn_sig
|
||||||
return Some(PtrArg {
|
.output()
|
||||||
idx: i,
|
.walk()
|
||||||
emission_id,
|
.filter_map(|arg| {
|
||||||
span: hir_ty.span,
|
arg.as_region().and_then(|lifetime| match lifetime.kind() {
|
||||||
ty_did: adt.did(),
|
ty::ReEarlyBound(r) => Some(r.def_id),
|
||||||
ty_name: name.ident.name,
|
ty::ReLateBound(_, r) => r.kind.get_id(),
|
||||||
method_renames,
|
ty::ReFree(r) => r.bound_region.get_id(),
|
||||||
ref_prefix: RefPrefix {
|
ty::ReStatic
|
||||||
lt: *lt,
|
| ty::ReVar(_)
|
||||||
mutability,
|
| ty::RePlaceholder(_)
|
||||||
},
|
| ty::ReErased
|
||||||
deref_ty,
|
| ty::ReError(_) => None,
|
||||||
});
|
})
|
||||||
|
})
|
||||||
|
.any(|def_id| {
|
||||||
|
matches!(
|
||||||
|
lifetime.res,
|
||||||
|
LifetimeName::Param(param_def_id) if def_id
|
||||||
|
.as_local()
|
||||||
|
.is_some_and(|def_id| def_id == param_def_id),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
// `&Cow<'a, T>` when the return type uses 'a is okay
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty_name = snippet_opt(cx, ty.span()).unwrap_or_else(|| args.type_at(1).to_string());
|
||||||
|
|
||||||
|
span_lint_hir_and_then(
|
||||||
|
cx,
|
||||||
|
PTR_ARG,
|
||||||
|
emission_id,
|
||||||
|
hir_ty.span,
|
||||||
|
"using a reference to `Cow` is not recommended",
|
||||||
|
|diag| {
|
||||||
|
diag.span_suggestion(
|
||||||
|
hir_ty.span,
|
||||||
|
"change this to",
|
||||||
|
format!("&{}{ty_name}", mutability.prefix_str()),
|
||||||
|
Applicability::Unspecified,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
},
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
return Some(PtrArg {
|
||||||
|
idx: i,
|
||||||
|
emission_id,
|
||||||
|
span: hir_ty.span,
|
||||||
|
ty_did: adt.did(),
|
||||||
|
ty_name: name.ident.name,
|
||||||
|
method_renames,
|
||||||
|
ref_prefix: RefPrefix { lt: *lt, mutability },
|
||||||
|
deref_ty,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
|
|
|
@ -41,16 +41,17 @@ declare_lint_pass!(PubUse => [PUB_USE]);
|
||||||
|
|
||||||
impl EarlyLintPass for PubUse {
|
impl EarlyLintPass for PubUse {
|
||||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||||
if let ItemKind::Use(_) = item.kind &&
|
if let ItemKind::Use(_) = item.kind
|
||||||
let VisibilityKind::Public = item.vis.kind {
|
&& let VisibilityKind::Public = item.vis.kind
|
||||||
span_lint_and_help(
|
{
|
||||||
cx,
|
span_lint_and_help(
|
||||||
PUB_USE,
|
cx,
|
||||||
item.span,
|
PUB_USE,
|
||||||
"using `pub use`",
|
item.span,
|
||||||
None,
|
"using `pub use`",
|
||||||
"move the exported item to a public module instead",
|
None,
|
||||||
);
|
"move the exported item to a public module instead",
|
||||||
}
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,16 +98,23 @@ enum IfBlockType<'hir> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
|
fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
|
||||||
if let StmtKind::Local(Local { pat, init: Some(init_expr), els: Some(els), .. }) = stmt.kind &&
|
if let StmtKind::Local(Local {
|
||||||
let Block { stmts: &[], expr: Some(els), .. } = els &&
|
pat,
|
||||||
let Some(inner_pat) = pat_and_expr_can_be_question_mark(cx, pat, els)
|
init: Some(init_expr),
|
||||||
|
els: Some(els),
|
||||||
|
..
|
||||||
|
}) = stmt.kind
|
||||||
|
&& let Block {
|
||||||
|
stmts: &[],
|
||||||
|
expr: Some(els),
|
||||||
|
..
|
||||||
|
} = els
|
||||||
|
&& let Some(inner_pat) = pat_and_expr_can_be_question_mark(cx, pat, els)
|
||||||
{
|
{
|
||||||
let mut applicability = Applicability::MaybeIncorrect;
|
let mut applicability = Applicability::MaybeIncorrect;
|
||||||
let init_expr_str = snippet_with_applicability(cx, init_expr.span, "..", &mut applicability);
|
let init_expr_str = snippet_with_applicability(cx, init_expr.span, "..", &mut applicability);
|
||||||
let receiver_str = snippet_with_applicability(cx, inner_pat.span, "..", &mut applicability);
|
let receiver_str = snippet_with_applicability(cx, inner_pat.span, "..", &mut applicability);
|
||||||
let sugg = format!(
|
let sugg = format!("let {receiver_str} = {init_expr_str}?;",);
|
||||||
"let {receiver_str} = {init_expr_str}?;",
|
|
||||||
);
|
|
||||||
span_lint_and_sugg(
|
span_lint_and_sugg(
|
||||||
cx,
|
cx,
|
||||||
QUESTION_MARK,
|
QUESTION_MARK,
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue