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

This commit is contained in:
Philipp Krones 2024-10-03 14:42:56 +02:00
commit d300cdfcda
No known key found for this signature in database
GPG key ID: 1CA0DF2AF59D68A5
190 changed files with 2531 additions and 865 deletions

View file

@ -1,3 +1,5 @@
use clippy_config::Conf;
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::eq_expr_value;
use clippy_utils::source::SpanRangeExt;
@ -7,7 +9,7 @@ use rustc_errors::Applicability;
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
use rustc_lint::{LateContext, LateLintPass, Level};
use rustc_session::declare_lint_pass;
use rustc_session::{RustcVersion, impl_lint_pass};
use rustc_span::def_id::LocalDefId;
use rustc_span::{Span, sym};
@ -69,9 +71,25 @@ declare_clippy_lint! {
}
// For each pairs, both orders are considered.
const METHODS_WITH_NEGATION: [(&str, &str); 2] = [("is_some", "is_none"), ("is_err", "is_ok")];
const METHODS_WITH_NEGATION: [(Option<RustcVersion>, &str, &str); 3] = [
(None, "is_some", "is_none"),
(None, "is_err", "is_ok"),
(Some(msrvs::IS_NONE_OR), "is_some_and", "is_none_or"),
];
declare_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]);
pub struct NonminimalBool {
msrv: Msrv,
}
impl NonminimalBool {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv.clone(),
}
}
}
impl_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]);
impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
fn check_fn(
@ -83,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
_: Span,
_: LocalDefId,
) {
NonminimalBoolVisitor { cx }.visit_body(body);
NonminimalBoolVisitor { cx, msrv: &self.msrv }.visit_body(body);
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
@ -100,6 +118,8 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
_ => {},
}
}
extract_msrv_attr!(LateContext);
}
fn inverted_bin_op_eq_str(op: BinOpKind) -> Option<&'static str> {
@ -176,11 +196,11 @@ fn check_inverted_bool_in_condition(
);
}
fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) {
fn check_simplify_not(cx: &LateContext<'_>, msrv: &Msrv, expr: &Expr<'_>) {
if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
&& !expr.span.from_expansion()
&& !inner.span.from_expansion()
&& let Some(suggestion) = simplify_not(cx, inner)
&& let Some(suggestion) = simplify_not(cx, msrv, inner)
&& cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
{
span_lint_and_sugg(
@ -197,6 +217,7 @@ fn check_simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) {
struct NonminimalBoolVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
msrv: &'a Msrv,
}
use quine_mc_cluskey::Bool;
@ -205,7 +226,7 @@ struct Hir2Qmm<'a, 'tcx, 'v> {
cx: &'a LateContext<'tcx>,
}
impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
impl<'v> Hir2Qmm<'_, '_, 'v> {
fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec<Bool>) -> Result<Vec<Bool>, String> {
for a in a {
if let ExprKind::Binary(binop, lhs, rhs) = &a.kind {
@ -289,10 +310,11 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
struct SuggestContext<'a, 'tcx, 'v> {
terminals: &'v [&'v Expr<'v>],
cx: &'a LateContext<'tcx>,
msrv: &'a Msrv,
output: String,
}
impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
impl SuggestContext<'_, '_, '_> {
fn recurse(&mut self, suggestion: &Bool) -> Option<()> {
use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
match suggestion {
@ -311,7 +333,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
},
Term(n) => {
let terminal = self.terminals[n as usize];
if let Some(str) = simplify_not(self.cx, terminal) {
if let Some(str) = simplify_not(self.cx, self.msrv, terminal) {
self.output.push_str(&str);
} else {
self.output.push('!');
@ -358,7 +380,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
}
}
fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
fn simplify_not(cx: &LateContext<'_>, curr_msrv: &Msrv, expr: &Expr<'_>) -> Option<String> {
match &expr.kind {
ExprKind::Binary(binop, lhs, rhs) => {
if !implements_ord(cx, lhs) {
@ -389,7 +411,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
Some(format!("{lhs_snippet}{op}{rhs_snippet}"))
})
},
ExprKind::MethodCall(path, receiver, [], _) => {
ExprKind::MethodCall(path, receiver, args, _) => {
let type_of_receiver = cx.typeck_results().expr_ty(receiver);
if !is_type_diagnostic_item(cx, type_of_receiver, sym::Option)
&& !is_type_diagnostic_item(cx, type_of_receiver, sym::Result)
@ -399,21 +421,41 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
METHODS_WITH_NEGATION
.iter()
.copied()
.flat_map(|(a, b)| vec![(a, b), (b, a)])
.find(|&(a, _)| {
let path: &str = path.ident.name.as_str();
a == path
.flat_map(|(msrv, a, b)| vec![(msrv, a, b), (msrv, b, a)])
.find(|&(msrv, a, _)| msrv.is_none_or(|msrv| curr_msrv.meets(msrv)) && a == path.ident.name.as_str())
.and_then(|(_, _, neg_method)| {
let negated_args = args
.iter()
.map(|arg| simplify_not(cx, curr_msrv, arg))
.collect::<Option<Vec<_>>>()?
.join(", ");
Some(format!(
"{}.{neg_method}({negated_args})",
receiver.span.get_source_text(cx)?
))
})
.and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", receiver.span.get_source_text(cx)?)))
},
ExprKind::Closure(closure) => {
let body = cx.tcx.hir().body(closure.body);
let params = body
.params
.iter()
.map(|param| param.span.get_source_text(cx).map(|t| t.to_string()))
.collect::<Option<Vec<_>>>()?
.join(", ");
let negated = simplify_not(cx, curr_msrv, body.value)?;
Some(format!("|{params}| {negated}"))
},
ExprKind::Unary(UnOp::Not, expr) => expr.span.get_source_text(cx).map(|t| t.to_string()),
_ => None,
}
}
fn suggest(cx: &LateContext<'_>, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String {
fn suggest(cx: &LateContext<'_>, msrv: &Msrv, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String {
let mut suggest_context = SuggestContext {
terminals,
cx,
msrv,
output: String::new(),
};
suggest_context.recurse(suggestion);
@ -475,7 +517,7 @@ fn terminal_stats(b: &Bool) -> Stats {
stats
}
impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
impl<'tcx> NonminimalBoolVisitor<'_, 'tcx> {
fn bool_expr(&self, e: &'tcx Expr<'_>) {
let mut h2q = Hir2Qmm {
terminals: Vec::new(),
@ -526,7 +568,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
diag.span_suggestion(
e.span,
"it would look like the following",
suggest(self.cx, suggestion, &h2q.terminals),
suggest(self.cx, self.msrv, suggestion, &h2q.terminals),
// nonminimal_bool can produce minimal but
// not human readable expressions (#3141)
Applicability::Unspecified,
@ -569,12 +611,12 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
}
};
if improvements.is_empty() {
check_simplify_not(self.cx, e);
check_simplify_not(self.cx, self.msrv, e);
} else {
nonminimal_bool_lint(
improvements
.into_iter()
.map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals))
.map(|suggestion| suggest(self.cx, self.msrv, suggestion, &h2q.terminals))
.collect(),
);
}
@ -582,7 +624,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'_, 'tcx> {
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
if !e.span.from_expansion() {
match &e.kind {

View file

@ -91,7 +91,7 @@ fn is_local_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>, ref_expr: &Expr<'_>)
#[derive(Default)]
struct InferVisitor(bool);
impl<'tcx> Visitor<'tcx> for InferVisitor {
impl Visitor<'_> for InferVisitor {
fn visit_ty(&mut self, t: &Ty<'_>) {
self.0 |= matches!(t.kind, TyKind::Infer | TyKind::OpaqueDef(..) | TyKind::TraitObject(..));
if !self.0 {

View file

@ -10,27 +10,27 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, ignore_publish: b
// only run the lint if publish is `None` (`publish = true` or skipped entirely)
// or if the vector isn't empty (`publish = ["something"]`)
if package.publish.as_ref().filter(|publish| publish.is_empty()).is_none() || ignore_publish {
if is_empty_str(&package.description) {
if is_empty_str(package.description.as_ref()) {
missing_warning(cx, package, "package.description");
}
if is_empty_str(&package.license) && is_empty_str(&package.license_file) {
if is_empty_str(package.license.as_ref()) && is_empty_str(package.license_file.as_ref()) {
missing_warning(cx, package, "either package.license or package.license_file");
}
if is_empty_str(&package.repository) {
if is_empty_str(package.repository.as_ref()) {
missing_warning(cx, package, "package.repository");
}
if is_empty_str(&package.readme) {
if is_empty_str(package.readme.as_ref()) {
missing_warning(cx, package, "package.readme");
}
if is_empty_vec(&package.keywords) {
if is_empty_vec(package.keywords.as_ref()) {
missing_warning(cx, package, "package.keywords");
}
if is_empty_vec(&package.categories) {
if is_empty_vec(package.categories.as_ref()) {
missing_warning(cx, package, "package.categories");
}
}
@ -42,8 +42,8 @@ fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, fiel
span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, message);
}
fn is_empty_str<T: AsRef<std::ffi::OsStr>>(value: &Option<T>) -> bool {
value.as_ref().map_or(true, |s| s.as_ref().is_empty())
fn is_empty_str<T: AsRef<std::ffi::OsStr>>(value: Option<&T>) -> bool {
value.map_or(true, |s| s.as_ref().is_empty())
}
fn is_empty_vec(value: &[String]) -> bool {

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_no_std_crate;
use clippy_utils::source::snippet_with_context;
use clippy_utils::std_or_core;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
use rustc_lint::LateContext;
@ -16,8 +16,8 @@ pub(super) fn check<'tcx>(
) {
if matches!(cast_to.kind, TyKind::Ptr(_))
&& let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind
&& let Some(std_or_core) = std_or_core(cx)
{
let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
let macro_name = match mutability {
Mutability::Not => "addr_of",
Mutability::Mut => "addr_of_mut",
@ -40,7 +40,7 @@ pub(super) fn check<'tcx>(
expr.span,
"borrow as raw pointer",
"try",
format!("{core_or_std}::ptr::{macro_name}!({snip})"),
format!("{std_or_core}::ptr::{macro_name}!({snip})"),
Applicability::MachineApplicable,
);
}

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::{ExprUseNode, expr_use_ctxt, is_no_std_crate};
use clippy_utils::{ExprUseNode, expr_use_ctxt, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability, Ty, TyKind};
use rustc_lint::LateContext;
@ -25,8 +25,8 @@ pub(super) fn check<'tcx>(
&& let use_cx = expr_use_ctxt(cx, expr)
// TODO: only block the lint if `cast_expr` is a temporary
&& !matches!(use_cx.use_node(cx), ExprUseNode::LetStmt(_) | ExprUseNode::ConstStatic(_))
&& let Some(std_or_core) = std_or_core(cx)
{
let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
let fn_name = match to_mutbl {
Mutability::Not => "from_ref",
Mutability::Mut => "from_mut",
@ -56,7 +56,7 @@ pub(super) fn check<'tcx>(
expr.span,
"reference as raw pointer",
"try",
format!("{core_or_std}::ptr::{fn_name}{turbofish}({cast_expr_sugg})"),
format!("{std_or_core}::ptr::{fn_name}{turbofish}({cast_expr_sugg})"),
app,
);
}

View file

@ -48,7 +48,7 @@ impl CheckedConversions {
impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
impl LateLintPass<'_> for CheckedConversions {
fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
if let ExprKind::Binary(op, lhs, rhs) = item.kind
&& let (lt1, gt1, op2) = match op.node {

View file

@ -202,6 +202,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::functions::MUST_USE_CANDIDATE_INFO,
crate::functions::MUST_USE_UNIT_INFO,
crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO,
crate::functions::REF_OPTION_INFO,
crate::functions::RENAMED_FUNCTION_PARAMS_INFO,
crate::functions::RESULT_LARGE_ERR_INFO,
crate::functions::RESULT_UNIT_ERR_INFO,

View file

@ -119,7 +119,7 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
match &expr.kind {
ExprKind::Block(

View file

@ -385,7 +385,7 @@ fn check_unsafe_derive_deserialize<'tcx>(
&& cx
.tcx
.inherent_impls(def.did())
.into_iter()
.iter()
.map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
.any(|imp| has_unsafe(cx, imp))
{

View file

@ -970,7 +970,7 @@ impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {

View file

@ -60,7 +60,7 @@ declare_clippy_lint! {
declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]);
impl<'tcx> LateLintPass<'tcx> for EmptyEnum {
impl LateLintPass<'_> for EmptyEnum {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if let ItemKind::Enum(..) = item.kind
// Only suggest the `never_type` if the feature is enabled

View file

@ -141,7 +141,7 @@ fn is_argument(tcx: TyCtxt<'_>, id: HirId) -> bool {
matches!(tcx.parent_hir_node(id), Node::Param(_))
}
impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
impl<'tcx> Delegate<'tcx> for EscapeDelegate<'_, 'tcx> {
fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
if cmt.place.projections.is_empty() {
if let PlaceBase::Local(lid) = cmt.place.base {
@ -188,7 +188,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
}
impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
impl<'tcx> EscapeDelegate<'_, 'tcx> {
fn is_large_box(&self, ty: Ty<'tcx>) -> bool {
// Large types need to be boxed to avoid stack overflows.
if let Some(boxed_ty) = ty.boxed_ty() {

View file

@ -135,7 +135,7 @@ impl NestingVisitor<'_, '_> {
}
}
impl<'conf, 'cx> Visitor<'_> for NestingVisitor<'conf, 'cx> {
impl Visitor<'_> for NestingVisitor<'_, '_> {
fn visit_block(&mut self, block: &Block) {
if block.span.from_expansion() {
return;

View file

@ -193,7 +193,7 @@ fn bound_to_trait_def_id(bound: &GenericBound<'_>) -> Option<LocalDefId> {
bound.trait_ref()?.trait_def_id()?.as_local()
}
impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
impl<'tcx> Visitor<'tcx> for TypeWalker<'_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) {

View file

@ -73,7 +73,7 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl
result: Vec<Span>,
}
impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for FindPanicUnwrap<'_, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) {
if is_panic(self.lcx, macro_call.def_id) {

View file

@ -219,7 +219,7 @@ struct FormatArgsExpr<'a, 'tcx> {
ignore_mixed: bool,
}
impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
impl FormatArgsExpr<'_, '_> {
fn check_templates(&self) {
for piece in &self.format_args.template {
if let FormatArgsPiece::Placeholder(placeholder) = piece

View file

@ -148,7 +148,7 @@ struct FormatImplExpr<'a, 'tcx> {
format_trait_impl: FormatTraitNames,
}
impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> {
impl FormatImplExpr<'_, '_> {
fn check_to_string_in_display(&self) {
if self.format_trait_impl.name == sym::Display
&& let ExprKind::MethodCall(path, self_arg, ..) = self.expr.kind

View file

@ -126,7 +126,7 @@ struct SelfFinder<'a, 'tcx> {
invalid: bool,
}
impl<'a, 'tcx> Visitor<'tcx> for SelfFinder<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for SelfFinder<'_, 'tcx> {
type NestedFilter = OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {
@ -181,6 +181,9 @@ fn convert_to_from(
let from = self_ty.span.get_source_text(cx)?;
let into = target_ty.span.get_source_text(cx)?;
let return_type = matches!(sig.decl.output, FnRetTy::Return(_))
.then_some(String::from("Self"))
.unwrap_or_default();
let mut suggestions = vec![
// impl Into<T> for U -> impl From<T> for U
// ~~~~ ~~~~
@ -197,13 +200,10 @@ fn convert_to_from(
// fn into([mut] self) -> T -> fn into([mut] v: T) -> T
// ~~~~ ~~~~
(self_ident.span, format!("val: {from}")),
];
if let FnRetTy::Return(_) = sig.decl.output {
// fn into(self) -> T -> fn into(self) -> Self
// ~ ~~~~
suggestions.push((sig.decl.output.span(), String::from("Self")));
}
(sig.decl.output.span(), return_type),
];
let mut finder = SelfFinder {
cx,

View file

@ -2,6 +2,7 @@ mod impl_trait_in_params;
mod misnamed_getters;
mod must_use;
mod not_unsafe_ptr_arg_deref;
mod ref_option;
mod renamed_function_params;
mod result;
mod too_many_arguments;
@ -399,6 +400,53 @@ declare_clippy_lint! {
"renamed function parameters in trait implementation"
}
declare_clippy_lint! {
/// ### What it does
/// Warns when a function signature uses `&Option<T>` instead of `Option<&T>`.
///
/// ### Why is this bad?
/// More flexibility, better memory optimization, and more idiomatic Rust code.
///
/// `&Option<T>` in a function signature breaks encapsulation because the caller must own T
/// and move it into an Option to call with it. When returned, the owner must internally store
/// it as `Option<T>` in order to return it.
/// At a lower level, `&Option<T>` points to memory with the `presence` bit flag plus the `T` value,
/// whereas `Option<&T>` is usually [optimized](https://doc.rust-lang.org/1.81.0/std/option/index.html#representation)
/// to a single pointer, so it may be more optimal.
///
/// See this [YouTube video](https://www.youtube.com/watch?v=6c7pZYP_iIE) by
/// Logan Smith for an in-depth explanation of why this is important.
///
/// ### Known problems
/// This lint recommends changing the function signatures, but it cannot
/// automatically change the function calls or the function implementations.
///
/// ### Example
/// ```no_run
/// // caller uses foo(&opt)
/// fn foo(a: &Option<String>) {}
/// # struct Unit {}
/// # impl Unit {
/// fn bar(&self) -> &Option<String> { &None }
/// # }
/// ```
/// Use instead:
/// ```no_run
/// // caller should use `foo1(opt.as_ref())`
/// fn foo1(a: Option<&String>) {}
/// // better yet, use string slice `foo2(opt.as_deref())`
/// fn foo2(a: Option<&str>) {}
/// # struct Unit {}
/// # impl Unit {
/// fn bar(&self) -> Option<&String> { None }
/// # }
/// ```
#[clippy::version = "1.82.0"]
pub REF_OPTION,
pedantic,
"function signature uses `&Option<T>` instead of `Option<&T>`"
}
pub struct Functions {
too_many_arguments_threshold: u64,
too_many_lines_threshold: u64,
@ -437,6 +485,7 @@ impl_lint_pass!(Functions => [
MISNAMED_GETTERS,
IMPL_TRAIT_IN_PARAMS,
RENAMED_FUNCTION_PARAMS,
REF_OPTION,
]);
impl<'tcx> LateLintPass<'tcx> for Functions {
@ -455,6 +504,16 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, def_id);
misnamed_getters::check_fn(cx, kind, decl, body, span);
impl_trait_in_params::check_fn(cx, &kind, body, hir_id);
ref_option::check_fn(
cx,
kind,
decl,
span,
hir_id,
def_id,
body,
self.avoid_breaking_exported_api,
);
}
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
@ -475,5 +534,6 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
must_use::check_trait_item(cx, item);
result::check_trait_item(cx, item, self.large_error_threshold);
impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api);
ref_option::check_trait_item(cx, item, self.avoid_breaking_exported_api);
}
}

View file

@ -0,0 +1,122 @@
use crate::functions::REF_OPTION;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_trait_impl_item;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{FnDecl, HirId};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, GenericArgKind, Mutability, Ty};
use rustc_span::def_id::LocalDefId;
use rustc_span::{Span, sym};
fn check_ty<'a>(cx: &LateContext<'a>, param: &rustc_hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) {
if let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind()
&& is_type_diagnostic_item(cx, *opt_ty, sym::Option)
&& let ty::Adt(_, opt_gen) = opt_ty.kind()
&& let [gen] = opt_gen.as_slice()
&& let GenericArgKind::Type(gen_ty) = gen.unpack()
&& !gen_ty.is_ref()
// Need to gen the original spans, so first parsing mid, and hir parsing afterward
&& let hir::TyKind::Ref(lifetime, hir::MutTy { ty, .. }) = param.kind
&& let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
&& let (Some(first), Some(last)) = (path.segments.first(), path.segments.last())
&& let Some(hir::GenericArgs {
args: [hir::GenericArg::Type(opt_ty)],
..
}) = last.args
{
let lifetime = snippet(cx, lifetime.ident.span, "..");
fixes.push((
param.span,
format!(
"{}<&{lifetime}{}{}>",
snippet(cx, first.ident.span.to(last.ident.span), ".."),
if lifetime.is_empty() { "" } else { " " },
snippet(cx, opt_ty.span, "..")
),
));
}
}
fn check_fn_sig<'a>(cx: &LateContext<'a>, decl: &FnDecl<'a>, span: Span, sig: ty::FnSig<'a>) {
let mut fixes = Vec::new();
// Check function arguments' types
for (param, param_ty) in decl.inputs.iter().zip(sig.inputs()) {
check_ty(cx, param, *param_ty, &mut fixes);
}
// Check return type
if let hir::FnRetTy::Return(ty) = &decl.output {
check_ty(cx, ty, sig.output(), &mut fixes);
}
if !fixes.is_empty() {
span_lint_and_then(
cx,
REF_OPTION,
span,
"it is more idiomatic to use `Option<&T>` instead of `&Option<T>`",
|diag| {
diag.multipart_suggestion("change this to", fixes, Applicability::Unspecified);
},
);
}
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn check_fn<'a>(
cx: &LateContext<'a>,
kind: FnKind<'_>,
decl: &FnDecl<'a>,
span: Span,
hir_id: HirId,
def_id: LocalDefId,
body: &hir::Body<'_>,
avoid_breaking_exported_api: bool,
) {
if avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) {
return;
}
if let FnKind::Closure = kind {
// Compute the span of the closure parameters + return type if set
let span = if let hir::FnRetTy::Return(out_ty) = &decl.output {
if decl.inputs.is_empty() {
out_ty.span
} else {
span.with_hi(out_ty.span.hi())
}
} else if let (Some(first), Some(last)) = (decl.inputs.first(), decl.inputs.last()) {
first.span.to(last.span)
} else {
// No parameters - no point in checking
return;
};
// Figure out the signature of the closure
let ty::Closure(_, args) = cx.typeck_results().expr_ty(body.value).kind() else {
return;
};
let sig = args.as_closure().sig().skip_binder();
check_fn_sig(cx, decl, span, sig);
} else if !is_trait_impl_item(cx, hir_id) {
let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
check_fn_sig(cx, decl, span, sig);
}
}
pub(super) fn check_trait_item<'a>(
cx: &LateContext<'a>,
trait_item: &hir::TraitItem<'a>,
avoid_breaking_exported_api: bool,
) {
if let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind
&& !(avoid_breaking_exported_api && cx.effective_visibilities.is_exported(trait_item.owner_id.def_id))
{
let def_id = trait_item.owner_id.def_id;
let ty_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
check_fn_sig(cx, sig.decl, sig.span, ty_sig);
}
}

View file

@ -281,7 +281,7 @@ impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'_, 'tcx> {
fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) {
if let Some(target) = ImplicitHasherType::new(self.cx, t) {
self.found.push(target);
@ -318,7 +318,7 @@ impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
}
}
impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn visit_body(&mut self, body: &Body<'tcx>) {

View file

@ -226,7 +226,7 @@ struct SliceIndexLintingVisitor<'a, 'tcx> {
max_suggested_slice: u64,
}
impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {

View file

@ -460,7 +460,7 @@ fn check_for_is_empty(
let is_empty = cx
.tcx
.inherent_impls(impl_ty)
.into_iter()
.iter()
.flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(is_empty))
.find(|item| item.kind == AssocKind::Fn);
@ -628,7 +628,7 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
/// Checks the inherent impl's items for an `is_empty(self)` method.
fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool {
let is_empty = sym!(is_empty);
cx.tcx.inherent_impls(id).into_iter().any(|imp| {
cx.tcx.inherent_impls(id).iter().any(|imp| {
cx.tcx
.associated_items(*imp)
.filter_by_name_unhygienic(is_empty)

View file

@ -609,7 +609,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(move |tcx| Box::new(await_holding_invalid::AwaitHolding::new(tcx, conf)));
store.register_late_pass(|_| Box::new(serde_api::SerdeApi));
store.register_late_pass(move |_| Box::new(types::Types::new(conf)));
store.register_late_pass(|_| Box::new(booleans::NonminimalBool));
store.register_late_pass(move |_| Box::new(booleans::NonminimalBool::new(conf)));
store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant));
store.register_late_pass(|_| Box::new(float_literal::FloatLiteral));
store.register_late_pass(|_| Box::new(ptr::Ptr));

View file

@ -6,12 +6,12 @@ use rustc_errors::Applicability;
use rustc_hir::FnRetTy::Return;
use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
use rustc_hir::intravisit::{
Visitor, walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
walk_poly_trait_ref, walk_trait_ref, walk_ty,
Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_where_predicate,
};
use rustc_hir::{
BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl,
ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef,
BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics,
Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef,
PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, lang_items,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -21,7 +21,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_session::declare_lint_pass;
use rustc_span::Span;
use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::{Ident, Symbol, kw};
use rustc_span::symbol::{Ident, kw};
use std::ops::ControlFlow;
declare_clippy_lint! {
@ -191,45 +191,10 @@ fn check_fn_inner<'tcx>(
if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) {
return;
}
let lts = elidable_lts
.iter()
// In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a
// `Node::GenericParam`.
.filter_map(|&def_id| cx.tcx.hir_node_by_def_id(def_id).ident())
.map(|ident| ident.to_string())
.collect::<Vec<_>>()
.join(", ");
span_lint_and_then(
cx,
NEEDLESS_LIFETIMES,
elidable_lts
.iter()
.map(|&lt| cx.tcx.def_span(lt))
.chain(usages.iter().filter_map(|usage| {
if let LifetimeName::Param(def_id) = usage.res
&& elidable_lts.contains(&def_id)
{
return Some(usage.ident.span);
}
None
}))
.collect_vec(),
format!("the following explicit lifetimes could be elided: {lts}"),
|diag| {
if sig.header.is_async() {
// async functions have usages whose spans point at the lifetime declaration which messes up
// suggestions
return;
};
if let Some(suggestions) = elision_suggestions(cx, generics, &elidable_lts, &usages) {
diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable);
}
},
);
// async functions have usages whose spans point at the lifetime declaration which messes up
// suggestions
let include_suggestions = !sig.header.is_async();
report_elidable_lifetimes(cx, generics, &elidable_lts, &usages, include_suggestions);
}
if report_extra_lifetimes {
@ -237,97 +202,6 @@ fn check_fn_inner<'tcx>(
}
}
fn elision_suggestions(
cx: &LateContext<'_>,
generics: &Generics<'_>,
elidable_lts: &[LocalDefId],
usages: &[Lifetime],
) -> Option<Vec<(Span, String)>> {
let explicit_params = generics
.params
.iter()
.filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait())
.collect::<Vec<_>>();
let mut suggestions = if elidable_lts.len() == explicit_params.len() {
// if all the params are elided remove the whole generic block
//
// fn x<'a>() {}
// ^^^^
vec![(generics.span, String::new())]
} else {
elidable_lts
.iter()
.map(|&id| {
let pos = explicit_params.iter().position(|param| param.def_id == id)?;
let param = explicit_params.get(pos)?;
let span = if let Some(next) = explicit_params.get(pos + 1) {
// fn x<'prev, 'a, 'next>() {}
// ^^^^
param.span.until(next.span)
} else {
// `pos` should be at least 1 here, because the param in position 0 would either have a `next`
// param or would have taken the `elidable_lts.len() == explicit_params.len()` branch.
let prev = explicit_params.get(pos - 1)?;
// fn x<'prev, 'a>() {}
// ^^^^
param.span.with_lo(prev.span.hi())
};
Some((span, String::new()))
})
.collect::<Option<Vec<_>>>()?
};
suggestions.extend(
usages
.iter()
.filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id)))
.map(|usage| {
match cx.tcx.parent_hir_node(usage.hir_id) {
Node::Ty(Ty {
kind: TyKind::Ref(..), ..
}) => {
// expand `&'a T` to `&'a T`
// ^^ ^^^
let span = cx.sess().source_map().span_extend_while_whitespace(usage.ident.span);
(span, String::new())
},
// `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
_ => (usage.ident.span, String::from("'_")),
}
}),
);
Some(suggestions)
}
// elision doesn't work for explicit self types, see rust-lang/rust#69064
fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
if let Some(ident) = ident
&& ident.name == kw::SelfLower
&& !func.implicit_self.has_implicit_self()
&& let Some(self_ty) = func.inputs.first()
{
let mut visitor = RefVisitor::new(cx);
visitor.visit_ty(self_ty);
!visitor.all_lts().is_empty()
} else {
false
}
}
fn named_lifetime(lt: &Lifetime) -> Option<LocalDefId> {
match lt.res {
LifetimeName::Param(id) if !lt.is_anonymous() => Some(id),
_ => None,
}
}
fn could_use_elision<'tcx>(
cx: &LateContext<'tcx>,
func: &'tcx FnDecl<'_>,
@ -450,6 +324,22 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<LocalDefId
.collect()
}
// elision doesn't work for explicit self types, see rust-lang/rust#69064
fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
if let Some(ident) = ident
&& ident.name == kw::SelfLower
&& !func.implicit_self.has_implicit_self()
&& let Some(self_ty) = func.inputs.first()
{
let mut visitor = RefVisitor::new(cx);
visitor.visit_ty(self_ty);
!visitor.all_lts().is_empty()
} else {
false
}
}
/// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve
/// relative order.
#[must_use]
@ -470,6 +360,13 @@ fn named_lifetime_occurrences(lts: &[Lifetime]) -> Vec<(LocalDefId, usize)> {
occurrences
}
fn named_lifetime(lt: &Lifetime) -> Option<LocalDefId> {
match lt.res {
LifetimeName::Param(id) if !lt.is_anonymous() => Some(id),
_ => None,
}
}
struct RefVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
lts: Vec<Lifetime>,
@ -500,7 +397,7 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> {
// for lifetimes as parameters of generics
fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
self.lts.push(*lifetime);
@ -594,23 +491,43 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
false
}
struct Usage {
lifetime: Lifetime,
in_where_predicate: bool,
in_generics_arg: bool,
}
struct LifetimeChecker<'cx, 'tcx, F> {
cx: &'cx LateContext<'tcx>,
map: FxHashMap<Symbol, Span>,
map: FxHashMap<LocalDefId, Vec<Usage>>,
where_predicate_depth: usize,
generic_args_depth: usize,
phantom: std::marker::PhantomData<F>,
}
impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> {
fn new(cx: &'cx LateContext<'tcx>, map: FxHashMap<Symbol, Span>) -> LifetimeChecker<'cx, 'tcx, F> {
fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'_>) -> LifetimeChecker<'cx, 'tcx, F> {
let map = generics
.params
.iter()
.filter_map(|par| match par.kind {
GenericParamKind::Lifetime {
kind: LifetimeParamKind::Explicit,
} => Some((par.def_id, Vec::new())),
_ => None,
})
.collect();
Self {
cx,
map,
where_predicate_depth: 0,
generic_args_depth: 0,
phantom: std::marker::PhantomData,
}
}
}
impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F>
impl<'tcx, F> Visitor<'tcx> for LifetimeChecker<'_, 'tcx, F>
where
F: NestedFilter<'tcx>,
{
@ -619,18 +536,27 @@ where
// for lifetimes as parameters of generics
fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
self.map.remove(&lifetime.ident.name);
if let LifetimeName::Param(def_id) = lifetime.res
&& let Some(usages) = self.map.get_mut(&def_id)
{
usages.push(Usage {
lifetime: *lifetime,
in_where_predicate: self.where_predicate_depth != 0,
in_generics_arg: self.generic_args_depth != 0,
});
}
}
fn visit_generic_param(&mut self, param: &'tcx GenericParam<'_>) {
// don't actually visit `<'a>` or `<'a: 'b>`
// we've already visited the `'a` declarations and
// don't want to spuriously remove them
// `'b` in `'a: 'b` is useless unless used elsewhere in
// a non-lifetime bound
if let GenericParamKind::Type { .. } = param.kind {
walk_generic_param(self, param);
}
fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) {
self.where_predicate_depth += 1;
walk_where_predicate(self, predicate);
self.where_predicate_depth -= 1;
}
fn visit_generic_args(&mut self, generic_args: &'tcx GenericArgs<'tcx>) -> Self::Result {
self.generic_args_depth += 1;
walk_generic_args(self, generic_args);
self.generic_args_depth -= 1;
}
fn nested_visit_map(&mut self) -> Self::Map {
@ -639,44 +565,28 @@ where
}
fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) {
let hs = generics
.params
.iter()
.filter_map(|par| match par.kind {
GenericParamKind::Lifetime {
kind: LifetimeParamKind::Explicit,
} => Some((par.name.ident().name, par.span)),
_ => None,
})
.collect();
let mut checker = LifetimeChecker::<hir_nested_filter::None>::new(cx, hs);
let mut checker = LifetimeChecker::<hir_nested_filter::None>::new(cx, generics);
walk_generics(&mut checker, generics);
walk_fn_decl(&mut checker, func);
for &v in checker.map.values() {
span_lint(
cx,
EXTRA_UNUSED_LIFETIMES,
v,
"this lifetime isn't used in the function definition",
);
for (def_id, usages) in checker.map {
if usages
.iter()
.all(|usage| usage.in_where_predicate && !usage.in_generics_arg)
{
span_lint(
cx,
EXTRA_UNUSED_LIFETIMES,
cx.tcx.def_span(def_id),
"this lifetime isn't used in the function definition",
);
}
}
}
fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) {
let hs = impl_
.generics
.params
.iter()
.filter_map(|par| match par.kind {
GenericParamKind::Lifetime {
kind: LifetimeParamKind::Explicit,
} => Some((par.name.ident().name, par.span)),
_ => None,
})
.collect();
let mut checker = LifetimeChecker::<middle_nested_filter::All>::new(cx, hs);
let mut checker = LifetimeChecker::<middle_nested_filter::All>::new(cx, impl_.generics);
walk_generics(&mut checker, impl_.generics);
if let Some(ref trait_ref) = impl_.of_trait {
@ -687,9 +597,176 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'
walk_impl_item_ref(&mut checker, item);
}
for &v in checker.map.values() {
span_lint(cx, EXTRA_UNUSED_LIFETIMES, v, "this lifetime isn't used in the impl");
for (&def_id, usages) in &checker.map {
if usages
.iter()
.all(|usage| usage.in_where_predicate && !usage.in_generics_arg)
{
span_lint(
cx,
EXTRA_UNUSED_LIFETIMES,
cx.tcx.def_span(def_id),
"this lifetime isn't used in the impl",
);
}
}
report_elidable_impl_lifetimes(cx, impl_, &checker.map);
}
// An `impl` lifetime is elidable if it satisfies the following conditions:
// - It is used exactly once.
// - That single use is not in `GenericArgs` in a `WherePredicate`. (Note that `GenericArgs` are
// different from `GenericParam`s.)
fn report_elidable_impl_lifetimes<'tcx>(
cx: &LateContext<'tcx>,
impl_: &'tcx Impl<'_>,
map: &FxHashMap<LocalDefId, Vec<Usage>>,
) {
let single_usages = map
.iter()
.filter_map(|(def_id, usages)| {
if let [
Usage {
lifetime,
in_where_predicate: false,
..
}
| Usage {
lifetime,
in_generics_arg: false,
..
},
] = usages.as_slice()
{
Some((def_id, lifetime))
} else {
None
}
})
.collect::<Vec<_>>();
if single_usages.is_empty() {
return;
}
let (elidable_lts, usages): (Vec<_>, Vec<_>) = single_usages.into_iter().unzip();
report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true);
}
/// Generate diagnostic messages for elidable lifetimes.
fn report_elidable_lifetimes(
cx: &LateContext<'_>,
generics: &Generics<'_>,
elidable_lts: &[LocalDefId],
usages: &[Lifetime],
include_suggestions: bool,
) {
let lts = elidable_lts
.iter()
// In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a
// `Node::GenericParam`.
.filter_map(|&def_id| cx.tcx.hir_node_by_def_id(def_id).ident())
.map(|ident| ident.to_string())
.collect::<Vec<_>>()
.join(", ");
span_lint_and_then(
cx,
NEEDLESS_LIFETIMES,
elidable_lts
.iter()
.map(|&lt| cx.tcx.def_span(lt))
.chain(usages.iter().filter_map(|usage| {
if let LifetimeName::Param(def_id) = usage.res
&& elidable_lts.contains(&def_id)
{
return Some(usage.ident.span);
}
None
}))
.collect_vec(),
format!("the following explicit lifetimes could be elided: {lts}"),
|diag| {
if !include_suggestions {
return;
};
if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) {
diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable);
}
},
);
}
fn elision_suggestions(
cx: &LateContext<'_>,
generics: &Generics<'_>,
elidable_lts: &[LocalDefId],
usages: &[Lifetime],
) -> Option<Vec<(Span, String)>> {
let explicit_params = generics
.params
.iter()
.filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait())
.collect::<Vec<_>>();
let mut suggestions = if elidable_lts.len() == explicit_params.len() {
// if all the params are elided remove the whole generic block
//
// fn x<'a>() {}
// ^^^^
vec![(generics.span, String::new())]
} else {
elidable_lts
.iter()
.map(|&id| {
let pos = explicit_params.iter().position(|param| param.def_id == id)?;
let param = explicit_params.get(pos)?;
let span = if let Some(next) = explicit_params.get(pos + 1) {
// fn x<'prev, 'a, 'next>() {}
// ^^^^
param.span.until(next.span)
} else {
// `pos` should be at least 1 here, because the param in position 0 would either have a `next`
// param or would have taken the `elidable_lts.len() == explicit_params.len()` branch.
let prev = explicit_params.get(pos - 1)?;
// fn x<'prev, 'a>() {}
// ^^^^
param.span.with_lo(prev.span.hi())
};
Some((span, String::new()))
})
.collect::<Option<Vec<_>>>()?
};
suggestions.extend(
usages
.iter()
.filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id)))
.map(|usage| {
match cx.tcx.parent_hir_node(usage.hir_id) {
Node::Ty(Ty {
kind: TyKind::Ref(..), ..
}) => {
// expand `&'a T` to `&'a T`
// ^^ ^^^
let span = cx.sess().source_map().span_extend_while_whitespace(usage.ident.span);
(span, String::new())
},
// `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
_ => (usage.ident.span, String::from("'_")),
}
}),
);
Some(suggestions)
}
struct BodyLifetimeChecker;

View file

@ -209,7 +209,7 @@ fn build_manual_memcpy_suggestion<'tcx>(
#[derive(Clone)]
struct MinifyingSugg<'a>(Sugg<'a>);
impl<'a> Display for MinifyingSugg<'a> {
impl Display for MinifyingSugg<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}

View file

@ -1,6 +1,6 @@
use super::MISSING_SPIN_LOOP;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_no_std_crate;
use clippy_utils::std_or_core;
use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind};
use rustc_lint::LateContext;
@ -41,6 +41,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'
&& [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name)
&& let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind()
&& cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did())
&& let Some(std_or_core) = std_or_core(cx)
{
span_lint_and_sugg(
cx,
@ -48,12 +49,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'
body.span,
"busy-waiting loop should at least have a spin loop hint",
"try",
(if is_no_std_crate(cx) {
"{ core::hint::spin_loop() }"
} else {
"{ std::hint::spin_loop() }"
})
.into(),
format!("{{ {std_or_core}::hint::spin_loop() }}"),
Applicability::MachineApplicable,
);
}

View file

@ -241,7 +241,7 @@ struct VarVisitor<'a, 'tcx> {
prefer_mutable: bool,
}
impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
impl<'tcx> VarVisitor<'_, 'tcx> {
fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
if let ExprKind::Path(ref seqpath) = seqexpr.kind
// the indexed container is referenced by a name
@ -292,7 +292,7 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind
// a range index op

View file

@ -123,7 +123,7 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for SameItemPushVisitor<'_, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
match &expr.kind {
// Non-determinism may occur ... don't give a lint

View file

@ -44,7 +44,7 @@ impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
// If node is a variable
if let Some(def_id) = path_to_local(expr) {
@ -138,7 +138,7 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for InitializeVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn visit_local(&mut self, l: &'tcx LetStmt<'_>) {

View file

@ -84,7 +84,7 @@ struct VarCollectorVisitor<'a, 'tcx> {
skip: bool,
}
impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
impl<'tcx> VarCollectorVisitor<'_, 'tcx> {
fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) {
if let ExprKind::Path(ref qpath) = ex.kind
&& let QPath::Resolved(None, _) = *qpath
@ -103,7 +103,7 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for VarCollectorVisitor<'_, 'tcx> {
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
match ex.kind {
ExprKind::Path(_) => self.insert_def_id(ex),

View file

@ -283,7 +283,7 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &
found_local: bool,
used_after: bool,
}
impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
impl<'tcx> Visitor<'tcx> for NestedLoopVisitor<'_, '_, 'tcx> {
type NestedFilter = OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {
self.cx.tcx.hir()

View file

@ -149,7 +149,7 @@ fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
&& !cx.tcx.is_doc_hidden(def_id)
}
impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for BodyVisitor<'_, 'tcx> {
fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) {
let from_expn = s.span.from_expansion();
if from_expn {

View file

@ -81,7 +81,7 @@ impl MacroUseImports {
}
}
impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
impl LateLintPass<'_> for MacroUseImports {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
if cx.sess().opts.edition >= Edition::Edition2018
&& let hir::ItemKind::Use(path, _kind) = &item.kind

View file

@ -741,7 +741,7 @@ enum MaybeBorrowedStmtKind<'a> {
Owned(StmtKind<'a>),
}
impl<'a> Clone for MaybeBorrowedStmtKind<'a> {
impl Clone for MaybeBorrowedStmtKind<'_> {
fn clone(&self) -> Self {
match self {
Self::Borrowed(t) => Self::Borrowed(t),

View file

@ -203,7 +203,7 @@ fn find_stripping<'tcx>(
results: Vec<Span>,
}
impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for StrippingFinder<'_, 'tcx> {
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
if is_ref_str(self.cx, ex)
&& let unref = peel_ref(ex)

View file

@ -251,7 +251,7 @@ fn lint_map_unit_fn(
}
}
impl<'tcx> LateLintPass<'tcx> for MapUnit {
impl LateLintPass<'_> for MapUnit {
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) {
if let hir::StmtKind::Semi(expr) = stmt.kind
&& !stmt.span.from_expansion()

View file

@ -40,7 +40,7 @@ struct MatchExprVisitor<'a, 'tcx> {
case_method: Option<CaseMethod>,
}
impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for MatchExprVisitor<'_, 'tcx> {
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
match ex.kind {
ExprKind::MethodCall(segment, receiver, [], _) if self.case_altered(segment.ident.as_str(), receiver) => {},
@ -49,7 +49,7 @@ impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx> MatchExprVisitor<'a, 'tcx> {
impl MatchExprVisitor<'_, '_> {
fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> bool {
if let Some(case_method) = get_case_method(segment_ident) {
let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs();

View file

@ -1045,7 +1045,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
if !from_expansion {
// These don't depend on a relationship between multiple arms
match_wild_err_arm::check(cx, ex, arms);
wild_in_or_pats::check(cx, arms);
wild_in_or_pats::check(cx, ex, arms);
}
if let MatchSource::TryDesugar(_) = source {

View file

@ -96,13 +96,13 @@ where
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct RangeBound<'a, T>(T, BoundKind, &'a SpannedRange<T>);
impl<'a, T: Copy + Ord> PartialOrd for RangeBound<'a, T> {
impl<T: Copy + Ord> PartialOrd for RangeBound<'_, T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'a, T: Copy + Ord> Ord for RangeBound<'a, T> {
impl<T: Copy + Ord> Ord for RangeBound<'_, T> {
fn cmp(&self, RangeBound(other_value, other_kind, _): &Self) -> Ordering {
let RangeBound(self_value, self_kind, _) = *self;
(self_value, self_kind).cmp(&(*other_value, *other_kind))

View file

@ -424,7 +424,7 @@ fn ty_has_erased_regions(ty: Ty<'_>) -> bool {
ty.visit_with(&mut V).is_break()
}
impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for SigDropHelper<'_, 'tcx> {
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
// We've emitted a lint on some neighborhood expression. That lint will suggest to move out the
// _parent_ expression (not the expression itself). Since we decide to move out the parent
@ -495,7 +495,7 @@ fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &[&'tcx Expr
helper.found_sig_drop_spans
}
impl<'a, 'tcx> Visitor<'tcx> for ArmSigDropHelper<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for ArmSigDropHelper<'_, 'tcx> {
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
if self.sig_drop_checker.is_sig_drop_expr(ex) {
self.found_sig_drop_spans.insert(ex.span);

View file

@ -1,11 +1,19 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_wild;
use rustc_hir::{Arm, PatKind};
use clippy_utils::{has_non_exhaustive_attr, is_wild};
use rustc_hir::{Arm, Expr, PatKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
use super::WILDCARD_IN_OR_PATTERNS;
pub(crate) fn check(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arms: &[Arm<'_>]) {
// first check if we are matching on an enum that has the non_exhaustive attribute
let ty = cx.typeck_results().expr_ty(expr).peel_refs();
if let ty::Adt(adt_def, _) = ty.kind()
&& has_non_exhaustive_attr(cx.tcx, *adt_def)
{
return;
};
for arm in arms {
if let PatKind::Or(fields) = arm.pat.kind {
// look for multiple fields in this arm that contains at least one Wild pattern

View file

@ -1,17 +1,17 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::expr_custom_deref_adjustment;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::{Span, sym};
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 matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut))
&& let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind()
&& let (_, ref_depth, Mutability::Mut) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv))
&& ref_depth >= 1
&& let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id)
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
&& is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex)

View file

@ -441,7 +441,7 @@ struct UsedCountVisitor<'a, 'tcx> {
count: usize,
}
impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for UsedCountVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {

View file

@ -130,7 +130,7 @@ struct UnwrapVisitor<'a, 'tcx> {
identifiers: FxHashSet<HirId>,
}
impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for UnwrapVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::All;
fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) {
@ -154,7 +154,7 @@ struct ReferenceVisitor<'a, 'tcx> {
unwrap_or_span: Span,
}
impl<'a, 'tcx> Visitor<'tcx> for ReferenceVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::All;
type Result = ControlFlow<()>;
fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'_>) -> ControlFlow<()> {

View file

@ -77,7 +77,7 @@ pub(super) fn check<'tcx>(
let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| {
cx.tcx
.inherent_impls(adt_def.did())
.into_iter()
.iter()
.flat_map(|impl_id| cx.tcx.associated_items(impl_id).filter_by_name_unhygienic(sugg))
.find_map(|assoc| {
if assoc.fn_has_self_parameter

View file

@ -5,7 +5,8 @@ use clippy_utils::ty::implements_trait;
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

@ -86,7 +86,7 @@ struct CloneOrCopyVisitor<'cx, 'tcx> {
references_to_binding: Vec<(Span, String)>,
}
impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
impl<'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn nested_visit_map(&mut self) -> Self::Map {
@ -123,7 +123,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
}
}
impl<'cx, 'tcx> CloneOrCopyVisitor<'cx, 'tcx> {
impl<'tcx> CloneOrCopyVisitor<'_, 'tcx> {
fn is_binding(&self, expr: &Expr<'tcx>) -> bool {
self.binding_hir_ids
.iter()

View file

@ -116,7 +116,7 @@ struct DivergenceVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
}
impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
impl<'tcx> DivergenceVisitor<'_, 'tcx> {
fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) {
match e.kind {
ExprKind::Closure(..) | ExprKind::If(..) | ExprKind::Loop(..) => {},
@ -148,7 +148,7 @@ fn stmt_might_diverge(stmt: &Stmt<'_>) -> bool {
!matches!(stmt.kind, StmtKind::Item(..))
}
impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for DivergenceVisitor<'_, 'tcx> {
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
match e.kind {
// fix #10776
@ -321,7 +321,7 @@ struct ReadVisitor<'a, 'tcx> {
last_expr: &'tcx Expr<'tcx>,
}
impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for ReadVisitor<'_, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
if expr.hir_id == self.last_expr.hir_id {
return;

View file

@ -55,7 +55,7 @@ pub struct MutVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
}
impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
if in_external_macro(self.cx.sess(), expr.span) {
return;

View file

@ -87,7 +87,7 @@ impl<'a, 'tcx> MutArgVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
@ -96,10 +96,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
self.found = true;
return;
},
ExprKind::If(..) => {
self.found = true;
return;
},
ExprKind::Path(_) => {
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
if adj

View file

@ -133,7 +133,7 @@ struct RetCollector {
loop_depth: u16,
}
impl<'tcx> Visitor<'tcx> for RetCollector {
impl Visitor<'_> for RetCollector {
fn visit_expr(&mut self, expr: &Expr<'_>) {
match expr.kind {
ExprKind::Ret(..) => {

View file

@ -311,7 +311,7 @@ struct MutablyUsedVariablesCtxt<'tcx> {
tcx: TyCtxt<'tcx>,
}
impl<'tcx> MutablyUsedVariablesCtxt<'tcx> {
impl MutablyUsedVariablesCtxt<'_> {
fn add_mutably_used_var(&mut self, used_id: HirId) {
self.mutably_used_vars.insert(used_id);
}

View file

@ -104,7 +104,7 @@ struct SimilarNamesLocalVisitor<'a, 'tcx> {
single_char_names: Vec<Vec<Ident>>,
}
impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
impl SimilarNamesLocalVisitor<'_, '_> {
fn check_single_char_names(&self) {
if self.single_char_names.last().map(Vec::len) == Some(0) {
return;
@ -152,7 +152,7 @@ fn chars_are_similar(a: char, b: char) -> bool {
struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);
impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
impl<'tcx> Visitor<'tcx> for SimilarNamesNameVisitor<'_, 'tcx, '_> {
fn visit_pat(&mut self, pat: &'tcx Pat) {
match pat.kind {
PatKind::Ident(_, ident, _) => {
@ -189,7 +189,7 @@ fn allowed_to_be_similar(interned_name: &str, list: &[&str]) -> bool {
.any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
}
impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
impl SimilarNamesNameVisitor<'_, '_, '_> {
fn check_short_ident(&mut self, ident: Ident) {
// Ignore shadowing
if self
@ -329,7 +329,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
}
}
impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
impl SimilarNamesLocalVisitor<'_, '_> {
/// ensure scoping rules work
fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
let n = self.names.len();
@ -340,7 +340,7 @@ impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'_, 'tcx> {
fn visit_local(&mut self, local: &'tcx Local) {
if let Some((init, els)) = &local.kind.init_else_opt() {
self.apply(|this| walk_expr(this, init));

View file

@ -159,7 +159,7 @@ struct NonSendField<'tcx> {
generic_params: Vec<Ty<'tcx>>,
}
impl<'tcx> NonSendField<'tcx> {
impl NonSendField<'_> {
fn generic_params_string(&self) -> String {
self.generic_params
.iter()

View file

@ -110,7 +110,7 @@ pub struct PassByRefOrValue {
avoid_breaking_exported_api: bool,
}
impl<'tcx> PassByRefOrValue {
impl PassByRefOrValue {
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
let ref_min_size = conf.trivial_copy_size_limit.unwrap_or_else(|| {
let bit_width = u64::from(tcx.sess.target.pointer_width);
@ -130,7 +130,7 @@ impl<'tcx> PassByRefOrValue {
}
}
fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, def_id: LocalDefId, decl: &FnDecl<'_>, span: Option<Span>) {
fn check_poly_fn(&mut self, cx: &LateContext<'_>, def_id: LocalDefId, decl: &FnDecl<'_>, span: Option<Span>) {
if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) {
return;
}

View file

@ -60,7 +60,7 @@ struct PathbufPushSearcher<'tcx> {
err_span: Span,
}
impl<'tcx> PathbufPushSearcher<'tcx> {
impl PathbufPushSearcher<'_> {
/// Try to generate a suggestion with `PathBuf::from`.
/// Returns `None` if the suggestion would be invalid.
fn gen_pathbuf_from(&self, cx: &LateContext<'_>) -> Option<String> {

View file

@ -271,14 +271,18 @@ fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
&& let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id()
&& let Some(name) = cx.tcx.get_diagnostic_name(fun_def_id)
{
// TODO: `ptr_slice_from_raw_parts` and its mutable variant should probably still be linted
// conditionally based on how the return value is used, but not universally like the other
// functions since there are valid uses for null slice pointers.
//
// See: https://github.com/rust-lang/rust-clippy/pull/13452/files#r1773772034
// `arg` positions where null would cause U.B.
let arg_indices: &[_] = match name {
sym::ptr_read
| sym::ptr_read_unaligned
| sym::ptr_read_volatile
| sym::ptr_replace
| sym::ptr_slice_from_raw_parts
| sym::ptr_slice_from_raw_parts_mut
| sym::ptr_write
| sym::ptr_write_bytes
| sym::ptr_write_unaligned

View file

@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
path: &'tcx hir::Path<'tcx>,
count: usize,
}
impl<'a, 'tcx> Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for ClosureUsageCount<'_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {

View file

@ -140,7 +140,7 @@ enum RetReplacement<'tcx> {
Expr(Cow<'tcx, str>, Applicability),
}
impl<'tcx> RetReplacement<'tcx> {
impl RetReplacement<'_> {
fn sugg_help(&self) -> &'static str {
match self {
Self::Empty | Self::Expr(..) => "remove `return`",
@ -158,7 +158,7 @@ impl<'tcx> RetReplacement<'tcx> {
}
}
impl<'tcx> Display for RetReplacement<'tcx> {
impl Display for RetReplacement<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Empty => write!(f, ""),
@ -421,7 +421,7 @@ fn check_final_expr<'tcx>(
if matches!(Level::from_attr(attr), Some(Level::Expect(_)))
&& let metas = attr.meta_item_list()
&& let Some(lst) = metas
&& let [NestedMetaItem::MetaItem(meta_item)] = lst.as_slice()
&& let [NestedMetaItem::MetaItem(meta_item), ..] = lst.as_slice()
&& let [tool, lint_name] = meta_item.path.segments.as_slice()
&& tool.ident.name == sym::clippy
&& matches!(

View file

@ -249,7 +249,7 @@ impl<'ap, 'lc, 'others, 'stmt, 'tcx> StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx
}
}
impl<'ap, 'lc, 'others, 'stmt, 'tcx> Visitor<'tcx> for StmtsChecker<'ap, 'lc, 'others, 'stmt, 'tcx> {
impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> {
fn visit_block(&mut self, block: &'tcx hir::Block<'tcx>) {
self.ap.curr_block_hir_id = block.hir_id;
self.ap.curr_block_span = block.span;

View file

@ -102,7 +102,7 @@ struct ImportUsageVisitor {
imports_referenced_with_self: Vec<Symbol>,
}
impl<'tcx> Visitor<'tcx> for ImportUsageVisitor {
impl Visitor<'_> for ImportUsageVisitor {
fn visit_expr(&mut self, expr: &Expr) {
if let ExprKind::Path(_, path) = &expr.kind
&& path.segments.len() > 1

View file

@ -229,7 +229,7 @@ struct VectorInitializationVisitor<'a, 'tcx> {
initialization_found: bool,
}
impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
impl<'tcx> VectorInitializationVisitor<'_, 'tcx> {
/// Checks if the given expression is extending a vector with `repeat(0).take(..)`
fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) {
if self.initialization_found
@ -299,7 +299,7 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
}
}
impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for VectorInitializationVisitor<'_, 'tcx> {
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
if self.initialization_found {
match stmt.kind {

View file

@ -332,7 +332,7 @@ struct IndexBinding<'a, 'tcx> {
applicability: &'a mut Applicability,
}
impl<'a, 'tcx> IndexBinding<'a, 'tcx> {
impl<'tcx> IndexBinding<'_, 'tcx> {
fn snippet_index_bindings(&mut self, exprs: &[&'tcx Expr<'tcx>]) -> String {
let mut bindings = FxHashSet::default();
for expr in exprs {

View file

@ -275,12 +275,15 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us
|k, ps1, idx| matches!(
k,
TupleStruct(qself2, path2, ps2)
if eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
if eq_maybe_qself(qself1.as_ref(), qself2.as_ref())
&& eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
),
|k| always_pat!(k, TupleStruct(_, _, ps) => ps),
),
// Transform a record pattern `S { fp_0, ..., fp_n }`.
Struct(qself1, path1, fps1, rest1) => extend_with_struct_pat(qself1, path1, fps1, *rest1, start, alternatives),
Struct(qself1, path1, fps1, rest1) => {
extend_with_struct_pat(qself1.as_ref(), path1, fps1, *rest1, start, alternatives)
},
};
alternatives[focus_idx].kind = focus_kind;
@ -292,7 +295,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us
/// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern
/// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal.
fn extend_with_struct_pat(
qself1: &Option<P<ast::QSelf>>,
qself1: Option<&P<ast::QSelf>>,
path1: &ast::Path,
fps1: &mut [ast::PatField],
rest1: ast::PatFieldsRest,
@ -307,7 +310,7 @@ fn extend_with_struct_pat(
|k| {
matches!(k, Struct(qself2, path2, fps2, rest2)
if rest1 == *rest2 // If one struct pattern has `..` so must the other.
&& eq_maybe_qself(qself1, qself2)
&& eq_maybe_qself(qself1, qself2.as_ref())
&& eq_path(path1, path2)
&& fps1.len() == fps2.len()
&& fps1.iter().enumerate().all(|(idx_1, fp1)| {

View file

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::is_def_id_trait_method;
use rustc_hir::def::DefKind;
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn};
use rustc_hir::{Body, Expr, ExprKind, FnDecl, Node, YieldSource};
use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Node, YieldSource};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_session::impl_lint_pass;
@ -67,7 +67,7 @@ struct AsyncFnVisitor<'a, 'tcx> {
async_depth: usize,
}
impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for AsyncFnVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
@ -137,17 +137,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
}
}
fn check_path(&mut self, cx: &LateContext<'tcx>, path: &rustc_hir::Path<'tcx>, hir_id: rustc_hir::HirId) {
fn is_node_func_call(node: Node<'_>, expected_receiver: Span) -> bool {
matches!(
node,
Node::Expr(Expr {
kind: ExprKind::Call(Expr { span, .. }, _) | ExprKind::MethodCall(_, Expr { span, .. }, ..),
..
}) if *span == expected_receiver
)
}
fn check_path(&mut self, cx: &LateContext<'tcx>, path: &rustc_hir::Path<'tcx>, hir_id: HirId) {
// Find paths to local async functions that aren't immediately called.
// E.g. `async fn f() {}; let x = f;`
// Depending on how `x` is used, f's asyncness might be required despite not having any `await`
@ -156,7 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
&& let Some(local_def_id) = def_id.as_local()
&& cx.tcx.def_kind(def_id) == DefKind::Fn
&& cx.tcx.asyncness(def_id).is_async()
&& !is_node_func_call(cx.tcx.parent_hir_node(hir_id), path.span)
&& let parent = cx.tcx.parent_hir_node(hir_id)
&& !matches!(
parent,
Node::Expr(Expr {
kind: ExprKind::Call(Expr { span, .. }, _),
..
}) if *span == path.span
)
{
self.async_fns_as_value.insert(local_def_id);
}

View file

@ -235,7 +235,7 @@ impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> {
fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
}
impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
impl<'tcx> UnwrappableVariablesVisitor<'_, 'tcx> {
fn visit_branch(
&mut self,
if_expr: &'tcx Expr<'_>,
@ -288,7 +288,7 @@ fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Opt
}
}
impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {

View file

@ -280,7 +280,7 @@ struct SkipTyCollector {
types_to_skip: Vec<HirId>,
}
impl<'tcx> Visitor<'tcx> for SkipTyCollector {
impl Visitor<'_> for SkipTyCollector {
fn visit_infer(&mut self, inf: &hir::InferArg) {
self.types_to_skip.push(inf.hir_id);

View file

@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
&& let ty = cx.tcx.type_of(item_def_id).instantiate_identity()
&& match_type(cx, ty, &paths::SYMBOL)
&& let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id)
&& let Ok(value) = value.to_u32()
&& let Some(value) = value.to_u32().discard_err()
{
self.symbol_map.insert(value, item_def_id);
}

View file

@ -72,8 +72,7 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
SimplifiedType::Bool,
]
.iter()
.flat_map(|&ty| cx.tcx.incoherent_impls(ty).into_iter())
.flatten()
.flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter())
.copied();
for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) {
let lang_item_path = cx.get_def_path(item_def_id);

View file

@ -270,7 +270,7 @@ struct LintCollector<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
}
impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for LintCollector<'_, 'tcx> {
type NestedFilter = nested_filter::All;
fn visit_path(&mut self, path: &Path<'_>, _: HirId) {

View file

@ -6,6 +6,7 @@ use rustc_errors::Applicability;
use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_local};
use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_session::declare_lint_pass;
use rustc_span::sym;
use std::ops::ControlFlow;
@ -118,7 +119,8 @@ enum WaitFinder<'a, 'tcx> {
Found(&'a LateContext<'tcx>, HirId),
}
impl<'a, 'tcx> Visitor<'tcx> for WaitFinder<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
type Result = ControlFlow<BreakReason>;
fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) -> Self::Result {
@ -204,6 +206,11 @@ impl<'a, 'tcx> Visitor<'tcx> for WaitFinder<'a, 'tcx> {
walk_expr(self, ex)
}
fn nested_visit_map(&mut self) -> Self::Map {
let (Self::Found(cx, _) | Self::WalkUpTo(cx, _)) = self;
cx.tcx.hir()
}
}
/// This function has shared logic between the different kinds of nodes that can trigger the lint.
@ -300,7 +307,7 @@ struct ExitPointFinder<'a, 'tcx> {
struct ExitCallFound;
impl<'a, 'tcx> Visitor<'tcx> for ExitPointFinder<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for ExitPointFinder<'_, 'tcx> {
type Result = ControlFlow<ExitCallFound>;
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result {