Skip to content

Rust 1.81 and later support elision with explicit self types #13992

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
store.register_late_pass(move |_| Box::new(loops::Loops::new(conf)));
store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
store.register_late_pass(move |_| Box::new(lifetimes::Lifetimes::new(conf)));
store.register_late_pass(|_| Box::new(entry::HashMapPass));
store.register_late_pass(|_| Box::new(minmax::MinMaxPass));
store.register_late_pass(|_| Box::new(zero_div_zero::ZeroDiv));
Expand Down
52 changes: 39 additions & 13 deletions clippy_lints/src/lifetimes.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::trait_ref_of_method;
use itertools::Itertools;
use rustc_ast::visit::{try_visit, walk_list};
Expand All @@ -20,7 +22,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::map::Map;
use rustc_middle::hir::nested_filter as middle_nested_filter;
use rustc_middle::lint::in_external_macro;
use rustc_session::declare_lint_pass;
use rustc_session::impl_lint_pass;
use rustc_span::Span;
use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::{Ident, kw};
Expand Down Expand Up @@ -91,7 +93,19 @@ declare_clippy_lint! {
"unused lifetimes in function definitions"
}

declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
pub struct Lifetimes {
msrv: Msrv,
}

impl Lifetimes {
pub fn new(conf: &'static Conf) -> Self {
Self {
msrv: conf.msrv.clone(),
}
}
}

impl_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);

impl<'tcx> LateLintPass<'tcx> for Lifetimes {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
Expand All @@ -102,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
..
} = item.kind
{
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true);
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, &self.msrv);
} else if let ItemKind::Impl(impl_) = item.kind {
if !item.span.from_expansion() {
report_extra_impl_lifetimes(cx, impl_);
Expand All @@ -121,6 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
item.generics,
item.span,
report_extra_lifetimes,
&self.msrv,
);
}
}
Expand All @@ -131,11 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
TraitFn::Required(sig) => (None, Some(sig)),
TraitFn::Provided(id) => (Some(id), None),
};
check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true);
check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true, &self.msrv);
}
}

extract_msrv_attr!(LateContext);
}

#[allow(clippy::too_many_arguments)]
fn check_fn_inner<'tcx>(
cx: &LateContext<'tcx>,
sig: &'tcx FnSig<'_>,
Expand All @@ -144,6 +162,7 @@ fn check_fn_inner<'tcx>(
generics: &'tcx Generics<'_>,
span: Span,
report_extra_lifetimes: bool,
msrv: &Msrv,
) {
if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) {
return;
Expand Down Expand Up @@ -195,7 +214,7 @@ fn check_fn_inner<'tcx>(
}
}

