Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
02e812af4d
2562 changed files with 34500 additions and 15442 deletions
|
|
@ -57,14 +57,13 @@ pub(super) fn check<'tcx>(
|
|||
};
|
||||
|
||||
let suggestion_source = reindent_multiline(
|
||||
format!(
|
||||
&format!(
|
||||
"std::path::Path::new({})
|
||||
.extension()
|
||||
.map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))",
|
||||
recv_source,
|
||||
ext_str.strip_prefix('.').unwrap()
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
true,
|
||||
Some(indent_of(cx, call_span).unwrap_or(0) + 4),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{is_mutable, is_trait_method, path_to_local};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_hir::{Expr, Node, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_span::{Span, sym};
|
||||
|
|
@ -28,14 +28,47 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp
|
|||
// if the resolved method is the same as the provided definition
|
||||
&& fn_def.def_id() == last_def.def_id
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
let mut sugg = vec![(call_span, String::from("next_back()"))];
|
||||
let mut dont_apply = false;
|
||||
// if `self_expr` is a reference, it is mutable because it is used for `.last()`
|
||||
if !(is_mutable(cx, self_expr) || self_type.is_ref()) {
|
||||
if let Some(hir_id) = path_to_local(self_expr)
|
||||
&& let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
|
||||
&& let PatKind::Binding(_, _, ident, _) = pat.kind
|
||||
{
|
||||
sugg.push((ident.span.shrink_to_lo(), String::from("mut ")));
|
||||
} else {
|
||||
// If we can't make the binding mutable, make the suggestion `Unspecified` to prevent it from being
|
||||
// automatically applied, and add a complementary help message.
|
||||
dont_apply = true;
|
||||
}
|
||||
}
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DOUBLE_ENDED_ITERATOR_LAST,
|
||||
call_span,
|
||||
expr.span,
|
||||
"called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator",
|
||||
"try",
|
||||
"next_back()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
|diag| {
|
||||
let expr_ty = cx.typeck_results().expr_ty(expr);
|
||||
let droppable_elements = expr_ty.has_significant_drop(cx.tcx, cx.typing_env());
|
||||
diag.multipart_suggestion(
|
||||
"try",
|
||||
sugg,
|
||||
if dont_apply {
|
||||
Applicability::Unspecified
|
||||
} else if droppable_elements {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
},
|
||||
);
|
||||
if droppable_elements {
|
||||
diag.note("this change will alter drop order which may be undesirable");
|
||||
}
|
||||
if dont_apply {
|
||||
diag.span_note(self_expr.span, "this must be made mutable to use `.next_back()`");
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ use rustc_lint::LateContext;
|
|||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::{Ident, Symbol, sym};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP, RESULT_FILTER_MAP};
|
||||
|
||||
|
|
@ -302,7 +301,7 @@ pub(super) fn check(
|
|||
filter_span.with_hi(expr.span.hi()),
|
||||
"`filter` for `Some` followed by `unwrap`",
|
||||
"consider using `flatten` instead",
|
||||
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, map_span)).into_owned(),
|
||||
reindent_multiline("flatten()", true, indent_of(cx, map_span)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
||||
|
|
@ -316,7 +315,7 @@ pub(super) fn check(
|
|||
filter_span.with_hi(expr.span.hi()),
|
||||
"`filter` for `Ok` followed by `unwrap`",
|
||||
"consider using `flatten` instead",
|
||||
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, map_span)).into_owned(),
|
||||
reindent_multiline("flatten()", true, indent_of(cx, map_span)),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
||||
|
|
|
|||
37
clippy_lints/src/methods/io_other_error.rs
Normal file
37
clippy_lints/src/methods/io_other_error.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{IO_ERROR_OTHER, Msrv};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args: &[Expr<'_>], msrv: &Msrv) {
|
||||
if let [error_kind, error] = args
|
||||
&& !expr.span.from_expansion()
|
||||
&& !error_kind.span.from_expansion()
|
||||
&& clippy_utils::is_expr_path_def_path(cx, path, &clippy_utils::paths::IO_ERROR_NEW)
|
||||
&& clippy_utils::is_expr_path_def_path(
|
||||
cx,
|
||||
clippy_utils::expr_or_init(cx, error_kind),
|
||||
&clippy_utils::paths::IO_ERRORKIND_OTHER,
|
||||
)
|
||||
&& let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind
|
||||
&& msrv.meets(IO_ERROR_OTHER)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
super::IO_OTHER_ERROR,
|
||||
expr.span,
|
||||
"this can be `std::io::Error::other(_)`",
|
||||
|diag| {
|
||||
diag.multipart_suggestion_verbose(
|
||||
"use `std::io::Error::other`",
|
||||
vec![
|
||||
(new_segment.ident.span, "other".to_owned()),
|
||||
(error_kind.span.until(error.span), String::new()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,6 @@ use rustc_hir as hir;
|
|||
use rustc_hir::QPath;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::symbol::{Ident, Symbol, sym};
|
||||
use std::borrow::Cow;
|
||||
|
||||
///
|
||||
/// Returns true if the expression is a method call to `method_name`
|
||||
|
|
@ -181,7 +180,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_arg: &hir
|
|||
filter_span.with_hi(expr.span.hi()),
|
||||
"`filter` for `is_ok` on iterator over `Result`s",
|
||||
"consider using `flatten` instead",
|
||||
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, filter_span)).into_owned(),
|
||||
reindent_multiline("flatten()", true, indent_of(cx, filter_span)),
|
||||
Applicability::HasPlaceholders,
|
||||
),
|
||||
Some(FilterType::IsSome) => span_lint_and_sugg(
|
||||
|
|
@ -190,7 +189,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_arg: &hir
|
|||
filter_span.with_hi(expr.span.hi()),
|
||||
"`filter` for `is_some` on iterator over `Option`",
|
||||
"consider using `flatten` instead",
|
||||
reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, filter_span)).into_owned(),
|
||||
reindent_multiline("flatten()", true, indent_of(cx, filter_span)),
|
||||
Applicability::HasPlaceholders,
|
||||
),
|
||||
}
|
||||
|
|
|
|||
113
clippy_lints/src/methods/manual_contains.rs
Normal file
113
clippy_lints/src/methods/manual_contains.rs
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::peel_hir_pat_refs;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_ast::UnOp;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{BinOpKind, Body, Expr, ExprKind, HirId, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self};
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::MANUAL_CONTAINS;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
|
||||
if !expr.span.from_expansion()
|
||||
// check if `iter().any()` can be replaced with `contains()`
|
||||
&& let ExprKind::Closure(closure) = closure_arg.kind
|
||||
&& let Body{params: [param],value} = cx.tcx.hir_body(closure.body)
|
||||
&& let ExprKind::Binary(op, lhs, rhs) = value.kind
|
||||
&& let (peeled_ref_pat, _) = peel_hir_pat_refs(param.pat)
|
||||
&& let Some((snip,snip_expr)) = can_replace_with_contains(cx, op, lhs, rhs, peeled_ref_pat.hir_id, &mut app)
|
||||
&& let ref_type = cx.typeck_results().expr_ty_adjusted(recv)
|
||||
&& let ty::Ref(_, inner_type, _) = ref_type.kind()
|
||||
&& let ty::Slice(slice_type) = inner_type.kind()
|
||||
&& *slice_type == cx.typeck_results().expr_ty(snip_expr)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_CONTAINS,
|
||||
expr.span,
|
||||
"using `contains()` instead of `iter().any()` is more efficient",
|
||||
"try",
|
||||
format!(
|
||||
"{}.contains({})",
|
||||
snippet_with_applicability(cx, recv.span, "_", &mut app),
|
||||
snip
|
||||
),
|
||||
app,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum EligibleArg {
|
||||
IsClosureArg,
|
||||
ContainsArg(String),
|
||||
}
|
||||
|
||||
fn try_get_eligible_arg<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
closure_arg_id: HirId,
|
||||
applicability: &mut Applicability,
|
||||
) -> Option<(EligibleArg, &'tcx Expr<'tcx>)> {
|
||||
let mut get_snippet = |expr: &Expr<'_>, needs_borrow: bool| {
|
||||
let sugg = Sugg::hir_with_applicability(cx, expr, "_", applicability);
|
||||
EligibleArg::ContainsArg((if needs_borrow { sugg.addr() } else { sugg }).to_string())
|
||||
};
|
||||
|
||||
match expr.kind {
|
||||
ExprKind::Path(QPath::Resolved(_, path)) => {
|
||||
if path.res == Res::Local(closure_arg_id) {
|
||||
Some((EligibleArg::IsClosureArg, expr))
|
||||
} else {
|
||||
Some((get_snippet(expr, true), expr))
|
||||
}
|
||||
},
|
||||
ExprKind::Unary(UnOp::Deref, inner) => {
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = inner.kind {
|
||||
if path.res == Res::Local(closure_arg_id) {
|
||||
Some((EligibleArg::IsClosureArg, expr))
|
||||
} else {
|
||||
Some((get_snippet(inner, false), expr))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if switch_to_eager_eval(cx, expr) {
|
||||
Some((get_snippet(expr, true), expr))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn can_replace_with_contains<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
bin_op: Spanned<BinOpKind>,
|
||||
left_expr: &'tcx Expr<'tcx>,
|
||||
right_expr: &'tcx Expr<'tcx>,
|
||||
closure_arg_id: HirId,
|
||||
applicability: &mut Applicability,
|
||||
) -> Option<(String, &'tcx Expr<'tcx>)> {
|
||||
if bin_op.node != BinOpKind::Eq {
|
||||
return None;
|
||||
}
|
||||
|
||||
let left_candidate = try_get_eligible_arg(cx, left_expr, closure_arg_id, applicability)?;
|
||||
let right_candidate = try_get_eligible_arg(cx, right_expr, closure_arg_id, applicability)?;
|
||||
match (left_candidate, right_candidate) {
|
||||
((EligibleArg::IsClosureArg, _), (EligibleArg::ContainsArg(snip), candidate_expr))
|
||||
| ((EligibleArg::ContainsArg(snip), candidate_expr), (EligibleArg::IsClosureArg, _)) => {
|
||||
Some((snip, candidate_expr))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(
|
|||
&& let Some(err_arg_snippet) = err_arg.span.get_source_text(cx)
|
||||
&& let Some(indent) = indent_of(cx, expr.span)
|
||||
{
|
||||
let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.as_str().into(), true, Some(indent + 4));
|
||||
let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.as_str(), true, Some(indent + 4));
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_OK_OR,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ mod implicit_clone;
|
|||
mod inefficient_to_string;
|
||||
mod inspect_for_each;
|
||||
mod into_iter_on_ref;
|
||||
mod io_other_error;
|
||||
mod is_digit_ascii_radix;
|
||||
mod is_empty;
|
||||
mod iter_cloned_collect;
|
||||
|
|
@ -54,6 +55,7 @@ mod iter_with_drain;
|
|||
mod iterator_step_by_zero;
|
||||
mod join_absolute_paths;
|
||||
mod manual_c_str_literals;
|
||||
mod manual_contains;
|
||||
mod manual_inspect;
|
||||
mod manual_is_variant_and;
|
||||
mod manual_next_back;
|
||||
|
|
@ -82,7 +84,6 @@ mod ok_expect;
|
|||
mod open_options;
|
||||
mod option_as_ref_cloned;
|
||||
mod option_as_ref_deref;
|
||||
mod option_map_or_err_ok;
|
||||
mod option_map_or_none;
|
||||
mod option_map_unwrap_or;
|
||||
mod or_fun_call;
|
||||
|
|
@ -114,6 +115,7 @@ mod suspicious_map;
|
|||
mod suspicious_splitn;
|
||||
mod suspicious_to_owned;
|
||||
mod type_id_on_box;
|
||||
mod unbuffered_bytes;
|
||||
mod uninit_assumed_init;
|
||||
mod unit_hash;
|
||||
mod unnecessary_fallible_conversions;
|
||||
|
|
@ -2642,7 +2644,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub MANUAL_OK_OR,
|
||||
pedantic,
|
||||
style,
|
||||
"finds patterns that can be encoded more concisely with `Option::ok_or`"
|
||||
}
|
||||
|
||||
|
|
@ -3784,31 +3786,6 @@ declare_clippy_lint! {
|
|||
"calls to `Path::join` which will overwrite the original path"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `_.map_or(Err(_), Ok)`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readability, this can be written more concisely as
|
||||
/// `_.ok_or(_)`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let opt = Some(1);
|
||||
/// opt.map_or(Err("error"), Ok);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let opt = Some(1);
|
||||
/// opt.ok_or("error");
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
pub OPTION_MAP_OR_ERR_OK,
|
||||
style,
|
||||
"using `Option.map_or(Err(_), Ok)`, which is more succinctly expressed as `Option.ok_or(_)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for iterators of `Result`s using `.filter(Result::is_ok).map(Result::unwrap)` that may
|
||||
|
|
@ -4433,11 +4410,88 @@ declare_clippy_lint! {
|
|||
"using `Option::and_then` or `Result::and_then` to chain a computation that returns an `Option` or a `Result`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calls to `Read::bytes` on types which don't implement `BufRead`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The default implementation calls `read` for each byte, which can be very inefficient for data that’s not in memory, such as `File`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// use std::io::Read;
|
||||
/// use std::fs::File;
|
||||
/// let file = File::open("./bytes.txt").unwrap();
|
||||
/// file.bytes();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// use std::io::{BufReader, Read};
|
||||
/// use std::fs::File;
|
||||
/// let file = BufReader::new(std::fs::File::open("./bytes.txt").unwrap());
|
||||
/// file.bytes();
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub UNBUFFERED_BYTES,
|
||||
perf,
|
||||
"calling .bytes() is very inefficient when data is not in memory"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `iter().any()` on slices when it can be replaced with `contains()` and suggests doing so.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `contains()` is more concise and idiomatic, sometimes more fast.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn foo(values: &[u8]) -> bool {
|
||||
/// values.iter().any(|&v| v == 10)
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn foo(values: &[u8]) -> bool {
|
||||
/// values.contains(&10)
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub MANUAL_CONTAINS,
|
||||
perf,
|
||||
"unnecessary `iter().any()` on slices that can be replaced with `contains()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// This lint warns on calling `io::Error::new(..)` with a kind of
|
||||
/// `io::ErrorKind::Other`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Since Rust 1.74, there's the `io::Error::other(_)` shortcut.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// use std::io;
|
||||
/// let _ = io::Error::new(io::ErrorKind::Other, "bad".to_string());
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let _ = std::io::Error::other("bad".to_string());
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub IO_OTHER_ERROR,
|
||||
style,
|
||||
"calling `std::io::Error::new(std::io::ErrorKind::Other, _)`"
|
||||
}
|
||||
|
||||
#[expect(clippy::struct_excessive_bools)]
|
||||
pub struct Methods {
|
||||
avoid_breaking_exported_api: bool,
|
||||
msrv: Msrv,
|
||||
allow_expect_in_tests: bool,
|
||||
allow_unwrap_in_tests: bool,
|
||||
allow_expect_in_consts: bool,
|
||||
allow_unwrap_in_consts: bool,
|
||||
allowed_dotfiles: FxHashSet<&'static str>,
|
||||
format_args: FormatArgsStorage,
|
||||
}
|
||||
|
|
@ -4452,6 +4506,8 @@ impl Methods {
|
|||
msrv: conf.msrv.clone(),
|
||||
allow_expect_in_tests: conf.allow_expect_in_tests,
|
||||
allow_unwrap_in_tests: conf.allow_unwrap_in_tests,
|
||||
allow_expect_in_consts: conf.allow_expect_in_consts,
|
||||
allow_unwrap_in_consts: conf.allow_unwrap_in_consts,
|
||||
allowed_dotfiles,
|
||||
format_args,
|
||||
}
|
||||
|
|
@ -4580,7 +4636,6 @@ impl_lint_pass!(Methods => [
|
|||
WAKER_CLONE_WAKE,
|
||||
UNNECESSARY_FALLIBLE_CONVERSIONS,
|
||||
JOIN_ABSOLUTE_PATHS,
|
||||
OPTION_MAP_OR_ERR_OK,
|
||||
RESULT_FILTER_MAP,
|
||||
ITER_FILTER_IS_SOME,
|
||||
ITER_FILTER_IS_OK,
|
||||
|
|
@ -4603,6 +4658,9 @@ impl_lint_pass!(Methods => [
|
|||
MANUAL_REPEAT_N,
|
||||
SLICED_STRING_AS_BYTES,
|
||||
RETURN_AND_THEN,
|
||||
UNBUFFERED_BYTES,
|
||||
MANUAL_CONTAINS,
|
||||
IO_OTHER_ERROR,
|
||||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
|
|
@ -4632,6 +4690,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
unnecessary_fallible_conversions::check_function(cx, expr, func);
|
||||
manual_c_str_literals::check(cx, expr, func, args, &self.msrv);
|
||||
useless_nonzero_new_unchecked::check(cx, expr, func, args, &self.msrv);
|
||||
io_other_error::check(cx, expr, func, args, &self.msrv);
|
||||
},
|
||||
ExprKind::MethodCall(method_call, receiver, args, _) => {
|
||||
let method_span = method_call.ident.span;
|
||||
|
|
@ -4860,6 +4919,9 @@ impl Methods {
|
|||
Some(("map", _, [map_arg], _, map_call_span)) => {
|
||||
map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "any");
|
||||
},
|
||||
Some(("iter", iter_recv, ..)) => {
|
||||
manual_contains::check(cx, expr, iter_recv, arg);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
|
|
@ -4879,6 +4941,7 @@ impl Methods {
|
|||
("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, &self.msrv),
|
||||
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
|
||||
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
|
||||
("bytes", []) => unbuffered_bytes::check(cx, expr, recv),
|
||||
("cloned", []) => {
|
||||
cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv);
|
||||
option_as_ref_cloned::check(cx, recv, span);
|
||||
|
|
@ -4946,6 +5009,7 @@ impl Methods {
|
|||
expr,
|
||||
recv,
|
||||
false,
|
||||
self.allow_expect_in_consts,
|
||||
self.allow_expect_in_tests,
|
||||
unwrap_expect_used::Variant::Expect,
|
||||
),
|
||||
|
|
@ -4959,6 +5023,7 @@ impl Methods {
|
|||
expr,
|
||||
recv,
|
||||
true,
|
||||
self.allow_expect_in_consts,
|
||||
self.allow_expect_in_tests,
|
||||
unwrap_expect_used::Variant::Expect,
|
||||
);
|
||||
|
|
@ -5147,7 +5212,6 @@ impl Methods {
|
|||
("map_or", [def, map]) => {
|
||||
option_map_or_none::check(cx, expr, recv, def, map);
|
||||
manual_ok_or::check(cx, expr, recv, def, map);
|
||||
option_map_or_err_ok::check(cx, expr, recv, def, map);
|
||||
unnecessary_map_or::check(cx, expr, recv, def, map, span, &self.msrv);
|
||||
},
|
||||
("map_or_else", [def, map]) => {
|
||||
|
|
@ -5333,6 +5397,7 @@ impl Methods {
|
|||
expr,
|
||||
recv,
|
||||
false,
|
||||
self.allow_unwrap_in_consts,
|
||||
self.allow_unwrap_in_tests,
|
||||
unwrap_expect_used::Variant::Unwrap,
|
||||
);
|
||||
|
|
@ -5344,6 +5409,7 @@ impl Methods {
|
|||
expr,
|
||||
recv,
|
||||
true,
|
||||
self.allow_unwrap_in_consts,
|
||||
self.allow_unwrap_in_tests,
|
||||
unwrap_expect_used::Variant::Unwrap,
|
||||
);
|
||||
|
|
@ -5357,7 +5423,7 @@ impl Methods {
|
|||
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, &self.msrv);
|
||||
},
|
||||
Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => {
|
||||
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method);
|
||||
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method, "unwrap_or");
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
|
@ -5376,6 +5442,9 @@ impl Methods {
|
|||
match method_call(recv) {
|
||||
Some(("map", recv, [map_arg], _, _))
|
||||
if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {},
|
||||
Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => {
|
||||
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method, "unwrap_or_else");
|
||||
},
|
||||
_ => {
|
||||
unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use super::OBFUSCATED_IF_ELSE;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -15,6 +16,7 @@ pub(super) fn check<'tcx>(
|
|||
then_arg: &'tcx hir::Expr<'_>,
|
||||
unwrap_arg: &'tcx hir::Expr<'_>,
|
||||
then_method_name: &str,
|
||||
unwrap_method_name: &str,
|
||||
) {
|
||||
let recv_ty = cx.typeck_results().expr_ty(then_recv);
|
||||
|
||||
|
|
@ -31,16 +33,40 @@ pub(super) fn check<'tcx>(
|
|||
snippet_with_applicability(cx, body.value.span, "..", &mut applicability)
|
||||
},
|
||||
"then_some" => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability),
|
||||
_ => String::new().into(),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// FIXME: Add `unwrap_or_else` symbol
|
||||
let els = match unwrap_method_name {
|
||||
"unwrap_or" => snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability),
|
||||
"unwrap_or_else" if let ExprKind::Closure(closure) = unwrap_arg.kind => {
|
||||
let body = cx.tcx.hir_body(closure.body);
|
||||
snippet_with_applicability(cx, body.value.span, "..", &mut applicability)
|
||||
},
|
||||
"unwrap_or_else" if let ExprKind::Path(_) = unwrap_arg.kind => {
|
||||
snippet_with_applicability(cx, unwrap_arg.span, "_", &mut applicability) + "()"
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let sugg = format!(
|
||||
"if {} {{ {} }} else {{ {} }}",
|
||||
Sugg::hir_with_applicability(cx, then_recv, "..", &mut applicability),
|
||||
if_then,
|
||||
snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability)
|
||||
els
|
||||
);
|
||||
|
||||
// To be parsed as an expression, the `if { … } else { … }` as the left operand of a binary operator
|
||||
// requires parentheses.
|
||||
let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::Binary(_, left, _) = parent_expr.kind
|
||||
&& left.hir_id == expr.hir_id
|
||||
{
|
||||
format!("({sugg})")
|
||||
} else {
|
||||
sugg
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
OBFUSCATED_IF_ELSE,
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_res_lang_ctor, path_res};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{ResultErr, ResultOk};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use super::OPTION_MAP_OR_ERR_OK;
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
recv: &'tcx Expr<'_>,
|
||||
or_expr: &'tcx Expr<'_>,
|
||||
map_expr: &'tcx Expr<'_>,
|
||||
) {
|
||||
// We check that it's called on an `Option` type.
|
||||
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option)
|
||||
// We check that first we pass an `Err`.
|
||||
&& let ExprKind::Call(call, &[arg]) = or_expr.kind
|
||||
&& is_res_lang_ctor(cx, path_res(cx, call), ResultErr)
|
||||
// And finally we check that it is mapped as `Ok`.
|
||||
&& is_res_lang_ctor(cx, path_res(cx, map_expr), ResultOk)
|
||||
{
|
||||
let msg = "called `map_or(Err(_), Ok)` on an `Option` value";
|
||||
let self_snippet = snippet(cx, recv.span, "..");
|
||||
let err_snippet = snippet(cx, arg.span, "..");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
OPTION_MAP_OR_ERR_OK,
|
||||
expr.span,
|
||||
msg,
|
||||
"consider using `ok_or`",
|
||||
format!("{self_snippet}.ok_or({err_snippet})"),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -104,7 +104,7 @@ pub(super) fn check<'tcx>(
|
|||
if (is_new(fun) && output_type_implements_default(fun))
|
||||
|| match call_expr {
|
||||
Some(call_expr) => is_default_equivalent(cx, call_expr),
|
||||
None => is_default_equivalent_call(cx, fun) || closure_body_returns_empty_to_string(cx, fun),
|
||||
None => is_default_equivalent_call(cx, fun, None) || closure_body_returns_empty_to_string(cx, fun),
|
||||
}
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
|
|||
"calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
|
||||
"try",
|
||||
format!("\"{}\"", pushed_path_lit.trim_start_matches(['/', '\\'])),
|
||||
Applicability::MachineApplicable,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{SpanlessEq, higher, is_integer_const, is_trait_method};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
|
@ -20,14 +21,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'
|
|||
&& let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind
|
||||
&& SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments)
|
||||
{
|
||||
span_lint(
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
expr.span,
|
||||
format!(
|
||||
"it is more idiomatic to use `{}.iter().enumerate()`",
|
||||
snippet(cx, recv.span, "_")
|
||||
),
|
||||
"using `.zip()` with a range and `.len()`",
|
||||
"try",
|
||||
format!("{}.iter().enumerate()", snippet(cx, recv.span, "_")),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
use std::ops::ControlFlow;
|
||||
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::paths::STDIN;
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::for_each_local_use_after_expr;
|
||||
use clippy_utils::{get_parent_expr, match_def_path};
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
|
|
@ -34,7 +33,7 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool {
|
|||
|
||||
pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
|
||||
if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
|
||||
&& match_def_path(cx, recv_adt.did(), &STDIN)
|
||||
&& cx.tcx.is_diagnostic_item(sym::Stdin, recv_adt.did())
|
||||
&& let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind
|
||||
&& let Res::Local(local_id) = path.res
|
||||
{
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ pub(super) fn check<'tcx>(
|
|||
"let {} = {}?;\n{}",
|
||||
arg_snip,
|
||||
recv_snip,
|
||||
reindent_multiline(inner.into(), false, indent_of(cx, expr.span))
|
||||
reindent_multiline(inner, false, indent_of(cx, expr.span))
|
||||
);
|
||||
|
||||
span_lint_and_sugg(cx, RETURN_AND_THEN, expr.span, msg, "try", sugg, applicability);
|
||||
|
|
|
|||
25
clippy_lints/src/methods/unbuffered_bytes.rs
Normal file
25
clippy_lints/src/methods/unbuffered_bytes.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
use super::UNBUFFERED_BYTES;
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::is_trait_method;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
|
||||
// Lint if the `.bytes()` call is from the `Read` trait and the implementor is not buffered.
|
||||
if is_trait_method(cx, expr, sym::IoRead)
|
||||
&& let Some(buf_read) = cx.tcx.get_diagnostic_item(sym::IoBufRead)
|
||||
&& let ty = cx.typeck_results().expr_ty_adjusted(recv)
|
||||
&& !implements_trait(cx, ty, buf_read, &[])
|
||||
{
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
UNBUFFERED_BYTES,
|
||||
expr.span,
|
||||
"calling .bytes() is very inefficient when data is not in memory",
|
||||
None,
|
||||
"consider using `BufReader`",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -98,10 +98,7 @@ pub(super) fn check(
|
|||
]),
|
||||
("None", "unwrap_or_else", _) => match args[0].kind {
|
||||
hir::ExprKind::Closure(hir::Closure { body, .. }) => Some(vec![
|
||||
(
|
||||
expr.span.with_hi(cx.tcx.hir_body(*body).value.span.lo()),
|
||||
String::new(),
|
||||
),
|
||||
(expr.span.with_hi(cx.tcx.hir_body(*body).value.span.lo()), String::new()),
|
||||
(expr.span.with_lo(args[0].span.hi()), String::new()),
|
||||
]),
|
||||
_ => None,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::sugg::{Sugg, make_binop};
|
||||
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
|
||||
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait, is_copy};
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{get_parent_expr, is_from_proc_macro, path_to_local_id};
|
||||
use rustc_ast::LitKind::Bool;
|
||||
|
|
@ -81,9 +81,11 @@ pub(super) fn check<'a>(
|
|||
&& (path_to_local_id(l, hir_id) ^ path_to_local_id(r, hir_id))
|
||||
&& !is_local_used(cx, non_binding_location, hir_id)
|
||||
&& let typeck_results = cx.typeck_results()
|
||||
&& typeck_results.expr_ty(l) == typeck_results.expr_ty(r)
|
||||
&& let l_ty = typeck_results.expr_ty(l)
|
||||
&& l_ty == typeck_results.expr_ty(r)
|
||||
&& let Some(partial_eq) = cx.tcx.get_diagnostic_item(sym::PartialEq)
|
||||
&& implements_trait(cx, recv_ty, partial_eq, &[recv_ty.into()])
|
||||
&& is_copy(cx, l_ty)
|
||||
{
|
||||
let wrap = variant.variant_name();
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use clippy_utils::{is_trait_method, std_or_core};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, GenericArgKind};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::iter;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::{is_never_like, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_in_test, is_lint_allowed};
|
||||
use clippy_utils::{is_in_test, is_inside_always_const_context, is_lint_allowed};
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::{LateContext, Lint};
|
||||
use rustc_middle::ty;
|
||||
|
|
@ -39,6 +39,7 @@ pub(super) fn check(
|
|||
expr: &Expr<'_>,
|
||||
recv: &Expr<'_>,
|
||||
is_err: bool,
|
||||
allow_unwrap_in_consts: bool,
|
||||
allow_unwrap_in_tests: bool,
|
||||
variant: Variant,
|
||||
) {
|
||||
|
|
@ -65,6 +66,10 @@ pub(super) fn check(
|
|||
return;
|
||||
}
|
||||
|
||||
if allow_unwrap_in_consts && is_inside_always_const_context(cx.tcx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
variant.lint(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{should_call_clone_as_function, walk_ptrs_ty_depth};
|
||||
use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth};
|
||||
use clippy_utils::{
|
||||
get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, peel_blocks, strip_pat_refs,
|
||||
};
|
||||
|
|
@ -55,12 +55,19 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str,
|
|||
let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty);
|
||||
let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty);
|
||||
if base_rcv_ty == base_res_ty && rcv_depth >= res_depth {
|
||||
// allow the `as_ref` or `as_mut` if it is followed by another method call
|
||||
if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let hir::ExprKind::MethodCall(segment, ..) = parent.kind
|
||||
&& segment.ident.span != expr.span
|
||||
{
|
||||
return;
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
// allow the `as_ref` or `as_mut` if it is followed by another method call
|
||||
if let hir::ExprKind::MethodCall(segment, ..) = parent.kind
|
||||
&& segment.ident.span != expr.span
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// allow the `as_ref` or `as_mut` if they belong to a closure that changes
|
||||
// the number of references
|
||||
if matches!(parent.kind, hir::ExprKind::Closure(..)) && rcv_depth != res_depth {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
|
@ -94,6 +101,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str,
|
|||
&& is_calling_clone(cx, arg)
|
||||
// And that we are not recommending recv.clone() over Arc::clone() or similar
|
||||
&& !should_call_clone_as_function(cx, rcv_ty)
|
||||
// https://github.com/rust-lang/rust-clippy/issues/12357
|
||||
&& let Some(clone_trait) = cx.tcx.lang_items().clone_trait()
|
||||
&& implements_trait(cx, cx.typeck_results().expr_ty(recvr), clone_trait, &[])
|
||||
{
|
||||
lint_as_ref_clone(cx, expr.span.with_hi(parent.span.hi()), recvr, call_name);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue