Skip to content

Commit 50802c5

Browse files
committed
Split elided_lifetime_in_paths into tied and untied
Types that contain a reference can be confusing when lifetime elision occurs: ```rust // Confusing fn foo(_: &u8) -> Bar { todo!() } // Less confusing fn foo(_: &u8) -> Bar<'_> { todo!() } ``` However, the previous lint did not distinguish when these types were not "tying" lifetimes across the function inputs / outputs: ```rust // Maybe a little confusing fn foo(_: Bar) {} // More explicit but noisier with less obvious value fn foo(_: Bar<'_>) {} ``` We now report different lints for each case, hopefully paving the way to marking the first case (when lifetimes are tied together) as warn-by-default. Additionally, when multiple errors occur in the same function during the tied case, they are coalesced into one error. There is also some help text pointing out where the lifetime source is.
1 parent efba226 commit 50802c5

File tree

17 files changed

+751
-40
lines changed

17 files changed

+751
-40
lines changed

compiler/rustc_errors/src/lib.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,16 +1937,24 @@ pub fn elided_lifetime_in_path_suggestion(
19371937
insertion_span: Span,
19381938
) -> ElidedLifetimeInPathSubdiag {
19391939
let expected = ExpectedLifetimeParameter { span: path_span, count: n };
1940+
let indicate = indicate_anonymous_lifetime(source_map, n, incl_angl_brckt, insertion_span);
1941+
ElidedLifetimeInPathSubdiag { expected, indicate }
1942+
}
1943+
1944+
pub fn indicate_anonymous_lifetime(
1945+
source_map: &SourceMap,
1946+
n: usize,
1947+
incl_angl_brckt: bool,
1948+
insertion_span: Span,
1949+
) -> Option<IndicateAnonymousLifetime> {
19401950
// Do not try to suggest anything if generated by a proc-macro.
1941-
let indicate = source_map.is_span_accessible(insertion_span).then(|| {
1951+
source_map.is_span_accessible(insertion_span).then(|| {
19421952
let anon_lts = vec!["'_"; n].join(", ");
19431953
let suggestion =
19441954
if incl_angl_brckt { format!("<{anon_lts}>") } else { format!("{anon_lts}, ") };
19451955

19461956
IndicateAnonymousLifetime { span: insertion_span.shrink_to_hi(), count: n, suggestion }
1947-
});
1948-
1949-
ElidedLifetimeInPathSubdiag { expected, indicate }
1957+
})
19501958
}
19511959

19521960
pub fn report_ambiguity_error<'a, G: EmissionGuarantee>(

compiler/rustc_lint/messages.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,8 @@ lint_hidden_glob_reexport = private item shadows public glob re-export
294294
295295
lint_hidden_lifetime_parameters = hidden lifetime parameters in types are deprecated
296296
297+
lint_hidden_lifetime_parameters_tied_source = the lifetime comes from here
298+
297299
lint_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label}
298300
.label = this {$label} contains {$count ->
299301
[one] an invisible

compiler/rustc_lint/src/early/diagnostics.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ use std::borrow::Cow;
55

66
use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
77
use rustc_errors::{
8-
Applicability, Diag, DiagArgValue, LintDiagnostic, elided_lifetime_in_path_suggestion,
8+
Applicability, Diag, DiagArgValue, ExpectedLifetimeParameter, LintDiagnostic,
9+
elided_lifetime_in_path_suggestion, indicate_anonymous_lifetime,
910
};
1011
use rustc_middle::middle::stability;
1112
use rustc_middle::ty::TyCtxt;
@@ -88,6 +89,27 @@ pub(super) fn decorate_lint(
8889
}
8990
.decorate_lint(diag);
9091
}
92+
93+
BuiltinLintDiag::ElidedLifetimesInPathsTied { elided_lifetime_source, suggestions } => {
94+
let elided_lifetime_source =
95+
elided_lifetime_source.map(|span| lints::ElidedLifetimesInPathsTiedSource { span });
96+
97+
let expected = suggestions
98+
.iter()
99+
.map(|&(span, count, _)| ExpectedLifetimeParameter { span, count })
100+
.collect();
101+
102+
let suggestions = suggestions
103+
.into_iter()
104+
.flat_map(|(span, n, incl_angl_brckt)| {
105+
indicate_anonymous_lifetime(sess.source_map(), n, incl_angl_brckt, span)
106+
})
107+
.collect();
108+
109+
lints::ElidedLifetimesInPathsTied { expected, suggestions, elided_lifetime_source }
110+
.decorate_lint(diag)
111+
}
112+
91113
BuiltinLintDiag::UnknownCrateTypes { span, candidate } => {
92114
let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate });
93115
lints::UnknownCrateTypes { sugg }.decorate_lint(diag);

