1
+ use std:: slice;
2
+
1
3
use rustc_data_structures:: fx:: FxIndexMap ;
2
4
use rustc_hir:: intravisit:: { self , Visitor } ;
3
5
use 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} ;
5
8
use rustc_span:: Span ;
6
9
use tracing:: instrument;
7
10
@@ -66,7 +69,84 @@ declare_lint! {
66
69
"detects when an elided lifetime uses different syntax between arguments and return values"
67
70
}
68
71
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
+ ] ) ;
70
150
71
151
impl < ' tcx > LateLintPass < ' tcx > for LifetimeStyle {
72
152
#[ instrument( skip_all) ]
@@ -91,6 +171,8 @@ impl<'tcx> LateLintPass<'tcx> for LifetimeStyle {
91
171
}
92
172
93
173
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 ) ;
94
176
}
95
177
}
96
178
@@ -233,6 +315,50 @@ fn build_mismatch_suggestion(
233
315
}
234
316
}
235
317
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
+
236
362
struct Info < ' tcx > {
237
363
type_span : Span ,
238
364
lifetime : & ' tcx hir:: Lifetime ,
@@ -299,3 +425,115 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeInfoCollector<'a, 'tcx> {
299
425
self . type_span = old_type_span;
300
426
}
301
427
}
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