Merge remote-tracking branch 'upstream/master' into rustup

This commit is contained in:
Philipp Krones 2025-02-27 21:40:02 +01:00
commit 02e812af4d
2562 changed files with 34500 additions and 15442 deletions

View file

@ -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),
);

View file

@ -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()`");
}
},
);
}
}

View file

@ -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,
);

View 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,
);
},
);
}
}

View file

@ -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,
),
}

View 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,
}
}

View file

@ -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,

View file

@ -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 thats 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");
},

View file

@ -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,

View file

@ -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,
);
}
}

View file

@ -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(

View file

@ -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,
);
}
}

View file

@ -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,
);
}
}

View file

@ -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
{

View file

@ -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);

View 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`",
);
}
}

View file

@ -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,

View file

@ -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();

View file

@ -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;

View file

@ -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(),

View file

@ -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);
}