if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) {
if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params, msrv) {
if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) {
return;
}
Expand All @@ -216,6 +235,7 @@ fn could_use_elision<'tcx>(
body: Option<BodyId>,
trait_sig: Option<&[Ident]>,
named_generics: &'tcx [GenericParam<'_>],
msrv: &Msrv,
) -> Option<(Vec<LocalDefId>, Vec<Lifetime>)> {
// There are two scenarios where elision works:
// * no output references, all input references have different LT
Expand Down Expand Up @@ -249,17 +269,17 @@ fn could_use_elision<'tcx>(
let input_lts = input_visitor.lts;
let output_lts = output_visitor.lts;

if let Some(trait_sig) = trait_sig {
if explicit_self_type(cx, func, trait_sig.first().copied()) {
return None;
}
if let Some(trait_sig) = trait_sig
&& non_elidable_self_type(cx, func, trait_sig.first().copied(), msrv)
{
return None;
}

if let Some(body_id) = body {
let body = cx.tcx.hir().body(body_id);

let first_ident = body.params.first().and_then(|param| param.pat.simple_ident());
if explicit_self_type(cx, func, first_ident) {
if non_elidable_self_type(cx, func, first_ident, msrv) {
return None;
}

Expand Down Expand Up @@ -332,9 +352,15 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet<LocalDefI
.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
// elision doesn't work for explicit self types before Rust 1.81, see rust-lang/rust#69064
fn non_elidable_self_type<'tcx>(
cx: &LateContext<'tcx>,
func: &FnDecl<'tcx>,
ident: Option<Ident>,
msrv: &Msrv,
) -> bool {
if !msrv.meets(msrvs::EXPLICIT_SELF_TYPE_ELISION)
&& let Some(ident) = ident
&& ident.name == kw::SelfLower
&& !func.implicit_self.has_implicit_self()
&& let Some(self_ty) = func.inputs.first()
Expand Down
2 changes: 1 addition & 1 deletion clippy_utils/src/msrvs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ macro_rules! msrv_aliases {
msrv_aliases! {
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY }
1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP }
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE }
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION }
1,80,0 { BOX_INTO_ITER }
1,77,0 { C_STR_LITERALS }
1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
Expand Down
28 changes: 23 additions & 5 deletions tests/ui/needless_lifetimes.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
clippy::boxed_local,
clippy::extra_unused_type_parameters,
clippy::needless_pass_by_value,
clippy::redundant_allocation,
clippy::unnecessary_wraps,
dyn_drop,
clippy::get_first
Expand Down Expand Up @@ -443,11 +444,20 @@ mod issue7296 {
fn implicit_mut(&mut self) -> &() {
&()
}

fn explicit<'a>(self: &'a Arc<Self>) -> &'a () {
#[clippy::msrv = "1.81"]
fn explicit(self: &Arc<Self>) -> &() {
&()
}
#[clippy::msrv = "1.81"]
fn explicit_mut(self: &mut Rc<Self>) -> &() {
&()
}
fn explicit_mut<'a>(self: &'a mut Rc<Self>) -> &'a () {
#[clippy::msrv = "1.80"]
fn explicit_older<'a>(self: &'a Arc<Self>) -> &'a () {
&()
}
#[clippy::msrv = "1.80"]
fn explicit_mut_older<'a>(self: &'a mut Rc<Self>) -> &'a () {
&()
}

Expand All @@ -462,8 +472,16 @@ mod issue7296 {
&()
}

fn explicit<'a>(self: &'a Arc<Self>) -> &'a ();
fn explicit_provided<'a>(self: &'a Arc<Self>) -> &'a () {
#[clippy::msrv = "1.81"]
fn explicit(self: &Arc<Self>) -> &();
#[clippy::msrv = "1.81"]
fn explicit_provided(self: &Arc<Self>) -> &() {
&()
}
#[clippy::msrv = "1.80"]
fn explicit_older<'a>(self: &'a Arc<Self>) -> &'a ();
#[clippy::msrv = "1.80"]
fn explicit_provided_older<'a>(self: &'a Arc<Self>) -> &'a () {
&()
}

Expand Down
20 changes: 19 additions & 1 deletion tests/ui/needless_lifetimes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
clippy::boxed_local,
clippy::extra_unused_type_parameters,
clippy::needless_pass_by_value,
clippy::redundant_allocation,
clippy::unnecessary_wraps,
dyn_drop,
clippy::get_first
Expand Down Expand Up @@ -443,13 +444,22 @@ mod issue7296 {
fn implicit_mut<'a>(&'a mut self) -> &'a () {
&()
}

#[clippy::msrv = "1.81"]
fn explicit<'a>(self: &'a Arc<Self>) -> &'a () {
&()
}
#[clippy::msrv = "1.81"]
fn explicit_mut<'a>(self: &'a mut Rc<Self>) -> &'a () {
&()
}
#[clippy::msrv = "1.80"]
fn explicit_older<'a>(self: &'a Arc<Self>) -> &'a () {
&()
}
#[clippy::msrv = "1.80"]
fn explicit_mut_older<'a>(self: &'a mut Rc<Self>) -> &'a () {
&()
}

fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
&()
Expand All @@ -462,10 +472,18 @@ mod issue7296 {
&()
}

#[clippy::msrv = "1.81"]
fn explicit<'a>(self: &'a Arc<Self>) -> &'a ();
#[clippy::msrv = "1.81"]
fn explicit_provided<'a>(self: &'a Arc<Self>) -> &'a () {
&()
}
#[clippy::msrv = "1.80"]
fn explicit_older<'a>(self: &'a Arc<Self>) -> &'a ();
#[clippy::msrv = "1.80"]
fn explicit_provided_older<'a>(self: &'a Arc<Self>) -> &'a () {
&()
}

fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a ();
fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
Expand Down
Loading
Loading