1 pub(crate) mod printf
{
2 use super::strcursor
::StrCursor
as Cur
;
3 use rustc_span
::InnerSpan
;
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.
10 /// A literal `%%` escape, with its start and end indices.
11 Escape((usize, usize)),
14 impl<'a
> Substitution
<'a
> {
15 pub fn as_str(&self) -> &str {
17 Substitution
::Format(fmt
) => fmt
.span
,
18 Substitution
::Escape(_
) => "%%",
22 pub fn position(&self) -> Option
<InnerSpan
> {
24 Substitution
::Format(fmt
) => Some(fmt
.position
),
25 &Substitution
::Escape((start
, end
)) => Some(InnerSpan
::new(start
, end
)),
29 pub fn set_position(&mut self, start
: usize, end
: usize) {
31 Substitution
::Format(fmt
) => fmt
.position
= InnerSpan
::new(start
, end
),
32 Substitution
::Escape(pos
) => *pos
= (start
, end
),
36 /// Translate this substitution into an equivalent Rust formatting directive.
38 /// This ignores cases where the substitution does not have an exact equivalent, or where
39 /// the substitution would be unnecessary.
40 pub fn translate(&self) -> Result
<String
, Option
<String
>> {
42 Substitution
::Format(fmt
) => fmt
.translate(),
43 Substitution
::Escape(_
) => Err(None
),
48 #[derive(Clone, PartialEq, Debug)]
49 /// A single `printf`-style formatting directive.
50 pub struct Format
<'a
> {
51 /// The entire original formatting directive.
53 /// The (1-based) parameter to be converted.
54 pub parameter
: Option
<u16>,
57 /// Minimum width of the output.
58 pub width
: Option
<Num
>,
59 /// Precision of the conversion.
60 pub precision
: Option
<Num
>,
61 /// Length modifier for the conversion.
62 pub length
: Option
<&'a
str>,
63 /// Type of parameter being converted.
65 /// Byte offset for the start and end of this formatting directive.
66 pub position
: InnerSpan
,
70 /// Translate this directive into an equivalent Rust formatting directive.
72 /// Returns `Err` in cases where the `printf` directive does not have an exact Rust
73 /// equivalent, rather than guessing.
74 pub fn translate(&self) -> Result
<String
, Option
<String
>> {
77 let (c_alt
, c_zero
, c_left
, c_plus
) = {
78 let mut c_alt
= false;
79 let mut c_zero
= false;
80 let mut c_left
= false;
81 let mut c_plus
= false;
82 for c
in self.flags
.chars() {
89 return Err(Some(format
!(
90 "the flag `{}` is unknown or unsupported",
96 (c_alt
, c_zero
, c_left
, c_plus
)
99 // Has a special form in Rust for numbers.
100 let fill
= c_zero
.then_some("0");
102 let align
= c_left
.then_some("<");
104 // Rust doesn't have an equivalent to the `' '` flag.
105 let sign
= c_plus
.then_some("+");
107 // Not *quite* the same, depending on the type...
110 let width
= match self.width
{
112 // NOTE: Rust doesn't support this.
114 "you have to use a positional or named parameter for the width".to_string(),
117 w @
Some(Num
::Arg(_
)) => w
,
118 w @
Some(Num
::Num(_
)) => w
,
122 let precision
= self.precision
;
124 // NOTE: although length *can* have an effect, we can't duplicate the effect in Rust, so
125 // we just ignore it.
127 let (type_
, use_zero_fill
, is_int
) = match self.type_
{
128 "d" | "i" | "u" => (None
, true, true),
129 "f" | "F" => (None
, false, false),
130 "s" | "c" => (None
, false, false),
131 "e" | "E" => (Some(self.type_
), true, false),
132 "x" | "X" | "o" => (Some(self.type_
), true, true),
133 "p" => (Some(self.type_
), false, true),
134 "g" => (Some("e"), true, false),
135 "G" => (Some("E"), true, false),
137 return Err(Some(format
!(
138 "the conversion specifier `{}` is unknown or unsupported",
144 let (fill
, width
, precision
) = match (is_int
, width
, precision
) {
145 (true, Some(_
), Some(_
)) => {
146 // Rust can't duplicate this insanity.
148 "width and precision cannot both be specified for integer conversions"
152 (true, None
, Some(p
)) => (Some("0"), Some(p
), None
),
153 (true, w
, None
) => (fill
, w
, None
),
154 (false, w
, p
) => (fill
, w
, p
),
157 let align
= match (self.type_
, width
.is_some(), align
.is_some()) {
158 ("s", true, false) => Some(">"),
162 let (fill
, zero_fill
) = match (fill
, use_zero_fill
) {
163 (Some("0"), true) => (None
, true),
164 (fill
, _
) => (fill
, false),
167 let alt
= match type_
{
168 Some("x" | "X") => alt
,
172 let has_options
= fill
.is_some()
178 || precision
.is_some()
181 // Initialise with a rough guess.
182 let cap
= self.span
.len() + if has_options { 2 }
else { 0 }
;
183 let mut s
= String
::with_capacity(cap
);
187 if let Some(arg
) = self.parameter
{
191 match arg
.checked_sub(1) {
193 None
=> return Err(None
),
196 Err(_
) => return Err(None
),
204 let align
= if let Some(fill
) = fill
{
211 if let Some(align
) = align
{
215 if let Some(sign
) = sign
{
227 if let Some(width
) = width
{
228 match width
.translate(&mut s
) {
229 Err(_
) => return Err(None
),
234 if let Some(precision
) = precision
{
236 match precision
.translate(&mut s
) {
237 Err(_
) => return Err(None
),
242 if let Some(type_
) = type_
{
252 /// A general number used in a `printf` formatting directive.
253 #[derive(Copy, Clone, PartialEq, Debug)]
255 // The range of these values is technically bounded by `NL_ARGMAX`... but, at least for GNU
256 // libc, it apparently has no real fixed limit. A `u16` is used here on the basis that it
257 // is *vanishingly* unlikely that *anyone* is going to try formatting something wider, or
258 // with more precision, than 32 thousand positions which is so wide it couldn't possibly fit
260 /// A specific, fixed value.
262 /// The value is derived from a positional argument.
264 /// The value is derived from the "next" unconverted argument.
269 fn from_str(s
: &str, arg
: Option
<&str>) -> Self {
270 if let Some(arg
) = arg
{
271 Num
::Arg(arg
.parse().unwrap_or_else(|_
| panic
!("invalid format arg `{:?}`", arg
)))
275 Num
::Num(s
.parse().unwrap_or_else(|_
| panic
!("invalid format num `{:?}`", s
)))
279 fn translate(&self, s
: &mut String
) -> std
::fmt
::Result
{
282 Num
::Num(n
) => write
!(s
, "{}", n
),
284 let n
= n
.checked_sub(1).ok_or(std
::fmt
::Error
)?
;
287 Num
::Next
=> write
!(s
, "*"),
292 /// Returns an iterator over all substitutions in a given string.
293 pub fn iter_subs(s
: &str, start_pos
: usize) -> Substitutions
<'_
> {
294 Substitutions { s, pos: start_pos }
297 /// Iterator over substitutions in a string.
298 pub struct Substitutions
<'a
> {
303 impl<'a
> Iterator
for Substitutions
<'a
> {
304 type Item
= Substitution
<'a
>;
305 fn next(&mut self) -> Option
<Self::Item
> {
306 let (mut sub
, tail
) = parse_next_substitution(self.s
)?
;
308 if let Some(InnerSpan { start, end }
) = sub
.position() {
309 sub
.set_position(start
+ self.pos
, end
+ self.pos
);
315 fn size_hint(&self) -> (usize, Option
<usize>) {
316 // Substitutions are at least 2 characters long.
317 (0, Some(self.s
.len() / 2))
332 /// Parse the next substitution from the input string.
333 pub fn parse_next_substitution(s
: &str) -> Option
<(Substitution
<'_
>, &str)> {
337 let start
= s
.find('
%'
)?
;
338 if let '
%'
= s
[start
+ 1..].chars().next()?
{
339 return Some((Substitution
::Escape((start
, start
+ 2)), &s
[start
+ 2..]));
342 Cur
::new_at(s
, start
)
345 // This is meant to be a translation of the following regex:
350 // (?: (?P<parameter> \d+) \$ )?
351 // (?P<flags> [-+ 0\#']* )
352 // (?P<width> \d+ | \* (?: (?P<widtha> \d+) \$ )? )?
353 // (?: \. (?P<precision> \d+ | \* (?: (?P<precisiona> \d+) \$ )? ) )?
356 // hh | h | ll | l | L | z | j | t
359 // | I32 | I64 | I | q
364 // Used to establish the full span at the end.
366 // The current position within the string.
367 let mut at
= at
.at_next_cp()?
;
368 // `c` is the next codepoint, `next` is a cursor after it.
369 let (mut c
, mut next
) = at
.next_cp()?
;
371 // Update `at`, `c`, and `next`, exiting if we're out of input.
372 macro_rules
! move_to
{
375 let (c_
, next_
) = at
.next_cp()?
;
381 // Constructs a result when parsing fails.
383 // Note: `move` used to capture copies of the cursors as they are *now*.
384 let fallback
= move || {
386 Substitution
::Format(Format
{
387 span
: start
.slice_between(next
).unwrap(),
393 type_
: at
.slice_between(next
).unwrap(),
394 position
: InnerSpan
::new(start
.at
, next
.at
),
400 // Next parsing state.
401 let mut state
= Start
;
403 // Sadly, Rust isn't *quite* smart enough to know these *must* be initialised by the end.
404 let mut parameter
: Option
<u16> = None
;
405 let mut flags
: &str = "";
406 let mut width
: Option
<Num
> = None
;
407 let mut precision
: Option
<Num
> = None
;
408 let mut length
: Option
<&str> = None
;
409 let mut type_
: &str = "";
412 if let Start
= state
{
415 let end
= at_next_cp_while(next
, char::is_ascii_digit
);
416 match end
.next_cp() {
417 // Yes, this *is* the parameter.
418 Some(('$'
, end2
)) => {
420 parameter
= Some(at
.slice_between(end
).unwrap().parse().unwrap());
423 // Wait, no, actually, it's the width.
428 width
= Some(Num
::from_str(at
.slice_between(end
).unwrap(), None
));
431 // It's invalid, is what it is.
432 None
=> return fallback(),
443 if let Flags
= state
{
444 let end
= at_next_cp_while(at
, is_flag
);
446 flags
= at
.slice_between(end
).unwrap();
450 if let Width
= state
{
457 let end
= at_next_cp_while(next
, char::is_ascii_digit
);
459 width
= Some(Num
::from_str(at
.slice_between(end
).unwrap(), None
));
470 if let WidthArg
= state
{
471 let end
= at_next_cp_while(at
, char::is_ascii_digit
);
472 match end
.next_cp() {
473 Some(('$'
, end2
)) => {
475 width
= Some(Num
::from_str("", Some(at
.slice_between(end
).unwrap())));
480 width
= Some(Num
::Next
);
486 if let Prec
= state
{
500 if let PrecInner
= state
{
503 let end
= at_next_cp_while(next
, char::is_ascii_digit
);
504 match end
.next_cp() {
505 Some(('$'
, end2
)) => {
507 precision
= Some(Num
::from_str("*", next
.slice_between(end
)));
512 precision
= Some(Num
::Next
);
518 let end
= at_next_cp_while(next
, char::is_ascii_digit
);
520 precision
= Some(Num
::from_str(at
.slice_between(end
).unwrap(), None
));
523 _
=> return fallback(),
527 if let Length
= state
{
528 let c1_next1
= next
.next_cp();
529 match (c
, c1_next1
) {
530 ('h'
, Some(('h'
, next1
))) | ('l'
, Some(('l'
, next1
))) => {
532 length
= Some(at
.slice_between(next1
).unwrap());
536 ('h'
| 'l'
| 'L'
| 'z'
| 'j'
| 't'
| 'q'
, _
) => {
538 length
= Some(at
.slice_between(next
).unwrap());
545 .and_then(|end
| end
.at_next_cp())
546 .map(|end
| (next
.slice_between(end
).unwrap(), end
));
547 let end
= match end
{
548 Some(("32" | "64", end
)) => end
,
552 length
= Some(at
.slice_between(end
).unwrap());
564 if let Type
= state
{
566 type_
= at
.slice_between(next
).unwrap();
568 // Don't use `move_to!` here, as we *can* be at the end of the input.
576 let position
= InnerSpan
::new(start
.at
, end
.at
);
579 span
: start
.slice_between(end
).unwrap(),
588 Some((Substitution
::Format(f
), end
.slice_after()))
591 fn at_next_cp_while
<F
>(mut cur
: Cur
<'_
>, mut pred
: F
) -> Cur
<'_
>
593 F
: FnMut(&char) -> bool
,
596 match cur
.next_cp() {
609 fn is_flag(c
: &char) -> bool
{
610 matches
!(c
, '
0'
| '
-'
| '
+'
| ' '
| '
#' | '\'')
618 use super::strcursor
::StrCursor
as Cur
;
619 use rustc_span
::InnerSpan
;
621 #[derive(Clone, PartialEq, Debug)]
622 pub enum Substitution
<'a
> {
623 Ordinal(u8, (usize, usize)),
624 Name(&'a
str, (usize, usize)),
625 Escape((usize, usize)),
628 impl Substitution
<'_
> {
629 pub fn as_str(&self) -> String
{
631 Substitution
::Ordinal(n
, _
) => format
!("${}", n
),
632 Substitution
::Name(n
, _
) => format
!("${}", n
),
633 Substitution
::Escape(_
) => "$$".into(),
637 pub fn position(&self) -> Option
<InnerSpan
> {
638 let (Self::Ordinal(_
, pos
) | Self::Name(_
, pos
) | Self::Escape(pos
)) = self;
639 Some(InnerSpan
::new(pos
.0, pos
.1))
642 pub fn set_position(&mut self, start
: usize, end
: usize) {
643 let (Self::Ordinal(_
, pos
) | Self::Name(_
, pos
) | Self::Escape(pos
)) = self;
647 pub fn translate(&self) -> Result
<String
, Option
<String
>> {
649 Substitution
::Ordinal(n
, _
) => Ok(format
!("{{{}}}", n
)),
650 Substitution
::Name(n
, _
) => Ok(format
!("{{{}}}", n
)),
651 Substitution
::Escape(_
) => Err(None
),
656 /// Returns an iterator over all substitutions in a given string.
657 pub fn iter_subs(s
: &str, start_pos
: usize) -> Substitutions
<'_
> {
658 Substitutions { s, pos: start_pos }
661 /// Iterator over substitutions in a string.
662 pub struct Substitutions
<'a
> {
667 impl<'a
> Iterator
for Substitutions
<'a
> {
668 type Item
= Substitution
<'a
>;
669 fn next(&mut self) -> Option
<Self::Item
> {
670 let (mut sub
, tail
) = parse_next_substitution(self.s
)?
;
672 if let Some(InnerSpan { start, end }
) = sub
.position() {
673 sub
.set_position(start
+ self.pos
, end
+ self.pos
);
679 fn size_hint(&self) -> (usize, Option
<usize>) {
680 (0, Some(self.s
.len()))
684 /// Parse the next substitution from the input string.
685 pub fn parse_next_substitution(s
: &str) -> Option
<(Substitution
<'_
>, &str)> {
687 let start
= s
.find('$'
)?
;
688 match s
[start
+ 1..].chars().next()?
{
689 '$'
=> return Some((Substitution
::Escape((start
, start
+ 2)), &s
[start
+ 2..])),
691 let n
= (c
as u8) - b'
0'
;
692 return Some((Substitution
::Ordinal(n
, (start
, start
+ 2)), &s
[start
+ 2..]));
694 _
=> { /* fall-through */ }
697 Cur
::new_at(s
, start
)
700 let at
= at
.at_next_cp()?
;
701 let (c
, inner
) = at
.next_cp()?
;
703 if !is_ident_head(c
) {
706 let end
= at_next_cp_while(inner
, is_ident_tail
);
707 let slice
= at
.slice_between(end
).unwrap();
708 let start
= at
.at
- 1;
709 let end_pos
= at
.at
+ slice
.len();
710 Some((Substitution
::Name(slice
, (start
, end_pos
)), end
.slice_after()))
714 fn at_next_cp_while
<F
>(mut cur
: Cur
<'_
>, mut pred
: F
) -> Cur
<'_
>
716 F
: FnMut(char) -> bool
,
719 match cur
.next_cp() {
732 fn is_ident_head(c
: char) -> bool
{
733 c
.is_ascii_alphabetic() || c
== '_'
736 fn is_ident_tail(c
: char) -> bool
{
737 c
.is_ascii_alphanumeric() || c
== '_'
745 pub struct StrCursor
<'a
> {
750 impl<'a
> StrCursor
<'a
> {
751 pub fn new_at(s
: &'a
str, at
: usize) -> StrCursor
<'a
> {
755 pub fn at_next_cp(mut self) -> Option
<StrCursor
<'a
>> {
756 match self.try_seek_right_cp() {
762 pub fn next_cp(mut self) -> Option
<(char, StrCursor
<'a
>)> {
763 let cp
= self.cp_after()?
;
764 self.seek_right(cp
.len_utf8());
768 fn slice_before(&self) -> &'a
str {
772 pub fn slice_after(&self) -> &'a
str {
776 pub fn slice_between(&self, until
: StrCursor
<'a
>) -> Option
<&'a
str> {
777 if !str_eq_literal(self.s
, until
.s
) {
780 use std
::cmp
::{max, min}
;
781 let beg
= min(self.at
, until
.at
);
782 let end
= max(self.at
, until
.at
);
783 Some(&self.s
[beg
..end
])
787 fn cp_after(&self) -> Option
<char> {
788 self.slice_after().chars().next()
791 fn try_seek_right_cp(&mut self) -> bool
{
792 match self.slice_after().chars().next() {
794 self.at
+= c
.len_utf8();
801 fn seek_right(&mut self, bytes
: usize) {
806 impl Copy
for StrCursor
<'_
> {}
808 impl<'a
> Clone
for StrCursor
<'a
> {
809 fn clone(&self) -> StrCursor
<'a
> {
814 impl std
::fmt
::Debug
for StrCursor
<'_
> {
815 fn fmt(&self, fmt
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
816 write
!(fmt
, "StrCursor({:?} | {:?})", self.slice_before(), self.slice_after())
820 fn str_eq_literal(a
: &str, b
: &str) -> bool
{
821 a
.as_bytes().as_ptr() == b
.as_bytes().as_ptr() && a
.len() == b
.len()