@@ -35,20 +35,60 @@ use crate::def_id::{DefId, LocalDefIdMap};
35
35
pub ( crate ) use crate :: hir_id:: { HirId , ItemLocalId , ItemLocalMap , OwnerId } ;
36
36
use crate :: intravisit:: { FnKind , VisitorExt } ;
37
37
38
+ #[ derive( Debug , Copy , Clone , PartialEq , Eq , HashStable_Generic ) ]
39
+ pub enum IsAnonInPath {
40
+ No ,
41
+ Yes ,
42
+ }
43
+
44
+ /// A lifetime. The valid field combinations are non-obvious. The following
45
+ /// example shows some of them. See also the comments on `LifetimeName`.
46
+ /// ```
47
+ /// #[repr(C)]
48
+ /// struct S<'a>(&'a u32); // LifetimeName::Param, name='a, IsAnonInPath::No
49
+ /// extern "C" {
50
+ /// fn f1(s: S); // LifetimeName::Param, name='_, IsAnonInPath::Yes
51
+ /// fn f2(s: S<'_>); // LifetimeName::Param, name='_, IsAnonInPath::No
52
+ /// fn f3<'a>(s: S<'a>); // LifetimeName::Param, name='a, IsAnonInPath::No
53
+ /// }
54
+ ///
55
+ /// struct St<'a> { x: &'a u32 } // LifetimeName::Param, name='a, IsAnonInPath::No
56
+ /// fn f() {
57
+ /// _ = St { x: &0 }; // LifetimeName::Infer, name='_, IsAnonInPath::Yes
58
+ /// _ = St::<'_> { x: &0 }; // LifetimeName::Infer, name='_, IsAnonInPath::No
59
+ /// }
60
+ ///
61
+ /// struct Name<'a>(&'a str); // LifetimeName::Param, name='a, IsAnonInPath::No
62
+ /// const A: Name = Name("a"); // LifetimeName::Static, name='_, IsAnonInPath::Yes
63
+ /// const B: &str = ""; // LifetimeName::Static, name='_, IsAnonInPath::No
64
+ /// static C: &'_ str = ""; // LifetimeName::Static, name='_, IsAnonInPath::No
65
+ /// static D: &'static str = ""; // LifetimeName::Static, name='static, IsAnonInPath::No
66
+ ///
67
+ /// trait Tr {}
68
+ /// fn tr(_: Box<dyn Tr>) {} // LifetimeName::ImplicitObjectLifetimeDefault,
69
+ /// name='_, IsAnonInPath::No
70
+ /// // (commented out because these cases trigger errors)
71
+ /// // struct S1<'a>(&'a str); // LifetimeName::Param, name='a, IsAnonInPath::No
72
+ /// // struct S2(S1); // LifetimeName::Error, name='_, IsAnonInPath::Yes
73
+ /// // struct S3(S1<'_>); // LifetimeName::Error, name='_, IsAnonInPath::No
74
+ /// // struct S4(S1<'a>); // LifetimeName::Error, name='a, IsAnonInPath::No
75
+ /// ```
38
76
#[ derive( Debug , Copy , Clone , HashStable_Generic ) ]
39
77
pub struct Lifetime {
40
78
#[ stable_hasher( ignore) ]
41
79
pub hir_id : HirId ,
42
80
43
- /// Either "`'a`", referring to a named lifetime definition,
44
- /// `'_` referring to an anonymous lifetime (either explicitly `'_` or `&type`),
45
- /// or "``" (i.e., `kw::Empty`) when appearing in path.
46
- ///
47
- /// See `Lifetime::suggestion_position` for practical use.
81
+ /// Either a named lifetime definition (e.g. `'a`, `'static`) or an
82
+ /// anonymous lifetime (`'_`, either explicitly written, or inserted for
83
+ /// things like `&type`).
48
84
pub ident : Ident ,
49
85
50
86
/// Semantics of this lifetime.
51
87
pub res : LifetimeName ,
88
+
89
+ /// Is the lifetime anonymous and in a path? Used only for error
90
+ /// suggestions. See `Lifetime::suggestion` for example use.
91
+ pub is_anon_in_path : IsAnonInPath ,
52
92
}
53
93
54
94
#[ derive( Debug , Copy , Clone , HashStable_Generic ) ]
@@ -111,11 +151,12 @@ pub enum LifetimeName {
111
151
/// that was already reported.
112
152
Error ,
113
153
114
- /// User wrote an anonymous lifetime, either `'_` or nothing.
115
- /// The semantics of this lifetime should be inferred by typechecking code.
154
+ /// User wrote an anonymous lifetime, either `'_` or nothing (which gets
155
+ /// converted to `'_`). The semantics of this lifetime should be inferred
156
+ /// by typechecking code.
116
157
Infer ,
117
158
118
- /// User wrote `'static`.
159
+ /// User wrote `'static` or nothing (which gets converted to `'_`) .
119
160
Static ,
120
161
}
121
162
@@ -135,34 +176,56 @@ impl LifetimeName {
135
176
136
177
impl fmt:: Display for Lifetime {
137
178
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
138
- if self . ident . name != kw :: Empty { self . ident . name . fmt ( f) } else { "'_" . fmt ( f ) }
179
+ self . ident . name . fmt ( f)
139
180
}
140
181
}
141
182
142
183
impl Lifetime {
184
+ pub fn new (
185
+ hir_id : HirId ,
186
+ ident : Ident ,
187
+ res : LifetimeName ,
188
+ is_anon_in_path : IsAnonInPath ,
189
+ ) -> Lifetime {
190
+ let lifetime = Lifetime { hir_id, ident, res, is_anon_in_path } ;
191
+
192
+ // Sanity check: elided lifetimes form a strict subset of anonymous lifetimes.
193
+ #[ cfg( debug_assertions) ]
194
+ match ( lifetime. is_elided ( ) , lifetime. is_anonymous ( ) ) {
195
+ ( false , false ) => { } // e.g. `'a`
196
+ ( false , true ) => { } // e.g. explicit `'_`
197
+ ( true , true ) => { } // e.g. `&x`
198
+ ( true , false ) => panic ! ( "bad Lifetime" ) ,
199
+ }
200
+
201
+ lifetime
202
+ }
203
+
143
204
pub fn is_elided ( & self ) -> bool {
144
205
self . res . is_elided ( )
145
206
}
146
207
147
208
pub fn is_anonymous ( & self ) -> bool {
148
- self . ident . name == kw:: Empty || self . ident . name == kw :: UnderscoreLifetime
209
+ self . ident . name == kw:: UnderscoreLifetime
149
210
}
150
211
151
212
pub fn suggestion ( & self , new_lifetime : & str ) -> ( Span , String ) {
152
213
debug_assert ! ( new_lifetime. starts_with( '\'' ) ) ;
153
214
154
- match ( self . ident . name . is_empty ( ) , self . ident . span . is_empty ( ) ) {
215
+ match ( self . is_anon_in_path , self . ident . span . is_empty ( ) ) {
155
216
// The user wrote `Path<T>`, and omitted the `'_,`.
156
- ( true , true ) => ( self . ident . span , format ! ( "{new_lifetime}, " ) ) ,
217
+ ( IsAnonInPath :: Yes , true ) => ( self . ident . span , format ! ( "{new_lifetime}, " ) ) ,
157
218
158
219
// The user wrote `Path` and omitted the `<'_>`.
159
- ( true , false ) => ( self . ident . span . shrink_to_hi ( ) , format ! ( "<{new_lifetime}>" ) ) ,
220
+ ( IsAnonInPath :: Yes , false ) => {
221
+ ( self . ident . span . shrink_to_hi ( ) , format ! ( "<{new_lifetime}>" ) )
222
+ }
160
223
161
224
// The user wrote `&type` or `&mut type`.
162
- ( false , true ) => ( self . ident . span , format ! ( "{new_lifetime} " ) ) ,
225
+ ( IsAnonInPath :: No , true ) => ( self . ident . span , format ! ( "{new_lifetime} " ) ) ,
163
226
164
227
// The user wrote `'a` or `'_`.
165
- ( false , false ) => ( self . ident . span , format ! ( "{new_lifetime}" ) ) ,
228
+ ( IsAnonInPath :: No , false ) => ( self . ident . span , format ! ( "{new_lifetime}" ) ) ,
166
229
}
167
230
}
168
231
}
0 commit comments