From 6f3941caa177b4337b7c0ccf9ef62001f2162370 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Fri, 2 Feb 2024 08:08:19 -0500 Subject: [PATCH] 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. --- compiler/rustc_errors/src/lib.rs | 16 +- compiler/rustc_lint/messages.ftl | 2 + .../rustc_lint/src/context/diagnostics.rs | 24 +- compiler/rustc_lint/src/lib.rs | 13 +- compiler/rustc_lint/src/lints.rs | 23 +- compiler/rustc_lint_defs/src/builtin.rs | 54 +++- compiler/rustc_lint_defs/src/lib.rs | 6 + compiler/rustc_resolve/src/late.rs | 176 +++++++++++-- src/tools/lint-docs/src/groups.rs | 1 + .../elided-lifetime-in-path-details.rs | 239 ++++++++++++++++++ ...lided-lifetime-in-path-details.tied.stderr | 167 ++++++++++++ ...ded-lifetime-in-path-details.untied.stderr | 62 +++++ tests/ui/lifetimes/elided-lint-in-mod.stderr | 1 + tests/ui/lifetimes/issue-91763.stderr | 1 + .../force-warn/allowed-by-default-lint.stderr | 3 +- tests/ui/lint/reasons.rs | 1 + tests/ui/lint/reasons.stderr | 3 +- 17 files changed, 752 insertions(+), 40 deletions(-) create mode 100644 tests/ui/lifetimes/elided-lifetime-in-path-details.rs create mode 100644 tests/ui/lifetimes/elided-lifetime-in-path-details.tied.stderr create mode 100644 tests/ui/lifetimes/elided-lifetime-in-path-details.untied.stderr diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index ceebcd46a6f7f..8cea3a1db13be 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1971,16 +1971,24 @@ pub fn elided_lifetime_in_path_suggestion( insertion_span: Span, ) -> ElidedLifetimeInPathSubdiag { let expected = ExpectedLifetimeParameter { span: path_span, count: n }; + let indicate = indicate_anonymous_lifetime(source_map, n, incl_angl_brckt, insertion_span); + ElidedLifetimeInPathSubdiag { expected, indicate } +} + +pub fn indicate_anonymous_lifetime( + source_map: &SourceMap, + n: usize, + incl_angl_brckt: bool, + insertion_span: Span, +) -> Option { // Do not try to suggest anything if generated by a proc-macro. - let indicate = source_map.is_span_accessible(insertion_span).then(|| { + source_map.is_span_accessible(insertion_span).then(|| { let anon_lts = vec!["'_"; n].join(", "); let suggestion = if incl_angl_brckt { format!("<{anon_lts}>") } else { format!("{anon_lts}, ") }; IndicateAnonymousLifetime { span: insertion_span.shrink_to_hi(), count: n, suggestion } - }); - - ElidedLifetimeInPathSubdiag { expected, indicate } + }) } pub fn report_ambiguity_error<'a, G: EmissionGuarantee>( diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 7a394a6d6c1ad..c350a8ccd2c7d 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -290,6 +290,8 @@ lint_hidden_glob_reexport = private item shadows public glob re-export lint_hidden_lifetime_parameters = hidden lifetime parameters in types are deprecated +lint_hidden_lifetime_parameters_tied_source = the lifetime comes from here + lint_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label} .label = this {$label} contains {$count -> [one] an invisible diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index f289d4c81b3c1..80c57c840b453 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -5,7 +5,8 @@ use std::borrow::Cow; use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_errors::{ - elided_lifetime_in_path_suggestion, Applicability, Diag, DiagArgValue, LintDiagnostic, + elided_lifetime_in_path_suggestion, indicate_anonymous_lifetime, Applicability, Diag, + DiagArgValue, ExpectedLifetimeParameter, LintDiagnostic, }; use rustc_middle::middle::stability; use rustc_session::lint::BuiltinLintDiag; @@ -82,6 +83,27 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: & } .decorate_lint(diag); } + + BuiltinLintDiag::ElidedLifetimesInPathsTied { elided_lifetime_source, suggestions } => { + let elided_lifetime_source = + elided_lifetime_source.map(|span| lints::ElidedLifetimesInPathsTiedSource { span }); + + let expected = suggestions + .iter() + .map(|&(span, count, _)| ExpectedLifetimeParameter { span, count }) + .collect(); + + let suggestions = suggestions + .into_iter() + .flat_map(|(span, n, incl_angl_brckt)| { + indicate_anonymous_lifetime(sess.source_map(), n, incl_angl_brckt, span) + }) + .collect(); + + lints::ElidedLifetimesInPathsTied { expected, suggestions, elided_lifetime_source } + .decorate_lint(diag) + } + BuiltinLintDiag::UnknownCrateTypes { span, candidate } => { let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate }); lints::UnknownCrateTypes { sugg }.decorate_lint(diag); diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 7a43c3b8ac7ba..acfbea78141f3 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -308,7 +308,8 @@ fn register_builtins(store: &mut LintStore) { BARE_TRAIT_OBJECTS, UNUSED_EXTERN_CRATES, ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, - ELIDED_LIFETIMES_IN_PATHS, + ELIDED_LIFETIMES_IN_PATHS_TIED, + ELIDED_LIFETIMES_IN_PATHS_UNTIED, EXPLICIT_OUTLIVES_REQUIREMENTS, // FIXME(#52665, #47816) not always applicable and not all // macros are ready for this yet. @@ -328,9 +329,14 @@ fn register_builtins(store: &mut LintStore) { add_lint_group!("deprecated_safe", DEPRECATED_SAFE_2024); + add_lint_group!( + "elided_lifetimes_in_paths", + ELIDED_LIFETIMES_IN_PATHS_TIED, + ELIDED_LIFETIMES_IN_PATHS_UNTIED, + ); + // Register renamed and removed lints. store.register_renamed("single_use_lifetime", "single_use_lifetimes"); - store.register_renamed("elided_lifetime_in_path", "elided_lifetimes_in_paths"); store.register_renamed("bare_trait_object", "bare_trait_objects"); store.register_renamed("unstable_name_collision", "unstable_name_collisions"); store.register_renamed("unused_doc_comment", "unused_doc_comments"); @@ -344,6 +350,9 @@ fn register_builtins(store: &mut LintStore) { store.register_renamed("unused_tuple_struct_fields", "dead_code"); store.register_renamed("static_mut_ref", "static_mut_refs"); + // Register renamed lint groups + store.register_renamed_group("elided_lifetime_in_path", "elided_lifetimes_in_paths"); + // These were moved to tool lints, but rustc still sees them when compiling normally, before // tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use // `register_removed` explicitly. diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 03962d796f4e2..2f0b7d9996fbb 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -5,7 +5,8 @@ use std::num::NonZero; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, ElidedLifetimeInPathSubdiag, - EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp, Subdiagnostic, SuggestionStyle, + EmissionGuarantee, ExpectedLifetimeParameter, IndicateAnonymousLifetime, LintDiagnostic, + MultiSpan, SubdiagMessageOp, Subdiagnostic, SuggestionStyle, }; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; @@ -2611,6 +2612,26 @@ pub struct ElidedLifetimesInPaths { pub subdiag: ElidedLifetimeInPathSubdiag, } +#[derive(LintDiagnostic)] +#[diag(lint_hidden_lifetime_parameters)] // deliberately the same translation +pub struct ElidedLifetimesInPathsTied { + #[subdiagnostic] + pub expected: Vec, + + #[subdiagnostic] + pub suggestions: Vec, + + #[subdiagnostic] + pub elided_lifetime_source: Option, +} + +#[derive(Subdiagnostic)] +#[label(lint_hidden_lifetime_parameters_tied_source)] +pub struct ElidedLifetimesInPathsTiedSource { + #[primary_span] + pub span: Span, +} + #[derive(LintDiagnostic)] #[diag(lint_invalid_crate_type_value)] pub struct UnknownCrateTypes { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index c731b03a87590..e1e030f41b2c0 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -41,7 +41,8 @@ declare_lint_pass! { DEPRECATED_WHERE_CLAUSE_LOCATION, DUPLICATE_MACRO_ATTRIBUTES, ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, - ELIDED_LIFETIMES_IN_PATHS, + ELIDED_LIFETIMES_IN_PATHS_TIED, + ELIDED_LIFETIMES_IN_PATHS_UNTIED, EXPLICIT_BUILTIN_CFGS_IN_FLAGS, EXPORTED_PRIVATE_DEPENDENCIES, FFI_UNWIND_CALLS, @@ -1829,19 +1830,21 @@ declare_lint! { } declare_lint! { - /// The `elided_lifetimes_in_paths` lint detects the use of hidden - /// lifetime parameters. + /// The `elided_lifetimes_in_paths_tied` lint detects the use of + /// hidden lifetime parameters when those lifetime parameters tie + /// an input lifetime parameter to an output lifetime parameter. /// /// ### Example /// /// ```rust,compile_fail - /// #![deny(elided_lifetimes_in_paths)] + /// #![deny(elided_lifetimes_in_paths_tied)] /// #![deny(warnings)] /// struct Foo<'a> { /// x: &'a u32 /// } /// - /// fn foo(x: &Foo) { + /// fn foo(x: Foo) -> &u32 { + /// &x.0 /// } /// ``` /// @@ -1858,11 +1861,50 @@ declare_lint! { /// may require a significant transition for old code. /// /// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions - pub ELIDED_LIFETIMES_IN_PATHS, + pub ELIDED_LIFETIMES_IN_PATHS_TIED, Allow, "hidden lifetime parameters in types are deprecated" } +declare_lint! { + /// The `elided_lifetimes_in_paths_untied` lint detects the use of + /// hidden lifetime parameters when those lifetime parameters do + /// not tie an input lifetime parameter to an output lifetime + /// parameter. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(elided_lifetimes_in_paths_untied)] + /// #![deny(warnings)] + /// struct Foo<'a> { + /// x: &'a u32 + /// } + /// + /// fn foo(x: Foo) -> u32 { + /// x.0 + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Elided lifetime parameters can make it difficult to see at a glance + /// that borrowing is occurring. This lint ensures that lifetime + /// parameters are always explicitly stated, even if it is the `'_` + /// [placeholder lifetime]. + /// + /// This lint is "allow" by default because it has some known issues, and + /// may require a significant transition for old code. + /// + /// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions + pub ELIDED_LIFETIMES_IN_PATHS_UNTIED, + Allow, + "hidden lifetime parameters in types make it hard to tell when borrowing is happening", + crate_level_only +} + declare_lint! { /// The `bare_trait_objects` lint suggests using `dyn Trait` for trait /// objects. diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 0f07de43e80ed..c3ce24b8e1009 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -585,6 +585,12 @@ pub enum BuiltinLintDiag { }, MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), ElidedLifetimesInPaths(usize, Span, bool, Span), + + ElidedLifetimesInPathsTied { + elided_lifetime_source: Option, + suggestions: Vec<(Span, usize, bool)>, + }, + UnknownCrateTypes { span: Span, candidate: Option, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 710099d9e90c8..69135bc4bc51a 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -660,10 +660,31 @@ struct DiagMetadata<'ast> { } #[derive(Debug)] -struct ResolvedNestedElisionTarget { +enum ResolvedElisionTarget { + /// Elision in `&u8` -> `&'_ u8` + TopLevel(NodeId, Span), + /// Elision in `Foo` -> `Foo<'_>` + Nested(NestedResolvedElisionTarget), +} + +impl ResolvedElisionTarget { + fn node_id(&self) -> NodeId { + match *self { + Self::TopLevel(n, _) => n, + Self::Nested(NestedResolvedElisionTarget { segment_id, .. }) => segment_id, + } + } +} + +#[derive(Debug)] +struct NestedResolvedElisionTarget { segment_id: NodeId, - elided_lifetime_span: Span, - diagnostic: lint::BuiltinLintDiag, + + // These four are used to enrich diagnostics + type_with_elision: Span, + insert_lifetime: Span, + expected_lifetimes: usize, + include_angle_bracket: bool, } struct LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { @@ -708,7 +729,7 @@ struct LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { lifetime_uses: FxHashMap, /// Track which types participated in lifetime elision - resolved_lifetime_elisions: Vec, + resolved_lifetime_elisions: Vec, } /// Walks the whole crate in DFS order, visiting each item, resolving names as it goes. @@ -1812,6 +1833,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { LifetimeElisionCandidate::Ignore, ); self.resolve_anonymous_lifetime(<, true); + + self.resolved_lifetime_elisions.push(ResolvedElisionTarget::TopLevel(anchor_id, span)); } #[instrument(level = "debug", skip(self))] @@ -2025,16 +2048,15 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { } if should_lint { - self.resolved_lifetime_elisions.push(ResolvedNestedElisionTarget { - segment_id, - elided_lifetime_span, - diagnostic: lint::BuiltinLintDiag::ElidedLifetimesInPaths( + self.resolved_lifetime_elisions.push(ResolvedElisionTarget::Nested( + NestedResolvedElisionTarget { + segment_id, + type_with_elision: path_span, + insert_lifetime: elided_lifetime_span, expected_lifetimes, - path_span, - !segment.has_generic_args, - elided_lifetime_span, - ), - }); + include_angle_bracket: !segment.has_generic_args, + }, + )); } } } @@ -2096,25 +2118,131 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { let elision_failures = replace(&mut self.diag_metadata.current_elision_failures, outer_failures); - if !elision_failures.is_empty() { - let Err(failure_info) = elision_lifetime else { bug!() }; - self.report_missing_lifetime_specifiers(elision_failures, Some(failure_info)); - } + + let elision_lifetime = match (elision_failures.is_empty(), elision_lifetime) { + (true, Ok(lifetime)) => Some(lifetime), + + (true, Err(_)) => None, + + (false, Ok(_)) => bug!(), + + (false, Err(failure_info)) => { + self.report_missing_lifetime_specifiers(elision_failures, Some(failure_info)); + None + } + }; + + // We've recorded all elisions that occurred in the params and + // outputs, categorized by top-level or nested. + // + // Our primary lint case is when an output lifetime is tied to + // an input lifetime. In that case, we want to warn about any + // nested hidden lifetimes in those params or outputs. + // + // The secondary case is for nested elisions that are not part + // of the tied lifetime relationship. let output_resolved_lifetime_elisions = replace(&mut self.resolved_lifetime_elisions, outer_resolved_lifetime_elisions); - let resolved_lifetime_elisions = - param_resolved_lifetime_elisions.into_iter().chain(output_resolved_lifetime_elisions); + match (output_resolved_lifetime_elisions.is_empty(), elision_lifetime) { + (true, _) | (_, None) => { + // Treat all parameters as untied + self.report_untied_lifetimes_elided_in_paths(param_resolved_lifetime_elisions); + } + (false, Some(elision_lifetime)) => { + let (primary, secondary): (Vec<_>, Vec<_>) = + param_resolved_lifetime_elisions.into_iter().partition(|re| { + // Recover the `NodeId` of an elided lifetime + let lvl1 = &self.r.lifetimes_res_map[&re.node_id()]; + let lvl2 = match lvl1 { + LifetimeRes::ElidedAnchor { start, .. } => { + &self.r.lifetimes_res_map[&start] + } + o => o, + }; - for re in resolved_lifetime_elisions { - let ResolvedNestedElisionTarget { segment_id, elided_lifetime_span, diagnostic } = re; + lvl2 == &elision_lifetime + }); + + self.report_tied_lifetimes_elided_in_paths( + fn_id, + primary, + output_resolved_lifetime_elisions, + ); + self.report_untied_lifetimes_elided_in_paths(secondary); + } + } + } + + fn report_untied_lifetimes_elided_in_paths( + &mut self, + resolved_elisions: impl IntoIterator, + ) { + for re in resolved_elisions { + let ResolvedElisionTarget::Nested(d) = re else { continue }; + + let NestedResolvedElisionTarget { + segment_id, + type_with_elision, + insert_lifetime, + expected_lifetimes, + include_angle_bracket, + } = d; self.r.lint_buffer.buffer_lint( - lint::builtin::ELIDED_LIFETIMES_IN_PATHS, + lint::builtin::ELIDED_LIFETIMES_IN_PATHS_UNTIED, segment_id, - elided_lifetime_span, - diagnostic, + insert_lifetime, + lint::BuiltinLintDiag::ElidedLifetimesInPaths( + expected_lifetimes, + type_with_elision, + include_angle_bracket, + insert_lifetime, + ), + ); + } + } + + fn report_tied_lifetimes_elided_in_paths( + &mut self, + fn_id: NodeId, + input_elided_lifetimes: Vec, + output_elided_lifetimes: Vec, + ) { + // Could this be an argument to this function so we don't have + // to re-find it? + let elided_lifetime_source = input_elided_lifetimes.iter().find_map(|hl| match *hl { + ResolvedElisionTarget::TopLevel(_, span) => Some(span), + _ => None, + }); + + let mut error_spans = Vec::new(); + let mut suggestions = Vec::new(); + + let elided_lifetimes = input_elided_lifetimes.into_iter().chain(output_elided_lifetimes); + for el in elided_lifetimes { + let ResolvedElisionTarget::Nested(n) = el else { continue }; + + let NestedResolvedElisionTarget { + segment_id: _, + type_with_elision, + insert_lifetime, + expected_lifetimes, + include_angle_bracket, + } = n; + + error_spans.push(type_with_elision); + + suggestions.push((insert_lifetime, expected_lifetimes, include_angle_bracket)); + } + + if !suggestions.is_empty() { + self.r.lint_buffer.buffer_lint( + lint::builtin::ELIDED_LIFETIMES_IN_PATHS_TIED, + fn_id, + error_spans, + BuiltinLintDiag::ElidedLifetimesInPathsTied { elided_lifetime_source, suggestions }, ); } } diff --git a/src/tools/lint-docs/src/groups.rs b/src/tools/lint-docs/src/groups.rs index 526b5323d8974..d63fb0c1ec369 100644 --- a/src/tools/lint-docs/src/groups.rs +++ b/src/tools/lint-docs/src/groups.rs @@ -12,6 +12,7 @@ static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[ ("let-underscore", "Lints that detect wildcard let bindings that are likely to be invalid"), ("rustdoc", "Rustdoc-specific lints"), ("rust-2018-idioms", "Lints to nudge you toward idiomatic features of Rust 2018"), + ("elided-lifetimes-in-paths", "Lints that detect the use of hidden lifetime parameters"), ("nonstandard-style", "Violation of standard naming conventions"), ("future-incompatible", "Lints that detect code that has future-compatibility problems"), ("rust-2018-compatibility", "Lints used to transition code from the 2015 edition to 2018"), diff --git a/tests/ui/lifetimes/elided-lifetime-in-path-details.rs b/tests/ui/lifetimes/elided-lifetime-in-path-details.rs new file mode 100644 index 0000000000000..61c94556fff75 --- /dev/null +++ b/tests/ui/lifetimes/elided-lifetime-in-path-details.rs @@ -0,0 +1,239 @@ +//@revisions: tied untied + +#![cfg_attr(tied, deny(elided_lifetimes_in_paths_tied))] +//[tied]~^ NOTE: the lint level is defined here +#![cfg_attr(untied, deny(elided_lifetimes_in_paths_untied))] +//[untied]~^ NOTE: the lint level is defined here + +struct ContainsLifetime<'a>(&'a u8); + +// ========== +// Core desired functionality + +fn top_level_to_nested(v: &u8) -> + //[tied]~^ NOTE lifetime comes from here + ContainsLifetime + //[tied]~^ ERROR hidden lifetime parameters + //[tied]~| NOTE expected lifetime parameter + //[tied]~| HELP indicate the anonymous lifetime +{ + ContainsLifetime(v) +} + +fn nested_to_top_level( + v: ContainsLifetime, + //[tied]~^ ERROR hidden lifetime parameters + //[tied]~| NOTE expected lifetime parameter + //[tied]~| HELP indicate the anonymous lifetime +) -> &u8 +{ + v.0 +} + +fn nested_to_nested( + v: ContainsLifetime, + //[tied]~^ ERROR hidden lifetime parameters + //[tied]~| NOTE expected lifetime parameter + //[tied]~| HELP indicate the anonymous lifetime +) -> ContainsLifetime + //[tied]~^ NOTE expected lifetime parameter + //[tied]~| HELP indicate the anonymous lifetime +{ + v +} + +fn top_level_to_top_level(v: &u8) -> &u8 { + v +} + +// ========== +// Mixed named and elided lifetimes + +fn named_top_level_to_nested<'a>(v: &'a u8) -> + ContainsLifetime + //[tied]~^ ERROR hidden lifetime parameters + //[tied]~| NOTE expected lifetime parameter + //[tied]~| HELP indicate the anonymous lifetime +{ + ContainsLifetime(v) +} + +// ========== +// Using named lifetimes everywhere should not report + +fn named_top_level_to_named_nested<'a>(v: &'a u8) -> ContainsLifetime<'a> { + ContainsLifetime(v) +} + +fn named_nested_to_named_top_level<'a>(v: ContainsLifetime<'a>) -> &'a u8 { + v.0 +} + +fn named_nested_to_named_nested<'a>(v: ContainsLifetime<'a>) -> ContainsLifetime<'a> { + v +} + +// ========== +// Using anonymous lifetimes everywhere should not report + +fn anon_top_level_to_anon_nested(v: &'_ u8) -> ContainsLifetime<'_> { + ContainsLifetime(v) +} + +fn anon_nested_to_anon_top_level(v: ContainsLifetime<'_>) -> &'_ u8 { + v.0 +} + +fn anon_nested_to_anon_nested(v: ContainsLifetime<'_>) -> ContainsLifetime<'_> { + v +} + +// ========== +// Mixing named and anonymous lifetimes should not report + +fn named_nested_to_anon_top_level<'a>(v: ContainsLifetime<'a>) -> &'_ u8 { + v.0 +} + +fn named_top_level_to_anon_top_level<'a>(v: &'a u8) -> ContainsLifetime<'_> { + ContainsLifetime(v) +} + +// ========== +// Lifetimes with nothing to tie to + +fn top_level_parameter(v: &u8) {} + +fn nested_parameter(v: ContainsLifetime) {} +//[untied]~^ ERROR hidden lifetime parameters +//[untied]~| NOTE expected lifetime parameter + +fn top_level_nested_parameter(v: &ContainsLifetime) {} +//[untied]~^ ERROR hidden lifetime parameters +//[untied]~| NOTE expected lifetime parameter + +// ========== +// More complicated types + +fn top_level_to_multiple_nested(v: &u8) -> ( + //[tied]~^ NOTE lifetime comes from here + ContainsLifetime, + //[tied]~^ ERROR hidden lifetime parameters + //[tied]~| NOTE expected lifetime parameter + //[tied]~| HELP indicate the anonymous lifetime + ContainsLifetime, + //[tied]~^ NOTE expected lifetime parameter + //[tied]~| HELP indicate the anonymous lifetime +) +{ + (ContainsLifetime(v), ContainsLifetime(v)) +} + +// ---------- + +struct AsAMethod(u8); + +impl AsAMethod { + fn top_level_to_nested( + v: &u8, + //[tied]~^ NOTE lifetime comes from here + ) -> + ContainsLifetime + //[tied]~^ ERROR hidden lifetime parameters + //[tied]~| NOTE expected lifetime parameter + //[tied]~| HELP indicate the anonymous lifetime + { + ContainsLifetime(v) + } + + fn nested_to_top_level( + v: ContainsLifetime, + //[tied]~^ ERROR hidden lifetime parameters + //[tied]~| NOTE expected lifetime parameter + //[tied]~| HELP indicate the anonymous lifetime + ) -> &u8 + { + v.0 + } + + fn nested_to_nested( + v: ContainsLifetime, + //[tied]~^ ERROR hidden lifetime parameters + //[tied]~| NOTE expected lifetime parameter + //[tied]~| HELP indicate the anonymous lifetime + ) -> ContainsLifetime + //[tied]~^ NOTE expected lifetime parameter + //[tied]~| HELP indicate the anonymous lifetime + { + v + } + + fn top_level_to_top_level(v: &u8) -> &u8 { + v + } + + fn self_to_nested( + &self, + //[tied]~^ NOTE lifetime comes from here + ) -> + ContainsLifetime + //[tied]~^ ERROR hidden lifetime parameters + //[tied]~| NOTE expected lifetime parameter + //[tied]~| HELP indicate the anonymous lifetime + { + ContainsLifetime(&self.0) + } + + fn self_to_nested_with_irrelevant_top_level_parameter( + &self, + //[tied]~^ NOTE lifetime comes from here + _: &u8 + ) -> + ContainsLifetime + //[tied]~^ ERROR hidden lifetime parameters + //[tied]~| NOTE expected lifetime parameter + //[tied]~| HELP indicate the anonymous lifetime + { + ContainsLifetime(&self.0) + } + + fn self_to_nested_with_irrelevant_nested_parameter( + &self, + //[tied]~^ NOTE lifetime comes from here + _: ContainsLifetime, + //[untied]~^ ERROR hidden lifetime parameters + //[untied]~| NOTE expected lifetime parameter + ) -> ContainsLifetime + //[tied]~^ ERROR hidden lifetime parameters + //[tied]~| NOTE expected lifetime parameter + //[tied]~| HELP indicate the anonymous lifetime + { + ContainsLifetime(&self.0) + } + + fn nested_in_parameter( + &self, + v: ContainsLifetime, + //[untied]~^ ERROR hidden lifetime parameters + //[untied]~| NOTE expected lifetime parameter + ) {} + + fn nested_in_parameter_with_return( + &self, + v: ContainsLifetime, + //[untied]~^ ERROR hidden lifetime parameters + //[untied]~| NOTE expected lifetime parameter + ) -> &u8 + { + &self.0 + } +} + +// // Do we need to worry about nested function signatures? +// // fn outer(_: fn(&) -> &) + +// // Do we need to worry about closures? + +// // Do we need to write tests for `self: Foo` syntax? + +fn main() {} diff --git a/tests/ui/lifetimes/elided-lifetime-in-path-details.tied.stderr b/tests/ui/lifetimes/elided-lifetime-in-path-details.tied.stderr new file mode 100644 index 0000000000000..93c2b6b8afc5b --- /dev/null +++ b/tests/ui/lifetimes/elided-lifetime-in-path-details.tied.stderr @@ -0,0 +1,167 @@ +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:15:5 + | +LL | fn top_level_to_nested(v: &u8) -> + | - the lifetime comes from here +LL | +LL | ContainsLifetime + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +note: the lint level is defined here + --> $DIR/elided-lifetime-in-path-details.rs:3:24 + | +LL | #![cfg_attr(tied, deny(elided_lifetimes_in_paths_tied))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: indicate the anonymous lifetime + | +LL | ContainsLifetime<'_> + | ++++ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:24:8 + | +LL | v: ContainsLifetime, + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | v: ContainsLifetime<'_>, + | ++++ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:34:8 + | +LL | v: ContainsLifetime, + | ^^^^^^^^^^^^^^^^ expected lifetime parameter +... +LL | ) -> ContainsLifetime + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | v: ContainsLifetime<'_>, + | ++++ +help: indicate the anonymous lifetime + | +LL | ) -> ContainsLifetime<'_> + | ++++ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:53:5 + | +LL | ContainsLifetime + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | ContainsLifetime<'_> + | ++++ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:120:5 + | +LL | fn top_level_to_multiple_nested(v: &u8) -> ( + | - the lifetime comes from here +LL | +LL | ContainsLifetime, + | ^^^^^^^^^^^^^^^^ expected lifetime parameter +... +LL | ContainsLifetime, + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | ContainsLifetime<'_>, + | ++++ +help: indicate the anonymous lifetime + | +LL | ContainsLifetime<'_>, + | ++++ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:141:9 + | +LL | v: &u8, + | - the lifetime comes from here +... +LL | ContainsLifetime + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | ContainsLifetime<'_> + | ++++ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:150:12 + | +LL | v: ContainsLifetime, + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | v: ContainsLifetime<'_>, + | ++++ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:160:12 + | +LL | v: ContainsLifetime, + | ^^^^^^^^^^^^^^^^ expected lifetime parameter +... +LL | ) -> ContainsLifetime + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | v: ContainsLifetime<'_>, + | ++++ +help: indicate the anonymous lifetime + | +LL | ) -> ContainsLifetime<'_> + | ++++ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:179:9 + | +LL | &self, + | - the lifetime comes from here +... +LL | ContainsLifetime + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | ContainsLifetime<'_> + | ++++ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:192:9 + | +LL | &self, + | - the lifetime comes from here +... +LL | ContainsLifetime + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | ContainsLifetime<'_> + | ++++ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:206:10 + | +LL | &self, + | - the lifetime comes from here +... +LL | ) -> ContainsLifetime + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | ) -> ContainsLifetime<'_> + | ++++ + +error: aborting due to 11 previous errors + diff --git a/tests/ui/lifetimes/elided-lifetime-in-path-details.untied.stderr b/tests/ui/lifetimes/elided-lifetime-in-path-details.untied.stderr new file mode 100644 index 0000000000000..a13ae94b570d6 --- /dev/null +++ b/tests/ui/lifetimes/elided-lifetime-in-path-details.untied.stderr @@ -0,0 +1,62 @@ +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:107:24 + | +LL | fn nested_parameter(v: ContainsLifetime) {} + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +note: the lint level is defined here + --> $DIR/elided-lifetime-in-path-details.rs:5:26 + | +LL | #![cfg_attr(untied, deny(elided_lifetimes_in_paths_untied))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: indicate the anonymous lifetime + | +LL | fn nested_parameter(v: ContainsLifetime<'_>) {} + | ++++ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:111:35 + | +LL | fn top_level_nested_parameter(v: &ContainsLifetime) {} + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | fn top_level_nested_parameter(v: &ContainsLifetime<'_>) {} + | ++++ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:203:12 + | +LL | _: ContainsLifetime, + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | _: ContainsLifetime<'_>, + | ++++ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:216:12 + | +LL | v: ContainsLifetime, + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | v: ContainsLifetime<'_>, + | ++++ + +error: hidden lifetime parameters in types are deprecated + --> $DIR/elided-lifetime-in-path-details.rs:223:12 + | +LL | v: ContainsLifetime, + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +LL | v: ContainsLifetime<'_>, + | ++++ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/lifetimes/elided-lint-in-mod.stderr b/tests/ui/lifetimes/elided-lint-in-mod.stderr index 1fee18028c66f..0433ba81cdcfb 100644 --- a/tests/ui/lifetimes/elided-lint-in-mod.stderr +++ b/tests/ui/lifetimes/elided-lint-in-mod.stderr @@ -11,6 +11,7 @@ note: the lint level is defined here | LL | #[deny(elided_lifetimes_in_paths)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[deny(elided_lifetimes_in_paths_untied)]` implied by `#[deny(elided_lifetimes_in_paths)]` help: indicate the anonymous lifetime | LL | fn test2(_: super::Foo<'_>) {} diff --git a/tests/ui/lifetimes/issue-91763.stderr b/tests/ui/lifetimes/issue-91763.stderr index f7293ed809c3a..6d5d219b5b236 100644 --- a/tests/ui/lifetimes/issue-91763.stderr +++ b/tests/ui/lifetimes/issue-91763.stderr @@ -9,6 +9,7 @@ note: the lint level is defined here | LL | #![deny(elided_lifetimes_in_paths)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[deny(elided_lifetimes_in_paths_untied)]` implied by `#[deny(elided_lifetimes_in_paths)]` help: indicate the anonymous lifetime | LL | fn f() -> Ptr<'_>; diff --git a/tests/ui/lint/force-warn/allowed-by-default-lint.stderr b/tests/ui/lint/force-warn/allowed-by-default-lint.stderr index ac98b5896ca7b..6f95ff999cf6c 100644 --- a/tests/ui/lint/force-warn/allowed-by-default-lint.stderr +++ b/tests/ui/lint/force-warn/allowed-by-default-lint.stderr @@ -4,7 +4,8 @@ warning: hidden lifetime parameters in types are deprecated LL | fn foo(x: &Foo) {} | ^^^ expected lifetime parameter | - = note: requested on the command line with `--force-warn elided-lifetimes-in-paths` + = note: `--force-warn elided-lifetimes-in-paths-untied` implied by `--force-warn elided-lifetimes-in-paths` + = help: to override `--force-warn elided-lifetimes-in-paths` add `#[allow(elided_lifetimes_in_paths_untied)]` help: indicate the anonymous lifetime | LL | fn foo(x: &Foo<'_>) {} diff --git a/tests/ui/lint/reasons.rs b/tests/ui/lint/reasons.rs index 917e7539aaed3..44751f70ec8c3 100644 --- a/tests/ui/lint/reasons.rs +++ b/tests/ui/lint/reasons.rs @@ -18,6 +18,7 @@ pub struct CheaterDetectionMechanism {} impl fmt::Debug for CheaterDetectionMechanism { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { //~^ WARN hidden lifetime parameters in types are deprecated + //~| NOTE implied by //~| NOTE expected lifetime parameter //~| NOTE explicit anonymous lifetimes aid //~| HELP indicate the anonymous lifetime diff --git a/tests/ui/lint/reasons.stderr b/tests/ui/lint/reasons.stderr index 8028785ab94be..54a28b4630a0e 100644 --- a/tests/ui/lint/reasons.stderr +++ b/tests/ui/lint/reasons.stderr @@ -12,13 +12,14 @@ note: the lint level is defined here | LL | #![warn(elided_lifetimes_in_paths, | ^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(elided_lifetimes_in_paths_untied)]` implied by `#[warn(elided_lifetimes_in_paths)]` help: indicate the anonymous lifetime | LL | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | ++++ warning: variable `Social_exchange_psychology` should have a snake case name - --> $DIR/reasons.rs:29:9 + --> $DIR/reasons.rs:30:9 | LL | let Social_exchange_psychology = CheaterDetectionMechanism {}; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case (notice the capitalization): `social_exchange_psychology`