compiler/rustc_lint/src/lib.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,8 @@ fn register_builtins(store: &mut LintStore) {
317317
BARE_TRAIT_OBJECTS,
318318
UNUSED_EXTERN_CRATES,
319319
ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
320-
ELIDED_LIFETIMES_IN_PATHS,
320+
ELIDED_LIFETIMES_IN_PATHS_TIED,
321+
ELIDED_LIFETIMES_IN_PATHS_UNTIED,
321322
EXPLICIT_OUTLIVES_REQUIREMENTS,
322323
// FIXME(#52665, #47816) not always applicable and not all
323324
// macros are ready for this yet.
@@ -337,9 +338,14 @@ fn register_builtins(store: &mut LintStore) {
337338

338339
add_lint_group!("deprecated_safe", DEPRECATED_SAFE_2024);
339340

341+
add_lint_group!(
342+
"elided_lifetimes_in_paths",
343+
ELIDED_LIFETIMES_IN_PATHS_TIED,
344+
ELIDED_LIFETIMES_IN_PATHS_UNTIED,
345+
);
346+
340347
// Register renamed and removed lints.
341348
store.register_renamed("single_use_lifetime", "single_use_lifetimes");
342-
store.register_renamed("elided_lifetime_in_path", "elided_lifetimes_in_paths");
343349
store.register_renamed("bare_trait_object", "bare_trait_objects");
344350
store.register_renamed("unstable_name_collision", "unstable_name_collisions");
345351
store.register_renamed("unused_doc_comment", "unused_doc_comments");
@@ -354,6 +360,9 @@ fn register_builtins(store: &mut LintStore) {
354360
store.register_renamed("static_mut_ref", "static_mut_refs");
355361
store.register_renamed("temporary_cstring_as_ptr", "dangling_pointers_from_temporaries");
356362

363+
// Register renamed lint groups
364+
store.register_renamed_group("elided_lifetime_in_path", "elided_lifetimes_in_paths");
365+
357366
// These were moved to tool lints, but rustc still sees them when compiling normally, before
358367
// tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use
359368
// `register_removed` explicitly.

compiler/rustc_lint/src/lints.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use rustc_abi::ExternAbi;
66
use rustc_errors::codes::*;
77
use rustc_errors::{
88
Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, ElidedLifetimeInPathSubdiag,
9-
EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp, Subdiagnostic, SuggestionStyle,
9+
EmissionGuarantee, ExpectedLifetimeParameter, IndicateAnonymousLifetime, LintDiagnostic,
10+
MultiSpan, SubdiagMessageOp, Subdiagnostic, SuggestionStyle,
1011
};
1112
use rustc_hir::def::Namespace;
1213
use rustc_hir::def_id::DefId;
@@ -2728,6 +2729,26 @@ impl<G: EmissionGuarantee> LintDiagnostic<'_, G> for ElidedNamedLifetime {
27282729
}
27292730
}
27302731

2732+
#[derive(LintDiagnostic)]
2733+
#[diag(lint_hidden_lifetime_parameters)] // deliberately the same translation
2734+
pub(crate) struct ElidedLifetimesInPathsTied {
2735+
#[subdiagnostic]
2736+
pub expected: Vec<ExpectedLifetimeParameter>,
2737+
2738+
#[subdiagnostic]
2739+
pub suggestions: Vec<IndicateAnonymousLifetime>,
2740+
2741+
#[subdiagnostic]
2742+
pub elided_lifetime_source: Option<ElidedLifetimesInPathsTiedSource>,
2743+
}
2744+
2745+
#[derive(Subdiagnostic)]
2746+
#[label(lint_hidden_lifetime_parameters_tied_source)]
2747+
pub(crate) struct ElidedLifetimesInPathsTiedSource {
2748+
#[primary_span]
2749+
pub span: Span,
2750+
}
2751+
27312752
#[derive(LintDiagnostic)]
27322753
#[diag(lint_invalid_crate_type_value)]
27332754
pub(crate) struct UnknownCrateTypes {

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ declare_lint_pass! {
3939
DEPRECATED_WHERE_CLAUSE_LOCATION,
4040
DUPLICATE_MACRO_ATTRIBUTES,
4141
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
42-
ELIDED_LIFETIMES_IN_PATHS,
42+
ELIDED_LIFETIMES_IN_PATHS_TIED,
43+
ELIDED_LIFETIMES_IN_PATHS_UNTIED,
4344
ELIDED_NAMED_LIFETIMES,
4445
EXPLICIT_BUILTIN_CFGS_IN_FLAGS,
4546
EXPORTED_PRIVATE_DEPENDENCIES,
@@ -1829,19 +1830,21 @@ declare_lint! {
18291830
}
18301831

18311832
declare_lint! {
1832-
/// The `elided_lifetimes_in_paths` lint detects the use of hidden
1833-
/// lifetime parameters.
1833+
/// The `elided_lifetimes_in_paths_tied` lint detects the use of
1834+
/// hidden lifetime parameters when those lifetime parameters tie
1835+
/// an input lifetime parameter to an output lifetime parameter.
18341836
///
18351837
/// ### Example
18361838
///
18371839
/// ```rust,compile_fail
1838-
/// #![deny(elided_lifetimes_in_paths)]
1840+
/// #![deny(elided_lifetimes_in_paths_tied)]
18391841
/// #![deny(warnings)]
18401842
/// struct Foo<'a> {
18411843
/// x: &'a u32
18421844
/// }
18431845
///
1844-
/// fn foo(x: &Foo) {
1846+
/// fn foo(x: Foo) -> &u32 {
1847+
/// &x.0
18451848
/// }
18461849
/// ```
18471850
///
@@ -1858,11 +1861,50 @@ declare_lint! {
18581861
/// may require a significant transition for old code.
18591862
///
18601863
/// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions
1861-
pub ELIDED_LIFETIMES_IN_PATHS,
1864+
pub ELIDED_LIFETIMES_IN_PATHS_TIED,
18621865
Allow,
18631866
"hidden lifetime parameters in types are deprecated"
18641867
}
18651868

1869+
declare_lint! {
1870+
/// The `elided_lifetimes_in_paths_untied` lint detects the use of
1871+
/// hidden lifetime parameters when those lifetime parameters do
1872+
/// not tie an input lifetime parameter to an output lifetime
1873+
/// parameter.
1874+
///
1875+
/// ### Example
1876+
///
1877+
/// ```rust,compile_fail
1878+
/// #![deny(elided_lifetimes_in_paths_untied)]
1879+
/// #![deny(warnings)]
1880+
/// struct Foo<'a> {
1881+
/// x: &'a u32
1882+
/// }
1883+
///
1884+
/// fn foo(x: Foo) -> u32 {
1885+
/// x.0
1886+
/// }
1887+
/// ```
1888+
///
1889+
/// {{produces}}
1890+
///
1891+
/// ### Explanation
1892+
///
1893+
/// Elided lifetime parameters can make it difficult to see at a glance
1894+
/// that borrowing is occurring. This lint ensures that lifetime
1895+
/// parameters are always explicitly stated, even if it is the `'_`
1896+
/// [placeholder lifetime].
1897+
///
1898+
/// This lint is "allow" by default because it has some known issues, and
1899+
/// may require a significant transition for old code.
1900+
///
1901+
/// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions
1902+
pub ELIDED_LIFETIMES_IN_PATHS_UNTIED,
1903+
Allow,
1904+
"hidden lifetime parameters in types make it hard to tell when borrowing is happening",
1905+
crate_level_only
1906+
}
1907+
18661908
declare_lint! {
18671909
/// The `elided_named_lifetimes` lint detects when an elided
18681910
/// lifetime ends up being a named lifetime, such as `'static`

compiler/rustc_lint_defs/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,10 @@ pub enum BuiltinLintDiag {
652652
},
653653
MacroExpandedMacroExportsAccessedByAbsolutePaths(Span),
654654
ElidedLifetimesInPaths(usize, Span, bool, Span),
655+
ElidedLifetimesInPathsTied {
656+
elided_lifetime_source: Option<Span>,
657+
suggestions: Vec<(Span, usize, bool)>,
658+
},
655659
ElidedNamedLifetimes {
656660
elided: (Span, MissingLifetimeKind),
657661
resolution: ElidedLifetimeResolution,

0 commit comments

Comments
 (0)