@@ -29,6 +29,8 @@ pub mod unescape;
2929#[ cfg( test) ]
3030mod tests;
3131
32+ use std:: num:: NonZeroU8 ;
33+
3234pub use crate :: cursor:: Cursor ;
3335
3436use self :: LiteralKind :: * ;
@@ -179,24 +181,27 @@ pub enum DocStyle {
179181/// `rustc_ast::ast::LitKind`).
180182#[ derive( Clone , Copy , Debug , PartialEq , Eq , PartialOrd , Ord ) ]
181183pub enum LiteralKind {
182- /// " 12_u8", " 0o100", " 0b120i99", " 1f32" .
184+ /// ` 12_u8`, ` 0o100`, ` 0b120i99`, ` 1f32` .
183185 Int { base : Base , empty_int : bool } ,
184- /// " 12.34f32", " 1e3" , but not " 1f32" .
186+ /// ` 12.34f32`, ` 1e3` , but not ` 1f32` .
185187 Float { base : Base , empty_exponent : bool } ,
186- /// " 'a'", " '\\'", " '''", "';"
188+ /// ` 'a'`, ` '\\'`, ` '''`, `';`
187189 Char { terminated : bool } ,
188- /// " b'a'", " b'\\'", " b'''", " b';"
190+ /// ` b'a'`, ` b'\\'`, ` b'''`, ` b';`
189191 Byte { terminated : bool } ,
190- /// "" abc"", "" abc"
192+ /// `" abc"`, `" abc`
191193 Str { terminated : bool } ,
192- /// " b"abc"", " b"abc"
194+ /// ` b"abc"`, ` b"abc`
193195 ByteStr { terminated : bool } ,
194196 /// `c"abc"`, `c"abc`
195197 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
197202 /// an invalid literal.
198203 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`
200205 /// indicates an invalid literal.
201206 RawByteStr { n_hashes : Option < u8 > } ,
202207 /// `cr"abc"`, "cr#"abc"#", `cr#"a`. `None` indicates an invalid literal.
@@ -365,6 +370,49 @@ impl Cursor<'_> {
365370 _ => self . ident_or_unknown_prefix ( ) ,
366371 } ,
367372
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+
368416 // Byte literal, byte string literal, raw byte string literal or identifier.
369417 'b' => self . c_or_byte_string (
370418 |terminated| ByteStr { terminated } ,
@@ -758,6 +806,34 @@ impl Cursor<'_> {
758806 false
759807 }
760808
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+
761837 /// Eats the double-quoted string and returns `n_hashes` and an error if encountered.
762838 fn raw_double_quoted_string ( & mut self , prefix_len : u32 ) -> Result < u8 , RawStrError > {
763839 // Wrap the actual function to handle the error with too many hashes.
0 commit comments