1+ use std:: slice;
2+
13use rustc_data_structures:: fx:: FxIndexMap ;
24use rustc_hir:: intravisit:: { self , Visitor } ;
35use rustc_hir:: { self as hir} ;
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_input_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
@@ -231,6 +313,47 @@ fn build_mismatch_suggestion(
231313 }
232314}
233315
316+ fn report_hidden_in_paths < ' tcx > (
317+ cx : & LateContext < ' tcx > ,
318+ info_map : & LifetimeInfoMap < ' tcx > ,
319+ lint : & ' static Lint ,
320+ ) {
321+ let relevant_lifetimes = info_map
322+ . iter ( )
323+ . filter ( |& ( & & res, _) | reportable_lifetime_resolution ( res) )
324+ . flat_map ( |( _, info) | info)
325+ . filter ( |info| info. source == LifetimeSource :: Path && info. syntax == SyntaxKind :: Hidden ) ;
326+
327+ let mut reporting_spans = Vec :: new ( ) ;
328+ let mut suggestions = Vec :: new ( ) ;
329+
330+ for info in relevant_lifetimes {
331+ reporting_spans. push ( info. reporting_span ( ) ) ;
332+ suggestions. push ( info. suggestion ( "'_" ) ) ;
333+ }
334+
335+ if reporting_spans. is_empty ( ) {
336+ return ;
337+ }
338+
339+ cx. emit_span_lint (
340+ lint,
341+ reporting_spans,
342+ lints:: HiddenLifetimeInPath {
343+ suggestions : lints:: HiddenLifetimeInPathSuggestion { suggestions } ,
344+ } ,
345+ ) ;
346+ }
347+
348+ /// We don't care about errors, nor do we care about the lifetime
349+ /// inside of a trait object.
350+ fn reportable_lifetime_resolution ( res : hir:: LifetimeName ) -> bool {
351+ matches ! (
352+ res,
353+ hir:: LifetimeName :: Param ( ..) | hir:: LifetimeName :: Infer | hir:: LifetimeName :: Static
354+ )
355+ }
356+
234357#[ derive( Debug , Copy , Clone , PartialEq ) ]
235358enum LifetimeSource {
236359 Reference ,
@@ -329,3 +452,115 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeInfoCollector<'a, 'tcx> {
329452 self . is_ref = old_is_ref;
330453 }
331454}
455+
456+ declare_lint ! {
457+ /// The `hidden_lifetimes_in_type_paths2` lint detects the use of
458+ /// hidden lifetime parameters in types not part of a function's
459+ /// arguments or return values.
460+ ///
461+ /// ### Example
462+ ///
463+ /// ```rust,compile_fail
464+ /// #![deny(hidden_lifetimes_in_input_paths2)]
465+ ///
466+ /// struct ContainsLifetime<'a>(&'a i32);
467+ ///
468+ /// static FOO: ContainsLifetime = ContainsLifetime(&42);
469+ /// ```
470+ ///
471+ /// {{produces}}
472+ ///
473+ /// ### Explanation
474+ ///
475+ /// Hidden lifetime parameters can make it difficult to see at a
476+ /// glance that borrowing is occurring.
477+ ///
478+ /// This lint ensures that lifetime parameters are always
479+ /// explicitly stated, even if it is the `'_` [placeholder
480+ /// lifetime].
481+ ///
482+ /// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions
483+ pub HIDDEN_LIFETIMES_IN_TYPE_PATHS2 ,
484+ Allow ,
485+ "hidden lifetime parameters in types outside function signatures are discouraged"
486+ }
487+
488+ #[ derive( Default ) ]
489+ pub ( crate ) struct HiddenLifetimesInTypePaths {
490+ inside_fn_signature : bool ,
491+ }
492+
493+ impl_lint_pass ! ( HiddenLifetimesInTypePaths => [ HIDDEN_LIFETIMES_IN_TYPE_PATHS2 ] ) ;
494+
495+ impl < ' tcx > LateLintPass < ' tcx > for HiddenLifetimesInTypePaths {
496+ #[ instrument( skip( self , cx) ) ]
497+ fn check_ty ( & mut self , cx : & LateContext < ' tcx > , ty : & ' tcx hir:: Ty < ' tcx , hir:: AmbigArg > ) {
498+ if self . inside_fn_signature {
499+ return ;
500+ }
501+
502+ // Do not lint about usages like `ContainsLifetime::method` or
503+ // `ContainsLifetimeAndType::<SomeType>::method`.
504+ if ty. source == hir:: TySource :: ImplicitSelf {
505+ return ;
506+ }
507+
508+ let hir:: TyKind :: Path ( path) = ty. kind else { return } ;
509+
510+ let path_segments = match path {
511+ hir:: QPath :: Resolved ( _ty, path) => path. segments ,
512+
513+ hir:: QPath :: TypeRelative ( _ty, path_segment) => slice:: from_ref ( path_segment) ,
514+
515+ hir:: QPath :: LangItem ( ..) => & [ ] ,
516+ } ;
517+
518+ let mut suggestions = Vec :: new ( ) ;
519+
520+ for path_segment in path_segments {
521+ for arg in path_segment. args ( ) . args {
522+ if let hir:: GenericArg :: Lifetime ( lifetime) = arg
523+ && lifetime. is_syntactically_hidden ( )
524+ && reportable_lifetime_resolution ( lifetime. res )
525+ {
526+ suggestions. push ( lifetime. suggestion ( "'_" , false ) )
527+ }
528+ }
529+ }
530+
531+ if suggestions. is_empty ( ) {
532+ return ;
533+ }
534+
535+ cx. emit_span_lint (
536+ HIDDEN_LIFETIMES_IN_TYPE_PATHS2 ,
537+ ty. span ,
538+ lints:: HiddenLifetimeInPath {
539+ suggestions : lints:: HiddenLifetimeInPathSuggestion { suggestions } ,
540+ } ,
541+ ) ;
542+ }
543+
544+ #[ instrument( skip_all) ]
545+ fn check_fn (
546+ & mut self ,
547+ _: & LateContext < ' tcx > ,
548+ _: hir:: intravisit:: FnKind < ' tcx > ,
549+ _: & ' tcx hir:: FnDecl < ' tcx > ,
550+ _: & ' tcx hir:: Body < ' tcx > ,
551+ _: rustc_span:: Span ,
552+ _: rustc_span:: def_id:: LocalDefId ,
553+ ) {
554+ // We make the assumption that we will visit the function
555+ // declaration first, before visiting the body.
556+ self . inside_fn_signature = true ;
557+ }
558+
559+ // This may be a function's body, which would indicate that we are
560+ // no longer in the signature. Even if it's not, a body cannot
561+ // occur inside a function signature.
562+ #[ instrument( skip_all) ]
563+ fn check_body ( & mut self , _: & LateContext < ' tcx > , _: & hir:: Body < ' tcx > ) {
564+ self . inside_fn_signature = false ;
565+ }
566+ }
0 commit comments