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
@@ -60,7 +63,84 @@ declare_lint! {
6063 "detects when an elided lifetime uses different syntax between arguments and return values"
6164}
6265
63- declare_lint_pass ! ( LifetimeStyle => [ MISMATCHED_ELIDED_LIFETIME_STYLES ] ) ;
66+ declare_lint ! {
67+ /// The `hidden_lifetimes_in_input_paths2` lint detects the use of
68+ /// hidden lifetime parameters in types occurring as a function
69+ /// argument.
70+ ///
71+ /// ### Example
72+ ///
73+ /// ```rust,compile_fail
74+ /// #![deny(hidden_lifetimes_in_input_paths2)]
75+ ///
76+ /// struct ContainsLifetime<'a>(&'a i32);
77+ ///
78+ /// fn foo(x: &ContainsLifetime) {}
79+ /// ```
80+ ///
81+ /// {{produces}}
82+ ///
83+ /// ### Explanation
84+ ///
85+ /// Hidden lifetime parameters can make it difficult to see at a
86+ /// glance that borrowing is occurring.
87+ ///
88+ /// This lint ensures that lifetime parameters are always
89+ /// explicitly stated, even if it is the `'_` [placeholder
90+ /// lifetime].
91+ ///
92+ /// This lint is "allow" by default as function arguments by
93+ /// themselves do not usually cause much confusion.
94+ ///
95+ /// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions
96+ pub HIDDEN_LIFETIMES_IN_INPUT_PATHS2 ,
97+ Allow ,
98+ "hidden lifetime parameters in types in function arguments may be confusing"
99+ }
100+
101+ declare_lint ! {
102+ /// The `hidden_lifetimes_in_output_paths2` lint detects the use
103+ /// of hidden lifetime parameters in types occurring as a function
104+ /// return value.
105+ ///
106+ /// ### Example
107+ ///
108+ /// ```rust,compile_fail
109+ /// #![deny(hidden_lifetimes_in_input_paths2)]
110+ ///
111+ /// struct ContainsLifetime<'a>(&'a i32);
112+ ///
113+ /// fn foo(x: &i32) -> ContainsLifetime {
114+ /// ContainsLifetime(x)
115+ /// }
116+ /// ```
117+ ///
118+ /// {{produces}}
119+ ///
120+ /// ### Explanation
121+ ///
122+ /// Hidden lifetime parameters can make it difficult to see at a
123+ /// glance that borrowing is occurring. This is especially true
124+ /// when a type is used as a function's return value: lifetime
125+ /// elision will link the return value's lifetime to an argument's
126+ /// lifetime, but no syntax in the function signature indicates
127+ /// that.
128+ ///
129+ /// This lint ensures that lifetime parameters are always
130+ /// explicitly stated, even if it is the `'_` [placeholder
131+ /// lifetime].
132+ ///
133+ /// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions
134+ pub HIDDEN_LIFETIMES_IN_OUTPUT_PATHS2 ,
135+ Allow ,
136+ "hidden lifetime parameters in types in function return values are deprecated"
137+ }
138+
139+ declare_lint_pass ! ( LifetimeStyle => [
140+ MISMATCHED_ELIDED_LIFETIME_STYLES ,
141+ HIDDEN_LIFETIMES_IN_INPUT_PATHS2 ,
142+ HIDDEN_LIFETIMES_IN_OUTPUT_PATHS2 ,
143+ ] ) ;
64144
65145impl < ' tcx > LateLintPass < ' tcx > for LifetimeStyle {
66146 #[ instrument( skip_all) ]
@@ -85,6 +165,8 @@ impl<'tcx> LateLintPass<'tcx> for LifetimeStyle {
85165 }
86166
87167 report_mismatches ( cx, & input_map, & output_map) ;
168+ report_hidden_in_paths ( cx, & input_map, HIDDEN_LIFETIMES_IN_INPUT_PATHS2 ) ;
169+ report_hidden_in_paths ( cx, & output_map, HIDDEN_LIFETIMES_IN_OUTPUT_PATHS2 ) ;
88170 }
89171}
90172
@@ -231,6 +313,47 @@ fn build_mismatch_suggestion(
231313 MismatchedElidedLifetimeStylesSuggestion :: Named { lifetime_name, suggestions, tool_only : false }
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. style == SyntaxStyle :: 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 ,
@@ -326,3 +449,114 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeInfoCollector<'a, 'tcx> {
326449 self . is_ref = old_is_ref;
327450 }
328451}
452+
453+ declare_lint ! {
454+ /// The `hidden_lifetimes_in_type_paths2` lint detects the use of
455+ /// hidden lifetime parameters in types not part of a function's
456+ /// arguments or return values.
457+ ///
458+ /// ### Example
459+ ///
460+ /// ```rust,compile_fail
461+ /// #![deny(hidden_lifetimes_in_input_paths2)]
462+ ///
463+ /// struct ContainsLifetime<'a>(&'a i32);
464+ ///
465+ /// static FOO: ContainsLifetime = ContainsLifetime(&42);
466+ /// ```
467+ ///
468+ /// {{produces}}
469+ ///
470+ /// ### Explanation
471+ ///
472+ /// Hidden lifetime parameters can make it difficult to see at a
473+ /// glance that borrowing is occurring.
474+ ///
475+ /// This lint ensures that lifetime parameters are always
476+ /// explicitly stated, even if it is the `'_` [placeholder
477+ /// lifetime].
478+ ///
479+ /// [placeholder lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#lifetime-elision-in-functions
480+ pub HIDDEN_LIFETIMES_IN_TYPE_PATHS2 ,
481+ Allow ,
482+ "hidden lifetime parameters in types outside function signatures are discouraged"
483+ }
484+
485+ #[ derive( Default ) ]
486+ pub ( crate ) struct HiddenLifetimesInTypePaths {
487+ inside_fn_signature : bool ,
488+ }
489+
490+ impl_lint_pass ! ( HiddenLifetimesInTypePaths => [ HIDDEN_LIFETIMES_IN_TYPE_PATHS2 ] ) ;
491+
492+ impl < ' tcx > LateLintPass < ' tcx > for HiddenLifetimesInTypePaths {
493+ #[ instrument( skip( self , cx) ) ]
494+ fn check_ty ( & mut self , cx : & LateContext < ' tcx > , ty : & ' tcx hir:: Ty < ' tcx , hir:: AmbigArg > ) {
495+ if self . inside_fn_signature {
496+ return ;
497+ }
498+
499+ let hir:: TyKind :: Path ( path) = ty. kind else { return } ;
500+
501+ let path_segments = match path {
502+ hir:: QPath :: Resolved ( _ty, path) => path. segments ,
503+
504+ hir:: QPath :: TypeRelative ( _ty, path_segment) => slice:: from_ref ( path_segment) ,
505+
506+ hir:: QPath :: LangItem ( ..) => & [ ] ,
507+ } ;
508+
509+ let mut suggestions = Vec :: new ( ) ;
510+
511+ for path_segment in path_segments {
512+ // Do not lint about usages like `ContainsLifetime::method`.
513+ if path_segment. infer_args {
514+ continue ;
515+ }
516+
517+ for arg in path_segment. args ( ) . args {
518+ if let hir:: GenericArg :: Lifetime ( lifetime) = arg
519+ && lifetime. is_syntactically_hidden ( )
520+ && reportable_lifetime_resolution ( lifetime. res )
521+ {
522+ suggestions. push ( lifetime. suggestion ( "'_" , false ) )
523+ }
524+ }
525+ }
526+
527+ if suggestions. is_empty ( ) {
528+ return ;
529+ }
530+
531+ cx. emit_span_lint (
532+ HIDDEN_LIFETIMES_IN_TYPE_PATHS2 ,
533+ ty. span ,
534+ lints:: HiddenLifetimeInPath {
535+ suggestions : lints:: HiddenLifetimeInPathSuggestion { suggestions } ,
536+ } ,
537+ ) ;
538+ }
539+
540+ #[ instrument( skip_all) ]
541+ fn check_fn (
542+ & mut self ,
543+ _: & LateContext < ' tcx > ,
544+ _: hir:: intravisit:: FnKind < ' tcx > ,
545+ _: & ' tcx hir:: FnDecl < ' tcx > ,
546+ _: & ' tcx hir:: Body < ' tcx > ,
547+ _: rustc_span:: Span ,
548+ _: rustc_span:: def_id:: LocalDefId ,
549+ ) {
550+ // We make the assumption that we will visit the function
551+ // declaration first, before visiting the body.
552+ self . inside_fn_signature = true ;
553+ }
554+
555+ // This may be a function's body, which would indicate that we are
556+ // no longer in the signature. Even if it's not, a body cannot
557+ // occur inside a function signature.
558+ #[ instrument( skip_all) ]
559+ fn check_body ( & mut self , _: & LateContext < ' tcx > , _: & hir:: Body < ' tcx > ) {
560+ self . inside_fn_signature = false ;
561+ }
562+ }
0 commit comments