Merge commit 'b40ea209e7' into clippyup
This commit is contained in:
parent
cde58f7174
commit
f6d1f368db
349 changed files with 10420 additions and 6013 deletions
|
|
@ -1,101 +1,82 @@
|
|||
use super::{contains_return, BIND_INSTEAD_OF_MAP};
|
||||
use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::source::{snippet, snippet_with_macro_callsite};
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::{in_macro, match_qpath, method_calls, paths, remove_blocks, visitors::find_all_ret_expressions};
|
||||
use clippy_utils::{in_macro, remove_blocks, visitors::find_all_ret_expressions};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::{LangItem, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::DefIdTree;
|
||||
use rustc_span::Span;
|
||||
|
||||
pub(crate) struct OptionAndThenSome;
|
||||
|
||||
impl BindInsteadOfMap for OptionAndThenSome {
|
||||
const TYPE_NAME: &'static str = "Option";
|
||||
const TYPE_QPATH: &'static [&'static str] = &paths::OPTION;
|
||||
|
||||
const VARIANT_LANG_ITEM: LangItem = LangItem::OptionSome;
|
||||
const BAD_METHOD_NAME: &'static str = "and_then";
|
||||
const BAD_VARIANT_NAME: &'static str = "Some";
|
||||
const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::OPTION_SOME;
|
||||
|
||||
const GOOD_METHOD_NAME: &'static str = "map";
|
||||
}
|
||||
|
||||
pub(crate) struct ResultAndThenOk;
|
||||
|
||||
impl BindInsteadOfMap for ResultAndThenOk {
|
||||
const TYPE_NAME: &'static str = "Result";
|
||||
const TYPE_QPATH: &'static [&'static str] = &paths::RESULT;
|
||||
|
||||
const VARIANT_LANG_ITEM: LangItem = LangItem::ResultOk;
|
||||
const BAD_METHOD_NAME: &'static str = "and_then";
|
||||
const BAD_VARIANT_NAME: &'static str = "Ok";
|
||||
const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::RESULT_OK;
|
||||
|
||||
const GOOD_METHOD_NAME: &'static str = "map";
|
||||
}
|
||||
|
||||
pub(crate) struct ResultOrElseErrInfo;
|
||||
|
||||
impl BindInsteadOfMap for ResultOrElseErrInfo {
|
||||
const TYPE_NAME: &'static str = "Result";
|
||||
const TYPE_QPATH: &'static [&'static str] = &paths::RESULT;
|
||||
|
||||
const VARIANT_LANG_ITEM: LangItem = LangItem::ResultErr;
|
||||
const BAD_METHOD_NAME: &'static str = "or_else";
|
||||
const BAD_VARIANT_NAME: &'static str = "Err";
|
||||
const BAD_VARIANT_QPATH: &'static [&'static str] = &paths::RESULT_ERR;
|
||||
|
||||
const GOOD_METHOD_NAME: &'static str = "map_err";
|
||||
}
|
||||
|
||||
pub(crate) trait BindInsteadOfMap {
|
||||
const TYPE_NAME: &'static str;
|
||||
const TYPE_QPATH: &'static [&'static str];
|
||||
|
||||
const VARIANT_LANG_ITEM: LangItem;
|
||||
const BAD_METHOD_NAME: &'static str;
|
||||
const BAD_VARIANT_NAME: &'static str;
|
||||
const BAD_VARIANT_QPATH: &'static [&'static str];
|
||||
|
||||
const GOOD_METHOD_NAME: &'static str;
|
||||
|
||||
fn no_op_msg() -> String {
|
||||
format!(
|
||||
fn no_op_msg(cx: &LateContext<'_>) -> Option<String> {
|
||||
let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?;
|
||||
let item_id = cx.tcx.parent(variant_id)?;
|
||||
Some(format!(
|
||||
"using `{}.{}({})`, which is a no-op",
|
||||
Self::TYPE_NAME,
|
||||
cx.tcx.item_name(item_id),
|
||||
Self::BAD_METHOD_NAME,
|
||||
Self::BAD_VARIANT_NAME
|
||||
)
|
||||
cx.tcx.item_name(variant_id),
|
||||
))
|
||||
}
|
||||
|
||||
fn lint_msg() -> String {
|
||||
format!(
|
||||
fn lint_msg(cx: &LateContext<'_>) -> Option<String> {
|
||||
let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?;
|
||||
let item_id = cx.tcx.parent(variant_id)?;
|
||||
Some(format!(
|
||||
"using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`",
|
||||
Self::TYPE_NAME,
|
||||
cx.tcx.item_name(item_id),
|
||||
Self::BAD_METHOD_NAME,
|
||||
Self::BAD_VARIANT_NAME,
|
||||
cx.tcx.item_name(variant_id),
|
||||
Self::GOOD_METHOD_NAME
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
fn lint_closure_autofixable(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
args: &[hir::Expr<'_>],
|
||||
recv: &hir::Expr<'_>,
|
||||
closure_expr: &hir::Expr<'_>,
|
||||
closure_args_span: Span,
|
||||
) -> bool {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Call(ref some_expr, ref some_args) = closure_expr.kind;
|
||||
if let hir::ExprKind::Path(ref qpath) = some_expr.kind;
|
||||
if match_qpath(qpath, Self::BAD_VARIANT_QPATH);
|
||||
if some_args.len() == 1;
|
||||
if let hir::ExprKind::Call(some_expr, [inner_expr]) = closure_expr.kind;
|
||||
if let hir::ExprKind::Path(QPath::Resolved(_, path)) = some_expr.kind;
|
||||
if Self::is_variant(cx, path.res);
|
||||
if !contains_return(inner_expr);
|
||||
if let Some(msg) = Self::lint_msg(cx);
|
||||
then {
|
||||
let inner_expr = &some_args[0];
|
||||
|
||||
if contains_return(inner_expr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let some_inner_snip = if inner_expr.span.from_expansion() {
|
||||
snippet_with_macro_callsite(cx, inner_expr.span, "_")
|
||||
} else {
|
||||
|
|
@ -103,13 +84,13 @@ pub(crate) trait BindInsteadOfMap {
|
|||
};
|
||||
|
||||
let closure_args_snip = snippet(cx, closure_args_span, "..");
|
||||
let option_snip = snippet(cx, args[0].span, "..");
|
||||
let option_snip = snippet(cx, recv.span, "..");
|
||||
let note = format!("{}.{}({} {})", option_snip, Self::GOOD_METHOD_NAME, closure_args_snip, some_inner_snip);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BIND_INSTEAD_OF_MAP,
|
||||
expr.span,
|
||||
Self::lint_msg().as_ref(),
|
||||
&msg,
|
||||
"try this",
|
||||
note,
|
||||
Applicability::MachineApplicable,
|
||||
|
|
@ -126,68 +107,84 @@ pub(crate) trait BindInsteadOfMap {
|
|||
let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| {
|
||||
if_chain! {
|
||||
if !in_macro(ret_expr.span);
|
||||
if let hir::ExprKind::Call(ref func_path, ref args) = ret_expr.kind;
|
||||
if let hir::ExprKind::Path(ref qpath) = func_path.kind;
|
||||
if match_qpath(qpath, Self::BAD_VARIANT_QPATH);
|
||||
if args.len() == 1;
|
||||
if !contains_return(&args[0]);
|
||||
if let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind;
|
||||
if let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind;
|
||||
if Self::is_variant(cx, path.res);
|
||||
if !contains_return(arg);
|
||||
then {
|
||||
suggs.push((ret_expr.span, args[0].span.source_callsite()));
|
||||
suggs.push((ret_expr.span, arg.span.source_callsite()));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if can_sugg {
|
||||
span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, Self::lint_msg().as_ref(), |diag| {
|
||||
multispan_sugg_with_applicability(
|
||||
diag,
|
||||
"try this",
|
||||
Applicability::MachineApplicable,
|
||||
std::iter::once((*method_calls(expr, 1).2.get(0).unwrap(), Self::GOOD_METHOD_NAME.into())).chain(
|
||||
suggs
|
||||
.into_iter()
|
||||
.map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())),
|
||||
),
|
||||
)
|
||||
});
|
||||
}
|
||||
can_sugg
|
||||
let (span, msg) = if_chain! {
|
||||
if can_sugg;
|
||||
if let hir::ExprKind::MethodCall(_, span, ..) = expr.kind;
|
||||
if let Some(msg) = Self::lint_msg(cx);
|
||||
then { (span, msg) } else { return false; }
|
||||
};
|
||||
span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, &msg, |diag| {
|
||||
multispan_sugg_with_applicability(
|
||||
diag,
|
||||
"try this",
|
||||
Applicability::MachineApplicable,
|
||||
std::iter::once((span, Self::GOOD_METHOD_NAME.into())).chain(
|
||||
suggs
|
||||
.into_iter()
|
||||
.map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())),
|
||||
),
|
||||
)
|
||||
});
|
||||
true
|
||||
}
|
||||
|
||||
/// Lint use of `_.and_then(|x| Some(y))` for `Option`s
|
||||
fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) -> bool {
|
||||
if !match_type(cx, cx.typeck_results().expr_ty(&args[0]), Self::TYPE_QPATH) {
|
||||
return false;
|
||||
fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def();
|
||||
if let Ok(vid) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM);
|
||||
if Some(adt.did) == cx.tcx.parent(vid);
|
||||
then {} else { return false; }
|
||||
}
|
||||
|
||||
match args[1].kind {
|
||||
match arg.kind {
|
||||
hir::ExprKind::Closure(_, _, body_id, closure_args_span, _) => {
|
||||
let closure_body = cx.tcx.hir().body(body_id);
|
||||
let closure_expr = remove_blocks(&closure_body.value);
|
||||
|
||||
if Self::lint_closure_autofixable(cx, expr, args, closure_expr, closure_args_span) {
|
||||
if Self::lint_closure_autofixable(cx, expr, recv, closure_expr, closure_args_span) {
|
||||
true
|
||||
} else {
|
||||
Self::lint_closure(cx, expr, closure_expr)
|
||||
}
|
||||
},
|
||||
// `_.and_then(Some)` case, which is no-op.
|
||||
hir::ExprKind::Path(ref qpath) if match_qpath(qpath, Self::BAD_VARIANT_QPATH) => {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BIND_INSTEAD_OF_MAP,
|
||||
expr.span,
|
||||
Self::no_op_msg().as_ref(),
|
||||
"use the expression directly",
|
||||
snippet(cx, args[0].span, "..").into(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
hir::ExprKind::Path(QPath::Resolved(_, path)) if Self::is_variant(cx, path.res) => {
|
||||
if let Some(msg) = Self::no_op_msg(cx) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BIND_INSTEAD_OF_MAP,
|
||||
expr.span,
|
||||
&msg,
|
||||
"use the expression directly",
|
||||
snippet(cx, recv.span, "..").into(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
true
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_variant(cx: &LateContext<'_>, res: Res) -> bool {
|
||||
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
|
||||
if let Ok(variant_id) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM) {
|
||||
return cx.tcx.parent(id) == Some(variant_id);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +1,34 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::BYTES_NTH;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'tcx [Expr<'tcx>]) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(_, _, ref args, _) = expr.kind;
|
||||
let ty = cx.typeck_results().expr_ty(&iter_args[0]).peel_refs();
|
||||
let caller_type = if is_type_diagnostic_item(cx, ty, sym::string_type) {
|
||||
Some("String")
|
||||
} else if ty.is_str() {
|
||||
Some("str")
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(caller_type) = caller_type;
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BYTES_NTH,
|
||||
expr.span,
|
||||
&format!("called `.byte().nth()` on a `{}`", caller_type),
|
||||
"try",
|
||||
format!(
|
||||
"{}.as_bytes().get({})",
|
||||
snippet_with_applicability(cx, iter_args[0].span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, args[1].span, "..", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, n_arg: &'tcx Expr<'tcx>) {
|
||||
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
let caller_type = if ty.is_str() {
|
||||
"str"
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::string_type) {
|
||||
"String"
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BYTES_NTH,
|
||||
expr.span,
|
||||
&format!("called `.byte().nth()` on a `{}`", caller_type),
|
||||
"try",
|
||||
format!(
|
||||
"{}.as_bytes().get({})",
|
||||
snippet_with_applicability(cx, recv.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, n_arg.span, "..", &mut applicability)
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ pub(super) fn check(
|
|||
) -> bool {
|
||||
if_chain! {
|
||||
if let Some(args) = method_chain_args(info.chain, chain_methods);
|
||||
if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind;
|
||||
if let hir::ExprKind::Call(fun, arg_char) = info.other.kind;
|
||||
if arg_char.len() == 1;
|
||||
if let hir::ExprKind::Path(ref qpath) = fun.kind;
|
||||
if let Some(segment) = single_segment_path(qpath);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::get_parent_node;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::sugg;
|
||||
use clippy_utils::ty::is_copy;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{BindingAnnotation, Expr, ExprKind, MatchSource, Node, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{self, adjustment::Adjust};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use std::iter;
|
||||
|
||||
|
|
@ -12,12 +14,26 @@ use super::CLONE_DOUBLE_REF;
|
|||
use super::CLONE_ON_COPY;
|
||||
|
||||
/// Checks for the `CLONE_ON_COPY` lint.
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
|
||||
if !(args.len() == 1 && method_name == sym::clone) {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, args: &[Expr<'_>]) {
|
||||
let arg = match args {
|
||||
[arg] if method_name == sym::clone => arg,
|
||||
_ => return,
|
||||
};
|
||||
if cx
|
||||
.typeck_results()
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.and_then(|id| cx.tcx.trait_of_item(id))
|
||||
.zip(cx.tcx.lang_items().clone_trait())
|
||||
.map_or(true, |(x, y)| x != y)
|
||||
{
|
||||
return;
|
||||
}
|
||||
let arg = &args[0];
|
||||
let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
|
||||
let arg_adjustments = cx.typeck_results().expr_adjustments(arg);
|
||||
let arg_ty = arg_adjustments
|
||||
.last()
|
||||
.map_or_else(|| cx.typeck_results().expr_ty(arg), |a| a.target);
|
||||
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Ref(_, inner, _) = arg_ty.kind() {
|
||||
if let ty::Ref(_, innermost, _) = inner.kind() {
|
||||
|
|
@ -61,57 +77,57 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Sym
|
|||
}
|
||||
|
||||
if is_copy(cx, ty) {
|
||||
let snip;
|
||||
if let Some(snippet) = sugg::Sugg::hir_opt(cx, arg) {
|
||||
let parent = cx.tcx.hir().get_parent_node(expr.hir_id);
|
||||
match &cx.tcx.hir().get(parent) {
|
||||
hir::Node::Expr(parent) => match parent.kind {
|
||||
// &*x is a nop, &x.clone() is not
|
||||
hir::ExprKind::AddrOf(..) => return,
|
||||
// (*x).func() is useless, x.clone().func() can work in case func borrows mutably
|
||||
hir::ExprKind::MethodCall(_, _, parent_args, _) if expr.hir_id == parent_args[0].hir_id => {
|
||||
return;
|
||||
},
|
||||
|
||||
_ => {},
|
||||
},
|
||||
hir::Node::Stmt(stmt) => {
|
||||
if let hir::StmtKind::Local(ref loc) = stmt.kind {
|
||||
if let hir::PatKind::Ref(..) = loc.pat.kind {
|
||||
// let ref y = *x borrows x, let ref y = x.clone() does not
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
let parent_is_suffix_expr = match get_parent_node(cx.tcx, expr.hir_id) {
|
||||
Some(Node::Expr(parent)) => match parent.kind {
|
||||
// &*x is a nop, &x.clone() is not
|
||||
ExprKind::AddrOf(..) => return,
|
||||
// (*x).func() is useless, x.clone().func() can work in case func borrows self
|
||||
ExprKind::MethodCall(_, _, [self_arg, ..], _)
|
||||
if expr.hir_id == self_arg.hir_id && ty != cx.typeck_results().expr_ty_adjusted(expr) =>
|
||||
{
|
||||
return;
|
||||
}
|
||||
ExprKind::MethodCall(_, _, [self_arg, ..], _) if expr.hir_id == self_arg.hir_id => true,
|
||||
ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
|
||||
| ExprKind::Field(..)
|
||||
| ExprKind::Index(..) => true,
|
||||
_ => false,
|
||||
},
|
||||
// local binding capturing a reference
|
||||
Some(Node::Local(l))
|
||||
if matches!(
|
||||
l.pat.kind,
|
||||
PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..)
|
||||
) =>
|
||||
{
|
||||
return;
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// x.clone() might have dereferenced x, possibly through Deref impls
|
||||
if cx.typeck_results().expr_ty(arg) == ty {
|
||||
snip = Some(("try removing the `clone` call", format!("{}", snippet)));
|
||||
} else {
|
||||
let deref_count = cx
|
||||
.typeck_results()
|
||||
.expr_adjustments(arg)
|
||||
.iter()
|
||||
.filter(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(_)))
|
||||
.count();
|
||||
let derefs: String = iter::repeat('*').take(deref_count).collect();
|
||||
snip = Some(("try dereferencing it", format!("{}{}", derefs, snippet)));
|
||||
}
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let snip = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut app).0;
|
||||
|
||||
let deref_count = arg_adjustments
|
||||
.iter()
|
||||
.take_while(|adj| matches!(adj.kind, Adjust::Deref(_)))
|
||||
.count();
|
||||
let (help, sugg) = if deref_count == 0 {
|
||||
("try removing the `clone` call", snip.into())
|
||||
} else if parent_is_suffix_expr {
|
||||
("try dereferencing it", format!("({}{})", "*".repeat(deref_count), snip))
|
||||
} else {
|
||||
snip = None;
|
||||
}
|
||||
span_lint_and_then(
|
||||
("try dereferencing it", format!("{}{}", "*".repeat(deref_count), snip))
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CLONE_ON_COPY,
|
||||
expr.span,
|
||||
&format!("using `clone` on type `{}` which implements the `Copy` trait", ty),
|
||||
|diag| {
|
||||
if let Some((text, snip)) = snip {
|
||||
diag.span_suggestion(expr.span, text, snip, Applicability::MachineApplicable);
|
||||
}
|
||||
},
|
||||
help,
|
||||
sugg,
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,9 +100,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Spa
|
|||
applicability: &mut Applicability,
|
||||
) -> Vec<String> {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref format_arg) = a.kind;
|
||||
if let hir::ExprKind::Match(ref format_arg_expr, _, _) = format_arg.kind;
|
||||
if let hir::ExprKind::Tup(ref format_arg_expr_tup) = format_arg_expr.kind;
|
||||
if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, format_arg) = a.kind;
|
||||
if let hir::ExprKind::Match(format_arg_expr, _, _) = format_arg.kind;
|
||||
if let hir::ExprKind::Tup(format_arg_expr_tup) = format_arg_expr.kind;
|
||||
|
||||
then {
|
||||
format_arg_expr_tup
|
||||
|
|
@ -155,7 +155,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Spa
|
|||
if block.stmts.len() == 1;
|
||||
if let hir::StmtKind::Local(local) = &block.stmts[0].kind;
|
||||
if let Some(arg_root) = &local.init;
|
||||
if let hir::ExprKind::Call(ref inner_fun, ref inner_args) = arg_root.kind;
|
||||
if let hir::ExprKind::Call(inner_fun, inner_args) = arg_root.kind;
|
||||
if is_expn_of(inner_fun.span, "format").is_some() && inner_args.len() == 1;
|
||||
if let hir::ExprKind::Call(_, format_args) = &inner_args[0].kind;
|
||||
then {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ use rustc_span::sym;
|
|||
use super::EXPECT_USED;
|
||||
|
||||
/// lint use of `expect()` for `Option`s and `Result`s
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, expect_args: &[hir::Expr<'_>]) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(&expect_args[0]).peel_refs();
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) {
|
||||
Some((EXPECT_USED, "an Option", "None"))
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ use rustc_span::source_map::Span;
|
|||
|
||||
use super::FILETYPE_IS_FILE;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||
let ty = cx.typeck_results().expr_ty(&args[0]);
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
|
||||
let ty = cx.typeck_results().expr_ty(recv);
|
||||
|
||||
if !match_type(cx, ty, &paths::FILE_TYPE) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,87 +1,174 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{is_trait_method, path_to_local_id, SpanlessEq};
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, SpanlessEq};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::{Expr, ExprKind, PatKind, UnOp};
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Expr, ExprKind, PatKind, QPath, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::TyS;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::MANUAL_FILTER_MAP;
|
||||
use super::MANUAL_FIND_MAP;
|
||||
use super::OPTION_FILTER_MAP;
|
||||
|
||||
/// lint use of `filter().map()` or `find().map()` for `Iterators`
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_find: bool) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind;
|
||||
if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind;
|
||||
if is_trait_method(cx, map_recv, sym::Iterator);
|
||||
|
||||
// filter(|x| ...is_some())...
|
||||
if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind;
|
||||
let filter_body = cx.tcx.hir().body(filter_body_id);
|
||||
if let [filter_param] = filter_body.params;
|
||||
// optional ref pattern: `filter(|&x| ..)`
|
||||
let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind {
|
||||
(ref_pat, true)
|
||||
} else {
|
||||
(filter_param.pat, false)
|
||||
};
|
||||
// closure ends with is_some() or is_ok()
|
||||
if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
|
||||
if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind;
|
||||
if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def();
|
||||
if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) {
|
||||
Some(false)
|
||||
} else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) {
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" };
|
||||
|
||||
// ...map(|x| ...unwrap())
|
||||
if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind;
|
||||
let map_body = cx.tcx.hir().body(map_body_id);
|
||||
if let [map_param] = map_body.params;
|
||||
if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind;
|
||||
// closure ends with expect() or unwrap()
|
||||
if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind;
|
||||
if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or);
|
||||
|
||||
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
|
||||
// in `filter(|x| ..)`, replace `*x` with `x`
|
||||
let a_path = if_chain! {
|
||||
if !is_filter_param_ref;
|
||||
if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind;
|
||||
then { expr_path } else { a }
|
||||
};
|
||||
// let the filter closure arg and the map closure arg be equal
|
||||
if_chain! {
|
||||
if path_to_local_id(a_path, filter_param_id);
|
||||
if path_to_local_id(b, map_param_id);
|
||||
if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b));
|
||||
then {
|
||||
return true;
|
||||
}
|
||||
fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool {
|
||||
match &expr.kind {
|
||||
hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name,
|
||||
hir::ExprKind::Path(QPath::Resolved(_, segments)) => {
|
||||
segments.segments.last().unwrap().ident.name == method_name
|
||||
},
|
||||
hir::ExprKind::Closure(_, _, c, _, _) => {
|
||||
let body = cx.tcx.hir().body(*c);
|
||||
let closure_expr = remove_blocks(&body.value);
|
||||
let arg_id = body.params[0].pat.hir_id;
|
||||
match closure_expr.kind {
|
||||
hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, _, args, _) => {
|
||||
if_chain! {
|
||||
if ident.name == method_name;
|
||||
if let hir::ExprKind::Path(path) = &args[0].kind;
|
||||
if let Res::Local(ref local) = cx.qpath_res(path, args[0].hir_id);
|
||||
then {
|
||||
return arg_id == *local
|
||||
}
|
||||
}
|
||||
false
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_option_filter_map<'tcx>(cx: &LateContext<'tcx>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool {
|
||||
is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some))
|
||||
}
|
||||
|
||||
/// lint use of `filter().map()` for `Iterators`
|
||||
fn lint_filter_some_map_unwrap(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
filter_recv: &hir::Expr<'_>,
|
||||
filter_arg: &hir::Expr<'_>,
|
||||
map_arg: &hir::Expr<'_>,
|
||||
target_span: Span,
|
||||
methods_span: Span,
|
||||
) {
|
||||
let iterator = is_trait_method(cx, expr, sym::Iterator);
|
||||
let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv), sym::option_type);
|
||||
if (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) {
|
||||
let msg = "`filter` for `Some` followed by `unwrap`";
|
||||
let help = "consider using `flatten` instead";
|
||||
let sugg = format!(
|
||||
"{}",
|
||||
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, target_span),)
|
||||
);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
OPTION_FILTER_MAP,
|
||||
methods_span,
|
||||
msg,
|
||||
help,
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// lint use of `filter().map()` or `find().map()` for `Iterators`
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &hir::Expr<'_>,
|
||||
filter_recv: &hir::Expr<'_>,
|
||||
filter_arg: &hir::Expr<'_>,
|
||||
filter_span: Span,
|
||||
map_recv: &hir::Expr<'_>,
|
||||
map_arg: &hir::Expr<'_>,
|
||||
map_span: Span,
|
||||
is_find: bool,
|
||||
) {
|
||||
lint_filter_some_map_unwrap(
|
||||
cx,
|
||||
expr,
|
||||
filter_recv,
|
||||
filter_arg,
|
||||
map_arg,
|
||||
map_span,
|
||||
filter_span.with_hi(expr.span.hi()),
|
||||
);
|
||||
if_chain! {
|
||||
if is_trait_method(cx, map_recv, sym::Iterator);
|
||||
|
||||
// filter(|x| ...is_some())...
|
||||
if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind;
|
||||
let filter_body = cx.tcx.hir().body(filter_body_id);
|
||||
if let [filter_param] = filter_body.params;
|
||||
// optional ref pattern: `filter(|&x| ..)`
|
||||
let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind {
|
||||
(ref_pat, true)
|
||||
} else {
|
||||
(filter_param.pat, false)
|
||||
};
|
||||
// closure ends with is_some() or is_ok()
|
||||
if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
|
||||
if let ExprKind::MethodCall(path, _, [filter_arg], _) = filter_body.value.kind;
|
||||
if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def();
|
||||
if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::option_type, opt_ty.did) {
|
||||
Some(false)
|
||||
} else if cx.tcx.is_diagnostic_item(sym::result_type, opt_ty.did) {
|
||||
Some(true)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" };
|
||||
|
||||
// ...map(|x| ...unwrap())
|
||||
if let ExprKind::Closure(_, _, map_body_id, ..) = map_arg.kind;
|
||||
let map_body = cx.tcx.hir().body(map_body_id);
|
||||
if let [map_param] = map_body.params;
|
||||
if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind;
|
||||
// closure ends with expect() or unwrap()
|
||||
if let ExprKind::MethodCall(seg, _, [map_arg, ..], _) = map_body.value.kind;
|
||||
if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or);
|
||||
|
||||
let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
|
||||
// in `filter(|x| ..)`, replace `*x` with `x`
|
||||
let a_path = if_chain! {
|
||||
if !is_filter_param_ref;
|
||||
if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind;
|
||||
then { expr_path } else { a }
|
||||
};
|
||||
// let the filter closure arg and the map closure arg be equal
|
||||
if_chain! {
|
||||
if path_to_local_id(a_path, filter_param_id);
|
||||
if path_to_local_id(b, map_param_id);
|
||||
if TyS::same_type(cx.typeck_results().expr_ty_adjusted(a), cx.typeck_results().expr_ty_adjusted(b));
|
||||
then {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
};
|
||||
if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg);
|
||||
then {
|
||||
let span = filter_span.with_hi(expr.span.hi());
|
||||
let (filter_name, lint) = if is_find {
|
||||
("find", MANUAL_FIND_MAP)
|
||||
} else {
|
||||
("filter", MANUAL_FILTER_MAP)
|
||||
};
|
||||
let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name);
|
||||
let to_opt = if is_result { ".ok()" } else { "" };
|
||||
let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident,
|
||||
snippet(cx, map_arg.span, ".."), to_opt);
|
||||
span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable);
|
||||
}
|
||||
false
|
||||
};
|
||||
if SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg);
|
||||
then {
|
||||
let span = filter_span.to(map_span);
|
||||
let (filter_name, lint) = if is_find {
|
||||
("find", MANUAL_FIND_MAP)
|
||||
} else {
|
||||
("filter", MANUAL_FILTER_MAP)
|
||||
};
|
||||
let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name);
|
||||
let to_opt = if is_result { ".ok()" } else { "" };
|
||||
let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident,
|
||||
snippet(cx, map_arg.span, ".."), to_opt);
|
||||
span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,15 +8,8 @@ use rustc_span::{source_map::Span, sym};
|
|||
|
||||
use super::FILTER_MAP_IDENTITY;
|
||||
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
filter_map_args: &[hir::Expr<'_>],
|
||||
filter_map_span: Span,
|
||||
) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_map_arg: &hir::Expr<'_>, filter_map_span: Span) {
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
let arg_node = &filter_map_args[1].kind;
|
||||
|
||||
let apply_lint = |message: &str| {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
@ -30,8 +23,8 @@ pub(super) fn check(
|
|||
};
|
||||
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Closure(_, _, body_id, _, _) = arg_node;
|
||||
let body = cx.tcx.hir().body(*body_id);
|
||||
if let hir::ExprKind::Closure(_, _, body_id, _, _) = filter_map_arg.kind;
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
|
||||
if let hir::PatKind::Binding(_, binding_id, ..) = body.params[0].pat.kind;
|
||||
if path_to_local_id(&body.value, binding_id);
|
||||
|
|
@ -41,7 +34,7 @@ pub(super) fn check(
|
|||
}
|
||||
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Path(ref qpath) = arg_node;
|
||||
if let hir::ExprKind::Path(ref qpath) = filter_map_arg.kind;
|
||||
|
||||
if match_qpath(qpath, &paths::STD_CONVERT_IDENTITY);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ const FILTER_MAP_NEXT_MSRV: RustcVersion = RustcVersion::new(1, 30, 0);
|
|||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
filter_args: &'tcx [hir::Expr<'_>],
|
||||
recv: &'tcx hir::Expr<'_>,
|
||||
arg: &'tcx hir::Expr<'_>,
|
||||
msrv: Option<&RustcVersion>,
|
||||
) {
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
|
|
@ -24,9 +25,9 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
|
||||
`.find_map(..)` instead";
|
||||
let filter_snippet = snippet(cx, filter_args[1].span, "..");
|
||||
let filter_snippet = snippet(cx, arg.span, "..");
|
||||
if filter_snippet.lines().count() <= 1 {
|
||||
let iter_snippet = snippet(cx, filter_args[0].span, "..");
|
||||
let iter_snippet = snippet(cx, recv.span, "..");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
FILTER_MAP_NEXT,
|
||||
|
|
|
|||
|
|
@ -9,14 +9,19 @@ use rustc_span::sym;
|
|||
use super::FILTER_NEXT;
|
||||
|
||||
/// lint use of `filter().next()` for `Iterators`
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) {
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
recv: &'tcx hir::Expr<'_>,
|
||||
filter_arg: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
// lint if caller of `.filter().next()` is an Iterator
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
|
||||
`.find(..)` instead";
|
||||
let filter_snippet = snippet(cx, filter_args[1].span, "..");
|
||||
let filter_snippet = snippet(cx, filter_arg.span, "..");
|
||||
if filter_snippet.lines().count() <= 1 {
|
||||
let iter_snippet = snippet(cx, filter_args[0].span, "..");
|
||||
let iter_snippet = snippet(cx, recv.span, "..");
|
||||
// add note if not multi-line
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ use super::FLAT_MAP_IDENTITY;
|
|||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
flat_map_args: &'tcx [hir::Expr<'_>],
|
||||
flat_map_arg: &'tcx hir::Expr<'_>,
|
||||
flat_map_span: Span,
|
||||
) {
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
let arg_node = &flat_map_args[1].kind;
|
||||
let arg_node = &flat_map_arg.kind;
|
||||
|
||||
let apply_lint = |message: &str| {
|
||||
span_lint_and_sugg(
|
||||
|
|
@ -35,7 +35,7 @@ pub(super) fn check<'tcx>(
|
|||
let body = cx.tcx.hir().body(*body_id);
|
||||
|
||||
if let hir::PatKind::Binding(_, _, binding_ident, _) = body.params[0].pat.kind;
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = body.value.kind;
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = body.value.kind;
|
||||
|
||||
if path.segments.len() == 1;
|
||||
if path.segments[0].ident.name == binding_ident.name;
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp
|
|||
}
|
||||
|
||||
fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String {
|
||||
let call_site = expr.span.source_callsite();
|
||||
if_chain! {
|
||||
let call_site = expr.span.source_callsite();
|
||||
if let Ok(snippet) = cx.sess().source_map().span_to_snippet(call_site);
|
||||
let snippet_split = snippet.split("::").collect::<Vec<_>>();
|
||||
if let Some((_, elements)) = snippet_split.split_last();
|
||||
|
|
|
|||
|
|
@ -11,18 +11,20 @@ use rustc_span::sym;
|
|||
|
||||
use super::GET_UNWRAP;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args: &'tcx [hir::Expr<'_>], is_mut: bool) {
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &hir::Expr<'_>,
|
||||
recv: &'tcx hir::Expr<'tcx>,
|
||||
get_arg: &'tcx hir::Expr<'_>,
|
||||
is_mut: bool,
|
||||
) {
|
||||
// Note: we don't want to lint `get_mut().unwrap` for `HashMap` or `BTreeMap`,
|
||||
// because they do not implement `IndexMut`
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let expr_ty = cx.typeck_results().expr_ty(&get_args[0]);
|
||||
let get_args_str = if get_args.len() > 1 {
|
||||
snippet_with_applicability(cx, get_args[1].span, "..", &mut applicability)
|
||||
} else {
|
||||
return; // not linting on a .get().unwrap() chain or variant
|
||||
};
|
||||
let expr_ty = cx.typeck_results().expr_ty(recv);
|
||||
let get_args_str = snippet_with_applicability(cx, get_arg.span, "..", &mut applicability);
|
||||
let mut needs_ref;
|
||||
let caller_type = if derefs_to_slice(cx, &get_args[0], expr_ty).is_some() {
|
||||
let caller_type = if derefs_to_slice(cx, recv, expr_ty).is_some() {
|
||||
needs_ref = get_args_str.parse::<usize>().is_ok();
|
||||
"slice"
|
||||
} else if is_type_diagnostic_item(cx, expr_ty, sym::vec_type) {
|
||||
|
|
@ -77,7 +79,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, get_args
|
|||
format!(
|
||||
"{}{}[{}]",
|
||||
borrow_str,
|
||||
snippet_with_applicability(cx, get_args[0].span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, recv.span, "..", &mut applicability),
|
||||
get_args_str
|
||||
),
|
||||
applicability,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use clippy_utils::is_diagnostic_assoc_item;
|
|||
pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, trait_diagnostic: Symbol) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(method_path, _, [arg], _) = &expr.kind;
|
||||
let return_type = cx.typeck_results().expr_ty(&expr);
|
||||
let return_type = cx.typeck_results().expr_ty(expr);
|
||||
let input_type = cx.typeck_results().expr_ty(arg).peel_refs();
|
||||
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did));
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ use rustc_span::sym;
|
|||
|
||||
use super::ITER_CLONED_COLLECT;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) {
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type);
|
||||
if let Some(slice) = derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0]));
|
||||
if let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv));
|
||||
if let Some(to_replace) = expr.span.trim_start(slice.span.source_callsite());
|
||||
|
||||
then {
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ use rustc_span::sym;
|
|||
|
||||
use super::ITER_COUNT;
|
||||
|
||||
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'tcx [Expr<'tcx>], iter_method: &str) {
|
||||
let ty = cx.typeck_results().expr_ty(&iter_args[0]);
|
||||
let caller_type = if derefs_to_slice(cx, &iter_args[0], ty).is_some() {
|
||||
pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, iter_method: &str) {
|
||||
let ty = cx.typeck_results().expr_ty(recv);
|
||||
let caller_type = if derefs_to_slice(cx, recv, ty).is_some() {
|
||||
"slice"
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::vec_type) {
|
||||
"Vec"
|
||||
|
|
@ -42,7 +42,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, iter_args: &'
|
|||
"try",
|
||||
format!(
|
||||
"{}.len()",
|
||||
snippet_with_applicability(cx, iter_args[0].span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, recv.span, "..", &mut applicability),
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,9 +13,7 @@ use rustc_span::symbol::sym;
|
|||
|
||||
use super::ITER_NEXT_SLICE;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, iter_args: &'tcx [hir::Expr<'_>]) {
|
||||
let caller_expr = &iter_args[0];
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, caller_expr: &'tcx hir::Expr<'_>) {
|
||||
// Skip lint if the `iter().next()` expression is a for loop argument,
|
||||
// since it is already covered by `&loops::ITER_NEXT_LOOP`
|
||||
let mut parent_expr_opt = get_parent_expr(cx, expr);
|
||||
|
|
@ -29,7 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, ite
|
|||
if derefs_to_slice(cx, caller_expr, cx.typeck_results().expr_ty(caller_expr)).is_some() {
|
||||
// caller is a Slice
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Index(ref caller_var, ref index_expr) = &caller_expr.kind;
|
||||
if let hir::ExprKind::Index(caller_var, index_expr) = &caller_expr.kind;
|
||||
if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen })
|
||||
= higher::range(index_expr);
|
||||
if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind;
|
||||
|
|
|
|||
|
|
@ -11,20 +11,20 @@ use super::ITER_NTH;
|
|||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &hir::Expr<'_>,
|
||||
nth_and_iter_args: &[&'tcx [hir::Expr<'tcx>]],
|
||||
iter_recv: &'tcx hir::Expr<'tcx>,
|
||||
nth_recv: &hir::Expr<'_>,
|
||||
nth_arg: &hir::Expr<'_>,
|
||||
is_mut: bool,
|
||||
) {
|
||||
let iter_args = nth_and_iter_args[1];
|
||||
let mut_str = if is_mut { "_mut" } else { "" };
|
||||
let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.typeck_results().expr_ty(&iter_args[0])).is_some() {
|
||||
let caller_type = if derefs_to_slice(cx, iter_recv, cx.typeck_results().expr_ty(iter_recv)).is_some() {
|
||||
"slice"
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vec_type) {
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::vec_type) {
|
||||
"Vec"
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&iter_args[0]), sym::vecdeque_type) {
|
||||
} else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(iter_recv), sym::vecdeque_type) {
|
||||
"VecDeque"
|
||||
} else {
|
||||
let nth_args = nth_and_iter_args[0];
|
||||
iter_nth_zero::check(cx, expr, &nth_args);
|
||||
iter_nth_zero::check(cx, expr, nth_recv, nth_arg);
|
||||
return; // caller is not a type that we want to lint
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ use rustc_span::sym;
|
|||
|
||||
use super::ITER_NTH_ZERO;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_args: &'tcx [hir::Expr<'_>]) {
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if is_trait_method(cx, expr, sym::Iterator);
|
||||
if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &nth_args[1]);
|
||||
if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg);
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
|
|
@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_args
|
|||
expr.span,
|
||||
"called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent",
|
||||
"try calling `.next()` instead of `.nth(0)`",
|
||||
format!("{}.next()", snippet_with_applicability(cx, nth_args[0].span, "..", &mut applicability)),
|
||||
format!("{}.next()", snippet_with_applicability(cx, recv.span, "..", &mut applicability)),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,19 +8,17 @@ use rustc_span::sym;
|
|||
|
||||
use super::ITER_SKIP_NEXT;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir::Expr<'_>]) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
|
||||
// lint if caller of skip is an Iterator
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
if let [caller, n] = skip_args {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ITER_SKIP_NEXT,
|
||||
expr.span.trim_start(caller.span).unwrap(),
|
||||
"called `skip(..).next()` on an iterator",
|
||||
"use `nth` instead",
|
||||
format!(".nth({})", snippet(cx, n.span, "..")),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ITER_SKIP_NEXT,
|
||||
expr.span.trim_start(recv.span).unwrap(),
|
||||
"called `skip(..).next()` on an iterator",
|
||||
"use `nth` instead",
|
||||
format!(".nth({})", snippet(cx, arg.span, "..")),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ use rustc_span::sym;
|
|||
|
||||
use super::ITERATOR_STEP_BY_ZERO;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) {
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) {
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &args[1]) {
|
||||
if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg) {
|
||||
span_lint(
|
||||
cx,
|
||||
ITERATOR_STEP_BY_ZERO,
|
||||
|
|
|
|||
|
|
@ -8,11 +8,14 @@ use rustc_hir as hir;
|
|||
use rustc_lint::LateContext;
|
||||
use rustc_target::abi::LayoutOf;
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr<'_>]], arith: &str) {
|
||||
let unwrap_arg = &args[0][1];
|
||||
let arith_lhs = &args[1][0];
|
||||
let arith_rhs = &args[1][1];
|
||||
|
||||
pub fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
arith_lhs: &hir::Expr<'_>,
|
||||
arith_rhs: &hir::Expr<'_>,
|
||||
unwrap_arg: &hir::Expr<'_>,
|
||||
arith: &str,
|
||||
) {
|
||||
let ty = cx.typeck_results().expr_ty(arith_lhs);
|
||||
if !ty.is_integral() {
|
||||
return;
|
||||
|
|
@ -41,44 +44,28 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[&[hir::Expr<'_>
|
|||
// "mul" is omitted because lhs can be negative.
|
||||
_ => return,
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
super::MANUAL_SATURATING_ARITHMETIC,
|
||||
expr.span,
|
||||
"manual saturating arithmetic",
|
||||
&format!("try using `saturating_{}`", arith),
|
||||
format!(
|
||||
"{}.saturating_{}({})",
|
||||
snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
|
||||
arith,
|
||||
snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
} else {
|
||||
match (mm, arith) {
|
||||
(MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (),
|
||||
_ => return,
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
super::MANUAL_SATURATING_ARITHMETIC,
|
||||
expr.span,
|
||||
"manual saturating arithmetic",
|
||||
&format!("try using `saturating_{}`", arith),
|
||||
format!(
|
||||
"{}.saturating_{}({})",
|
||||
snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
|
||||
arith,
|
||||
snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
super::MANUAL_SATURATING_ARITHMETIC,
|
||||
expr.span,
|
||||
"manual saturating arithmetic",
|
||||
&format!("try using `saturating_{}`", arith),
|
||||
format!(
|
||||
"{}.saturating_{}({})",
|
||||
snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
|
||||
arith,
|
||||
snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@ use super::MAP_COLLECT_RESULT_UNIT;
|
|||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
map_args: &[hir::Expr<'_>],
|
||||
collect_args: &[hir::Expr<'_>],
|
||||
iter: &hir::Expr<'_>,
|
||||
map_fn: &hir::Expr<'_>,
|
||||
collect_recv: &hir::Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
// called on Iterator
|
||||
if let [map_expr] = collect_args;
|
||||
if is_trait_method(cx, map_expr, sym::Iterator);
|
||||
if is_trait_method(cx, collect_recv, sym::Iterator);
|
||||
// return of collect `Result<(),_>`
|
||||
let collect_ret_ty = cx.typeck_results().expr_ty(expr);
|
||||
if is_type_diagnostic_item(cx, collect_ret_ty, sym::result_type);
|
||||
|
|
@ -28,7 +28,6 @@ pub(super) fn check(
|
|||
if let Some(result_t) = substs.types().next();
|
||||
if result_t.is_unit();
|
||||
// get parts for snippet
|
||||
if let [iter, map_fn] = map_args;
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -11,10 +11,15 @@ use rustc_span::symbol::sym;
|
|||
use super::MAP_FLATTEN;
|
||||
|
||||
/// lint use of `map().flatten()` for `Iterators` and 'Options'
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) {
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
recv: &'tcx hir::Expr<'_>,
|
||||
map_arg: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
// lint if caller of `.map().flatten()` is an Iterator
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]);
|
||||
let map_closure_ty = cx.typeck_results().expr_ty(map_arg);
|
||||
let is_map_to_option = match map_closure_ty.kind() {
|
||||
ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
|
||||
let map_closure_sig = match map_closure_ty.kind() {
|
||||
|
|
@ -34,12 +39,12 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
|
|||
// `(...).map(...)` has type `impl Iterator<Item=impl Iterator<...>>
|
||||
"flat_map"
|
||||
};
|
||||
let func_snippet = snippet(cx, map_args[1].span, "..");
|
||||
let func_snippet = snippet(cx, map_arg.span, "..");
|
||||
let hint = format!(".{0}({1})", method_to_use, func_snippet);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_FLATTEN,
|
||||
expr.span.with_lo(map_args[0].span.hi()),
|
||||
expr.span.with_lo(recv.span.hi()),
|
||||
"called `map(..).flatten()` on an `Iterator`",
|
||||
&format!("try using `{}` instead", method_to_use),
|
||||
hint,
|
||||
|
|
@ -48,13 +53,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
|
|||
}
|
||||
|
||||
// lint if caller of `.map().flatten()` is an Option
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) {
|
||||
let func_snippet = snippet(cx, map_args[1].span, "..");
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type) {
|
||||
let func_snippet = snippet(cx, map_arg.span, "..");
|
||||
let hint = format!(".and_then({})", func_snippet);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_FLATTEN,
|
||||
expr.span.with_lo(map_args[0].span.hi()),
|
||||
expr.span.with_lo(recv.span.hi()),
|
||||
"called `map(..).flatten()` on an `Option`",
|
||||
"try using `and_then` instead",
|
||||
hint,
|
||||
|
|
|
|||
|
|
@ -18,22 +18,23 @@ const MAP_UNWRAP_OR_MSRV: RustcVersion = RustcVersion::new(1, 41, 0);
|
|||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
map_args: &'tcx [hir::Expr<'_>],
|
||||
unwrap_args: &'tcx [hir::Expr<'_>],
|
||||
recv: &'tcx hir::Expr<'_>,
|
||||
map_arg: &'tcx hir::Expr<'_>,
|
||||
unwrap_arg: &'tcx hir::Expr<'_>,
|
||||
msrv: Option<&RustcVersion>,
|
||||
) -> bool {
|
||||
if !meets_msrv(msrv, &MAP_UNWRAP_OR_MSRV) {
|
||||
return false;
|
||||
}
|
||||
// lint if the caller of `map()` is an `Option`
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type);
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::result_type);
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type);
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type);
|
||||
|
||||
if is_option || is_result {
|
||||
// Don't make a suggestion that may fail to compile due to mutably borrowing
|
||||
// the same variable twice.
|
||||
let map_mutated_vars = mutated_variables(&map_args[0], cx);
|
||||
let unwrap_mutated_vars = mutated_variables(&unwrap_args[1], cx);
|
||||
let map_mutated_vars = mutated_variables(recv, cx);
|
||||
let unwrap_mutated_vars = mutated_variables(unwrap_arg, cx);
|
||||
if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) {
|
||||
if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
|
||||
return false;
|
||||
|
|
@ -51,14 +52,14 @@ pub(super) fn check<'tcx>(
|
|||
`.map_or_else(<g>, <f>)` instead"
|
||||
};
|
||||
// get snippets for args to map() and unwrap_or_else()
|
||||
let map_snippet = snippet(cx, map_args[1].span, "..");
|
||||
let unwrap_snippet = snippet(cx, unwrap_args[1].span, "..");
|
||||
let map_snippet = snippet(cx, map_arg.span, "..");
|
||||
let unwrap_snippet = snippet(cx, unwrap_arg.span, "..");
|
||||
// lint, with note if neither arg is > 1 line and both map() and
|
||||
// unwrap_or_else() have the same span
|
||||
let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
|
||||
let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt();
|
||||
let same_span = map_arg.span.ctxt() == unwrap_arg.span.ctxt();
|
||||
if same_span && !multiline {
|
||||
let var_snippet = snippet(cx, map_args[0].span, "..");
|
||||
let var_snippet = snippet(cx, recv.span, "..");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_UNWRAP_OR,
|
||||
|
|
|
|||
|
|
@ -62,17 +62,18 @@ mod zst_offset;
|
|||
use bind_instead_of_map::BindInsteadOfMap;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||
use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, method_calls, paths, return_ty};
|
||||
use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, paths, return_ty};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{PrimTy, QPath, TraitItem, TraitItemKind};
|
||||
use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::{self, TraitRef, Ty, TyS};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::{sym, SymbolStr};
|
||||
use rustc_span::symbol::SymbolStr;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
|
@ -205,6 +206,13 @@ declare_clippy_lint! {
|
|||
/// |`to_` | not `_mut` |`self` | `Copy` |
|
||||
/// |`to_` | not `_mut` |`&self` | not `Copy` |
|
||||
///
|
||||
/// Note: Clippy doesn't trigger methods with `to_` prefix in:
|
||||
/// - Traits definition.
|
||||
/// Clippy can not tell if a type that implements a trait is `Copy` or not.
|
||||
/// - Traits implementation, when `&self` is taken.
|
||||
/// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
|
||||
/// (see e.g. the `std::string::ToString` trait).
|
||||
///
|
||||
/// Please find more info here:
|
||||
/// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
|
||||
///
|
||||
|
|
@ -896,6 +904,28 @@ declare_clippy_lint! {
|
|||
"using `Iterator::step_by(0)`, which will panic at runtime"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for indirect collection of populated `Option`
|
||||
///
|
||||
/// **Why is this bad?** `Option` is like a collection of 0-1 things, so `flatten`
|
||||
/// automatically does this without suspicious-looking `unwrap` calls.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let _ = std::iter::empty::<Option<i32>>().flatten();
|
||||
/// ```
|
||||
pub OPTION_FILTER_MAP,
|
||||
complexity,
|
||||
"filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for the use of `iter.nth(0)`.
|
||||
///
|
||||
|
|
@ -1651,6 +1681,7 @@ impl_lint_pass!(Methods => [
|
|||
FILTER_MAP_IDENTITY,
|
||||
MANUAL_FILTER_MAP,
|
||||
MANUAL_FIND_MAP,
|
||||
OPTION_FILTER_MAP,
|
||||
FILTER_MAP_NEXT,
|
||||
FLAT_MAP_IDENTITY,
|
||||
MAP_FLATTEN,
|
||||
|
|
@ -1681,140 +1712,38 @@ impl_lint_pass!(Methods => [
|
|||
IMPLICIT_CLONE
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(SymbolStr, &'tcx [hir::Expr<'tcx>], Span)> {
|
||||
if let ExprKind::MethodCall(path, span, args, _) = recv.kind {
|
||||
if !args.iter().any(|e| e.span.from_expansion()) {
|
||||
return Some((path.ident.name.as_str(), args, span));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Same as `method_call` but the `SymbolStr` is dereferenced into a temporary `&str`
|
||||
macro_rules! method_call {
|
||||
($expr:expr) => {
|
||||
method_call($expr)
|
||||
.as_ref()
|
||||
.map(|&(ref name, args, span)| (&**name, args, span))
|
||||
};
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if in_macro(expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
let (method_names, arg_lists, method_spans) = method_calls(expr, 2);
|
||||
let method_names: Vec<SymbolStr> = method_names.iter().map(|s| s.as_str()).collect();
|
||||
let method_names: Vec<&str> = method_names.iter().map(|s| &**s).collect();
|
||||
|
||||
match method_names.as_slice() {
|
||||
["unwrap", "get"] => get_unwrap::check(cx, expr, arg_lists[1], false),
|
||||
["unwrap", "get_mut"] => get_unwrap::check(cx, expr, arg_lists[1], true),
|
||||
["unwrap", ..] => unwrap_used::check(cx, expr, arg_lists[0]),
|
||||
["expect", "ok"] => ok_expect::check(cx, expr, arg_lists[1]),
|
||||
["expect", ..] => expect_used::check(cx, expr, arg_lists[0]),
|
||||
["unwrap_or", "map"] => option_map_unwrap_or::check(cx, expr, arg_lists[1], arg_lists[0], method_spans[1]),
|
||||
["unwrap_or_else", "map"] => {
|
||||
if !map_unwrap_or::check(cx, expr, arg_lists[1], arg_lists[0], self.msrv.as_ref()) {
|
||||
unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "unwrap_or");
|
||||
}
|
||||
},
|
||||
["map_or", ..] => option_map_or_none::check(cx, expr, arg_lists[0]),
|
||||
["and_then", ..] => {
|
||||
let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, arg_lists[0]);
|
||||
let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, arg_lists[0]);
|
||||
if !biom_option_linted && !biom_result_linted {
|
||||
unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "and");
|
||||
}
|
||||
},
|
||||
["or_else", ..] => {
|
||||
if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, arg_lists[0]) {
|
||||
unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "or");
|
||||
}
|
||||
},
|
||||
["next", "filter"] => filter_next::check(cx, expr, arg_lists[1]),
|
||||
["next", "skip_while"] => skip_while_next::check(cx, expr, arg_lists[1]),
|
||||
["next", "iter"] => iter_next_slice::check(cx, expr, arg_lists[1]),
|
||||
["map", "filter"] => filter_map::check(cx, expr, false),
|
||||
["map", "filter_map"] => filter_map_map::check(cx, expr),
|
||||
["next", "filter_map"] => filter_map_next::check(cx, expr, arg_lists[1], self.msrv.as_ref()),
|
||||
["map", "find"] => filter_map::check(cx, expr, true),
|
||||
["flat_map", "filter"] => filter_flat_map::check(cx, expr),
|
||||
["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr),
|
||||
["flat_map", ..] => flat_map_identity::check(cx, expr, arg_lists[0], method_spans[0]),
|
||||
["flatten", "map"] => map_flatten::check(cx, expr, arg_lists[1]),
|
||||
[option_check_method, "find"] if "is_some" == *option_check_method || "is_none" == *option_check_method => {
|
||||
search_is_some::check(
|
||||
cx,
|
||||
expr,
|
||||
"find",
|
||||
option_check_method,
|
||||
arg_lists[1],
|
||||
arg_lists[0],
|
||||
method_spans[1],
|
||||
)
|
||||
},
|
||||
[option_check_method, "position"]
|
||||
if "is_some" == *option_check_method || "is_none" == *option_check_method =>
|
||||
{
|
||||
search_is_some::check(
|
||||
cx,
|
||||
expr,
|
||||
"position",
|
||||
option_check_method,
|
||||
arg_lists[1],
|
||||
arg_lists[0],
|
||||
method_spans[1],
|
||||
)
|
||||
},
|
||||
[option_check_method, "rposition"]
|
||||
if "is_some" == *option_check_method || "is_none" == *option_check_method =>
|
||||
{
|
||||
search_is_some::check(
|
||||
cx,
|
||||
expr,
|
||||
"rposition",
|
||||
option_check_method,
|
||||
arg_lists[1],
|
||||
arg_lists[0],
|
||||
method_spans[1],
|
||||
)
|
||||
},
|
||||
["extend", ..] => string_extend_chars::check(cx, expr, arg_lists[0]),
|
||||
["count", "into_iter"] => iter_count::check(cx, expr, &arg_lists[1], "into_iter"),
|
||||
["count", "iter"] => iter_count::check(cx, expr, &arg_lists[1], "iter"),
|
||||
["count", "iter_mut"] => iter_count::check(cx, expr, &arg_lists[1], "iter_mut"),
|
||||
["nth", "iter"] => iter_nth::check(cx, expr, &arg_lists, false),
|
||||
["nth", "iter_mut"] => iter_nth::check(cx, expr, &arg_lists, true),
|
||||
["nth", "bytes"] => bytes_nth::check(cx, expr, &arg_lists[1]),
|
||||
["nth", ..] => iter_nth_zero::check(cx, expr, arg_lists[0]),
|
||||
["step_by", ..] => iterator_step_by_zero::check(cx, expr, arg_lists[0]),
|
||||
["next", "skip"] => iter_skip_next::check(cx, expr, arg_lists[1]),
|
||||
["collect", "cloned"] => iter_cloned_collect::check(cx, expr, arg_lists[1]),
|
||||
["as_ref"] => useless_asref::check(cx, expr, "as_ref", arg_lists[0]),
|
||||
["as_mut"] => useless_asref::check(cx, expr, "as_mut", arg_lists[0]),
|
||||
["fold", ..] => unnecessary_fold::check(cx, expr, arg_lists[0], method_spans[0]),
|
||||
["filter_map", ..] => {
|
||||
unnecessary_filter_map::check(cx, expr, arg_lists[0]);
|
||||
filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]);
|
||||
},
|
||||
["count", "map"] => suspicious_map::check(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["assume_init"] => uninit_assumed_init::check(cx, &arg_lists[0][0], expr),
|
||||
["unwrap_or", arith @ ("checked_add" | "checked_sub" | "checked_mul")] => {
|
||||
manual_saturating_arithmetic::check(cx, expr, &arg_lists, &arith["checked_".len()..])
|
||||
},
|
||||
["add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub"] => {
|
||||
zst_offset::check(cx, expr, arg_lists[0])
|
||||
},
|
||||
["is_file", ..] => filetype_is_file::check(cx, expr, arg_lists[0]),
|
||||
["map", "as_ref"] => {
|
||||
option_as_ref_deref::check(cx, expr, arg_lists[1], arg_lists[0], false, self.msrv.as_ref())
|
||||
},
|
||||
["map", "as_mut"] => {
|
||||
option_as_ref_deref::check(cx, expr, arg_lists[1], arg_lists[0], true, self.msrv.as_ref())
|
||||
},
|
||||
["unwrap_or_else", ..] => unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "unwrap_or"),
|
||||
["get_or_insert_with", ..] => unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "get_or_insert"),
|
||||
["ok_or_else", ..] => unnecessary_lazy_eval::check(cx, expr, arg_lists[0], "ok_or"),
|
||||
["collect", "map"] => map_collect_result_unit::check(cx, expr, arg_lists[1], arg_lists[0]),
|
||||
["for_each", "inspect"] => inspect_for_each::check(cx, expr, method_spans[1]),
|
||||
["to_owned", ..] => implicit_clone::check(cx, expr, sym::ToOwned),
|
||||
["to_os_string", ..] => implicit_clone::check(cx, expr, sym::OsStr),
|
||||
["to_path_buf", ..] => implicit_clone::check(cx, expr, sym::Path),
|
||||
["to_vec", ..] => implicit_clone::check(cx, expr, sym::slice),
|
||||
_ => {},
|
||||
}
|
||||
check_methods(cx, expr, self.msrv.as_ref());
|
||||
|
||||
match expr.kind {
|
||||
hir::ExprKind::Call(ref func, ref args) => {
|
||||
hir::ExprKind::Call(func, args) => {
|
||||
from_iter_instead_of_collect::check(cx, expr, args, &func.kind);
|
||||
},
|
||||
hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => {
|
||||
hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => {
|
||||
or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
|
||||
expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
|
||||
clone_on_copy::check(cx, expr, method_call.ident.name, args);
|
||||
|
|
@ -1824,9 +1753,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args);
|
||||
single_char_pattern::check(cx, expr, method_call.ident.name, args);
|
||||
},
|
||||
hir::ExprKind::Binary(op, ref lhs, ref rhs)
|
||||
if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne =>
|
||||
{
|
||||
hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
|
||||
let mut info = BinaryExprInfo {
|
||||
expr,
|
||||
chain: lhs,
|
||||
|
|
@ -1834,7 +1761,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
eq: op.node == hir::BinOpKind::Eq,
|
||||
};
|
||||
lint_binary_expr_with_method_call(cx, &mut info);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -1850,10 +1777,9 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
let self_ty = cx.tcx.type_of(item.def_id);
|
||||
|
||||
let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
|
||||
|
||||
if_chain! {
|
||||
if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
|
||||
if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir().body(id)).next();
|
||||
if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next();
|
||||
|
||||
let method_sig = cx.tcx.fn_sig(impl_item.def_id);
|
||||
let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
|
||||
|
|
@ -1873,7 +1799,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
method_config.output_type.matches(&sig.decl.output) &&
|
||||
method_config.self_kind.matches(cx, self_ty, first_arg_ty) &&
|
||||
fn_header_equals(method_config.fn_header, sig.header) &&
|
||||
method_config.lifetime_param_cond(&impl_item)
|
||||
method_config.lifetime_param_cond(impl_item)
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
|
|
@ -1902,6 +1828,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
self_ty,
|
||||
first_arg_ty,
|
||||
first_arg.pat.span,
|
||||
implements_trait,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
|
@ -1960,11 +1887,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
if_chain! {
|
||||
if let TraitItemKind::Fn(ref sig, _) = item.kind;
|
||||
if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
|
||||
let first_arg_span = first_arg_ty.span;
|
||||
let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
|
||||
let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty();
|
||||
|
||||
then {
|
||||
let first_arg_span = first_arg_ty.span;
|
||||
let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
|
||||
let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty();
|
||||
wrong_self_convention::check(
|
||||
cx,
|
||||
&item.ident.name.as_str(),
|
||||
|
|
@ -1972,6 +1898,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
self_ty,
|
||||
first_arg_ty,
|
||||
first_arg_span,
|
||||
false,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
|
@ -1997,6 +1924,140 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) {
|
||||
if let Some((name, [recv, args @ ..], span)) = method_call!(expr) {
|
||||
match (name, args) {
|
||||
("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [recv, _]) => {
|
||||
zst_offset::check(cx, expr, recv)
|
||||
},
|
||||
("and_then", [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);
|
||||
if !biom_option_linted && !biom_result_linted {
|
||||
unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
|
||||
}
|
||||
},
|
||||
("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
|
||||
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
|
||||
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
|
||||
("collect", []) => match method_call!(recv) {
|
||||
Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
|
||||
Some(("map", [m_recv, m_arg], _)) => {
|
||||
map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
("count", []) => match method_call!(recv) {
|
||||
Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
|
||||
iter_count::check(cx, expr, recv2, name);
|
||||
},
|
||||
Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
|
||||
_ => {},
|
||||
},
|
||||
("expect", [_]) => match method_call!(recv) {
|
||||
Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
|
||||
_ => expect_used::check(cx, expr, recv),
|
||||
},
|
||||
("extend", [arg]) => string_extend_chars::check(cx, expr, recv, arg),
|
||||
("filter_map", [arg]) => {
|
||||
unnecessary_filter_map::check(cx, expr, arg);
|
||||
filter_map_identity::check(cx, expr, arg, span);
|
||||
},
|
||||
("flat_map", [flm_arg]) => match method_call!(recv) {
|
||||
Some(("filter", [_, _], _)) => filter_flat_map::check(cx, expr),
|
||||
Some(("filter_map", [_, _], _)) => filter_map_flat_map::check(cx, expr),
|
||||
_ => flat_map_identity::check(cx, expr, flm_arg, span),
|
||||
},
|
||||
("flatten", []) => {
|
||||
if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
|
||||
map_flatten::check(cx, expr, recv, map_arg);
|
||||
}
|
||||
},
|
||||
("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
|
||||
("for_each", [_]) => {
|
||||
if let Some(("inspect", [_, _], span2)) = method_call!(recv) {
|
||||
inspect_for_each::check(cx, expr, span2);
|
||||
}
|
||||
},
|
||||
("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
|
||||
("is_file", []) => filetype_is_file::check(cx, expr, recv),
|
||||
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
|
||||
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
|
||||
("map", [m_arg]) => {
|
||||
if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) {
|
||||
match (name, args) {
|
||||
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv),
|
||||
("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv),
|
||||
("filter", [f_arg]) => {
|
||||
filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false)
|
||||
},
|
||||
("filter_map", [_]) => filter_map_map::check(cx, expr),
|
||||
("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
|
||||
("next", []) => {
|
||||
if let Some((name, [recv, args @ ..], _)) = method_call!(recv) {
|
||||
match (name, args) {
|
||||
("filter", [arg]) => filter_next::check(cx, expr, recv, arg),
|
||||
("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv),
|
||||
("iter", []) => iter_next_slice::check(cx, expr, recv),
|
||||
("skip", [arg]) => iter_skip_next::check(cx, expr, recv, arg),
|
||||
("skip_while", [_]) => skip_while_next::check(cx, expr),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
("nth", [n_arg]) => match method_call!(recv) {
|
||||
Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
|
||||
Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
|
||||
Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
|
||||
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
|
||||
},
|
||||
("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
|
||||
("or_else", [arg]) => {
|
||||
if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
|
||||
unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
|
||||
}
|
||||
},
|
||||
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
|
||||
("to_os_string", []) => implicit_clone::check(cx, expr, sym::OsStr),
|
||||
("to_owned", []) => implicit_clone::check(cx, expr, sym::ToOwned),
|
||||
("to_path_buf", []) => implicit_clone::check(cx, expr, sym::Path),
|
||||
("to_vec", []) => implicit_clone::check(cx, expr, sym::slice),
|
||||
("unwrap", []) => match method_call!(recv) {
|
||||
Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false),
|
||||
Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
|
||||
_ => unwrap_used::check(cx, expr, recv),
|
||||
},
|
||||
("unwrap_or", [u_arg]) => match method_call!(recv) {
|
||||
Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
|
||||
manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
|
||||
},
|
||||
Some(("map", [m_recv, m_arg], span)) => {
|
||||
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span)
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
("unwrap_or_else", [u_arg]) => match method_call!(recv) {
|
||||
Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {},
|
||||
_ => unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"),
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
|
||||
if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) {
|
||||
search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span)
|
||||
}
|
||||
}
|
||||
|
||||
/// Used for `lint_binary_expr_with_method_call`.
|
||||
#[derive(Copy, Clone)]
|
||||
struct BinaryExprInfo<'a> {
|
||||
|
|
@ -2209,10 +2270,10 @@ impl OutType {
|
|||
let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[]));
|
||||
match (self, ty) {
|
||||
(Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true,
|
||||
(Self::Unit, &hir::FnRetTy::Return(ref ty)) if is_unit(ty) => true,
|
||||
(Self::Bool, &hir::FnRetTy::Return(ref ty)) if is_bool(ty) => true,
|
||||
(Self::Any, &hir::FnRetTy::Return(ref ty)) if !is_unit(ty) => true,
|
||||
(Self::Ref, &hir::FnRetTy::Return(ref ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)),
|
||||
(Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true,
|
||||
(Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true,
|
||||
(Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true,
|
||||
(Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ use rustc_span::sym;
|
|||
use super::OK_EXPECT;
|
||||
|
||||
/// lint use of `ok().expect()` for `Result`s
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ok_args: &[hir::Expr<'_>]) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
// lint if the caller of `ok()` is a `Result`
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&ok_args[0]), sym::result_type);
|
||||
let result_type = cx.typeck_results().expr_ty(&ok_args[0]);
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type);
|
||||
let result_type = cx.typeck_results().expr_ty(recv);
|
||||
if let Some(error_type) = get_error_type(cx, result_type);
|
||||
if has_debug_impl(error_type, cx);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ const OPTION_AS_REF_DEREF_MSRV: RustcVersion = RustcVersion::new(1, 40, 0);
|
|||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &hir::Expr<'_>,
|
||||
as_ref_args: &[hir::Expr<'_>],
|
||||
map_args: &[hir::Expr<'_>],
|
||||
as_ref_recv: &hir::Expr<'_>,
|
||||
map_arg: &hir::Expr<'_>,
|
||||
is_mut: bool,
|
||||
msrv: Option<&RustcVersion>,
|
||||
) {
|
||||
|
|
@ -29,7 +29,7 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not);
|
||||
|
||||
let option_ty = cx.typeck_results().expr_ty(&as_ref_args[0]);
|
||||
let option_ty = cx.typeck_results().expr_ty(as_ref_recv);
|
||||
if !is_type_diagnostic_item(cx, option_ty, sym::option_type) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -46,9 +46,9 @@ pub(super) fn check<'tcx>(
|
|||
&paths::VEC_AS_MUT_SLICE,
|
||||
];
|
||||
|
||||
let is_deref = match map_args[1].kind {
|
||||
let is_deref = match map_arg.kind {
|
||||
hir::ExprKind::Path(ref expr_qpath) => cx
|
||||
.qpath_res(expr_qpath, map_args[1].hir_id)
|
||||
.qpath_res(expr_qpath, map_arg.hir_id)
|
||||
.opt_def_id()
|
||||
.map_or(false, |fun_def_id| {
|
||||
deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path))
|
||||
|
|
@ -77,10 +77,10 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, ref inner) if same_mutability(m) => {
|
||||
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, inner) if same_mutability(m) => {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner1) = inner.kind;
|
||||
if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner2) = inner1.kind;
|
||||
if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind;
|
||||
if let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind;
|
||||
then {
|
||||
path_to_local_id(inner2, closure_body.params[0].pat.hir_id)
|
||||
} else {
|
||||
|
|
@ -96,12 +96,12 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
if is_deref {
|
||||
let current_method = if is_mut {
|
||||
format!(".as_mut().map({})", snippet(cx, map_args[1].span, ".."))
|
||||
format!(".as_mut().map({})", snippet(cx, map_arg.span, ".."))
|
||||
} else {
|
||||
format!(".as_ref().map({})", snippet(cx, map_args[1].span, ".."))
|
||||
format!(".as_ref().map({})", snippet(cx, map_arg.span, ".."))
|
||||
};
|
||||
let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" };
|
||||
let hint = format!("{}.{}()", snippet(cx, as_ref_args[0].span, ".."), method_hint);
|
||||
let hint = format!("{}.{}()", snippet(cx, as_ref_recv.span, ".."), method_hint);
|
||||
let suggestion = format!("try using {} instead", method_hint);
|
||||
|
||||
let msg = format!(
|
||||
|
|
|
|||
|
|
@ -11,9 +11,15 @@ use super::OPTION_MAP_OR_NONE;
|
|||
use super::RESULT_MAP_OR_INTO_OPTION;
|
||||
|
||||
/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_or_args: &'tcx [hir::Expr<'_>]) {
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::option_type);
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_or_args[0]), sym::result_type);
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
recv: &'tcx hir::Expr<'_>,
|
||||
def_arg: &'tcx hir::Expr<'_>,
|
||||
map_arg: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type);
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type);
|
||||
|
||||
// There are two variants of this `map_or` lint:
|
||||
// (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>`
|
||||
|
|
@ -25,7 +31,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
|
|||
}
|
||||
|
||||
let (lint_name, msg, instead, hint) = {
|
||||
let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = map_or_args[1].kind {
|
||||
let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
|
||||
match_qpath(qpath, &paths::OPTION_NONE)
|
||||
} else {
|
||||
return;
|
||||
|
|
@ -36,15 +42,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
|
|||
return;
|
||||
}
|
||||
|
||||
let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_or_args[2].kind {
|
||||
let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
|
||||
match_qpath(qpath, &paths::OPTION_SOME)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if is_option {
|
||||
let self_snippet = snippet(cx, map_or_args[0].span, "..");
|
||||
let func_snippet = snippet(cx, map_or_args[2].span, "..");
|
||||
let self_snippet = snippet(cx, recv.span, "..");
|
||||
let func_snippet = snippet(cx, map_arg.span, "..");
|
||||
let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
|
||||
`and_then(..)` instead";
|
||||
(
|
||||
|
|
@ -56,7 +62,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map
|
|||
} else if f_arg_is_some {
|
||||
let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
|
||||
`ok()` instead";
|
||||
let self_snippet = snippet(cx, map_or_args[0].span, "..");
|
||||
let self_snippet = snippet(cx, recv.span, "..");
|
||||
(
|
||||
RESULT_MAP_OR_INTO_OPTION,
|
||||
msg,
|
||||
|
|
|
|||
|
|
@ -18,13 +18,15 @@ use super::MAP_UNWRAP_OR;
|
|||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &rustc_hir::Expr<'_>,
|
||||
map_args: &'tcx [rustc_hir::Expr<'_>],
|
||||
unwrap_args: &'tcx [rustc_hir::Expr<'_>],
|
||||
recv: &rustc_hir::Expr<'_>,
|
||||
map_arg: &'tcx rustc_hir::Expr<'_>,
|
||||
unwrap_recv: &rustc_hir::Expr<'_>,
|
||||
unwrap_arg: &'tcx rustc_hir::Expr<'_>,
|
||||
map_span: Span,
|
||||
) {
|
||||
// lint if the caller of `map()` is an `Option`
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&map_args[0]), sym::option_type) {
|
||||
if !is_copy(cx, cx.typeck_results().expr_ty(&unwrap_args[1])) {
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type) {
|
||||
if !is_copy(cx, cx.typeck_results().expr_ty(unwrap_arg)) {
|
||||
// Do not lint if the `map` argument uses identifiers in the `map`
|
||||
// argument that are also used in the `unwrap_or` argument
|
||||
|
||||
|
|
@ -32,27 +34,27 @@ pub(super) fn check<'tcx>(
|
|||
cx,
|
||||
identifiers: FxHashSet::default(),
|
||||
};
|
||||
unwrap_visitor.visit_expr(&unwrap_args[1]);
|
||||
unwrap_visitor.visit_expr(unwrap_arg);
|
||||
|
||||
let mut map_expr_visitor = MapExprVisitor {
|
||||
cx,
|
||||
identifiers: unwrap_visitor.identifiers,
|
||||
found_identifier: false,
|
||||
};
|
||||
map_expr_visitor.visit_expr(&map_args[1]);
|
||||
map_expr_visitor.visit_expr(map_arg);
|
||||
|
||||
if map_expr_visitor.found_identifier {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if differing_macro_contexts(unwrap_args[1].span, map_span) {
|
||||
if differing_macro_contexts(unwrap_arg.span, map_span) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
// get snippet for unwrap_or()
|
||||
let unwrap_snippet = snippet_with_applicability(cx, unwrap_args[1].span, "..", &mut applicability);
|
||||
let unwrap_snippet = snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability);
|
||||
// lint message
|
||||
// comparing the snippet from source to raw text ("None") below is safe
|
||||
// because we already have checked the type.
|
||||
|
|
@ -70,14 +72,14 @@ pub(super) fn check<'tcx>(
|
|||
);
|
||||
|
||||
span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| {
|
||||
let map_arg_span = map_args[1].span;
|
||||
let map_arg_span = map_arg.span;
|
||||
|
||||
let mut suggestion = vec![
|
||||
(
|
||||
map_span,
|
||||
String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }),
|
||||
),
|
||||
(expr.span.with_lo(unwrap_args[0].span.hi()), String::from("")),
|
||||
(expr.span.with_lo(unwrap_recv.span.hi()), String::from("")),
|
||||
];
|
||||
|
||||
if !unwrap_snippet_none {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use rustc_hir::{BlockCheckMode, UnsafeSource};
|
|||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::symbol::sym;
|
||||
use rustc_span::symbol::{kw, sym};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::OR_FUN_CALL;
|
||||
|
|
@ -38,8 +38,8 @@ pub(super) fn check<'tcx>(
|
|||
if !or_has_args;
|
||||
if name == "unwrap_or";
|
||||
if let hir::ExprKind::Path(ref qpath) = fun.kind;
|
||||
let path = &*last_path_segment(qpath).ident.as_str();
|
||||
if ["default", "new"].contains(&path);
|
||||
let path = last_path_segment(qpath).ident.name;
|
||||
if matches!(path, kw::Default | sym::new);
|
||||
let arg_ty = cx.typeck_results().expr_ty(arg);
|
||||
if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
|
||||
if implements_trait(cx, arg_ty, default_trait_id, &[]);
|
||||
|
|
@ -86,7 +86,7 @@ pub(super) fn check<'tcx>(
|
|||
(&paths::RESULT, true, &["or", "unwrap_or"], "else"),
|
||||
];
|
||||
|
||||
if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = &arg.kind {
|
||||
if let hir::ExprKind::MethodCall(path, _, args, _) = &arg.kind {
|
||||
if path.ident.as_str() == "len" {
|
||||
let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
|
||||
|
||||
|
|
@ -105,7 +105,7 @@ pub(super) fn check<'tcx>(
|
|||
if KNOW_TYPES.iter().any(|k| k.2.contains(&name));
|
||||
|
||||
if is_lazyness_candidate(cx, arg);
|
||||
if !contains_return(&arg);
|
||||
if !contains_return(arg);
|
||||
|
||||
let self_ty = cx.typeck_results().expr_ty(self_expr);
|
||||
|
||||
|
|
@ -158,7 +158,7 @@ pub(super) fn check<'tcx>(
|
|||
|
||||
if args.len() == 2 {
|
||||
match args[1].kind {
|
||||
hir::ExprKind::Call(ref fun, ref or_args) => {
|
||||
hir::ExprKind::Call(fun, or_args) => {
|
||||
let or_has_args = !or_args.is_empty();
|
||||
if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
|
||||
let fun_span = if or_has_args { None } else { Some(fun.span) };
|
||||
|
|
|
|||
|
|
@ -15,35 +15,37 @@ use super::SEARCH_IS_SOME;
|
|||
|
||||
/// lint searching an Iterator followed by `is_some()`
|
||||
/// or calling `find()` on a string followed by `is_some()` or `is_none()`
|
||||
#[allow(clippy::too_many_lines)]
|
||||
#[allow(clippy::too_many_arguments, clippy::too_many_lines)]
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
cx: &LateContext<'_>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
search_method: &str,
|
||||
option_check_method: &str,
|
||||
search_args: &'tcx [hir::Expr<'_>],
|
||||
is_some_args: &'tcx [hir::Expr<'_>],
|
||||
is_some: bool,
|
||||
search_recv: &hir::Expr<'_>,
|
||||
search_arg: &'tcx hir::Expr<'_>,
|
||||
is_some_recv: &hir::Expr<'_>,
|
||||
method_span: Span,
|
||||
) {
|
||||
let option_check_method = if is_some { "is_some" } else { "is_none" };
|
||||
// lint if caller of search is an Iterator
|
||||
if is_trait_method(cx, &is_some_args[0], sym::Iterator) {
|
||||
if is_trait_method(cx, is_some_recv, sym::Iterator) {
|
||||
let msg = format!(
|
||||
"called `{}()` after searching an `Iterator` with `{}`",
|
||||
option_check_method, search_method
|
||||
);
|
||||
let search_snippet = snippet(cx, search_args[1].span, "..");
|
||||
let search_snippet = snippet(cx, search_arg.span, "..");
|
||||
if search_snippet.lines().count() <= 1 {
|
||||
// suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()`
|
||||
// suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()`
|
||||
let any_search_snippet = if_chain! {
|
||||
if search_method == "find";
|
||||
if let hir::ExprKind::Closure(_, _, body_id, ..) = search_args[1].kind;
|
||||
if let hir::ExprKind::Closure(_, _, body_id, ..) = search_arg.kind;
|
||||
let closure_body = cx.tcx.hir().body(body_id);
|
||||
if let Some(closure_arg) = closure_body.params.get(0);
|
||||
then {
|
||||
if let hir::PatKind::Ref(..) = closure_arg.pat.kind {
|
||||
Some(search_snippet.replacen('&', "", 1))
|
||||
} else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(&closure_arg.pat).kind {
|
||||
} else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(closure_arg.pat).kind {
|
||||
let name = &*ident.name.as_str();
|
||||
Some(search_snippet.replace(&format!("*{}", name), name))
|
||||
} else {
|
||||
|
|
@ -54,38 +56,34 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
};
|
||||
// add note if not multi-line
|
||||
match option_check_method {
|
||||
"is_some" => {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SEARCH_IS_SOME,
|
||||
method_span.with_hi(expr.span.hi()),
|
||||
&msg,
|
||||
"use `any()` instead",
|
||||
format!(
|
||||
"any({})",
|
||||
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
"is_none" => {
|
||||
let iter = snippet(cx, search_args[0].span, "..");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SEARCH_IS_SOME,
|
||||
expr.span,
|
||||
&msg,
|
||||
"use `!_.any()` instead",
|
||||
format!(
|
||||
"!{}.any({})",
|
||||
iter,
|
||||
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
_ => (),
|
||||
if is_some {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SEARCH_IS_SOME,
|
||||
method_span.with_hi(expr.span.hi()),
|
||||
&msg,
|
||||
"use `any()` instead",
|
||||
format!(
|
||||
"any({})",
|
||||
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
let iter = snippet(cx, search_recv.span, "..");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SEARCH_IS_SOME,
|
||||
expr.span,
|
||||
&msg,
|
||||
"use `!_.any()` instead",
|
||||
format!(
|
||||
"!{}.any({})",
|
||||
iter,
|
||||
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let hint = format!(
|
||||
|
|
@ -110,14 +108,14 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
};
|
||||
if_chain! {
|
||||
if is_string_or_str_slice(&search_args[0]);
|
||||
if is_string_or_str_slice(&search_args[1]);
|
||||
if is_string_or_str_slice(search_recv);
|
||||
if is_string_or_str_slice(search_arg);
|
||||
then {
|
||||
let msg = format!("called `{}()` after calling `find()` on a string", option_check_method);
|
||||
match option_check_method {
|
||||
"is_some" => {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability);
|
||||
let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SEARCH_IS_SOME,
|
||||
|
|
@ -129,9 +127,9 @@ pub(super) fn check<'tcx>(
|
|||
);
|
||||
},
|
||||
"is_none" => {
|
||||
let string = snippet(cx, search_args[0].span, "..");
|
||||
let string = snippet(cx, search_recv.span, "..");
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability);
|
||||
let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SEARCH_IS_SOME,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use rustc_span::sym;
|
|||
use super::SKIP_WHILE_NEXT;
|
||||
|
||||
/// lint use of `skip_while().next()` for `Iterators`
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, _skip_while_args: &'tcx [hir::Expr<'_>]) {
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
// lint if caller of `.skip_while().next()` is an Iterator
|
||||
if is_trait_method(cx, expr, sym::Iterator) {
|
||||
span_lint_and_help(
|
||||
|
|
|
|||
|
|
@ -10,12 +10,11 @@ use rustc_span::symbol::sym;
|
|||
|
||||
use super::STRING_EXTEND_CHARS;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
if !is_type_diagnostic_item(cx, obj_ty, sym::string_type) {
|
||||
return;
|
||||
}
|
||||
let arg = &args[1];
|
||||
if let Some(arglists) = method_chain_args(arg, &["chars"]) {
|
||||
let target = &arglists[0][0];
|
||||
let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
|
||||
|
|
@ -36,7 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp
|
|||
"try this",
|
||||
format!(
|
||||
"{}.push_str({}{})",
|
||||
snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, recv.span, "..", &mut applicability),
|
||||
ref_str,
|
||||
snippet_with_applicability(cx, target.span, "..", &mut applicability)
|
||||
),
|
||||
|
|
|
|||
|
|
@ -8,15 +8,8 @@ use rustc_span::sym;
|
|||
|
||||
use super::SUSPICIOUS_MAP;
|
||||
|
||||
pub fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &hir::Expr<'_>,
|
||||
map_args: &[hir::Expr<'_>],
|
||||
count_args: &[hir::Expr<'_>],
|
||||
) {
|
||||
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if let [count_recv] = count_args;
|
||||
if let [_, map_arg] = map_args;
|
||||
if is_trait_method(cx, count_recv, sym::Iterator);
|
||||
let closure = expr_or_init(cx, map_arg);
|
||||
if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(closure.hir_id);
|
||||
|
|
|
|||
|
|
@ -8,18 +8,18 @@ use rustc_middle::ty::{self, Ty};
|
|||
use super::UNINIT_ASSUMED_INIT;
|
||||
|
||||
/// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter)
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, outer: &hir::Expr<'_>) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Call(ref callee, ref args) = expr.kind;
|
||||
if let hir::ExprKind::Call(callee, args) = recv.kind;
|
||||
if args.is_empty();
|
||||
if let hir::ExprKind::Path(ref path) = callee.kind;
|
||||
if match_qpath(path, &paths::MEM_MAYBEUNINIT_UNINIT);
|
||||
if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(outer));
|
||||
if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(expr));
|
||||
then {
|
||||
span_lint(
|
||||
cx,
|
||||
UNINIT_ASSUMED_INIT,
|
||||
outer.span,
|
||||
expr.span,
|
||||
"this call for this type may be undefined behavior"
|
||||
);
|
||||
}
|
||||
|
|
@ -28,9 +28,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, outer: &hir::Exp
|
|||
|
||||
fn is_maybe_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Array(ref component, _) => is_maybe_uninit_ty_valid(cx, component),
|
||||
ty::Tuple(ref types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)),
|
||||
ty::Adt(ref adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT),
|
||||
ty::Array(component, _) => is_maybe_uninit_ty_valid(cx, component),
|
||||
ty::Tuple(types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)),
|
||||
ty::Adt(adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::usage::mutated_variables;
|
||||
use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -10,20 +9,20 @@ use rustc_span::sym;
|
|||
|
||||
use super::UNNECESSARY_FILTER_MAP;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
|
||||
if !is_trait_method(cx, expr, sym::Iterator) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let hir::ExprKind::Closure(_, _, body_id, ..) = args[1].kind {
|
||||
if let hir::ExprKind::Closure(_, _, body_id, ..) = arg.kind {
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
let arg_id = body.params[0].pat.hir_id;
|
||||
let mutates_arg =
|
||||
mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
|
||||
|
||||
let (mut found_mapping, mut found_filtering) = check_expression(&cx, arg_id, &body.value);
|
||||
let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, &body.value);
|
||||
|
||||
let mut return_visitor = ReturnVisitor::new(&cx, arg_id);
|
||||
let mut return_visitor = ReturnVisitor::new(cx, arg_id);
|
||||
return_visitor.visit_expr(&body.value);
|
||||
found_mapping |= return_visitor.found_mapping;
|
||||
found_filtering |= return_visitor.found_filtering;
|
||||
|
|
@ -53,38 +52,35 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp
|
|||
// returns (found_mapping, found_filtering)
|
||||
fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tcx hir::Expr<'_>) -> (bool, bool) {
|
||||
match &expr.kind {
|
||||
hir::ExprKind::Call(ref func, ref args) => {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Path(ref path) = func.kind;
|
||||
then {
|
||||
if match_qpath(path, &paths::OPTION_SOME) {
|
||||
if path_to_local_id(&args[0], arg_id) {
|
||||
return (false, false)
|
||||
}
|
||||
return (true, false);
|
||||
hir::ExprKind::Call(func, args) => {
|
||||
if let hir::ExprKind::Path(ref path) = func.kind {
|
||||
if match_qpath(path, &paths::OPTION_SOME) {
|
||||
if path_to_local_id(&args[0], arg_id) {
|
||||
return (false, false);
|
||||
}
|
||||
// We don't know. It might do anything.
|
||||
return (true, true);
|
||||
return (true, false);
|
||||
}
|
||||
// We don't know. It might do anything.
|
||||
return (true, true);
|
||||
}
|
||||
(true, true)
|
||||
},
|
||||
hir::ExprKind::Block(ref block, _) => block
|
||||
hir::ExprKind::Block(block, _) => block
|
||||
.expr
|
||||
.as_ref()
|
||||
.map_or((false, false), |expr| check_expression(cx, arg_id, &expr)),
|
||||
.map_or((false, false), |expr| check_expression(cx, arg_id, expr)),
|
||||
hir::ExprKind::Match(_, arms, _) => {
|
||||
let mut found_mapping = false;
|
||||
let mut found_filtering = false;
|
||||
for arm in *arms {
|
||||
let (m, f) = check_expression(cx, arg_id, &arm.body);
|
||||
let (m, f) = check_expression(cx, arg_id, arm.body);
|
||||
found_mapping |= m;
|
||||
found_filtering |= f;
|
||||
}
|
||||
(found_mapping, found_filtering)
|
||||
},
|
||||
// There must be an else_arm or there will be a type error
|
||||
hir::ExprKind::If(_, ref if_arm, Some(ref else_arm)) => {
|
||||
hir::ExprKind::If(_, if_arm, Some(else_arm)) => {
|
||||
let if_check = check_expression(cx, arg_id, if_arm);
|
||||
let else_check = check_expression(cx, arg_id, else_arm);
|
||||
(if_check.0 | else_check.0, if_check.1 | else_check.1)
|
||||
|
|
|
|||
|
|
@ -11,11 +11,17 @@ use rustc_span::{source_map::Span, sym};
|
|||
|
||||
use super::UNNECESSARY_FOLD;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir::Expr<'_>], fold_span: Span) {
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
init: &hir::Expr<'_>,
|
||||
acc: &hir::Expr<'_>,
|
||||
fold_span: Span,
|
||||
) {
|
||||
fn check_fold_with_op(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
fold_args: &[hir::Expr<'_>],
|
||||
acc: &hir::Expr<'_>,
|
||||
fold_span: Span,
|
||||
op: hir::BinOpKind,
|
||||
replacement_method_name: &str,
|
||||
|
|
@ -23,18 +29,18 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir
|
|||
) {
|
||||
if_chain! {
|
||||
// Extract the body of the closure passed to fold
|
||||
if let hir::ExprKind::Closure(_, _, body_id, _, _) = fold_args[2].kind;
|
||||
if let hir::ExprKind::Closure(_, _, body_id, _, _) = acc.kind;
|
||||
let closure_body = cx.tcx.hir().body(body_id);
|
||||
let closure_expr = remove_blocks(&closure_body.value);
|
||||
|
||||
// Check if the closure body is of the form `acc <op> some_expr(x)`
|
||||
if let hir::ExprKind::Binary(ref bin_op, ref left_expr, ref right_expr) = closure_expr.kind;
|
||||
if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind;
|
||||
if bin_op.node == op;
|
||||
|
||||
// Extract the names of the two arguments to the closure
|
||||
if let [param_a, param_b] = closure_body.params;
|
||||
if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(¶m_a.pat).kind;
|
||||
if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(¶m_b.pat).kind;
|
||||
if let PatKind::Binding(_, first_arg_id, ..) = strip_pat_refs(param_a.pat).kind;
|
||||
if let PatKind::Binding(_, second_arg_id, second_arg_ident, _) = strip_pat_refs(param_b.pat).kind;
|
||||
|
||||
if path_to_local_id(left_expr, first_arg_id);
|
||||
if replacement_has_args || path_to_local_id(right_expr, second_arg_id);
|
||||
|
|
@ -74,25 +80,14 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, fold_args: &[hir
|
|||
return;
|
||||
}
|
||||
|
||||
assert!(
|
||||
fold_args.len() == 3,
|
||||
"Expected fold_args to have three entries - the receiver, the initial value and the closure"
|
||||
);
|
||||
|
||||
// Check if the first argument to .fold is a suitable literal
|
||||
if let hir::ExprKind::Lit(ref lit) = fold_args[1].kind {
|
||||
if let hir::ExprKind::Lit(ref lit) = init.kind {
|
||||
match lit.node {
|
||||
ast::LitKind::Bool(false) => {
|
||||
check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Or, "any", true)
|
||||
},
|
||||
ast::LitKind::Bool(true) => {
|
||||
check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::And, "all", true)
|
||||
},
|
||||
ast::LitKind::Int(0, _) => {
|
||||
check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Add, "sum", false)
|
||||
},
|
||||
ast::LitKind::Bool(false) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, "any", true),
|
||||
ast::LitKind::Bool(true) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, "all", true),
|
||||
ast::LitKind::Int(0, _) => check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, "sum", false),
|
||||
ast::LitKind::Int(1, _) => {
|
||||
check_fold_with_op(cx, expr, fold_args, fold_span, hir::BinOpKind::Mul, "product", false)
|
||||
check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, "product", false)
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,14 +14,15 @@ use super::UNNECESSARY_LAZY_EVALUATIONS;
|
|||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
args: &'tcx [hir::Expr<'_>],
|
||||
recv: &'tcx hir::Expr<'_>,
|
||||
arg: &'tcx hir::Expr<'_>,
|
||||
simplify_using: &str,
|
||||
) {
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::option_type);
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]), sym::result_type);
|
||||
let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::option_type);
|
||||
let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::result_type);
|
||||
|
||||
if is_option || is_result {
|
||||
if let hir::ExprKind::Closure(_, _, eid, _, _) = args[1].kind {
|
||||
if let hir::ExprKind::Closure(_, _, eid, _, _) = arg.kind {
|
||||
let body = cx.tcx.hir().body(eid);
|
||||
let body_expr = &body.value;
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ pub(super) fn check<'tcx>(
|
|||
&format!("use `{}` instead", simplify_using),
|
||||
format!(
|
||||
"{0}.{1}({2})",
|
||||
snippet(cx, args[0].span, ".."),
|
||||
snippet(cx, recv.span, ".."),
|
||||
simplify_using,
|
||||
snippet(cx, body_expr.span, ".."),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ use rustc_span::sym;
|
|||
use super::UNWRAP_USED;
|
||||
|
||||
/// lint use of `unwrap()` for `Option`s and `Result`s
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, unwrap_args: &[hir::Expr<'_>]) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(&unwrap_args[0]).peel_refs();
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::option_type) {
|
||||
Some((UNWRAP_USED, "an Option", "None"))
|
||||
|
|
|
|||
|
|
@ -10,12 +10,11 @@ use rustc_lint::LateContext;
|
|||
use super::USELESS_ASREF;
|
||||
|
||||
/// Checks for the `USELESS_ASREF` lint.
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, as_ref_args: &[hir::Expr<'_>]) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str, recvr: &hir::Expr<'_>) {
|
||||
// when we get here, we've already checked that the call name is "as_ref" or "as_mut"
|
||||
// check if the call is to the actual `AsRef` or `AsMut` trait
|
||||
if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) {
|
||||
// check if the type after `as_ref` or `as_mut` is the same as before
|
||||
let recvr = &as_ref_args[0];
|
||||
let rcv_ty = cx.typeck_results().expr_ty(recvr);
|
||||
let res_ty = cx.typeck_results().expr_ty(expr);
|
||||
let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ pub(super) fn derefs_to_slice<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind {
|
||||
if let hir::ExprKind::MethodCall(path, _, args, _) = expr.kind {
|
||||
if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) {
|
||||
Some(&args[0])
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -21,8 +21,10 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [
|
|||
|
||||
// Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types).
|
||||
// Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
|
||||
(&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]),
|
||||
(&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), Convention::ImplementsTrait(false)], &[SelfKind::Value]),
|
||||
(&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false),
|
||||
Convention::IsTraitItem(false)], &[SelfKind::Ref]),
|
||||
(&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true),
|
||||
Convention::IsTraitItem(false), Convention::ImplementsTrait(false)], &[SelfKind::Value]),
|
||||
];
|
||||
|
||||
enum Convention {
|
||||
|
|
@ -32,18 +34,27 @@ enum Convention {
|
|||
NotEndsWith(&'static str),
|
||||
IsSelfTypeCopy(bool),
|
||||
ImplementsTrait(bool),
|
||||
IsTraitItem(bool),
|
||||
}
|
||||
|
||||
impl Convention {
|
||||
#[must_use]
|
||||
fn check<'tcx>(&self, cx: &LateContext<'tcx>, self_ty: &'tcx TyS<'tcx>, other: &str, is_trait_def: bool) -> bool {
|
||||
fn check<'tcx>(
|
||||
&self,
|
||||
cx: &LateContext<'tcx>,
|
||||
self_ty: &'tcx TyS<'tcx>,
|
||||
other: &str,
|
||||
implements_trait: bool,
|
||||
is_trait_item: bool,
|
||||
) -> bool {
|
||||
match *self {
|
||||
Self::Eq(this) => this == other,
|
||||
Self::StartsWith(this) => other.starts_with(this) && this != other,
|
||||
Self::EndsWith(this) => other.ends_with(this) && this != other,
|
||||
Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, is_trait_def),
|
||||
Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, implements_trait, is_trait_item),
|
||||
Self::IsSelfTypeCopy(is_true) => is_true == is_copy(cx, self_ty),
|
||||
Self::ImplementsTrait(is_true) => is_true == is_trait_def,
|
||||
Self::ImplementsTrait(is_true) => is_true == implements_trait,
|
||||
Self::IsTraitItem(is_true) => is_true == is_trait_item,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -60,12 +71,17 @@ impl fmt::Display for Convention {
|
|||
},
|
||||
Self::ImplementsTrait(is_true) => {
|
||||
let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") };
|
||||
format!("Method{} implement{} a trait", negation, s_suffix).fmt(f)
|
||||
format!("method{} implement{} a trait", negation, s_suffix).fmt(f)
|
||||
},
|
||||
Self::IsTraitItem(is_true) => {
|
||||
let suffix = if is_true { " is" } else { " is not" };
|
||||
format!("method{} a trait item", suffix).fmt(f)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
item_name: &str,
|
||||
|
|
@ -73,6 +89,7 @@ pub(super) fn check<'tcx>(
|
|||
self_ty: &'tcx TyS<'tcx>,
|
||||
first_arg_ty: &'tcx TyS<'tcx>,
|
||||
first_arg_span: Span,
|
||||
implements_trait: bool,
|
||||
is_trait_item: bool,
|
||||
) {
|
||||
let lint = if is_pub {
|
||||
|
|
@ -83,7 +100,7 @@ pub(super) fn check<'tcx>(
|
|||
if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| {
|
||||
convs
|
||||
.iter()
|
||||
.all(|conv| conv.check(cx, self_ty, item_name, is_trait_item))
|
||||
.all(|conv| conv.check(cx, self_ty, item_name, implements_trait, is_trait_item))
|
||||
}) {
|
||||
if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
|
||||
let suggestion = {
|
||||
|
|
@ -99,6 +116,7 @@ pub(super) fn check<'tcx>(
|
|||
.filter_map(|conv| {
|
||||
if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_)))
|
||||
|| matches!(conv, Convention::ImplementsTrait(_))
|
||||
|| matches!(conv, Convention::IsTraitItem(_))
|
||||
{
|
||||
None
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -6,10 +6,9 @@ use rustc_middle::ty;
|
|||
|
||||
use super::ZST_OFFSET;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if args.len() == 2;
|
||||
if let ty::RawPtr(ty::TypeAndMut { ref ty, .. }) = cx.typeck_results().expr_ty(&args[0]).kind();
|
||||
if let ty::RawPtr(ty::TypeAndMut { ty, .. }) = cx.typeck_results().expr_ty(recv).kind();
|
||||
if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty));
|
||||
if layout.is_zst();
|
||||
then {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue