Properly handle lifetimes when checking generic arguments len

And also, prepare for correct lowering of lifetime. We still don't handle most lifetimes correctly, but a bit more of the foundation to lifetime elision is now implemented.
This commit is contained in:
Chayim Refael Friedman 2025-04-24 08:35:20 +03:00
parent 3d00e247dd
commit adcf699ea3
16 changed files with 858 additions and 226 deletions

View file

@ -0,0 +1,112 @@
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: elided-lifetimes-in-path
//
// This diagnostic is triggered when lifetimes are elided in paths. It is a lint only for some cases,
// and a hard error for others.
pub(crate) fn elided_lifetimes_in_path(
ctx: &DiagnosticsContext<'_>,
d: &hir::ElidedLifetimesInPath,
) -> Diagnostic {
if d.hard_error {
Diagnostic::new_with_syntax_node_ptr(
ctx,
DiagnosticCode::RustcHardError("E0726"),
"implicit elided lifetime not allowed here",
d.generics_or_segment.map(Into::into),
)
.experimental()
} else {
Diagnostic::new_with_syntax_node_ptr(
ctx,
DiagnosticCode::RustcLint("elided_lifetimes_in_paths"),
"hidden lifetime parameters in types are deprecated",
d.generics_or_segment.map(Into::into),
)
.experimental()
}
}
#[cfg(test)]
mod tests {
use crate::tests::check_diagnostics;
#[test]
fn fn_() {
check_diagnostics(
r#"
#![warn(elided_lifetimes_in_paths)]
struct Foo<'a>(&'a ());
fn foo(_: Foo) {}
// ^^^ warn: hidden lifetime parameters in types are deprecated
"#,
);
check_diagnostics(
r#"
#![warn(elided_lifetimes_in_paths)]
struct Foo<'a>(&'a ());
fn foo(_: Foo<'_>) -> Foo { loop {} }
// ^^^ warn: hidden lifetime parameters in types are deprecated
"#,
);
}
#[test]
fn async_fn() {
check_diagnostics(
r#"
struct Foo<'a>(&'a ());
async fn foo(_: Foo) {}
// ^^^ error: implicit elided lifetime not allowed here
"#,
);
check_diagnostics(
r#"
#![warn(elided_lifetimes_in_paths)]
struct Foo<'a>(&'a ());
fn foo(_: Foo<'_>) -> Foo { loop {} }
// ^^^ warn: hidden lifetime parameters in types are deprecated
"#,
);
}
#[test]
fn no_error_when_explicitly_elided() {
check_diagnostics(
r#"
#![warn(elided_lifetimes_in_paths)]
struct Foo<'a>(&'a ());
trait Trait<'a> {}
fn foo(_: Foo<'_>) -> Foo<'_> { loop {} }
async fn bar(_: Foo<'_>) -> Foo<'_> { loop {} }
impl Foo<'_> {}
impl Trait<'_> for Foo<'_> {}
"#,
);
}
#[test]
fn impl_() {
check_diagnostics(
r#"
struct Foo<'a>(&'a ());
trait Trait<'a> {}
impl Foo {}
// ^^^ error: implicit elided lifetime not allowed here
impl Trait for Foo<'_> {}
// ^^^^^ error: implicit elided lifetime not allowed here
"#,
);
}
}

View file

@ -0,0 +1,92 @@
use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
// Diagnostic: missing-lifetime
//
// This diagnostic is triggered when a lifetime argument is missing.
pub(crate) fn missing_lifetime(
ctx: &DiagnosticsContext<'_>,
d: &hir::MissingLifetime,
) -> Diagnostic {
Diagnostic::new_with_syntax_node_ptr(
ctx,
DiagnosticCode::RustcHardError("E0106"),
"missing lifetime specifier",
d.generics_or_segment.map(Into::into),
)
.experimental()
}
#[cfg(test)]
mod tests {
use crate::tests::check_diagnostics;
#[test]
fn in_fields() {
check_diagnostics(
r#"
struct Foo<'a>(&'a ());
struct Bar(Foo);
// ^^^ error: missing lifetime specifier
"#,
);
}
#[test]
fn bounds() {
check_diagnostics(
r#"
struct Foo<'a, T>(&'a T);
trait Trait<'a> {
type Assoc;
}
fn foo<'a, T: Trait>(
// ^^^^^ error: missing lifetime specifier
_: impl Trait<'a, Assoc: Trait>,
// ^^^^^ error: missing lifetime specifier
)
where
Foo<T>: Trait<'a>,
// ^^^ error: missing lifetime specifier
{
}
"#,
);
}
#[test]
fn generic_defaults() {
check_diagnostics(
r#"
struct Foo<'a>(&'a ());
struct Bar<T = Foo>(T);
// ^^^ error: missing lifetime specifier
"#,
);
}
#[test]
fn type_alias_type() {
check_diagnostics(
r#"
struct Foo<'a>(&'a ());
type Bar = Foo;
// ^^^ error: missing lifetime specifier
"#,
);
}
#[test]
fn const_param_ty() {
check_diagnostics(
r#"
struct Foo<'a>(&'a ());
fn bar<const F: Foo>() {}
// ^^^ error: missing lifetime specifier
"#,
);
}
}

View file

@ -27,6 +27,7 @@ mod handlers {
pub(crate) mod await_outside_of_async;
pub(crate) mod bad_rtn;
pub(crate) mod break_outside_of_loop;
pub(crate) mod elided_lifetimes_in_path;
pub(crate) mod expected_function;
pub(crate) mod generic_args_prohibited;
pub(crate) mod inactive_code;
@ -40,6 +41,7 @@ mod handlers {
pub(crate) mod malformed_derive;
pub(crate) mod mismatched_arg_count;
pub(crate) mod missing_fields;
pub(crate) mod missing_lifetime;
pub(crate) mod missing_match_arms;
pub(crate) mod missing_unsafe;
pub(crate) mod moved_out_of_ref;
@ -503,6 +505,8 @@ pub fn semantic_diagnostics(
AnyDiagnostic::BadRtn(d) => handlers::bad_rtn::bad_rtn(&ctx, &d),
AnyDiagnostic::IncorrectGenericsLen(d) => handlers::incorrect_generics_len::incorrect_generics_len(&ctx, &d),
AnyDiagnostic::IncorrectGenericsOrder(d) => handlers::incorrect_generics_order::incorrect_generics_order(&ctx, &d),
AnyDiagnostic::MissingLifetime(d) => handlers::missing_lifetime::missing_lifetime(&ctx, &d),
AnyDiagnostic::ElidedLifetimesInPath(d) => handlers::elided_lifetimes_in_path::elided_lifetimes_in_path(&ctx, &d),
};
res.push(d)
}