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} ;
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_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
+ ] ) ;
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
@@ -231,6 +313,47 @@ fn build_mismatch_suggestion(
231
313
}
232
314
}
233
315
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
+
234
357
#[ derive( Debug , Copy , Clone , PartialEq ) ]
235
358
enum LifetimeSource {
236
359
Reference ,
@@ -329,3 +452,115 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeInfoCollector<'a, 'tcx> {
329
452
self . is_ref = old_is_ref;
330
453
}
331
454
}
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