@@ -35,20 +35,60 @@ use crate::def_id::{DefId, LocalDefIdMap};
3535pub ( crate ) use crate :: hir_id:: { HirId , ItemLocalId , ItemLocalMap , OwnerId } ;
3636use crate :: intravisit:: { FnKind , VisitorExt } ;
3737
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); // res=Param, name='a, IsAnonInPath::No
49+ /// unsafe extern "C" {
50+ /// fn f1(s: S); // res=Param, name='_, IsAnonInPath::Yes
51+ /// fn f2(s: S<'_>); // res=Param, name='_, IsAnonInPath::No
52+ /// fn f3<'a>(s: S<'a>); // res=Param, name='a, IsAnonInPath::No
53+ /// }
54+ ///
55+ /// struct St<'a> { x: &'a u32 } // res=Param, name='a, IsAnonInPath::No
56+ /// fn f() {
57+ /// _ = St { x: &0 }; // res=Infer, name='_, IsAnonInPath::Yes
58+ /// _ = St::<'_> { x: &0 }; // res=Infer, name='_, IsAnonInPath::No
59+ /// }
60+ ///
61+ /// struct Name<'a>(&'a str); // res=Param, name='a, IsAnonInPath::No
62+ /// const A: Name = Name("a"); // res=Static, name='_, IsAnonInPath::Yes
63+ /// const B: &str = ""; // res=Static, name='_, IsAnonInPath::No
64+ /// static C: &'_ str = ""; // res=Static, name='_, IsAnonInPath::No
65+ /// static D: &'static str = ""; // res=Static, name='static, IsAnonInPath::No
66+ ///
67+ /// trait Tr {}
68+ /// fn tr(_: Box<dyn Tr>) {} // res=ImplicitObjectLifetimeDefault, name='_, IsAnonInPath::No
69+ ///
70+ /// // (commented out because these cases trigger errors)
71+ /// // struct S1<'a>(&'a str); // res=Param, name='a, IsAnonInPath::No
72+ /// // struct S2(S1); // res=Error, name='_, IsAnonInPath::Yes
73+ /// // struct S3(S1<'_>); // res=Error, name='_, IsAnonInPath::No
74+ /// // struct S4(S1<'a>); // res=Error, name='a, IsAnonInPath::No
75+ /// ```
3876#[ derive( Debug , Copy , Clone , HashStable_Generic ) ]
3977pub struct Lifetime {
4078 #[ stable_hasher( ignore) ]
4179 pub hir_id : HirId ,
4280
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`).
4884 pub ident : Ident ,
4985
5086 /// Semantics of this lifetime.
5187 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 ,
5292}
5393
5494#[ derive( Debug , Copy , Clone , HashStable_Generic ) ]
@@ -111,11 +151,12 @@ pub enum LifetimeName {
111151 /// that was already reported.
112152 Error ,
113153
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.
116157 Infer ,
117158
118- /// User wrote `'static`.
159+ /// User wrote `'static` or nothing (which gets converted to `'_`) .
119160 Static ,
120161}
121162
@@ -135,59 +176,57 @@ impl LifetimeName {
135176
136177impl fmt:: Display for Lifetime {
137178 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)
139180 }
140181}
141182
142- pub enum LifetimeSuggestionPosition {
143- /// The user wrote `'a` or `'_`.
144- Normal ,
145- /// The user wrote `&type` or `&mut type`.
146- Ampersand ,
147- /// The user wrote `Path` and omitted the `<'_>`.
148- ElidedPath ,
149- /// The user wrote `Path<T>`, and omitted the `'_,`.
150- ElidedPathArgument ,
151- /// The user wrote `dyn Trait` and omitted the `+ '_`.
152- ObjectDefault ,
153- }
154-
155183impl 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+
156204 pub fn is_elided ( & self ) -> bool {
157205 self . res . is_elided ( )
158206 }
159207
160208 pub fn is_anonymous ( & self ) -> bool {
161- self . ident . name == kw:: Empty || self . ident . name == kw:: UnderscoreLifetime
162- }
163-
164- pub fn suggestion_position ( & self ) -> ( LifetimeSuggestionPosition , Span ) {
165- if self . ident . name == kw:: Empty {
166- if self . ident . span . is_empty ( ) {
167- ( LifetimeSuggestionPosition :: ElidedPathArgument , self . ident . span )
168- } else {
169- ( LifetimeSuggestionPosition :: ElidedPath , self . ident . span . shrink_to_hi ( ) )
170- }
171- } else if self . res == LifetimeName :: ImplicitObjectLifetimeDefault {
172- ( LifetimeSuggestionPosition :: ObjectDefault , self . ident . span )
173- } else if self . ident . span . is_empty ( ) {
174- ( LifetimeSuggestionPosition :: Ampersand , self . ident . span )
175- } else {
176- ( LifetimeSuggestionPosition :: Normal , self . ident . span )
177- }
209+ self . ident . name == kw:: UnderscoreLifetime
178210 }
179211
180212 pub fn suggestion ( & self , new_lifetime : & str ) -> ( Span , String ) {
181213 debug_assert ! ( new_lifetime. starts_with( '\'' ) ) ;
182- let ( pos, span) = self . suggestion_position ( ) ;
183- let code = match pos {
184- LifetimeSuggestionPosition :: Normal => format ! ( "{new_lifetime}" ) ,
185- LifetimeSuggestionPosition :: Ampersand => format ! ( "{new_lifetime} " ) ,
186- LifetimeSuggestionPosition :: ElidedPath => format ! ( "<{new_lifetime}>" ) ,
187- LifetimeSuggestionPosition :: ElidedPathArgument => format ! ( "{new_lifetime}, " ) ,
188- LifetimeSuggestionPosition :: ObjectDefault => format ! ( "+ {new_lifetime}" ) ,
189- } ;
190- ( span, code)
214+
215+ match ( self . is_anon_in_path , self . ident . span . is_empty ( ) ) {
216+ // The user wrote `Path<T>`, and omitted the `'_,`.
217+ ( IsAnonInPath :: Yes , true ) => ( self . ident . span , format ! ( "{new_lifetime}, " ) ) ,
218+
219+ // The user wrote `Path` and omitted the `<'_>`.
220+ ( IsAnonInPath :: Yes , false ) => {
221+ ( self . ident . span . shrink_to_hi ( ) , format ! ( "<{new_lifetime}>" ) )
222+ }
223+
224+ // The user wrote `&type` or `&mut type`.
225+ ( IsAnonInPath :: No , true ) => ( self . ident . span , format ! ( "{new_lifetime} " ) ) ,
226+
227+ // The user wrote `'a` or `'_`.
228+ ( IsAnonInPath :: No , false ) => ( self . ident . span , format ! ( "{new_lifetime}" ) ) ,
229+ }
191230 }
192231}
193232
0 commit comments