]> git.lizzy.rs Git - rust.git/blob - src/librustc_builtin_macros/format_foreign.rs
Rollup merge of #69577 - GuillaumeGomez:cleanup-e0375, r=Dylan-DPC
[rust.git] / src / librustc_builtin_macros / format_foreign.rs
1 pub mod printf {
2     use super::strcursor::StrCursor as Cur;
3     use rustc_span::InnerSpan;
4
5     /// Represents a single `printf`-style substitution.
6     #[derive(Clone, PartialEq, Debug)]
7     pub enum Substitution<'a> {
8         /// A formatted output substitution with its internal byte offset.
9         Format(Format<'a>),
10         /// A literal `%%` escape.
11         Escape,
12     }
13
14     impl<'a> Substitution<'a> {
15         pub fn as_str(&self) -> &str {
16             match *self {
17                 Substitution::Format(ref fmt) => fmt.span,
18                 Substitution::Escape => "%%",
19             }
20         }
21
22         pub fn position(&self) -> Option<InnerSpan> {
23             match *self {
24                 Substitution::Format(ref fmt) => Some(fmt.position),
25                 _ => None,
26             }
27         }
28
29         pub fn set_position(&mut self, start: usize, end: usize) {
30             match self {
31                 Substitution::Format(ref mut fmt) => {
32                     fmt.position = InnerSpan::new(start, end);
33                 }
34                 _ => {}
35             }
36         }
37
38         /// Translate this substitution into an equivalent Rust formatting directive.
39         ///
40         /// This ignores cases where the substitution does not have an exact equivalent, or where
41         /// the substitution would be unnecessary.
42         pub fn translate(&self) -> Option<String> {
43             match *self {
44                 Substitution::Format(ref fmt) => fmt.translate(),
45                 Substitution::Escape => None,
46             }
47         }
48     }
49
50     #[derive(Clone, PartialEq, Debug)]
51     /// A single `printf`-style formatting directive.
52     pub struct Format<'a> {
53         /// The entire original formatting directive.
54         pub span: &'a str,
55         /// The (1-based) parameter to be converted.
56         pub parameter: Option<u16>,
57         /// Formatting flags.
58         pub flags: &'a str,
59         /// Minimum width of the output.
60         pub width: Option<Num>,
61         /// Precision of the conversion.
62         pub precision: Option<Num>,
63         /// Length modifier for the conversion.
64         pub length: Option<&'a str>,
65         /// Type of parameter being converted.
66         pub type_: &'a str,
67         /// Byte offset for the start and end of this formatting directive.
68         pub position: InnerSpan,
69     }
70
71     impl Format<'_> {
72         /// Translate this directive into an equivalent Rust formatting directive.
73         ///
74         /// Returns `None` in cases where the `printf` directive does not have an exact Rust
75         /// equivalent, rather than guessing.
76         pub fn translate(&self) -> Option<String> {
77             use std::fmt::Write;
78
79             let (c_alt, c_zero, c_left, c_plus) = {
80                 let mut c_alt = false;
81                 let mut c_zero = false;
82                 let mut c_left = false;
83                 let mut c_plus = false;
84                 for c in self.flags.chars() {
85                     match c {
86                         '#' => c_alt = true,
87                         '0' => c_zero = true,
88                         '-' => c_left = true,
89                         '+' => c_plus = true,
90                         _ => return None,
91                     }
92                 }
93                 (c_alt, c_zero, c_left, c_plus)
94             };
95
96             // Has a special form in Rust for numbers.
97             let fill = c_zero.then_some("0");
98
99             let align = c_left.then_some("<");
100
101             // Rust doesn't have an equivalent to the `' '` flag.
102             let sign = c_plus.then_some("+");
103
104             // Not *quite* the same, depending on the type...
105             let alt = c_alt;
106
107             let width = match self.width {
108                 Some(Num::Next) => {
109                     // NOTE: Rust doesn't support this.
110                     return None;
111                 }
112                 w @ Some(Num::Arg(_)) => w,
113                 w @ Some(Num::Num(_)) => w,
114                 None => None,
115             };
116
117             let precision = self.precision;
118
119             // NOTE: although length *can* have an effect, we can't duplicate the effect in Rust, so
120             // we just ignore it.
121
122             let (type_, use_zero_fill, is_int) = match self.type_ {
123                 "d" | "i" | "u" => (None, true, true),
124                 "f" | "F" => (None, false, false),
125                 "s" | "c" => (None, false, false),
126                 "e" | "E" => (Some(self.type_), true, false),
127                 "x" | "X" | "o" => (Some(self.type_), true, true),
128                 "p" => (Some(self.type_), false, true),
129                 "g" => (Some("e"), true, false),
130                 "G" => (Some("E"), true, false),
131                 _ => return None,
132             };
133
134             let (fill, width, precision) = match (is_int, width, precision) {
135                 (true, Some(_), Some(_)) => {
136                     // Rust can't duplicate this insanity.
137                     return None;
138                 }
139                 (true, None, Some(p)) => (Some("0"), Some(p), None),
140                 (true, w, None) => (fill, w, None),
141                 (false, w, p) => (fill, w, p),
142             };
143
144             let align = match (self.type_, width.is_some(), align.is_some()) {
145                 ("s", true, false) => Some(">"),
146                 _ => align,
147             };
148
149             let (fill, zero_fill) = match (fill, use_zero_fill) {
150                 (Some("0"), true) => (None, true),
151                 (fill, _) => (fill, false),
152             };
153
154             let alt = match type_ {
155                 Some("x") | Some("X") => alt,
156                 _ => false,
157             };
158
159             let has_options = fill.is_some()
160                 || align.is_some()
161                 || sign.is_some()
162                 || alt
163                 || zero_fill
164                 || width.is_some()
165                 || precision.is_some()
166                 || type_.is_some();
167
168             // Initialise with a rough guess.
169             let cap = self.span.len() + if has_options { 2 } else { 0 };
170             let mut s = String::with_capacity(cap);
171
172             s.push_str("{");
173
174             if let Some(arg) = self.parameter {
175                 write!(s, "{}", arg.checked_sub(1)?).ok()?;
176             }
177
178             if has_options {
179                 s.push_str(":");
180
181                 let align = if let Some(fill) = fill {
182                     s.push_str(fill);
183                     align.or(Some(">"))
184                 } else {
185                     align
186                 };
187
188                 if let Some(align) = align {
189                     s.push_str(align);
190                 }
191
192                 if let Some(sign) = sign {
193                     s.push_str(sign);
194                 }
195
196                 if alt {
197                     s.push_str("#");
198                 }
199
200                 if zero_fill {
201                     s.push_str("0");
202                 }
203
204                 if let Some(width) = width {
205                     width.translate(&mut s).ok()?;
206                 }
207
208                 if let Some(precision) = precision {
209                     s.push_str(".");
210                     precision.translate(&mut s).ok()?;
211                 }
212
213                 if let Some(type_) = type_ {
214                     s.push_str(type_);
215                 }
216             }
217
218             s.push_str("}");
219             Some(s)
220         }
221     }
222
223     /// A general number used in a `printf` formatting directive.
224     #[derive(Copy, Clone, PartialEq, Debug)]
225     pub enum Num {
226         // The range of these values is technically bounded by `NL_ARGMAX`... but, at least for GNU
227         // libc, it apparently has no real fixed limit.  A `u16` is used here on the basis that it
228         // is *vanishingly* unlikely that *anyone* is going to try formatting something wider, or
229         // with more precision, than 32 thousand positions which is so wide it couldn't possibly fit
230         // on a screen.
231         /// A specific, fixed value.
232         Num(u16),
233         /// The value is derived from a positional argument.
234         Arg(u16),
235         /// The value is derived from the "next" unconverted argument.
236         Next,
237     }
238
239     impl Num {
240         fn from_str(s: &str, arg: Option<&str>) -> Self {
241             if let Some(arg) = arg {
242                 Num::Arg(arg.parse().unwrap_or_else(|_| panic!("invalid format arg `{:?}`", arg)))
243             } else if s == "*" {
244                 Num::Next
245             } else {
246                 Num::Num(s.parse().unwrap_or_else(|_| panic!("invalid format num `{:?}`", s)))
247             }
248         }
249
250         fn translate(&self, s: &mut String) -> std::fmt::Result {
251             use std::fmt::Write;
252             match *self {
253                 Num::Num(n) => write!(s, "{}", n),
254                 Num::Arg(n) => {
255                     let n = n.checked_sub(1).ok_or(std::fmt::Error)?;
256                     write!(s, "{}$", n)
257                 }
258                 Num::Next => write!(s, "*"),
259             }
260         }
261     }
262
263     /// Returns an iterator over all substitutions in a given string.
264     pub fn iter_subs(s: &str, start_pos: usize) -> Substitutions<'_> {
265         Substitutions { s, pos: start_pos }
266     }
267
268     /// Iterator over substitutions in a string.
269     pub struct Substitutions<'a> {
270         s: &'a str,
271         pos: usize,
272     }
273
274     impl<'a> Iterator for Substitutions<'a> {
275         type Item = Substitution<'a>;
276         fn next(&mut self) -> Option<Self::Item> {
277             let (mut sub, tail) = parse_next_substitution(self.s)?;
278             self.s = tail;
279             match sub {
280                 Substitution::Format(_) => {
281                     if let Some(inner_span) = sub.position() {
282                         sub.set_position(inner_span.start + self.pos, inner_span.end + self.pos);
283                         self.pos += inner_span.end;
284                     }
285                 }
286                 Substitution::Escape => self.pos += 2,
287             }
288             Some(sub)
289         }
290
291         fn size_hint(&self) -> (usize, Option<usize>) {
292             // Substitutions are at least 2 characters long.
293             (0, Some(self.s.len() / 2))
294         }
295     }
296
297     enum State {
298         Start,
299         Flags,
300         Width,
301         WidthArg,
302         Prec,
303         PrecInner,
304         Length,
305         Type,
306     }
307
308     /// Parse the next substitution from the input string.
309     pub fn parse_next_substitution(s: &str) -> Option<(Substitution<'_>, &str)> {
310         use self::State::*;
311
312         let at = {
313             let start = s.find('%')?;
314             match s[start + 1..].chars().next()? {
315                 '%' => return Some((Substitution::Escape, &s[start + 2..])),
316                 _ => { /* fall-through */ }
317             }
318
319             Cur::new_at(&s[..], start)
320         };
321
322         // This is meant to be a translation of the following regex:
323         //
324         // ```regex
325         // (?x)
326         // ^ %
327         // (?: (?P<parameter> \d+) \$ )?
328         // (?P<flags> [-+ 0\#']* )
329         // (?P<width> \d+ | \* (?: (?P<widtha> \d+) \$ )? )?
330         // (?: \. (?P<precision> \d+ | \* (?: (?P<precisiona> \d+) \$ )? ) )?
331         // (?P<length>
332         //     # Standard
333         //     hh | h | ll | l | L | z | j | t
334         //
335         //     # Other
336         //     | I32 | I64 | I | q
337         // )?
338         // (?P<type> . )
339         // ```
340
341         // Used to establish the full span at the end.
342         let start = at;
343         // The current position within the string.
344         let mut at = at.at_next_cp()?;
345         // `c` is the next codepoint, `next` is a cursor after it.
346         let (mut c, mut next) = at.next_cp()?;
347
348         // Update `at`, `c`, and `next`, exiting if we're out of input.
349         macro_rules! move_to {
350             ($cur:expr) => {{
351                 at = $cur;
352                 let (c_, next_) = at.next_cp()?;
353                 c = c_;
354                 next = next_;
355             }};
356         }
357
358         // Constructs a result when parsing fails.
359         //
360         // Note: `move` used to capture copies of the cursors as they are *now*.
361         let fallback = move || {
362             return Some((
363                 Substitution::Format(Format {
364                     span: start.slice_between(next).unwrap(),
365                     parameter: None,
366                     flags: "",
367                     width: None,
368                     precision: None,
369                     length: None,
370                     type_: at.slice_between(next).unwrap(),
371                     position: InnerSpan::new(start.at, next.at),
372                 }),
373                 next.slice_after(),
374             ));
375         };
376
377         // Next parsing state.
378         let mut state = Start;
379
380         // Sadly, Rust isn't *quite* smart enough to know these *must* be initialised by the end.
381         let mut parameter: Option<u16> = None;
382         let mut flags: &str = "";
383         let mut width: Option<Num> = None;
384         let mut precision: Option<Num> = None;
385         let mut length: Option<&str> = None;
386         let mut type_: &str = "";
387         let end: Cur<'_>;
388
389         if let Start = state {
390             match c {
391                 '1'..='9' => {
392                     let end = at_next_cp_while(next, is_digit);
393                     match end.next_cp() {
394                         // Yes, this *is* the parameter.
395                         Some(('$', end2)) => {
396                             state = Flags;
397                             parameter = Some(at.slice_between(end).unwrap().parse().unwrap());
398                             move_to!(end2);
399                         }
400                         // Wait, no, actually, it's the width.
401                         Some(_) => {
402                             state = Prec;
403                             parameter = None;
404                             flags = "";
405                             width = Some(Num::from_str(at.slice_between(end).unwrap(), None));
406                             move_to!(end);
407                         }
408                         // It's invalid, is what it is.
409                         None => return fallback(),
410                     }
411                 }
412                 _ => {
413                     state = Flags;
414                     parameter = None;
415                     move_to!(at);
416                 }
417             }
418         }
419
420         if let Flags = state {
421             let end = at_next_cp_while(at, is_flag);
422             state = Width;
423             flags = at.slice_between(end).unwrap();
424             move_to!(end);
425         }
426
427         if let Width = state {
428             match c {
429                 '*' => {
430                     state = WidthArg;
431                     move_to!(next);
432                 }
433                 '1'..='9' => {
434                     let end = at_next_cp_while(next, is_digit);
435                     state = Prec;
436                     width = Some(Num::from_str(at.slice_between(end).unwrap(), None));
437                     move_to!(end);
438                 }
439                 _ => {
440                     state = Prec;
441                     width = None;
442                     move_to!(at);
443                 }
444             }
445         }
446
447         if let WidthArg = state {
448             let end = at_next_cp_while(at, is_digit);
449             match end.next_cp() {
450                 Some(('$', end2)) => {
451                     state = Prec;
452                     width = Some(Num::from_str("", Some(at.slice_between(end).unwrap())));
453                     move_to!(end2);
454                 }
455                 _ => {
456                     state = Prec;
457                     width = Some(Num::Next);
458                     move_to!(end);
459                 }
460             }
461         }
462
463         if let Prec = state {
464             match c {
465                 '.' => {
466                     state = PrecInner;
467                     move_to!(next);
468                 }
469                 _ => {
470                     state = Length;
471                     precision = None;
472                     move_to!(at);
473                 }
474             }
475         }
476
477         if let PrecInner = state {
478             match c {
479                 '*' => {
480                     let end = at_next_cp_while(next, is_digit);
481                     match end.next_cp() {
482                         Some(('$', end2)) => {
483                             state = Length;
484                             precision = Some(Num::from_str("*", next.slice_between(end)));
485                             move_to!(end2);
486                         }
487                         _ => {
488                             state = Length;
489                             precision = Some(Num::Next);
490                             move_to!(end);
491                         }
492                     }
493                 }
494                 '0'..='9' => {
495                     let end = at_next_cp_while(next, is_digit);
496                     state = Length;
497                     precision = Some(Num::from_str(at.slice_between(end).unwrap(), None));
498                     move_to!(end);
499                 }
500                 _ => return fallback(),
501             }
502         }
503
504         if let Length = state {
505             let c1_next1 = next.next_cp();
506             match (c, c1_next1) {
507                 ('h', Some(('h', next1))) | ('l', Some(('l', next1))) => {
508                     state = Type;
509                     length = Some(at.slice_between(next1).unwrap());
510                     move_to!(next1);
511                 }
512
513                 ('h', _) | ('l', _) | ('L', _) | ('z', _) | ('j', _) | ('t', _) | ('q', _) => {
514                     state = Type;
515                     length = Some(at.slice_between(next).unwrap());
516                     move_to!(next);
517                 }
518
519                 ('I', _) => {
520                     let end = next
521                         .at_next_cp()
522                         .and_then(|end| end.at_next_cp())
523                         .map(|end| (next.slice_between(end).unwrap(), end));
524                     let end = match end {
525                         Some(("32", end)) => end,
526                         Some(("64", end)) => end,
527                         _ => next,
528                     };
529                     state = Type;
530                     length = Some(at.slice_between(end).unwrap());
531                     move_to!(end);
532                 }
533
534                 _ => {
535                     state = Type;
536                     length = None;
537                     move_to!(at);
538                 }
539             }
540         }
541
542         if let Type = state {
543             drop(c);
544             type_ = at.slice_between(next).unwrap();
545
546             // Don't use `move_to!` here, as we *can* be at the end of the input.
547             at = next;
548         }
549
550         drop(c);
551         drop(next);
552
553         end = at;
554         let position = InnerSpan::new(start.at, end.at);
555
556         let f = Format {
557             span: start.slice_between(end).unwrap(),
558             parameter,
559             flags,
560             width,
561             precision,
562             length,
563             type_,
564             position,
565         };
566         Some((Substitution::Format(f), end.slice_after()))
567     }
568
569     fn at_next_cp_while<F>(mut cur: Cur<'_>, mut pred: F) -> Cur<'_>
570     where
571         F: FnMut(char) -> bool,
572     {
573         loop {
574             match cur.next_cp() {
575                 Some((c, next)) => {
576                     if pred(c) {
577                         cur = next;
578                     } else {
579                         return cur;
580                     }
581                 }
582                 None => return cur,
583             }
584         }
585     }
586
587     fn is_digit(c: char) -> bool {
588         match c {
589             '0'..='9' => true,
590             _ => false,
591         }
592     }
593
594     fn is_flag(c: char) -> bool {
595         match c {
596             '0' | '-' | '+' | ' ' | '#' | '\'' => true,
597             _ => false,
598         }
599     }
600
601     #[cfg(test)]
602     mod tests;
603 }
604
605 pub mod shell {
606     use super::strcursor::StrCursor as Cur;
607     use rustc_span::InnerSpan;
608
609     #[derive(Clone, PartialEq, Debug)]
610     pub enum Substitution<'a> {
611         Ordinal(u8, (usize, usize)),
612         Name(&'a str, (usize, usize)),
613         Escape((usize, usize)),
614     }
615
616     impl Substitution<'_> {
617         pub fn as_str(&self) -> String {
618             match self {
619                 Substitution::Ordinal(n, _) => format!("${}", n),
620                 Substitution::Name(n, _) => format!("${}", n),
621                 Substitution::Escape(_) => "$$".into(),
622             }
623         }
624
625         pub fn position(&self) -> Option<InnerSpan> {
626             match self {
627                 Substitution::Ordinal(_, pos)
628                 | Substitution::Name(_, pos)
629                 | Substitution::Escape(pos) => Some(InnerSpan::new(pos.0, pos.1)),
630             }
631         }
632
633         pub fn set_position(&mut self, start: usize, end: usize) {
634             match self {
635                 Substitution::Ordinal(_, ref mut pos)
636                 | Substitution::Name(_, ref mut pos)
637                 | Substitution::Escape(ref mut pos) => *pos = (start, end),
638             }
639         }
640
641         pub fn translate(&self) -> Option<String> {
642             match *self {
643                 Substitution::Ordinal(n, _) => Some(format!("{{{}}}", n)),
644                 Substitution::Name(n, _) => Some(format!("{{{}}}", n)),
645                 Substitution::Escape(_) => None,
646             }
647         }
648     }
649
650     /// Returns an iterator over all substitutions in a given string.
651     pub fn iter_subs(s: &str, start_pos: usize) -> Substitutions<'_> {
652         Substitutions { s, pos: start_pos }
653     }
654
655     /// Iterator over substitutions in a string.
656     pub struct Substitutions<'a> {
657         s: &'a str,
658         pos: usize,
659     }
660
661     impl<'a> Iterator for Substitutions<'a> {
662         type Item = Substitution<'a>;
663         fn next(&mut self) -> Option<Self::Item> {
664             match parse_next_substitution(self.s) {
665                 Some((mut sub, tail)) => {
666                     self.s = tail;
667                     if let Some(InnerSpan { start, end }) = sub.position() {
668                         sub.set_position(start + self.pos, end + self.pos);
669                         self.pos += end;
670                     }
671                     Some(sub)
672                 }
673                 None => None,
674             }
675         }
676
677         fn size_hint(&self) -> (usize, Option<usize>) {
678             (0, Some(self.s.len()))
679         }
680     }
681
682     /// Parse the next substitution from the input string.
683     pub fn parse_next_substitution(s: &str) -> Option<(Substitution<'_>, &str)> {
684         let at = {
685             let start = s.find('$')?;
686             match s[start + 1..].chars().next()? {
687                 '$' => return Some((Substitution::Escape((start, start + 2)), &s[start + 2..])),
688                 c @ '0'..='9' => {
689                     let n = (c as u8) - b'0';
690                     return Some((Substitution::Ordinal(n, (start, start + 2)), &s[start + 2..]));
691                 }
692                 _ => { /* fall-through */ }
693             }
694
695             Cur::new_at(&s[..], start)
696         };
697
698         let at = at.at_next_cp()?;
699         let (c, inner) = at.next_cp()?;
700
701         if !is_ident_head(c) {
702             None
703         } else {
704             let end = at_next_cp_while(inner, is_ident_tail);
705             let slice = at.slice_between(end).unwrap();
706             let start = at.at - 1;
707             let end_pos = at.at + slice.len();
708             Some((Substitution::Name(slice, (start, end_pos)), end.slice_after()))
709         }
710     }
711
712     fn at_next_cp_while<F>(mut cur: Cur<'_>, mut pred: F) -> Cur<'_>
713     where
714         F: FnMut(char) -> bool,
715     {
716         loop {
717             match cur.next_cp() {
718                 Some((c, next)) => {
719                     if pred(c) {
720                         cur = next;
721                     } else {
722                         return cur;
723                     }
724                 }
725                 None => return cur,
726             }
727         }
728     }
729
730     fn is_ident_head(c: char) -> bool {
731         match c {
732             'a'..='z' | 'A'..='Z' | '_' => true,
733             _ => false,
734         }
735     }
736
737     fn is_ident_tail(c: char) -> bool {
738         match c {
739             '0'..='9' => true,
740             c => is_ident_head(c),
741         }
742     }
743
744     #[cfg(test)]
745     mod tests;
746 }
747
748 mod strcursor {
749     pub struct StrCursor<'a> {
750         s: &'a str,
751         pub at: usize,
752     }
753
754     impl<'a> StrCursor<'a> {
755         pub fn new_at(s: &'a str, at: usize) -> StrCursor<'a> {
756             StrCursor { s, at }
757         }
758
759         pub fn at_next_cp(mut self) -> Option<StrCursor<'a>> {
760             match self.try_seek_right_cp() {
761                 true => Some(self),
762                 false => None,
763             }
764         }
765
766         pub fn next_cp(mut self) -> Option<(char, StrCursor<'a>)> {
767             let cp = self.cp_after()?;
768             self.seek_right(cp.len_utf8());
769             Some((cp, self))
770         }
771
772         fn slice_before(&self) -> &'a str {
773             &self.s[0..self.at]
774         }
775
776         pub fn slice_after(&self) -> &'a str {
777             &self.s[self.at..]
778         }
779
780         pub fn slice_between(&self, until: StrCursor<'a>) -> Option<&'a str> {
781             if !str_eq_literal(self.s, until.s) {
782                 None
783             } else {
784                 use std::cmp::{max, min};
785                 let beg = min(self.at, until.at);
786                 let end = max(self.at, until.at);
787                 Some(&self.s[beg..end])
788             }
789         }
790
791         fn cp_after(&self) -> Option<char> {
792             self.slice_after().chars().next()
793         }
794
795         fn try_seek_right_cp(&mut self) -> bool {
796             match self.slice_after().chars().next() {
797                 Some(c) => {
798                     self.at += c.len_utf8();
799                     true
800                 }
801                 None => false,
802             }
803         }
804
805         fn seek_right(&mut self, bytes: usize) {
806             self.at += bytes;
807         }
808     }
809
810     impl Copy for StrCursor<'_> {}
811
812     impl<'a> Clone for StrCursor<'a> {
813         fn clone(&self) -> StrCursor<'a> {
814             *self
815         }
816     }
817
818     impl std::fmt::Debug for StrCursor<'_> {
819         fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
820             write!(fmt, "StrCursor({:?} | {:?})", self.slice_before(), self.slice_after())
821         }
822     }
823
824     fn str_eq_literal(a: &str, b: &str) -> bool {
825         a.as_bytes().as_ptr() == b.as_bytes().as_ptr() && a.len() == b.len()
826     }
827 }