Skip to content
Open
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
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/error_codes/E0307.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ also be the underlying implementing type, like `Foo` in the following
example:

```
# #![allow(self_lifetime_elision_not_applicable)]
# struct Foo;
# trait Trait {
# fn foo(&self);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/error_codes/E0772.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ internal data was omitted, meaning that the compiler inferred the lifetime
compiler to look like this:

```
# #![allow(self_lifetime_elision_not_applicable)]
# trait Person {}
#
# impl dyn Person {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir/src/attrs/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ pub struct OnUnimplementedCondition {
pub pred: Predicate,
}
impl OnUnimplementedCondition {
pub fn matches_predicate(self: &OnUnimplementedCondition, options: &ConditionOptions) -> bool {
pub fn matches_predicate(&self, options: &ConditionOptions) -> bool {
self.pred.eval(&mut |p| match p {
FlagOrNv::Flag(b) => options.has_flag(*b),
FlagOrNv::NameValue(NameValue { name, value }) => {
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/early/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,9 @@ impl<'a> Diagnostic<'a, ()> for DecorateBuiltinLint<'_, '_> {
}
.into_diag(dcx, level)
}
BuiltinLintDiag::SelfLifetimeElisionNotApplicable { span } => {
lints::SelfLifetimeElisionNotApplicable { span }.into_diag(dcx, level)
}
BuiltinLintDiag::UnreachableCfg { span, wildcard_span } => match wildcard_span {
Some(wildcard_span) => {
lints::UnreachableCfgSelectPredicateWildcard { span, wildcard_span }
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3973,3 +3973,11 @@ pub(crate) struct MalformedOnConstAttrLint {
#[diag("`Eq::assert_receiver_is_total_eq` should never be implemented by hand")]
#[note("this method was used to add checks to the `Eq` derive macro")]
pub(crate) struct EqInternalMethodImplemented;

#[derive(Diagnostic)]
#[diag("`self` parameter type does not contain `Self`")]
#[help("use `&self`, `&mut self`, or `self: &Self` for correct lifetime elision")]
pub(crate) struct SelfLifetimeElisionNotApplicable {
#[primary_span]
pub span: Span,
}
33 changes: 33 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ declare_lint_pass! {
RUST_2024_INCOMPATIBLE_PAT,
RUST_2024_PRELUDE_COLLISIONS,
SELF_CONSTRUCTOR_FROM_OUTER_ITEM,
SELF_LIFETIME_ELISION_NOT_APPLICABLE,
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
SHADOWING_SUPERTRAIT_ITEMS,
SINGLE_USE_LIFETIMES,
Expand Down Expand Up @@ -2862,6 +2863,38 @@ declare_lint! {
};
}

declare_lint! {
/// The `self_lifetime_elision_not_applicable` lint detects `self` parameters
/// whose type does not syntactically contain `Self`, causing lifetime
/// elision to rely on an unreliable name-matching heuristic.
///
/// ### Example
///
/// ```rust,compile_fail
/// struct Foo<'a>(&'a ());
///
/// impl<'a> Foo<'a> {
/// fn get(self: &Foo<'a>) -> &() { self.0 }
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// When a `self` parameter uses a concrete type name instead of `Self`,
/// the compiler uses a name-matching heuristic to determine if self-type
/// lifetime elision applies. This heuristic cannot see through type
/// aliases, ignores generic arguments, and may silently choose an
/// incorrect lifetime. Use `&self` or `self: &Self` instead.
pub SELF_LIFETIME_ELISION_NOT_APPLICABLE,
Deny,
"self-type lifetime elision for non-`Self` type",
@future_incompatible = FutureIncompatibleInfo {
reason: fcw!(FutureReleaseError #140611),
};
}

declare_lint! {
/// The `semicolon_in_expressions_from_macros` lint detects trailing semicolons
/// in macro bodies when the macro is invoked in expression position.
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_lint_defs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,9 @@ pub enum BuiltinLintDiag {
span: Span,
lifetimes_in_scope: MultiSpan,
},
SelfLifetimeElisionNotApplicable {
span: Span,
},
UnusedCrateDependency {
extern_crate: Symbol,
local_crate: Symbol,
Expand Down
62 changes: 62 additions & 0 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2505,6 +2505,16 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
// Handle `self` specially.
if index == 0 && has_self {
let self_lifetime = self.find_lifetime_for_self(ty);

if self.self_type_has_reference(ty) && !self.self_param_has_genuine_self(ty) {
self.r.lint_buffer.buffer_lint(
lint::builtin::SELF_LIFETIME_ELISION_NOT_APPLICABLE,
ty.id,
ty.span,
lint::BuiltinLintDiag::SelfLifetimeElisionNotApplicable { span: ty.span },
);
}

elision_lifetime = match self_lifetime {
// We found `self` elision.
Set1::One(lifetime) => Elision::Self_(lifetime),
Expand Down Expand Up @@ -2533,6 +2543,58 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
Err((all_candidates, parameter_info))
}

/// Returns `true` if `ty` syntactically contains `Self`.
fn self_param_has_genuine_self(&self, ty: &'ast Ty) -> bool {
struct GenuineSelfVisitor<'a, 'ra, 'tcx> {
r: &'a Resolver<'ra, 'tcx>,
found: bool,
}
impl<'ra> Visitor<'ra> for GenuineSelfVisitor<'_, '_, '_> {
fn visit_ty(&mut self, ty: &'ra Ty) {
match ty.kind {
TyKind::ImplicitSelf => self.found = true,
TyKind::Path(None, _) => {
if matches!(
self.r.partial_res_map[&ty.id].full_res(),
Some(Res::SelfTyParam { .. } | Res::SelfTyAlias { .. })
) {
self.found = true;
}
}
_ => {}
}
if !self.found {
visit::walk_ty(self, ty);
}
}
fn visit_expr(&mut self, _: &'ra Expr) {}
}
let mut visitor = GenuineSelfVisitor { r: self.r, found: false };
visitor.visit_ty(ty);
visitor.found
}

/// Returns `true` if `ty` contains a reference type (`&` or pinned `&`).
fn self_type_has_reference(&self, ty: &'ast Ty) -> bool {
struct RefVisitor {
found: bool,
}
impl<'ra> Visitor<'ra> for RefVisitor {
fn visit_ty(&mut self, ty: &'ra Ty) {
if matches!(ty.kind, TyKind::Ref(..) | TyKind::PinnedRef(..)) {
self.found = true;
}
if !self.found {
visit::walk_ty(self, ty);
}
}
fn visit_expr(&mut self, _: &'ra Expr) {}
}
let mut visitor = RefVisitor { found: false };
visitor.visit_ty(ty);
visitor.found
}

/// List all the lifetimes that appear in the provided type.
fn find_lifetime_for_self(&self, ty: &'ast Ty) -> Set1<LifetimeRes> {
/// Visits a type to find all the &references, and determines the
Expand Down
1 change: 1 addition & 0 deletions tests/ui/async-await/inference_var_self_argument.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![allow(self_lifetime_elision_not_applicable)]
//! This is a regression test for an ICE.
//@ edition: 2021

Expand Down
6 changes: 3 additions & 3 deletions tests/ui/async-await/inference_var_self_argument.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0307]: invalid `self` parameter type: `&dyn Foo`
--> $DIR/inference_var_self_argument.rs:5:24
--> $DIR/inference_var_self_argument.rs:6:24
|
LL | async fn foo(self: &dyn Foo) {
| ^^^^^^^^
Expand All @@ -8,14 +8,14 @@ LL | async fn foo(self: &dyn Foo) {
= help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)

error[E0038]: the trait `Foo` is not dyn compatible
--> $DIR/inference_var_self_argument.rs:5:33
--> $DIR/inference_var_self_argument.rs:6:33
|
LL | async fn foo(self: &dyn Foo) {
| ^ `Foo` is not dyn compatible
|
note: for a trait to be dyn compatible it needs to allow building a vtable
for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
--> $DIR/inference_var_self_argument.rs:5:14
--> $DIR/inference_var_self_argument.rs:6:14
|
LL | trait Foo {
| --- this trait is not dyn compatible...
Expand Down
1 change: 1 addition & 0 deletions tests/ui/closures/2229_closure_analysis/issue-88476.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//@ edition:2021

#![feature(rustc_attrs)]
#![allow(self_lifetime_elision_not_applicable)]

// Test that we can't move out of struct that impls `Drop`.

Expand Down
20 changes: 10 additions & 10 deletions tests/ui/closures/2229_closure_analysis/issue-88476.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0658]: attributes on expressions are experimental
--> $DIR/issue-88476.rs:20:13
--> $DIR/issue-88476.rs:21:13
|
LL | let x = #[rustc_capture_analysis] move || {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -9,7 +9,7 @@ LL | let x = #[rustc_capture_analysis] move || {
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error[E0658]: attributes on expressions are experimental
--> $DIR/issue-88476.rs:48:13
--> $DIR/issue-88476.rs:49:13
|
LL | let c = #[rustc_capture_analysis] move || {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -19,7 +19,7 @@ LL | let c = #[rustc_capture_analysis] move || {
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

error: First Pass analysis includes:
--> $DIR/issue-88476.rs:20:39
--> $DIR/issue-88476.rs:21:39
|
LL | let x = #[rustc_capture_analysis] move || {
| _______________________________________^
Expand All @@ -28,13 +28,13 @@ LL | | };
| |_____^
|
note: Capturing f[(0, 0)] -> Immutable
--> $DIR/issue-88476.rs:26:26
--> $DIR/issue-88476.rs:27:26
|
LL | println!("{:?}", f.0);
| ^^^

error: Min Capture analysis includes:
--> $DIR/issue-88476.rs:20:39
--> $DIR/issue-88476.rs:21:39
|
LL | let x = #[rustc_capture_analysis] move || {
| _______________________________________^
Expand All @@ -43,13 +43,13 @@ LL | | };
| |_____^
|
note: Min Capture f[] -> ByValue
--> $DIR/issue-88476.rs:26:26
--> $DIR/issue-88476.rs:27:26
|
LL | println!("{:?}", f.0);
| ^^^

error: First Pass analysis includes:
--> $DIR/issue-88476.rs:48:39
--> $DIR/issue-88476.rs:49:39
|
LL | let c = #[rustc_capture_analysis] move || {
| _______________________________________^
Expand All @@ -58,13 +58,13 @@ LL | | };
| |_____^
|
note: Capturing character[(0, 0)] -> Immutable
--> $DIR/issue-88476.rs:54:24
--> $DIR/issue-88476.rs:55:24
|
LL | println!("{}", character.hp)
| ^^^^^^^^^^^^

error: Min Capture analysis includes:
--> $DIR/issue-88476.rs:48:39
--> $DIR/issue-88476.rs:49:39
|
LL | let c = #[rustc_capture_analysis] move || {
| _______________________________________^
Expand All @@ -73,7 +73,7 @@ LL | | };
| |_____^
|
note: Min Capture character[(0, 0)] -> ByValue
--> $DIR/issue-88476.rs:54:24
--> $DIR/issue-88476.rs:55:24
|
LL | println!("{}", character.hp)
| ^^^^^^^^^^^^
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//@ check-pass
//@ edition:2021

#![allow(self_lifetime_elision_not_applicable)]

use std::rc::Rc;

// Test that we restrict precision when moving not-`Copy` types, if any of the parent paths
Expand Down
1 change: 1 addition & 0 deletions tests/ui/internal-lints/rustc_pass_by_value_self.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#![feature(rustc_attrs)]
#![deny(rustc::disallowed_pass_by_ref)]
#![allow(unused)]
#![allow(self_lifetime_elision_not_applicable)]

#[rustc_pass_by_value]
struct TyCtxt<'tcx> {
Expand Down
10 changes: 5 additions & 5 deletions tests/ui/internal-lints/rustc_pass_by_value_self.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: passing `TyCtxt<'tcx>` by reference
--> $DIR/rustc_pass_by_value_self.rs:18:15
--> $DIR/rustc_pass_by_value_self.rs:19:15
|
LL | fn by_ref(&self) {}
| ^^^^^ help: try passing by value: `TyCtxt<'tcx>`
Expand All @@ -11,25 +11,25 @@ LL | #![deny(rustc::disallowed_pass_by_ref)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: passing `Ty<'tcx>` by reference
--> $DIR/rustc_pass_by_value_self.rs:30:21
--> $DIR/rustc_pass_by_value_self.rs:31:21
|
LL | fn by_ref(self: &Ty<'tcx>) {}
| ^^^^^^^^^ help: try passing by value: `Ty<'tcx>`

error: passing `Foo` by reference
--> $DIR/rustc_pass_by_value_self.rs:37:17
--> $DIR/rustc_pass_by_value_self.rs:38:17
|
LL | fn with_ref(&self) {}
| ^^^^^ help: try passing by value: `Foo`

error: passing `WithParameters<T, 1>` by reference
--> $DIR/rustc_pass_by_value_self.rs:47:17
--> $DIR/rustc_pass_by_value_self.rs:48:17
|
LL | fn with_ref(&self) {}
| ^^^^^ help: try passing by value: `WithParameters<T, 1>`

error: passing `WithParameters<T, 1, u8>` by reference
--> $DIR/rustc_pass_by_value_self.rs:51:17
--> $DIR/rustc_pass_by_value_self.rs:52:17
|
LL | fn with_ref(&self) {}
| ^^^^^ help: try passing by value: `WithParameters<T, 1, u8>`
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/issues/issue-17905-2.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(self_lifetime_elision_not_applicable)]

#[derive(Debug)]
struct Pair<T, V> (T, V);

Expand Down
Loading
Loading