@@ -29,6 +29,8 @@ pub mod unescape;
29
29
#[ cfg( test) ]
30
30
mod tests;
31
31
32
+ use std:: num:: NonZeroU8 ;
33
+
32
34
pub use crate :: cursor:: Cursor ;
33
35
34
36
use self :: LiteralKind :: * ;
@@ -179,24 +181,27 @@ pub enum DocStyle {
179
181
/// `rustc_ast::ast::LitKind`).
180
182
#[ derive( Clone , Copy , Debug , PartialEq , Eq , PartialOrd , Ord ) ]
181
183
pub enum LiteralKind {
182
- /// " 12_u8", " 0o100", " 0b120i99", " 1f32" .
184
+ /// ` 12_u8`, ` 0o100`, ` 0b120i99`, ` 1f32` .
183
185
Int { base : Base , empty_int : bool } ,
184
- /// " 12.34f32", " 1e3" , but not " 1f32" .
186
+ /// ` 12.34f32`, ` 1e3` , but not ` 1f32` .
185
187
Float { base : Base , empty_exponent : bool } ,
186
- /// " 'a'", " '\\'", " '''", "';"
188
+ /// ` 'a'`, ` '\\'`, ` '''`, `';`
187
189
Char { terminated : bool } ,
188
- /// " b'a'", " b'\\'", " b'''", " b';"
190
+ /// ` b'a'`, ` b'\\'`, ` b'''`, ` b';`
189
191
Byte { terminated : bool } ,
190
- /// "" abc"", "" abc"
192
+ /// `" abc"`, `" abc`
191
193
Str { terminated : bool } ,
192
- /// " b"abc"", " b"abc"
194
+ /// ` b"abc"`, ` b"abc`
193
195
ByteStr { terminated : bool } ,
194
196
/// `c"abc"`, `c"abc`
195
197
CStr { terminated : bool } ,
196
- /// "r"abc"", "r#"abc"#", "r####"ab"###"c"####", "r#"a". `None` indicates
198
+ /// `#"abc"#`, `#"a`, `##"a"#`. `None` indicates no closing quote.
199
+ /// Allows fewer hashes to close the string to support older editions.
200
+ GuardedStr { n_start_hashes : Option < NonZeroU8 > , n_end_hashes : u8 } ,
201
+ /// `r"abc"`, `r#"abc"#`, `r####"ab"###"c"####`, `r#"a`. `None` indicates
197
202
/// an invalid literal.
198
203
RawStr { n_hashes : Option < u8 > } ,
199
- /// " br"abc"", " br#"abc"#", " br####"ab"###"c"####", " br#"a" . `None`
204
+ /// ` br"abc"`, ` br#"abc"#`, ` br####"ab"###"c"####`, ` br#"a` . `None`
200
205
/// indicates an invalid literal.
201
206
RawByteStr { n_hashes : Option < u8 > } ,
202
207
/// `cr"abc"`, "cr#"abc"#", `cr#"a`. `None` indicates an invalid literal.
@@ -365,6 +370,49 @@ impl Cursor<'_> {
365
370
_ => self . ident_or_unknown_prefix ( ) ,
366
371
} ,
367
372
373
+ // Guarded string literal (reserved syntax).
374
+ '#' if matches ! ( self . first( ) , '"' | '#' ) => {
375
+ // Create a backup to restore later if this
376
+ // turns out to not be a guarded literal.
377
+ let backup = self . clone ( ) ;
378
+
379
+ let mut n_start_hashes: u32 = 1 ; // Already captured one `#`.
380
+ while self . first ( ) == '#' {
381
+ n_start_hashes += 1 ;
382
+ self . bump ( ) ;
383
+ }
384
+
385
+ if self . first ( ) == '"' {
386
+ self . bump ( ) ;
387
+
388
+ let res = self . guarded_double_quoted_string ( n_start_hashes) ;
389
+ let suffix_start = self . pos_within_token ( ) ;
390
+
391
+ if let ( Ok ( n_end_hashes) , Ok ( n) ) = ( res, u8:: try_from ( n_start_hashes) ) {
392
+ self . eat_literal_suffix ( ) ;
393
+
394
+ Literal {
395
+ kind : GuardedStr {
396
+ n_start_hashes : NonZeroU8 :: new ( n) ,
397
+ // Always succeeds because `n_end_hashes <= n`
398
+ n_end_hashes : n_end_hashes. try_into ( ) . unwrap ( ) ,
399
+ } ,
400
+ suffix_start,
401
+ }
402
+ } else {
403
+ Literal {
404
+ kind : GuardedStr { n_start_hashes : None , n_end_hashes : 0 } ,
405
+ suffix_start,
406
+ }
407
+ }
408
+ } else {
409
+ // Not a guarded string, so restore old state.
410
+ * self = backup;
411
+ // Return a pound token.
412
+ Pound
413
+ }
414
+ }
415
+
368
416
// Byte literal, byte string literal, raw byte string literal or identifier.
369
417
'b' => self . c_or_byte_string (
370
418
|terminated| ByteStr { terminated } ,
@@ -758,6 +806,34 @@ impl Cursor<'_> {
758
806
false
759
807
}
760
808
809
+ /// Eats the double-quoted string and returns `n_hashes` and an error if encountered.
810
+ fn guarded_double_quoted_string ( & mut self , n_start_hashes : u32 ) -> Result < u32 , RawStrError > {
811
+ debug_assert ! ( self . prev( ) == '"' ) ;
812
+
813
+ // Lex the string itself as a normal string literal
814
+ // so we can recover that for older editions later.
815
+ if !self . double_quoted_string ( ) {
816
+ return Err ( RawStrError :: NoTerminator {
817
+ expected : n_start_hashes,
818
+ found : 0 ,
819
+ possible_terminator_offset : None ,
820
+ } ) ;
821
+ }
822
+
823
+ // Consume closing '#' symbols.
824
+ // Note that this will not consume extra trailing `#` characters:
825
+ // `###"abcde"####` is lexed as a `GuardedStr { n_hashes: 3 }`
826
+ // followed by a `#` token.
827
+ let mut n_end_hashes = 0 ;
828
+ while self . first ( ) == '#' && n_end_hashes < n_start_hashes {
829
+ n_end_hashes += 1 ;
830
+ self . bump ( ) ;
831
+ }
832
+
833
+ // Handle `n_end_hashes < n_start_hashes` later.
834
+ Ok ( n_end_hashes)
835
+ }
836
+
761
837
/// Eats the double-quoted string and returns `n_hashes` and an error if encountered.
762
838
fn raw_double_quoted_string ( & mut self , prefix_len : u32 ) -> Result < u8 , RawStrError > {
763
839
// Wrap the actual function to handle the error with too many hashes.
0 commit comments