1+ use std:: slice;
2+
13use rustc_data_structures:: fx:: FxIndexMap ;
24use rustc_hir:: intravisit:: { self , Visitor } ;
35use rustc_hir:: { self as hir, LifetimeSource , LifetimeSyntax } ;
4- use rustc_session:: { declare_lint, declare_lint_pass} ;
6+ use rustc_session:: lint:: Lint ;
7+ use rustc_session:: { declare_lint, declare_lint_pass, impl_lint_pass} ;
58use rustc_span:: Span ;
69use tracing:: instrument;
710
@@ -66,7 +69,84 @@ declare_lint! {
6669 "detects when an elided lifetime uses different syntax between arguments and return values"
6770}
6871
69- declare_lint_pass ! ( LifetimeStyle => [ MISMATCHED_LIFETIME_SYNTAXES ] ) ;
72+ declare_lint ! {
73+ /// The `hidden_lifetimes_in_input_paths2` lint detects the use of
74+ /// hidden lifetime parameters in types occurring as a function
75+ /// argument.
76+ ///
77+ /// ### Example
78+ ///
79+ /// ```rust,compile_fail
80+ /// #![deny(hidden_lifetimes_in_input_paths2)]
81+ ///
82+ /// struct ContainsLifetime<'a>(&'a i32);
83+ ///
84+ /// fn foo(x: ContainsLifetime) {}
85+ /// ```
86+ ///
87+ /// {{produces}}
88+ ///
89+ /// ### Explanation
90+ ///
91+ /// Hidden lifetime parameters can make it difficult to see at a
92+ /// glance that borrowing is occurring.
93+ ///
94+ /// This lint ensures that lifetime parameters are always
95+ /// explicitly stated, even if it is the `'_` [placeholder
96+ /// lifetime].
97+ ///
98+ /// This lint is "allow" by default as function arguments by
99+ /// themselves do not usually cause much confusion.
100+ ///
101+ /// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions
102+ pub HIDDEN_LIFETIMES_IN_INPUT_PATHS2 ,
103+ Allow ,
104+ "hidden lifetime parameters in types in function arguments may be confusing"
105+ }
106+
107+ declare_lint ! {
108+ /// The `hidden_lifetimes_in_output_paths2` lint detects the use
109+ /// of hidden lifetime parameters in types occurring as a function
110+ /// return value.
111+ ///
112+ /// ### Example
113+ ///
114+ /// ```rust,compile_fail
115+ /// #![deny(hidden_lifetimes_in_output_paths2)]
116+ ///
117+ /// struct ContainsLifetime<'a>(&'a i32);
118+ ///
119+ /// fn foo(x: &i32) -> ContainsLifetime {
120+ /// ContainsLifetime(x)
121+ /// }
122+ /// ```
123+ ///
124+ /// {{produces}}
125+ ///
126+ /// ### Explanation
127+ ///
128+ /// Hidden lifetime parameters can make it difficult to see at a
129+ /// glance that borrowing is occurring. This is especially true
130+ /// when a type is used as a function's return value: lifetime
131+ /// elision will link the return value's lifetime to an argument's
132+ /// lifetime, but no syntax in the function signature indicates
133+ /// that.
134+ ///
135+ /// This lint ensures that lifetime parameters are always
136+ /// explicitly stated, even if it is the `'_` [placeholder
137+ /// lifetime].
138+ ///
139+ /// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions
140+ pub HIDDEN_LIFETIMES_IN_OUTPUT_PATHS2 ,
141+ Allow ,
142+ "hidden lifetime parameters in types in function return values are deprecated"
143+ }
144+
145+ declare_lint_pass ! ( LifetimeStyle => [
146+ MISMATCHED_LIFETIME_SYNTAXES ,
147+ HIDDEN_LIFETIMES_IN_INPUT_PATHS2 ,
148+ HIDDEN_LIFETIMES_IN_OUTPUT_PATHS2 ,
149+ ] ) ;
70150
71151impl < ' tcx > LateLintPass < ' tcx > for LifetimeStyle {
72152 #[ instrument( skip_all) ]
@@ -91,6 +171,8 @@ impl<'tcx> LateLintPass<'tcx> for LifetimeStyle {
91171 }
92172
93173 report_mismatches ( cx, & input_map, & output_map) ;
174+ report_hidden_in_paths ( cx, & input_map, HIDDEN_LIFETIMES_IN_INPUT_PATHS2 ) ;
175+ report_hidden_in_paths ( cx, & output_map, HIDDEN_LIFETIMES_IN_OUTPUT_PATHS2 ) ;
94176 }
95177}
96178
@@ -233,6 +315,50 @@ fn build_mismatch_suggestion(
233315 }
234316}
235317
318+ fn report_hidden_in_paths < ' tcx > (
319+ cx : & LateContext < ' tcx > ,
320+ info_map : & LifetimeInfoMap < ' tcx > ,
321+ lint : & ' static Lint ,
322+ ) {
323+ let relevant_lifetimes = info_map
324+ . iter ( )
325+ . filter ( |& ( & & res, _) | reportable_lifetime_resolution ( res) )
326+ . flat_map ( |( _, info) | info)
327+ . filter ( |info| {
328+ matches ! ( info. lifetime. source, LifetimeSource :: Path { .. } )
329+ && info. lifetime . is_syntactically_hidden ( )
330+ } ) ;
331+
332+ let mut reporting_spans = Vec :: new ( ) ;
333+ let mut suggestions = Vec :: new ( ) ;
334+
335+ for info in relevant_lifetimes {
336+ reporting_spans. push ( info. reporting_span ( ) ) ;
337+ suggestions. push ( info. suggestion ( "'_" ) ) ;
338+ }
339+
340+ if reporting_spans. is_empty ( ) {
341+ return ;
342+ }
343+
344+ cx. emit_span_lint (
345+ lint,
346+ reporting_spans,
347+ lints:: HiddenLifetimeInPath {
348+ suggestions : lints:: HiddenLifetimeInPathSuggestion { suggestions } ,
349+ } ,
350+ ) ;
351+ }
352+
353+ /// We don't care about errors, nor do we care about the lifetime
354+ /// inside of a trait object.
355+ fn reportable_lifetime_resolution ( res : hir:: LifetimeName ) -> bool {
356+ matches ! (
357+ res,
358+ hir:: LifetimeName :: Param ( ..) | hir:: LifetimeName :: Infer | hir:: LifetimeName :: Static
359+ )
360+ }
361+
236362struct Info < ' tcx > {
237363 type_span : Span ,
238364 lifetime : & ' tcx hir:: Lifetime ,
@@ -299,3 +425,115 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeInfoCollector<'a, 'tcx> {
299425 self . type_span = old_type_span;
300426 }
301427}
428+
429+ declare_lint ! {
430+ /// The `hidden_lifetimes_in_type_paths2` lint detects the use of
431+ /// hidden lifetime parameters in types not part of a function's
432+ /// arguments or return values.
433+ ///
434+ /// ### Example
435+ ///
436+ /// ```rust,compile_fail
437+ /// #![deny(hidden_lifetimes_in_type_paths2)]
438+ ///
439+ /// struct ContainsLifetime<'a>(&'a i32);
440+ ///
441+ /// static FOO: ContainsLifetime = ContainsLifetime(&42);
442+ /// ```
443+ ///
444+ /// {{produces}}
445+ ///
446+ /// ### Explanation
447+ ///
448+ /// Hidden lifetime parameters can make it difficult to see at a
449+ /// glance that borrowing is occurring.
450+ ///
451+ /// This lint ensures that lifetime parameters are always
452+ /// explicitly stated, even if it is the `'_` [placeholder
453+ /// lifetime].
454+ ///
455+ /// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions
456+ pub HIDDEN_LIFETIMES_IN_TYPE_PATHS2 ,
457+ Allow ,
458+ "hidden lifetime parameters in types outside function signatures are discouraged"
459+ }
460+
461+ #[ derive( Default ) ]
462+ pub ( crate ) struct HiddenLifetimesInTypePaths {
463+ inside_fn_signature : bool ,
464+ }
465+
466+ impl_lint_pass ! ( HiddenLifetimesInTypePaths => [ HIDDEN_LIFETIMES_IN_TYPE_PATHS2 ] ) ;
467+
468+ impl < ' tcx > LateLintPass < ' tcx > for HiddenLifetimesInTypePaths {
469+ #[ instrument( skip( self , cx) ) ]
470+ fn check_ty ( & mut self , cx : & LateContext < ' tcx > , ty : & ' tcx hir:: Ty < ' tcx , hir:: AmbigArg > ) {
471+ if self . inside_fn_signature {
472+ return ;
473+ }
474+
475+ // Do not lint about usages like `ContainsLifetime::method` or
476+ // `ContainsLifetimeAndType::<SomeType>::method`.
477+ if ty. source == hir:: TySource :: ImplicitSelf {
478+ return ;
479+ }
480+
481+ let hir:: TyKind :: Path ( path) = ty. kind else { return } ;
482+
483+ let path_segments = match path {
484+ hir:: QPath :: Resolved ( _ty, path) => path. segments ,
485+
486+ hir:: QPath :: TypeRelative ( _ty, path_segment) => slice:: from_ref ( path_segment) ,
487+
488+ hir:: QPath :: LangItem ( ..) => & [ ] ,
489+ } ;
490+
491+ let mut suggestions = Vec :: new ( ) ;
492+
493+ for path_segment in path_segments {
494+ for arg in path_segment. args ( ) . args {
495+ if let hir:: GenericArg :: Lifetime ( lifetime) = arg
496+ && lifetime. is_syntactically_hidden ( )
497+ && reportable_lifetime_resolution ( lifetime. res )
498+ {
499+ suggestions. push ( lifetime. suggestion ( "'_" ) )
500+ }
501+ }
502+ }
503+
504+ if suggestions. is_empty ( ) {
505+ return ;
506+ }
507+
508+ cx. emit_span_lint (
509+ HIDDEN_LIFETIMES_IN_TYPE_PATHS2 ,
510+ ty. span ,
511+ lints:: HiddenLifetimeInPath {
512+ suggestions : lints:: HiddenLifetimeInPathSuggestion { suggestions } ,
513+ } ,
514+ ) ;
515+ }
516+
517+ #[ instrument( skip_all) ]
518+ fn check_fn (
519+ & mut self ,
520+ _: & LateContext < ' tcx > ,
521+ _: hir:: intravisit:: FnKind < ' tcx > ,
522+ _: & ' tcx hir:: FnDecl < ' tcx > ,
523+ _: & ' tcx hir:: Body < ' tcx > ,
524+ _: rustc_span:: Span ,
525+ _: rustc_span:: def_id:: LocalDefId ,
526+ ) {
527+ // We make the assumption that we will visit the function
528+ // declaration first, before visiting the body.
529+ self . inside_fn_signature = true ;
530+ }
531+
532+ // This may be a function's body, which would indicate that we are
533+ // no longer in the signature. Even if it's not, a body cannot
534+ // occur inside a function signature.
535+ #[ instrument( skip_all) ]
536+ fn check_body ( & mut self , _: & LateContext < ' tcx > , _: & hir:: Body < ' tcx > ) {
537+ self . inside_fn_signature = false ;
538+ }
539+ }
0 commit comments