From 50802c5cef8ab10a75c1a1df27531aefd82665fd 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 + compiler/rustc_lint/src/early/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 | 4 + compiler/rustc_resolve/src/late.rs | 176 +++++++++++-- src/tools/lint-docs/src/groups.rs | 1 + .../elided-lifetime-in-path-details.rs | 241 ++++++++++++++++++ ...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 | 2 +- tests/ui/lint/reasons.rs | 1 + tests/ui/lint/reasons.stderr | 3 +- 17 files changed, 751 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 9af17db9a6e6c..9d54fdef62b7f 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1937,16 +1937,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 480d97e377a40..911812d7c4eae 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -294,6 +294,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/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index aeb5a03a4f7e3..bf5c8455db66f 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -5,7 +5,8 @@ use std::borrow::Cow; use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_errors::{ - Applicability, Diag, DiagArgValue, LintDiagnostic, elided_lifetime_in_path_suggestion, + Applicability, Diag, DiagArgValue, ExpectedLifetimeParameter, LintDiagnostic, + elided_lifetime_in_path_suggestion, indicate_anonymous_lifetime, }; use rustc_middle::middle::stability; use rustc_middle::ty::TyCtxt; @@ -88,6 +89,27 @@ pub(super) fn decorate_lint( } .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 16d1b69b58d57..45d1b4ed94d9c 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -317,7 +317,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. @@ -337,9 +338,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"); @@ -354,6 +360,9 @@ fn register_builtins(store: &mut LintStore) { store.register_renamed("static_mut_ref", "static_mut_refs"); store.register_renamed("temporary_cstring_as_ptr", "dangling_pointers_from_temporaries"); + // 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 368d36bfdd0b2..3722a4f865d29 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -6,7 +6,8 @@ use rustc_abi::ExternAbi; 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; @@ -2728,6 +2729,26 @@ impl LintDiagnostic<'_, G> for ElidedNamedLifetime { } } +#[derive(LintDiagnostic)] +#[diag(lint_hidden_lifetime_parameters)] // deliberately the same translation +pub(crate) 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(crate) struct ElidedLifetimesInPathsTiedSource { + #[primary_span] + pub span: Span, +} + #[derive(LintDiagnostic)] #[diag(lint_invalid_crate_type_value)] pub(crate) struct UnknownCrateTypes { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9561b3de5f8b6..b966d9ce61dfa 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -39,7 +39,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, ELIDED_NAMED_LIFETIMES, EXPLICIT_BUILTIN_CFGS_IN_FLAGS, EXPORTED_PRIVATE_DEPENDENCIES, @@ -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 `elided_named_lifetimes` lint detects when an elided /// lifetime ends up being a named lifetime, such as `'static` diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 7ffe4e4e4901c..2ad171b797815 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -652,6 +652,10 @@ pub enum BuiltinLintDiag { }, MacroExpandedMacroExportsAccessedByAbsolutePaths(Span), ElidedLifetimesInPaths(usize, Span, bool, Span), + ElidedLifetimesInPathsTied { + elided_lifetime_source: Option, + suggestions: Vec<(Span, usize, bool)>, + }, ElidedNamedLifetimes { elided: (Span, MissingLifetimeKind), resolution: ElidedLifetimeResolution, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 85d570c9bb29a..a8e7982096e37 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -690,10 +690,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, 'ast, 'ra, 'tcx> { @@ -738,7 +759,7 @@ struct LateResolutionVisitor<'a, 'ast, 'ra, '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. @@ -1909,6 +1930,8 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { LifetimeElisionCandidate::Ignore, ); self.resolve_anonymous_lifetime(<, anchor_id, true); + + self.resolved_lifetime_elisions.push(ResolvedElisionTarget::TopLevel(anchor_id, span)); } #[instrument(level = "debug", skip(self))] @@ -2127,16 +2150,15 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, '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, + }, + )); } } } @@ -2240,25 +2262,131 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, '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..59b5eac2788e2 --- /dev/null +++ b/tests/ui/lifetimes/elided-lifetime-in-path-details.rs @@ -0,0 +1,241 @@ +//@revisions: tied untied + +#![allow(elided_named_lifetimes)] + +#![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..c9685a50978e3 --- /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:17: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:5: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:26: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:36: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:55: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:122: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:143: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:152: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:162: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:181: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:194: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:208: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..e350c52530126 --- /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:109:24 + | +LL | fn nested_parameter(v: ContainsLifetime) {} + | ^^^^^^^^^^^^^^^^ expected lifetime parameter + | +note: the lint level is defined here + --> $DIR/elided-lifetime-in-path-details.rs:7: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:113: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:205: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:218: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:225: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..31da24c305c67 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,7 @@ 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: 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`