Merge commit 'f51aade56f' into clippyup
This commit is contained in:
parent
7ba06ec9c5
commit
fb41bfa774
244 changed files with 9305 additions and 4024 deletions
70
clippy_lints/src/methods/bytecount.rs
Normal file
70
clippy_lints/src/methods/bytecount.rs
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::match_type;
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, UintTy};
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::NAIVE_BYTECOUNT;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
filter_recv: &'tcx Expr<'_>,
|
||||
filter_arg: &'tcx Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
if let [param] = body.params;
|
||||
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
|
||||
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
|
||||
if op.node == BinOpKind::Eq;
|
||||
if match_type(cx,
|
||||
cx.typeck_results().expr_ty(filter_recv).peel_refs(),
|
||||
&paths::SLICE_ITER);
|
||||
let operand_is_arg = |expr| {
|
||||
let expr = peel_ref_operators(cx, peel_blocks(expr));
|
||||
path_to_local_id(expr, arg_id)
|
||||
};
|
||||
let needle = if operand_is_arg(l) {
|
||||
r
|
||||
} else if operand_is_arg(r) {
|
||||
l
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
|
||||
if !is_local_used(cx, needle, arg_id);
|
||||
then {
|
||||
let haystack = if let ExprKind::MethodCall(path, args, _) =
|
||||
filter_recv.kind {
|
||||
let p = path.ident.name;
|
||||
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
|
||||
&args[0]
|
||||
} else {
|
||||
filter_recv
|
||||
}
|
||||
} else {
|
||||
filter_recv
|
||||
};
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NAIVE_BYTECOUNT,
|
||||
expr.span,
|
||||
"you appear to be counting bytes the naive way",
|
||||
"consider using the bytecount crate",
|
||||
format!("bytecount::count({}, {})",
|
||||
snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
|
||||
snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
37
clippy_lints/src/methods/bytes_count_to_len.rs
Normal file
37
clippy_lints/src/methods/bytes_count_to_len.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
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 as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::BYTES_COUNT_TO_LEN;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
count_recv: &'tcx hir::Expr<'_>,
|
||||
bytes_recv: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id);
|
||||
if cx.tcx.type_of(impl_id).is_str();
|
||||
let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs();
|
||||
if ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String);
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BYTES_COUNT_TO_LEN,
|
||||
expr.span,
|
||||
"using long and hard to read `.bytes().count()`",
|
||||
"consider calling `.len()` instead",
|
||||
format!("{}.len()", snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability)),
|
||||
applicability
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{source_map::Spanned, symbol::sym, Span};
|
||||
|
||||
use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
call_span: Span,
|
||||
recv: &'tcx Expr<'_>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if cx.tcx.type_of(impl_id).is_str();
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind;
|
||||
if (2..=6).contains(&ext_literal.as_str().len());
|
||||
let ext_str = ext_literal.as_str();
|
||||
if ext_str.starts_with('.');
|
||||
if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
|
||||
|| ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
|
||||
let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
if recv_ty.is_str() || is_type_diagnostic_item(cx, recv_ty, sym::String);
|
||||
then {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
call_span,
|
||||
"case-sensitive file extension comparison",
|
||||
None,
|
||||
"consider using a case-insensitive comparison instead",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
96
clippy_lints/src/methods/collapsible_str_replace.rs
Normal file
96
clippy_lints/src/methods/collapsible_str_replace.rs
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{eq_expr_value, get_parent_expr};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use super::method_call;
|
||||
use super::COLLAPSIBLE_STR_REPLACE;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
from: &'tcx hir::Expr<'tcx>,
|
||||
to: &'tcx hir::Expr<'tcx>,
|
||||
) {
|
||||
let replace_methods = collect_replace_calls(cx, expr, to);
|
||||
if replace_methods.methods.len() > 1 {
|
||||
let from_kind = cx.typeck_results().expr_ty(from).peel_refs().kind();
|
||||
// If the parent node's `to` argument is the same as the `to` argument
|
||||
// of the last replace call in the current chain, don't lint as it was already linted
|
||||
if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let Some(("replace", [_, current_from, current_to], _)) = method_call(parent)
|
||||
&& eq_expr_value(cx, to, current_to)
|
||||
&& from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
check_consecutive_replace_calls(cx, expr, &replace_methods, to);
|
||||
}
|
||||
}
|
||||
|
||||
struct ReplaceMethods<'tcx> {
|
||||
methods: VecDeque<&'tcx hir::Expr<'tcx>>,
|
||||
from_args: VecDeque<&'tcx hir::Expr<'tcx>>,
|
||||
}
|
||||
|
||||
fn collect_replace_calls<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
to_arg: &'tcx hir::Expr<'tcx>,
|
||||
) -> ReplaceMethods<'tcx> {
|
||||
let mut methods = VecDeque::new();
|
||||
let mut from_args = VecDeque::new();
|
||||
|
||||
let _: Option<()> = for_each_expr(expr, |e| {
|
||||
if let Some(("replace", [_, from, to], _)) = method_call(e) {
|
||||
if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
|
||||
methods.push_front(e);
|
||||
from_args.push_front(from);
|
||||
ControlFlow::Continue(())
|
||||
} else {
|
||||
ControlFlow::BREAK
|
||||
}
|
||||
} else {
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
});
|
||||
|
||||
ReplaceMethods { methods, from_args }
|
||||
}
|
||||
|
||||
/// Check a chain of `str::replace` calls for `collapsible_str_replace` lint.
|
||||
fn check_consecutive_replace_calls<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
replace_methods: &ReplaceMethods<'tcx>,
|
||||
to_arg: &'tcx hir::Expr<'tcx>,
|
||||
) {
|
||||
let from_args = &replace_methods.from_args;
|
||||
let from_arg_reprs: Vec<String> = from_args
|
||||
.iter()
|
||||
.map(|from_arg| snippet(cx, from_arg.span, "..").to_string())
|
||||
.collect();
|
||||
let app = Applicability::MachineApplicable;
|
||||
let earliest_replace_call = replace_methods.methods.front().unwrap();
|
||||
if let Some((_, [..], span_lo)) = method_call(earliest_replace_call) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
COLLAPSIBLE_STR_REPLACE,
|
||||
expr.span.with_lo(span_lo.lo()),
|
||||
"used consecutive `str::replace` call",
|
||||
"replace with",
|
||||
format!(
|
||||
"replace([{}], {})",
|
||||
from_arg_reprs.join(", "),
|
||||
snippet(cx, to_arg.span, ".."),
|
||||
),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,18 +7,26 @@ 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<'_>, recv: &hir::Expr<'_>, allow_expect_in_tests: bool) {
|
||||
/// lint use of `expect()` or `expect_err` for `Result` and `expect()` for `Option`.
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
recv: &hir::Expr<'_>,
|
||||
is_err: bool,
|
||||
allow_expect_in_tests: bool,
|
||||
) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
|
||||
Some((EXPECT_USED, "an Option", "None", ""))
|
||||
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
|
||||
Some((EXPECT_USED, "a Result", "Err", "an "))
|
||||
Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an "))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let method = if is_err { "expect_err" } else { "expect" };
|
||||
|
||||
if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -28,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
|||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
&format!("used `expect()` on `{kind}` value"),
|
||||
&format!("used `{method}()` on `{kind}` value"),
|
||||
None,
|
||||
&format!("if this value is {none_prefix}`{none_value}`, it will panic"),
|
||||
);
|
||||
|
|
|
|||
39
clippy_lints/src/methods/get_first.rs
Normal file
39
clippy_lints/src/methods/get_first.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_slice_of_primitives;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::GET_FIRST;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx hir::Expr<'_>,
|
||||
recv: &'tcx hir::Expr<'_>,
|
||||
arg: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if cx.tcx.type_of(impl_id).is_slice();
|
||||
if let Some(_) = is_slice_of_primitives(cx, recv);
|
||||
if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind;
|
||||
then {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
GET_FIRST,
|
||||
expr.span,
|
||||
&format!("accessing first element with `{0}.get(0)`", slice_name),
|
||||
"try",
|
||||
format!("{}.first()", slice_name),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
107
clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
Normal file
107
clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{get_expr_use_or_unification_node, is_lang_ctor, is_no_std_crate};
|
||||
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Expr, ExprKind, Node};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS};
|
||||
|
||||
enum IterType {
|
||||
Iter,
|
||||
IterMut,
|
||||
IntoIter,
|
||||
}
|
||||
|
||||
impl IterType {
|
||||
fn ref_prefix(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Iter => "&",
|
||||
Self::IterMut => "&mut ",
|
||||
Self::IntoIter => "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
|
||||
let item = match &recv.kind {
|
||||
ExprKind::Array(v) if v.len() <= 1 => v.first(),
|
||||
ExprKind::Path(p) => {
|
||||
if is_lang_ctor(cx, p, OptionNone) {
|
||||
None
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
ExprKind::Call(f, some_args) if some_args.len() == 1 => {
|
||||
if let ExprKind::Path(p) = &f.kind {
|
||||
if is_lang_ctor(cx, p, OptionSome) {
|
||||
Some(&some_args[0])
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
let iter_type = match method_name {
|
||||
"iter" => IterType::Iter,
|
||||
"iter_mut" => IterType::IterMut,
|
||||
"into_iter" => IterType::IntoIter,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) {
|
||||
Some((Node::Expr(parent), child_id)) => match parent.kind {
|
||||
ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false,
|
||||
ExprKind::If(_, _, _)
|
||||
| ExprKind::Match(_, _, _)
|
||||
| ExprKind::Closure(_)
|
||||
| ExprKind::Ret(_)
|
||||
| ExprKind::Break(_, _) => true,
|
||||
_ => false,
|
||||
},
|
||||
Some((Node::Stmt(_) | Node::Local(_), _)) => false,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if is_unified {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(i) = item {
|
||||
let sugg = format!(
|
||||
"{}::iter::once({}{})",
|
||||
if is_no_std_crate(cx) { "core" } else { "std" },
|
||||
iter_type.ref_prefix(),
|
||||
snippet(cx, i.span, "...")
|
||||
);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ITER_ON_SINGLE_ITEMS,
|
||||
expr.span,
|
||||
&format!("`{method_name}` call on a collection with only one item"),
|
||||
"try",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
} else {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
ITER_ON_EMPTY_COLLECTIONS,
|
||||
expr.span,
|
||||
&format!("`{method_name}` call on an empty collection"),
|
||||
"try",
|
||||
if is_no_std_crate(cx) {
|
||||
"core::iter::empty()".to_string()
|
||||
} else {
|
||||
"std::iter::empty()".to_string()
|
||||
},
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
64
clippy_lints/src/methods/manual_ok_or.rs
Normal file
64
clippy_lints/src/methods/manual_ok_or.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_lang_ctor, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{ResultErr, ResultOk};
|
||||
use rustc_hir::{Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use super::MANUAL_OK_OR;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
or_expr: &'tcx Expr<'_>,
|
||||
map_expr: &'tcx Expr<'_>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Option);
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, [err_arg]) = or_expr.kind;
|
||||
if is_lang_ctor(cx, err_path, ResultErr);
|
||||
if is_ok_wrapping(cx, map_expr);
|
||||
if let Some(recv_snippet) = snippet_opt(cx, recv.span);
|
||||
if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
|
||||
if let Some(indent) = indent_of(cx, expr.span);
|
||||
then {
|
||||
let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_OK_OR,
|
||||
expr.span,
|
||||
"this pattern reimplements `Option::ok_or`",
|
||||
"replace with",
|
||||
format!(
|
||||
"{}.ok_or({})",
|
||||
recv_snippet,
|
||||
reindented_err_arg_snippet
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Path(ref qpath) = map_expr.kind {
|
||||
if is_lang_ctor(cx, qpath, ResultOk) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if_chain! {
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
|
||||
let body = cx.tcx.hir().body(body);
|
||||
if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
|
||||
if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
|
||||
if is_lang_ctor(cx, ok_path, ResultOk);
|
||||
then { path_to_local_id(ok_arg, param_id) } else { false }
|
||||
}
|
||||
}
|
||||
122
clippy_lints/src/methods/map_clone.rs
Normal file
122
clippy_lints/src/methods/map_clone.rs
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::Mutability;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::MAP_CLONE;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'_>,
|
||||
e: &hir::Expr<'_>,
|
||||
recv: &hir::Expr<'_>,
|
||||
arg: &'tcx hir::Expr<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
|
||||
if cx.tcx.impl_of_method(method_id)
|
||||
.map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id), sym::Option))
|
||||
|| is_diag_trait_item(cx, method_id, sym::Iterator);
|
||||
if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind;
|
||||
then {
|
||||
let closure_body = cx.tcx.hir().body(body);
|
||||
let closure_expr = peel_blocks(&closure_body.value);
|
||||
match closure_body.params[0].pat.kind {
|
||||
hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
|
||||
hir::BindingAnnotation::Unannotated, .., name, None
|
||||
) = inner.kind {
|
||||
if ident_eq(name, closure_expr) {
|
||||
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
|
||||
}
|
||||
},
|
||||
hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
|
||||
match closure_expr.kind {
|
||||
hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
|
||||
if ident_eq(name, inner) {
|
||||
if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
|
||||
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
|
||||
if ident_eq(name, obj) && method.ident.name == sym::clone;
|
||||
if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
|
||||
if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
|
||||
if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
|
||||
// no autoderefs
|
||||
if !cx.typeck_results().expr_adjustments(obj).iter()
|
||||
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
|
||||
then {
|
||||
let obj_ty = cx.typeck_results().expr_ty(obj);
|
||||
if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
|
||||
if matches!(mutability, Mutability::Not) {
|
||||
let copy = is_copy(cx, *ty);
|
||||
lint_explicit_closure(cx, e.span, recv.span, copy, msrv);
|
||||
}
|
||||
} else {
|
||||
lint_needless_cloning(cx, e.span, recv.span);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
|
||||
path.segments.len() == 1 && path.segments[0].ident == name
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_CLONE,
|
||||
root.trim_start(receiver).unwrap(),
|
||||
"you are needlessly cloning iterator elements",
|
||||
"remove the `map` call",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
||||
fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
|
||||
("you are using an explicit closure for copying elements", "copied")
|
||||
} else {
|
||||
("you are using an explicit closure for cloning elements", "cloned")
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MAP_CLONE,
|
||||
replace,
|
||||
message,
|
||||
&format!("consider calling the dedicated `{}` method", sugg_method),
|
||||
format!(
|
||||
"{}.{}()",
|
||||
snippet_with_applicability(cx, root, "..", &mut applicability),
|
||||
sugg_method,
|
||||
),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
34
clippy_lints/src/methods/map_err_ignore.rs
Normal file
34
clippy_lints/src/methods/map_err_ignore.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::MAP_ERR_IGNORE;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'_>, e: &Expr<'_>, arg: &'tcx Expr<'_>) {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
|
||||
&& is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result)
|
||||
&& let ExprKind::Closure(&Closure {
|
||||
capture_clause: CaptureBy::Ref,
|
||||
body,
|
||||
fn_decl_span,
|
||||
..
|
||||
}) = arg.kind
|
||||
&& let closure_body = cx.tcx.hir().body(body)
|
||||
&& let [param] = closure_body.params
|
||||
&& let PatKind::Wild = param.pat.kind
|
||||
{
|
||||
// span the area of the closure capture and warn that the
|
||||
// original error will be thrown away
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
MAP_ERR_IGNORE,
|
||||
fn_decl_span,
|
||||
"`map_err(|_|...` wildcard pattern discards the original error",
|
||||
None,
|
||||
"consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
30
clippy_lints/src/methods/mut_mutex_lock.rs
Normal file
30
clippy_lints/src/methods/mut_mutex_lock.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, Mutability};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::MUT_MUTEX_LOCK;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) {
|
||||
if_chain! {
|
||||
if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind();
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Mutex);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MUT_MUTEX_LOCK,
|
||||
name_span,
|
||||
"calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
|
||||
"change this to",
|
||||
"get_mut".to_owned(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
178
clippy_lints/src/methods/open_options.rs
Normal file
178
clippy_lints/src/methods/open_options.rs
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::paths;
|
||||
use clippy_utils::ty::match_type;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::{Span, Spanned};
|
||||
|
||||
use super::NONSENSICAL_OPEN_OPTIONS;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
|
||||
&& match_type(cx, cx.tcx.type_of(impl_id), &paths::OPEN_OPTIONS)
|
||||
{
|
||||
let mut options = Vec::new();
|
||||
get_open_options(cx, recv, &mut options);
|
||||
check_open_options(cx, &options, e.span);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
enum Argument {
|
||||
True,
|
||||
False,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum OpenOption {
|
||||
Write,
|
||||
Read,
|
||||
Truncate,
|
||||
Create,
|
||||
Append,
|
||||
}
|
||||
|
||||
fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
|
||||
if let ExprKind::MethodCall(path, arguments, _) = argument.kind {
|
||||
let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
|
||||
|
||||
// Only proceed if this is a call on some object of type std::fs::OpenOptions
|
||||
if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
|
||||
let argument_option = match arguments[1].kind {
|
||||
ExprKind::Lit(ref span) => {
|
||||
if let Spanned {
|
||||
node: LitKind::Bool(lit),
|
||||
..
|
||||
} = *span
|
||||
{
|
||||
if lit { Argument::True } else { Argument::False }
|
||||
} else {
|
||||
// The function is called with a literal which is not a boolean literal.
|
||||
// This is theoretically possible, but not very likely.
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => Argument::Unknown,
|
||||
};
|
||||
|
||||
match path.ident.as_str() {
|
||||
"create" => {
|
||||
options.push((OpenOption::Create, argument_option));
|
||||
},
|
||||
"append" => {
|
||||
options.push((OpenOption::Append, argument_option));
|
||||
},
|
||||
"truncate" => {
|
||||
options.push((OpenOption::Truncate, argument_option));
|
||||
},
|
||||
"read" => {
|
||||
options.push((OpenOption::Read, argument_option));
|
||||
},
|
||||
"write" => {
|
||||
options.push((OpenOption::Write, argument_option));
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
get_open_options(cx, &arguments[0], options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], span: Span) {
|
||||
let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
|
||||
let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
|
||||
(false, false, false, false, false);
|
||||
// This code is almost duplicated (oh, the irony), but I haven't found a way to
|
||||
// unify it.
|
||||
|
||||
for option in options {
|
||||
match *option {
|
||||
(OpenOption::Create, arg) => {
|
||||
if create {
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
span,
|
||||
"the method `create` is called more than once",
|
||||
);
|
||||
} else {
|
||||
create = true;
|
||||
}
|
||||
create_arg = create_arg || (arg == Argument::True);
|
||||
},
|
||||
(OpenOption::Append, arg) => {
|
||||
if append {
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
span,
|
||||
"the method `append` is called more than once",
|
||||
);
|
||||
} else {
|
||||
append = true;
|
||||
}
|
||||
append_arg = append_arg || (arg == Argument::True);
|
||||
},
|
||||
(OpenOption::Truncate, arg) => {
|
||||
if truncate {
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
span,
|
||||
"the method `truncate` is called more than once",
|
||||
);
|
||||
} else {
|
||||
truncate = true;
|
||||
}
|
||||
truncate_arg = truncate_arg || (arg == Argument::True);
|
||||
},
|
||||
(OpenOption::Read, arg) => {
|
||||
if read {
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
span,
|
||||
"the method `read` is called more than once",
|
||||
);
|
||||
} else {
|
||||
read = true;
|
||||
}
|
||||
read_arg = read_arg || (arg == Argument::True);
|
||||
},
|
||||
(OpenOption::Write, arg) => {
|
||||
if write {
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
span,
|
||||
"the method `write` is called more than once",
|
||||
);
|
||||
} else {
|
||||
write = true;
|
||||
}
|
||||
write_arg = write_arg || (arg == Argument::True);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if read && truncate && read_arg && truncate_arg && !(write && write_arg) {
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
span,
|
||||
"file opened with `truncate` and `read`",
|
||||
);
|
||||
}
|
||||
if append && truncate && append_arg && truncate_arg {
|
||||
span_lint(
|
||||
cx,
|
||||
NONSENSICAL_OPEN_OPTIONS,
|
||||
span,
|
||||
"file opened with `append` and `truncate`",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -78,7 +78,7 @@ pub(super) fn check<'tcx>(
|
|||
map_span,
|
||||
String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }),
|
||||
),
|
||||
(expr.span.with_lo(unwrap_recv.span.hi()), String::from("")),
|
||||
(expr.span.with_lo(unwrap_recv.span.hi()), String::new()),
|
||||
];
|
||||
|
||||
if !unwrap_snippet_none {
|
||||
|
|
|
|||
37
clippy_lints/src/methods/path_buf_push_overwrite.rs
Normal file
37
clippy_lints/src/methods/path_buf_push_overwrite.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
use std::path::{Component, Path};
|
||||
|
||||
use super::PATH_BUF_PUSH_OVERWRITE;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::PathBuf);
|
||||
if let ExprKind::Lit(ref lit) = arg.kind;
|
||||
if let LitKind::Str(ref path_lit, _) = lit.node;
|
||||
if let pushed_path = Path::new(path_lit.as_str());
|
||||
if let Some(pushed_path_lit) = pushed_path.to_str();
|
||||
if pushed_path.has_root();
|
||||
if let Some(root) = pushed_path.components().next();
|
||||
if root == Component::RootDir;
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PATH_BUF_PUSH_OVERWRITE,
|
||||
lit.span,
|
||||
"calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
|
||||
"try",
|
||||
format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
clippy_lints/src/methods/range_zip_with_len.rs
Normal file
34
clippy_lints/src/methods/range_zip_with_len.rs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{higher, SpanlessEq};
|
||||
use clippy_utils::{is_integer_const, is_trait_method};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::RANGE_ZIP_WITH_LEN;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if is_trait_method(cx, expr, sym::Iterator);
|
||||
// range expression in `.zip()` call: `0..x.len()`
|
||||
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
|
||||
if is_integer_const(cx, start, 0);
|
||||
// `.len()` call
|
||||
if let ExprKind::MethodCall(len_path, [len_recv], _) = end.kind;
|
||||
if len_path.ident.name == sym::len;
|
||||
// `.iter()` and `.len()` called on same `Path`
|
||||
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind;
|
||||
if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
|
||||
then {
|
||||
span_lint(cx,
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
expr.span,
|
||||
&format!("it is more idiomatic to use `{}.iter().enumerate()`",
|
||||
snippet(cx, recv.span, "_"))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
52
clippy_lints/src/methods/repeat_once.rs
Normal file
52
clippy_lints/src/methods/repeat_once.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
use clippy_utils::consts::{constant_context, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::REPEAT_ONCE;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
repeat_arg: &'tcx Expr<'_>,
|
||||
) {
|
||||
if constant_context(cx, cx.typeck_results()).expr(repeat_arg) == Some(Constant::Int(1)) {
|
||||
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
if ty.is_str() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on str",
|
||||
"consider using `.to_string()` instead",
|
||||
format!("{}.to_string()", snippet(cx, recv.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if ty.builtin_index().is_some() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on slice",
|
||||
"consider using `.to_vec()` instead",
|
||||
format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else if is_type_diagnostic_item(cx, ty, sym::String) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REPEAT_ONCE,
|
||||
expr.span,
|
||||
"calling `repeat(1)` on a string literal",
|
||||
"consider using `.clone()` instead",
|
||||
format!("{}.clone()", snippet(cx, recv.span, r#""...""#)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
clippy_lints/src/methods/stable_sort_primitive.rs
Normal file
31
clippy_lints/src/methods/stable_sort_primitive.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_slice_of_primitives;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::STABLE_SORT_PRIMITIVE;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
|
||||
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
|
||||
&& cx.tcx.type_of(impl_id).is_slice()
|
||||
&& let Some(slice_type) = is_slice_of_primitives(cx, recv)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
STABLE_SORT_PRIMITIVE,
|
||||
e.span,
|
||||
&format!("used `sort` on primitive type `{}`", slice_type),
|
||||
|diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let recv_snip = snippet_with_context(cx, recv.span, e.span.ctxt(), "..", &mut app).0;
|
||||
diag.span_suggestion(e.span, "try", format!("{}.sort_unstable()", recv_snip), app);
|
||||
diag.note(
|
||||
"an unstable sort typically performs faster without any observable difference for this data type",
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
36
clippy_lints/src/methods/suspicious_to_owned.rs
Normal file
36
clippy_lints/src/methods/suspicious_to_owned.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_diag_trait_item;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::SUSPICIOUS_TO_OWNED;
|
||||
|
||||
pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if is_diag_trait_item(cx, method_def_id, sym::ToOwned);
|
||||
let input_type = cx.typeck_results().expr_ty(expr);
|
||||
if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind();
|
||||
if cx.tcx.is_diagnostic_item(sym::Cow, adt.did());
|
||||
then {
|
||||
let mut app = Applicability::MaybeIncorrect;
|
||||
let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0;
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SUSPICIOUS_TO_OWNED,
|
||||
expr.span,
|
||||
&format!("this `to_owned` call clones the {0} itself and does not cause the {0} contents to become owned", input_type),
|
||||
"consider using, depending on intent",
|
||||
format!("{0}.clone()` or `{0}.into_owned()", recv_snip),
|
||||
app,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{is_expr_diagnostic_item, ty::is_uninit_value_valid_for_ty};
|
||||
use clippy_utils::{is_path_diagnostic_item, ty::is_uninit_value_valid_for_ty};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -12,7 +12,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
|||
if_chain! {
|
||||
if let hir::ExprKind::Call(callee, args) = recv.kind;
|
||||
if args.is_empty();
|
||||
if is_expr_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
|
||||
if is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
|
||||
if !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr));
|
||||
then {
|
||||
span_lint(
|
||||
|
|
|
|||
29
clippy_lints/src/methods/unit_hash.rs
Normal file
29
clippy_lints/src/methods/unit_hash.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::source::snippet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::UNIT_HASH;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
|
||||
if is_trait_method(cx, expr, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
UNIT_HASH,
|
||||
expr.span,
|
||||
"this call to `hash` on the unit type will do nothing",
|
||||
|diag| {
|
||||
diag.span_suggestion(
|
||||
expr.span,
|
||||
"remove the call to `hash` or consider using",
|
||||
format!("0_u8.hash({})", snippet(cx, arg.span, ".."),),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
diag.note("the implementation of `Hash` for `()` is a no-op");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
224
clippy_lints/src/methods/unnecessary_sort_by.rs
Normal file
224
clippy_lints/src/methods/unnecessary_sort_by.rs
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, subst::GenericArgKind};
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::iter;
|
||||
|
||||
use super::UNNECESSARY_SORT_BY;
|
||||
|
||||
enum LintTrigger {
|
||||
Sort(SortDetection),
|
||||
SortByKey(SortByKeyDetection),
|
||||
}
|
||||
|
||||
struct SortDetection {
|
||||
vec_name: String,
|
||||
}
|
||||
|
||||
struct SortByKeyDetection {
|
||||
vec_name: String,
|
||||
closure_arg: String,
|
||||
closure_body: String,
|
||||
reverse: bool,
|
||||
}
|
||||
|
||||
/// Detect if the two expressions are mirrored (identical, except one
|
||||
/// contains a and the other replaces it with b)
|
||||
fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident: &Ident) -> bool {
|
||||
match (&a_expr.kind, &b_expr.kind) {
|
||||
// Two boxes with mirrored contents
|
||||
(ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => {
|
||||
mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
|
||||
},
|
||||
// Two arrays with mirrored contents
|
||||
(ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => {
|
||||
iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
|
||||
},
|
||||
// The two exprs are function calls.
|
||||
// Check to see that the function itself and its arguments are mirrored
|
||||
(ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => {
|
||||
mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
|
||||
&& iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
|
||||
},
|
||||
// The two exprs are method calls.
|
||||
// Check to see that the function is the same and the arguments are mirrored
|
||||
// This is enough because the receiver of the method is listed in the arguments
|
||||
(ExprKind::MethodCall(left_segment, left_args, _), ExprKind::MethodCall(right_segment, right_args, _)) => {
|
||||
left_segment.ident == right_segment.ident
|
||||
&& iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
|
||||
},
|
||||
// Two tuples with mirrored contents
|
||||
(ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => {
|
||||
iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
|
||||
},
|
||||
// Two binary ops, which are the same operation and which have mirrored arguments
|
||||
(ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => {
|
||||
left_op.node == right_op.node
|
||||
&& mirrored_exprs(left_left, a_ident, right_left, b_ident)
|
||||
&& mirrored_exprs(left_right, a_ident, right_right, b_ident)
|
||||
},
|
||||
// Two unary ops, which are the same operation and which have the same argument
|
||||
(ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => {
|
||||
left_op == right_op && mirrored_exprs(left_expr, a_ident, right_expr, b_ident)
|
||||
},
|
||||
// The two exprs are literals of some kind
|
||||
(ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node,
|
||||
(ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(left, a_ident, right, b_ident),
|
||||
(ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => {
|
||||
mirrored_exprs(left_block, a_ident, right_block, b_ident)
|
||||
},
|
||||
(ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => {
|
||||
left_ident.name == right_ident.name && mirrored_exprs(left_expr, a_ident, right_expr, right_ident)
|
||||
},
|
||||
// Two paths: either one is a and the other is b, or they're identical to each other
|
||||
(
|
||||
ExprKind::Path(QPath::Resolved(
|
||||
_,
|
||||
Path {
|
||||
segments: left_segments,
|
||||
..
|
||||
},
|
||||
)),
|
||||
ExprKind::Path(QPath::Resolved(
|
||||
_,
|
||||
Path {
|
||||
segments: right_segments,
|
||||
..
|
||||
},
|
||||
)),
|
||||
) => {
|
||||
(iter::zip(*left_segments, *right_segments).all(|(left, right)| left.ident == right.ident)
|
||||
&& left_segments
|
||||
.iter()
|
||||
.all(|seg| &seg.ident != a_ident && &seg.ident != b_ident))
|
||||
|| (left_segments.len() == 1
|
||||
&& &left_segments[0].ident == a_ident
|
||||
&& right_segments.len() == 1
|
||||
&& &right_segments[0].ident == b_ident)
|
||||
},
|
||||
// Matching expressions, but one or both is borrowed
|
||||
(
|
||||
ExprKind::AddrOf(left_kind, Mutability::Not, left_expr),
|
||||
ExprKind::AddrOf(right_kind, Mutability::Not, right_expr),
|
||||
) => left_kind == right_kind && mirrored_exprs(left_expr, a_ident, right_expr, b_ident),
|
||||
(_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => mirrored_exprs(a_expr, a_ident, right_expr, b_ident),
|
||||
(ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(left_expr, a_ident, b_expr, b_ident),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option<LintTrigger> {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if cx.tcx.type_of(impl_id).is_slice();
|
||||
if let ExprKind::Closure(&Closure { body, .. }) = arg.kind;
|
||||
if let closure_body = cx.tcx.hir().body(body);
|
||||
if let &[
|
||||
Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
|
||||
Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
|
||||
] = &closure_body.params;
|
||||
if let ExprKind::MethodCall(method_path, [left_expr, right_expr], _) = closure_body.value.kind;
|
||||
if method_path.ident.name == sym::cmp;
|
||||
if is_trait_method(cx, &closure_body.value, sym::Ord);
|
||||
then {
|
||||
let (closure_body, closure_arg, reverse) = if mirrored_exprs(
|
||||
left_expr,
|
||||
left_ident,
|
||||
right_expr,
|
||||
right_ident
|
||||
) {
|
||||
(Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false)
|
||||
} else if mirrored_exprs(left_expr, right_ident, right_expr, left_ident) {
|
||||
(Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true)
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
let vec_name = Sugg::hir(cx, recv, "..").to_string();
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Path(QPath::Resolved(_, Path {
|
||||
segments: [PathSegment { ident: left_name, .. }], ..
|
||||
})) = &left_expr.kind;
|
||||
if left_name == left_ident;
|
||||
if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
|
||||
implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
|
||||
});
|
||||
then {
|
||||
return Some(LintTrigger::Sort(SortDetection { vec_name }));
|
||||
}
|
||||
}
|
||||
|
||||
if !expr_borrows(cx, left_expr) {
|
||||
return Some(LintTrigger::SortByKey(SortByKeyDetection {
|
||||
vec_name,
|
||||
closure_arg,
|
||||
closure_body,
|
||||
reverse,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
|
||||
}
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
is_unstable: bool,
|
||||
) {
|
||||
match detect_lint(cx, expr, recv, arg) {
|
||||
Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
"use Vec::sort_by_key here instead",
|
||||
"try",
|
||||
format!(
|
||||
"{}.sort{}_by_key(|{}| {})",
|
||||
trigger.vec_name,
|
||||
if is_unstable { "_unstable" } else { "" },
|
||||
trigger.closure_arg,
|
||||
if trigger.reverse {
|
||||
format!("std::cmp::Reverse({})", trigger.closure_body)
|
||||
} else {
|
||||
trigger.closure_body.to_string()
|
||||
},
|
||||
),
|
||||
if trigger.reverse {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
},
|
||||
),
|
||||
Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_SORT_BY,
|
||||
expr.span,
|
||||
"use Vec::sort here instead",
|
||||
"try",
|
||||
format!(
|
||||
"{}.sort{}()",
|
||||
trigger.vec_name,
|
||||
if is_unstable { "_unstable" } else { "" },
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
),
|
||||
None => {},
|
||||
}
|
||||
}
|
||||
|
|
@ -3,11 +3,10 @@ use super::unnecessary_iter_cloned::{self, is_into_iter};
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::{
|
||||
contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
|
||||
get_associated_type, get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, peel_mid_ty_refs,
|
||||
};
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
|
||||
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -279,7 +278,19 @@ fn check_other_call_arg<'tcx>(
|
|||
&trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
|
||||
call_substs,
|
||||
);
|
||||
implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
|
||||
// if `expr` is a `String` and generic target is [u8], skip
|
||||
// (https://github.com/rust-lang/rust-clippy/issues/9317).
|
||||
if let [subst] = composed_substs[..]
|
||||
&& let GenericArgKind::Type(arg_ty) = subst.unpack()
|
||||
&& arg_ty.is_slice()
|
||||
&& let inner_ty = arg_ty.builtin_index().unwrap()
|
||||
&& let ty::Uint(ty::UintTy::U8) = inner_ty.kind()
|
||||
&& let self_ty = cx.typeck_results().expr_ty(expr).peel_refs()
|
||||
&& is_type_diagnostic_item(cx, self_ty, sym::String) {
|
||||
false
|
||||
} else {
|
||||
implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
|
@ -292,7 +303,7 @@ fn check_other_call_arg<'tcx>(
|
|||
// (https://github.com/rust-lang/rust-clippy/issues/8507).
|
||||
if (n_refs == 0 && !receiver_ty.is_ref())
|
||||
|| trait_predicate.def_id() != as_ref_trait_id
|
||||
|| !contains_ty(fn_sig.output(), input);
|
||||
|| !fn_sig.output().contains(input);
|
||||
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
|
|
@ -360,25 +371,15 @@ fn get_input_traits_and_projections<'tcx>(
|
|||
) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
|
||||
let mut trait_predicates = Vec::new();
|
||||
let mut projection_predicates = Vec::new();
|
||||
for (predicate, _) in cx.tcx.predicates_of(callee_def_id).predicates.iter() {
|
||||
// `substs` should have 1 + n elements. The first is the type on the left hand side of an
|
||||
// `as`. The remaining n are trait parameters.
|
||||
let is_input_substs = |substs: SubstsRef<'tcx>| {
|
||||
if_chain! {
|
||||
if let Some(arg) = substs.iter().next();
|
||||
if let GenericArgKind::Type(arg_ty) = arg.unpack();
|
||||
if arg_ty == input;
|
||||
then { true } else { false }
|
||||
}
|
||||
};
|
||||
for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() {
|
||||
match predicate.kind().skip_binder() {
|
||||
PredicateKind::Trait(trait_predicate) => {
|
||||
if is_input_substs(trait_predicate.trait_ref.substs) {
|
||||
if trait_predicate.trait_ref.self_ty() == input {
|
||||
trait_predicates.push(trait_predicate);
|
||||
}
|
||||
},
|
||||
PredicateKind::Projection(projection_predicate) => {
|
||||
if is_input_substs(projection_predicate.projection_ty.substs) {
|
||||
if projection_predicate.projection_ty.self_ty() == input {
|
||||
projection_predicates.push(projection_predicate);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,18 +7,26 @@ use rustc_span::sym;
|
|||
|
||||
use super::{EXPECT_USED, UNWRAP_USED};
|
||||
|
||||
/// lint use of `unwrap()` for `Option`s and `Result`s
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_unwrap_in_tests: bool) {
|
||||
/// lint use of `unwrap()` or `unwrap_err` for `Result` and `unwrap()` for `Option`.
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
recv: &hir::Expr<'_>,
|
||||
is_err: bool,
|
||||
allow_unwrap_in_tests: bool,
|
||||
) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
|
||||
Some((UNWRAP_USED, "an Option", "None", ""))
|
||||
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
|
||||
Some((UNWRAP_USED, "a Result", "Err", "an "))
|
||||
Some((UNWRAP_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an "))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let method_suffix = if is_err { "_err" } else { "" };
|
||||
|
||||
if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -27,7 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
|||
let help = if is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
|
||||
format!(
|
||||
"if you don't want to handle the `{none_value}` case gracefully, consider \
|
||||
using `expect()` to provide a better panic message"
|
||||
using `expect{method_suffix}()` to provide a better panic message"
|
||||
)
|
||||
} else {
|
||||
format!("if this value is {none_prefix}`{none_value}`, it will panic")
|
||||
|
|
@ -37,7 +45,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
|||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
&format!("used `unwrap()` on `{kind}` value"),
|
||||
&format!("used `unwrap{method_suffix}()` on `{kind}` value"),
|
||||
None,
|
||||
&help,
|
||||
);
|
||||
|
|
|
|||
45
clippy_lints/src/methods/vec_resize_to_zero.rs
Normal file
45
clippy_lints/src/methods/vec_resize_to_zero.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
use super::VEC_RESIZE_TO_ZERO;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
count_arg: &'tcx Expr<'_>,
|
||||
default_arg: &'tcx Expr<'_>,
|
||||
name_span: Span,
|
||||
) {
|
||||
if_chain! {
|
||||
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
|
||||
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Vec);
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = count_arg.kind;
|
||||
if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = default_arg.kind;
|
||||
then {
|
||||
let method_call_span = expr.span.with_lo(name_span.lo());
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
VEC_RESIZE_TO_ZERO,
|
||||
expr.span,
|
||||
"emptying a vector with `resize`",
|
||||
|db| {
|
||||
db.help("the arguments may be inverted...");
|
||||
db.span_suggestion(
|
||||
method_call_span,
|
||||
"...or you can empty the vector with",
|
||||
"clear()".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
clippy_lints/src/methods/verbose_file_reads.rs
Normal file
28
clippy_lints/src/methods/verbose_file_reads.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
use super::VERBOSE_FILE_READS;
|
||||
|
||||
pub(super) const READ_TO_END_MSG: (&str, &str) = ("use of `File::read_to_end`", "consider using `fs::read` instead");
|
||||
pub(super) const READ_TO_STRING_MSG: (&str, &str) = (
|
||||
"use of `File::read_to_string`",
|
||||
"consider using `fs::read_to_string` instead",
|
||||
);
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
(msg, help): (&str, &str),
|
||||
) {
|
||||
if is_trait_method(cx, expr, sym::IoRead)
|
||||
&& matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _)))
|
||||
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File)
|
||||
{
|
||||
span_lint_and_help(cx, VERBOSE_FILE_READS, expr.span, msg, None, help);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue