@@ -27,6 +27,15 @@ use std::string;
27
27
28
28
use rustc_span:: { InnerSpan , Symbol } ;
29
29
30
+ /// The type of format string that we are parsing.
31
+ #[ derive( Copy , Clone , Debug , Eq , PartialEq ) ]
32
+ pub enum ParseMode {
33
+ /// A normal format string as per `format_args!`.
34
+ Format ,
35
+ /// An inline assembly template string for `asm!`.
36
+ InlineAsm ,
37
+ }
38
+
30
39
#[ derive( Copy , Clone ) ]
31
40
struct InnerOffset ( usize ) ;
32
41
@@ -163,6 +172,7 @@ pub struct ParseError {
163
172
/// This is a recursive-descent parser for the sake of simplicity, and if
164
173
/// necessary there's probably lots of room for improvement performance-wise.
165
174
pub struct Parser < ' a > {
175
+ mode : ParseMode ,
166
176
input : & ' a str ,
167
177
cur : iter:: Peekable < str:: CharIndices < ' a > > ,
168
178
/// Error messages accumulated during parsing
@@ -179,6 +189,8 @@ pub struct Parser<'a> {
179
189
last_opening_brace : Option < InnerSpan > ,
180
190
/// Whether the source string is comes from `println!` as opposed to `format!` or `print!`
181
191
append_newline : bool ,
192
+ /// Whether this formatting string is a literal or it comes from a macro.
193
+ is_literal : bool ,
182
194
}
183
195
184
196
impl < ' a > Iterator for Parser < ' a > {
@@ -201,7 +213,9 @@ impl<'a> Iterator for Parser<'a> {
201
213
if let Some ( end) = self . must_consume ( '}' ) {
202
214
let start = self . to_span_index ( pos) ;
203
215
let end = self . to_span_index ( end + 1 ) ;
204
- self . arg_places . push ( start. to ( end) ) ;
216
+ if self . is_literal {
217
+ self . arg_places . push ( start. to ( end) ) ;
218
+ }
205
219
}
206
220
Some ( NextArgument ( arg) )
207
221
}
@@ -235,10 +249,13 @@ impl<'a> Parser<'a> {
235
249
pub fn new (
236
250
s : & ' a str ,
237
251
style : Option < usize > ,
238
- skips : Vec < usize > ,
252
+ snippet : Option < string :: String > ,
239
253
append_newline : bool ,
254
+ mode : ParseMode ,
240
255
) -> Parser < ' a > {
256
+ let ( skips, is_literal) = find_skips_from_snippet ( snippet, style) ;
241
257
Parser {
258
+ mode,
242
259
input : s,
243
260
cur : s. char_indices ( ) . peekable ( ) ,
244
261
errors : vec ! [ ] ,
@@ -248,6 +265,7 @@ impl<'a> Parser<'a> {
248
265
skips,
249
266
last_opening_brace : None ,
250
267
append_newline,
268
+ is_literal,
251
269
}
252
270
}
253
271
@@ -426,7 +444,10 @@ impl<'a> Parser<'a> {
426
444
/// Parses an `Argument` structure, or what's contained within braces inside the format string.
427
445
fn argument ( & mut self ) -> Argument < ' a > {
428
446
let pos = self . position ( ) ;
429
- let format = self . format ( ) ;
447
+ let format = match self . mode {
448
+ ParseMode :: Format => self . format ( ) ,
449
+ ParseMode :: InlineAsm => self . inline_asm ( ) ,
450
+ } ;
430
451
431
452
// Resolve position after parsing format spec.
432
453
let pos = match pos {
@@ -574,6 +595,36 @@ impl<'a> Parser<'a> {
574
595
spec
575
596
}
576
597
598
+ /// Parses an inline assembly template modifier at the current position, returning the modifier
599
+ /// in the `ty` field of the `FormatSpec` struct.
600
+ fn inline_asm ( & mut self ) -> FormatSpec < ' a > {
601
+ let mut spec = FormatSpec {
602
+ fill : None ,
603
+ align : AlignUnknown ,
604
+ flags : 0 ,
605
+ precision : CountImplied ,
606
+ precision_span : None ,
607
+ width : CountImplied ,
608
+ width_span : None ,
609
+ ty : & self . input [ ..0 ] ,
610
+ ty_span : None ,
611
+ } ;
612
+ if !self . consume ( ':' ) {
613
+ return spec;
614
+ }
615
+
616
+ let ty_span_start = self . cur . peek ( ) . map ( |( pos, _) | * pos) ;
617
+ spec. ty = self . word ( ) ;
618
+ let ty_span_end = self . cur . peek ( ) . map ( |( pos, _) | * pos) ;
619
+ if !spec. ty . is_empty ( ) {
620
+ spec. ty_span = ty_span_start
621
+ . and_then ( |s| ty_span_end. map ( |e| ( s, e) ) )
622
+ . map ( |( start, end) | self . to_span_index ( start) . to ( self . to_span_index ( end) ) ) ;
623
+ }
624
+
625
+ spec
626
+ }
627
+
577
628
/// Parses a `Count` parameter at the current position. This does not check
578
629
/// for 'CountIsNextParam' because that is only used in precision, not
579
630
/// width.
@@ -652,5 +703,103 @@ impl<'a> Parser<'a> {
652
703
}
653
704
}
654
705
706
+ /// Finds the indices of all characters that have been processed and differ between the actual
707
+ /// written code (code snippet) and the `InternedString` that gets processed in the `Parser`
708
+ /// in order to properly synthethise the intra-string `Span`s for error diagnostics.
709
+ fn find_skips_from_snippet (
710
+ snippet : Option < string:: String > ,
711
+ str_style : Option < usize > ,
712
+ ) -> ( Vec < usize > , bool ) {
713
+ let snippet = match snippet {
714
+ Some ( ref s) if s. starts_with ( '"' ) || s. starts_with ( "r#" ) => s,
715
+ _ => return ( vec ! [ ] , false ) ,
716
+ } ;
717
+
718
+ fn find_skips ( snippet : & str , is_raw : bool ) -> Vec < usize > {
719
+ let mut eat_ws = false ;
720
+ let mut s = snippet. chars ( ) . enumerate ( ) . peekable ( ) ;
721
+ let mut skips = vec ! [ ] ;
722
+ while let Some ( ( pos, c) ) = s. next ( ) {
723
+ match ( c, s. peek ( ) ) {
724
+ // skip whitespace and empty lines ending in '\\'
725
+ ( '\\' , Some ( ( next_pos, '\n' ) ) ) if !is_raw => {
726
+ eat_ws = true ;
727
+ skips. push ( pos) ;
728
+ skips. push ( * next_pos) ;
729
+ let _ = s. next ( ) ;
730
+ }
731
+ ( '\\' , Some ( ( next_pos, '\n' | 'n' | 't' ) ) ) if eat_ws => {
732
+ skips. push ( pos) ;
733
+ skips. push ( * next_pos) ;
734
+ let _ = s. next ( ) ;
735
+ }
736
+ ( ' ' | '\n' | '\t' , _) if eat_ws => {
737
+ skips. push ( pos) ;
738
+ }
739
+ ( '\\' , Some ( ( next_pos, 'n' | 't' | '0' | '\\' | '\'' | '\"' ) ) ) => {
740
+ skips. push ( * next_pos) ;
741
+ let _ = s. next ( ) ;
742
+ }
743
+ ( '\\' , Some ( ( _, 'x' ) ) ) if !is_raw => {
744
+ for _ in 0 ..3 {
745
+ // consume `\xAB` literal
746
+ if let Some ( ( pos, _) ) = s. next ( ) {
747
+ skips. push ( pos) ;
748
+ } else {
749
+ break ;
750
+ }
751
+ }
752
+ }
753
+ ( '\\' , Some ( ( _, 'u' ) ) ) if !is_raw => {
754
+ if let Some ( ( pos, _) ) = s. next ( ) {
755
+ skips. push ( pos) ;
756
+ }
757
+ if let Some ( ( next_pos, next_c) ) = s. next ( ) {
758
+ if next_c == '{' {
759
+ skips. push ( next_pos) ;
760
+ let mut i = 0 ; // consume up to 6 hexanumeric chars + closing `}`
761
+ while let ( Some ( ( next_pos, c) ) , true ) = ( s. next ( ) , i < 7 ) {
762
+ if c. is_digit ( 16 ) {
763
+ skips. push ( next_pos) ;
764
+ } else if c == '}' {
765
+ skips. push ( next_pos) ;
766
+ break ;
767
+ } else {
768
+ break ;
769
+ }
770
+ i += 1 ;
771
+ }
772
+ } else if next_c. is_digit ( 16 ) {
773
+ skips. push ( next_pos) ;
774
+ // We suggest adding `{` and `}` when appropriate, accept it here as if
775
+ // it were correct
776
+ let mut i = 0 ; // consume up to 6 hexanumeric chars
777
+ while let ( Some ( ( next_pos, c) ) , _) = ( s. next ( ) , i < 6 ) {
778
+ if c. is_digit ( 16 ) {
779
+ skips. push ( next_pos) ;
780
+ } else {
781
+ break ;
782
+ }
783
+ i += 1 ;
784
+ }
785
+ }
786
+ }
787
+ }
788
+ _ if eat_ws => {
789
+ // `take_while(|c| c.is_whitespace())`
790
+ eat_ws = false ;
791
+ }
792
+ _ => { }
793
+ }
794
+ }
795
+ skips
796
+ }
797
+
798
+ let r_start = str_style. map ( |r| r + 1 ) . unwrap_or ( 0 ) ;
799
+ let r_end = str_style. map ( |r| r) . unwrap_or ( 0 ) ;
800
+ let s = & snippet[ r_start + 1 ..snippet. len ( ) - r_end - 1 ] ;
801
+ ( find_skips ( s, str_style. is_some ( ) ) , true )
802
+ }
803
+
655
804
#[ cfg( test) ]
656
805
mod tests;
0 commit comments