Skip to main content

rustc_builtin_macros/format_foreign/printf/
mod.rs

1use rustc_span::InnerSpan;
2
3use super::StrCursor as Cur;
4
5/// Represents a single `printf`-style substitution.
6#[derive(#[automatically_derived]
impl<'a> ::core::clone::Clone for Substitution<'a> {
    #[inline]
    fn clone(&self) -> Substitution<'a> {
        match self {
            Substitution::Format(__self_0) =>
                Substitution::Format(::core::clone::Clone::clone(__self_0)),
            Substitution::Escape(__self_0) =>
                Substitution::Escape(::core::clone::Clone::clone(__self_0)),
        }
    }
}Clone, #[automatically_derived]
impl<'a> ::core::marker::StructuralPartialEq for Substitution<'a> { }
#[automatically_derived]
impl<'a> ::core::cmp::PartialEq for Substitution<'a> {
    #[inline]
    fn eq(&self, other: &Substitution<'a>) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (Substitution::Format(__self_0),
                    Substitution::Format(__arg1_0)) => __self_0 == __arg1_0,
                (Substitution::Escape(__self_0),
                    Substitution::Escape(__arg1_0)) => __self_0 == __arg1_0,
                _ => unsafe { ::core::intrinsics::unreachable() }
            }
    }
}PartialEq, #[automatically_derived]
impl<'a> ::core::fmt::Debug for Substitution<'a> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Substitution::Format(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Format",
                    &__self_0),
            Substitution::Escape(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Escape",
                    &__self_0),
        }
    }
}Debug)]
7pub(crate) enum Substitution<'a> {
8    /// A formatted output substitution with its internal byte offset.
9    Format(Format<'a>),
10    /// A literal `%%` escape, with its start and end indices.
11    Escape((usize, usize)),
12}
13
14impl ToString for Substitution<'_> {
15    fn to_string(&self) -> String {
16        match self {
17            Substitution::Format(fmt) => fmt.span.into(),
18            Substitution::Escape(_) => "%%".into(),
19        }
20    }
21}
22
23impl Substitution<'_> {
24    pub(crate) fn position(&self) -> InnerSpan {
25        match self {
26            Substitution::Format(fmt) => fmt.position,
27            &Substitution::Escape((start, end)) => InnerSpan::new(start, end),
28        }
29    }
30
31    pub(crate) fn set_position(&mut self, start: usize, end: usize) {
32        match self {
33            Substitution::Format(fmt) => fmt.position = InnerSpan::new(start, end),
34            Substitution::Escape(pos) => *pos = (start, end),
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(crate) fn translate(&self) -> Result<String, Option<String>> {
43        match self {
44            Substitution::Format(fmt) => fmt.translate(),
45            Substitution::Escape(_) => Err(None),
46        }
47    }
48}
49
50#[derive(#[automatically_derived]
impl<'a> ::core::clone::Clone for Format<'a> {
    #[inline]
    fn clone(&self) -> Format<'a> {
        Format {
            span: ::core::clone::Clone::clone(&self.span),
            parameter: ::core::clone::Clone::clone(&self.parameter),
            flags: ::core::clone::Clone::clone(&self.flags),
            width: ::core::clone::Clone::clone(&self.width),
            precision: ::core::clone::Clone::clone(&self.precision),
            length: ::core::clone::Clone::clone(&self.length),
            type_: ::core::clone::Clone::clone(&self.type_),
            position: ::core::clone::Clone::clone(&self.position),
        }
    }
}Clone, #[automatically_derived]
impl<'a> ::core::marker::StructuralPartialEq for Format<'a> { }
#[automatically_derived]
impl<'a> ::core::cmp::PartialEq for Format<'a> {
    #[inline]
    fn eq(&self, other: &Format<'a>) -> bool {
        self.span == other.span && self.parameter == other.parameter &&
                                self.flags == other.flags && self.width == other.width &&
                        self.precision == other.precision &&
                    self.length == other.length && self.type_ == other.type_ &&
            self.position == other.position
    }
}PartialEq, #[automatically_derived]
impl<'a> ::core::fmt::Debug for Format<'a> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        let names: &'static _ =
            &["span", "parameter", "flags", "width", "precision", "length",
                        "type_", "position"];
        let values: &[&dyn ::core::fmt::Debug] =
            &[&self.span, &self.parameter, &self.flags, &self.width,
                        &self.precision, &self.length, &self.type_,
                        &&self.position];
        ::core::fmt::Formatter::debug_struct_fields_finish(f, "Format", names,
            values)
    }
}Debug)]
51/// A single `printf`-style formatting directive.
52pub(crate) struct Format<'a> {
53    /// The entire original formatting directive.
54    span: &'a str,
55    /// The (1-based) parameter to be converted.
56    parameter: Option<u16>,
57    /// Formatting flags.
58    flags: &'a str,
59    /// Minimum width of the output.
60    width: Option<Num>,
61    /// Precision of the conversion.
62    precision: Option<Num>,
63    /// Length modifier for the conversion.
64    length: Option<&'a str>,
65    /// Type of parameter being converted.
66    type_: &'a str,
67    /// Byte offset for the start and end of this formatting directive.
68    position: InnerSpan,
69}
70
71impl Format<'_> {
72    /// Translate this directive into an equivalent Rust formatting directive.
73    ///
74    /// Returns `Err` in cases where the `printf` directive does not have an exact Rust
75    /// equivalent, rather than guessing.
76    pub(crate) fn translate(&self) -> Result<String, 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                    _ => {
91                        return Err(Some(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("the flag `{0}` is unknown or unsupported",
                c))
    })format!("the flag `{c}` is unknown or unsupported")));
92                    }
93                }
94            }
95            (c_alt, c_zero, c_left, c_plus)
96        };
97
98        // Has a special form in Rust for numbers.
99        let fill = c_zero.then_some("0");
100
101        let align = c_left.then_some("<");
102
103        // Rust doesn't have an equivalent to the `' '` flag.
104        let sign = c_plus.then_some("+");
105
106        // Not *quite* the same, depending on the type...
107        let alt = c_alt;
108
109        let width = match self.width {
110            Some(Num::Next) => {
111                // NOTE: Rust doesn't support this.
112                return Err(Some(
113                    "you have to use a positional or named parameter for the width".to_string(),
114                ));
115            }
116            w @ Some(Num::Arg(_)) => w,
117            w @ Some(Num::Num(_)) => w,
118            None => None,
119        };
120
121        let precision = self.precision;
122
123        // NOTE: although length *can* have an effect, we can't duplicate the effect in Rust, so
124        // we just ignore it.
125
126        let (type_, use_zero_fill, is_int) = match self.type_ {
127            "d" | "i" | "u" => (None, true, true),
128            "f" | "F" => (None, false, false),
129            "s" | "c" => (None, false, false),
130            "e" | "E" => (Some(self.type_), true, false),
131            "x" | "X" | "o" => (Some(self.type_), true, true),
132            "p" => (Some(self.type_), false, true),
133            "g" => (Some("e"), true, false),
134            "G" => (Some("E"), true, false),
135            _ => {
136                return Err(Some(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("the conversion specifier `{0}` is unknown or unsupported",
                self.type_))
    })format!(
137                    "the conversion specifier `{}` is unknown or unsupported",
138                    self.type_
139                )));
140            }
141        };
142
143        let (fill, width, precision) = match (is_int, width, precision) {
144            (true, Some(_), Some(_)) => {
145                // Rust can't duplicate this insanity.
146                return Err(Some(
147                    "width and precision cannot both be specified for integer conversions"
148                        .to_string(),
149                ));
150            }
151            (true, None, Some(p)) => (Some("0"), Some(p), None),
152            (true, w, None) => (fill, w, None),
153            (false, w, p) => (fill, w, p),
154        };
155
156        let align = match (self.type_, width.is_some(), align.is_some()) {
157            ("s", true, false) => Some(">"),
158            _ => align,
159        };
160
161        let (fill, zero_fill) = match (fill, use_zero_fill) {
162            (Some("0"), true) => (None, true),
163            (fill, _) => (fill, false),
164        };
165
166        let alt = match type_ {
167            Some("x" | "X") => alt,
168            _ => false,
169        };
170
171        let has_options = fill.is_some()
172            || align.is_some()
173            || sign.is_some()
174            || alt
175            || zero_fill
176            || width.is_some()
177            || precision.is_some()
178            || type_.is_some();
179
180        // Initialise with a rough guess.
181        let cap = self.span.len() + if has_options { 2 } else { 0 };
182        let mut s = String::with_capacity(cap);
183
184        s.push('{');
185
186        if let Some(arg) = self.parameter {
187            s.write_fmt(format_args!("{0}", arg.checked_sub(1).ok_or(None)?))write!(s, "{}", arg.checked_sub(1).ok_or(None)?).map_err(|_| None)?;
188        }
189
190        if has_options {
191            s.push(':');
192
193            let align = if let Some(fill) = fill {
194                s.push_str(fill);
195                align.or(Some(">"))
196            } else {
197                align
198            };
199
200            if let Some(align) = align {
201                s.push_str(align);
202            }
203
204            if let Some(sign) = sign {
205                s.push_str(sign);
206            }
207
208            if alt {
209                s.push('#');
210            }
211
212            if zero_fill {
213                s.push('0');
214            }
215
216            if let Some(width) = width {
217                width.translate(&mut s).map_err(|_| None)?;
218            }
219
220            if let Some(precision) = precision {
221                s.push('.');
222                precision.translate(&mut s).map_err(|_| None)?;
223            }
224
225            if let Some(type_) = type_ {
226                s.push_str(type_);
227            }
228        }
229
230        s.push('}');
231        Ok(s)
232    }
233}
234
235/// A general number used in a `printf` formatting directive.
236#[derive(#[automatically_derived]
impl ::core::marker::Copy for Num { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Num { }
#[automatically_derived]
impl ::core::clone::Clone for Num {
    #[inline]
    fn clone(&self) -> Num {
        let _: ::core::clone::AssertParamIsClone<u16>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for Num { }
#[automatically_derived]
impl ::core::cmp::PartialEq for Num {
    #[inline]
    fn eq(&self, other: &Num) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (Num::Num(__self_0), Num::Num(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (Num::Arg(__self_0), Num::Arg(__arg1_0)) =>
                    __self_0 == __arg1_0,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::fmt::Debug for Num {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Num::Num(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Num",
                    &__self_0),
            Num::Arg(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Arg",
                    &__self_0),
            Num::Next => ::core::fmt::Formatter::write_str(f, "Next"),
        }
    }
}Debug)]
237enum Num {
238    // The range of these values is technically bounded by `NL_ARGMAX`... but, at least for GNU
239    // libc, it apparently has no real fixed limit. A `u16` is used here on the basis that it
240    // is *vanishingly* unlikely that *anyone* is going to try formatting something wider, or
241    // with more precision, than 32 thousand positions which is so wide it couldn't possibly fit
242    // on a screen.
243    /// A specific, fixed value.
244    Num(u16),
245    /// The value is derived from a positional argument.
246    Arg(u16),
247    /// The value is derived from the "next" unconverted argument.
248    Next,
249}
250
251impl Num {
252    fn from_str(s: &str, arg: Option<&str>) -> Option<Self> {
253        if let Some(arg) = arg {
254            arg.parse().ok().map(Num::Arg)
255        } else if s == "*" {
256            Some(Num::Next)
257        } else {
258            s.parse().ok().map(Num::Num)
259        }
260    }
261
262    fn translate(&self, s: &mut String) -> std::fmt::Result {
263        use std::fmt::Write;
264        match *self {
265            Num::Num(n) => s.write_fmt(format_args!("{0}", n))write!(s, "{n}"),
266            Num::Arg(n) => {
267                let n = n.checked_sub(1).ok_or(std::fmt::Error)?;
268                s.write_fmt(format_args!("{0}$", n))write!(s, "{n}$")
269            }
270            Num::Next => s.write_fmt(format_args!("*"))write!(s, "*"),
271        }
272    }
273}
274
275/// Returns an iterator over all substitutions in a given string.
276pub(crate) fn iter_subs(s: &str, start_pos: usize) -> Substitutions<'_> {
277    Substitutions { s, pos: start_pos }
278}
279
280/// Iterator over substitutions in a string.
281pub(crate) struct Substitutions<'a> {
282    s: &'a str,
283    pos: usize,
284}
285
286impl<'a> Iterator for Substitutions<'a> {
287    type Item = Substitution<'a>;
288    fn next(&mut self) -> Option<Self::Item> {
289        let (mut sub, tail) = parse_next_substitution(self.s)?;
290        self.s = tail;
291        let InnerSpan { start, end } = sub.position();
292        sub.set_position(start + self.pos, end + self.pos);
293        self.pos += end;
294        Some(sub)
295    }
296
297    fn size_hint(&self) -> (usize, Option<usize>) {
298        // Substitutions are at least 2 characters long.
299        (0, Some(self.s.len() / 2))
300    }
301}
302
303enum State {
304    Start,
305    Flags,
306    Width,
307    WidthArg,
308    Prec,
309    PrecInner,
310    Length,
311    Type,
312}
313
314/// Parse the next substitution from the input string.
315fn parse_next_substitution(s: &str) -> Option<(Substitution<'_>, &str)> {
316    use self::State::*;
317
318    let at = {
319        let start = s.find('%')?;
320        if let '%' = s[start + 1..].chars().next()? {
321            return Some((Substitution::Escape((start, start + 2)), &s[start + 2..]));
322        }
323
324        Cur::new_at(s, start)
325    };
326
327    // This is meant to be a translation of the following regex:
328    //
329    // ```regex
330    // (?x)
331    // ^ %
332    // (?: (?Box<parameter> \d+) \$ )?
333    // (?Box<flags> [-+ 0\#']* )
334    // (?Box<width> \d+ | \* (?: (?Box<widtha> \d+) \$ )? )?
335    // (?: \. (?Box<precision> \d+ | \* (?: (?Box<precisiona> \d+) \$ )? ) )?
336    // (?Box<length>
337    //     # Standard
338    //     hh | h | ll | l | L | z | j | t
339    //
340    //     # Other
341    //     | I32 | I64 | I | q
342    // )?
343    // (?Box<type> . )
344    // ```
345
346    // Used to establish the full span at the end.
347    let start = at;
348    // The current position within the string.
349    let mut at = at.at_next_cp()?;
350    // `c` is the next codepoint, `next` is a cursor after it.
351    let (mut c, mut next) = at.next_cp()?;
352
353    // Update `at`, `c`, and `next`, exiting if we're out of input.
354    macro_rules! move_to {
355        ($cur:expr) => {{
356            at = $cur;
357            let (c_, next_) = at.next_cp()?;
358            c = c_;
359            next = next_;
360        }};
361    }
362
363    // Constructs a result when parsing fails.
364    //
365    // Note: `move` used to capture copies of the cursors as they are *now*.
366    let fallback = move || {
367        Some((
368            Substitution::Format(Format {
369                span: start.slice_between(next).unwrap(),
370                parameter: None,
371                flags: "",
372                width: None,
373                precision: None,
374                length: None,
375                type_: at.slice_between(next).unwrap(),
376                position: InnerSpan::new(start.at, next.at),
377            }),
378            next.slice_after(),
379        ))
380    };
381
382    // Next parsing state.
383    let mut state = Start;
384
385    // Sadly, Rust isn't *quite* smart enough to know these *must* be initialised by the end.
386    let mut parameter: Option<u16> = None;
387    let mut flags: &str = "";
388    let mut width: Option<Num> = None;
389    let mut precision: Option<Num> = None;
390    let mut length: Option<&str> = None;
391    let mut type_: &str = "";
392    let end: Cur<'_>;
393
394    if let Start = state {
395        match c {
396            '1'..='9' => {
397                let end = at_next_cp_while(next, char::is_ascii_digit);
398                match end.next_cp() {
399                    // Yes, this *is* the parameter.
400                    Some(('$', end2)) => {
401                        state = Flags;
402                        parameter = at.slice_between(end).unwrap().parse().ok();
403                        { at = end2; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end2);
404                    }
405                    // Wait, no, actually, it's the width.
406                    Some(_) => {
407                        state = Prec;
408                        parameter = None;
409                        flags = "";
410                        width = at.slice_between(end).and_then(|num| Num::from_str(num, None));
411                        if width.is_none() {
412                            return fallback();
413                        }
414                        { at = end; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end);
415                    }
416                    // It's invalid, is what it is.
417                    None => return fallback(),
418                }
419            }
420            _ => {
421                state = Flags;
422                parameter = None;
423                { at = at; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(at);
424            }
425        }
426    }
427
428    if let Flags = state {
429        let end = at_next_cp_while(at, is_flag);
430        state = Width;
431        flags = at.slice_between(end).unwrap();
432        { at = end; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end);
433    }
434
435    if let Width = state {
436        match c {
437            '*' => {
438                state = WidthArg;
439                { at = next; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(next);
440            }
441            '1'..='9' => {
442                let end = at_next_cp_while(next, char::is_ascii_digit);
443                state = Prec;
444                width = at.slice_between(end).and_then(|num| Num::from_str(num, None));
445                if width.is_none() {
446                    return fallback();
447                }
448                { at = end; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end);
449            }
450            _ => {
451                state = Prec;
452                width = None;
453                { at = at; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(at);
454            }
455        }
456    }
457
458    if let WidthArg = state {
459        let end = at_next_cp_while(at, char::is_ascii_digit);
460        match end.next_cp() {
461            Some(('$', end2)) => {
462                state = Prec;
463                width = Num::from_str("", at.slice_between(end));
464                { at = end2; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end2);
465            }
466            _ => {
467                state = Prec;
468                width = Some(Num::Next);
469                { at = end; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end);
470            }
471        }
472    }
473
474    if let Prec = state {
475        match c {
476            '.' => {
477                state = PrecInner;
478                { at = next; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(next);
479            }
480            _ => {
481                state = Length;
482                precision = None;
483                { at = at; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(at);
484            }
485        }
486    }
487
488    if let PrecInner = state {
489        match c {
490            '*' => {
491                let end = at_next_cp_while(next, char::is_ascii_digit);
492                match end.next_cp() {
493                    Some(('$', end2)) => {
494                        state = Length;
495                        precision = Num::from_str("*", next.slice_between(end));
496                        { at = end2; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end2);
497                    }
498                    _ => {
499                        state = Length;
500                        precision = Some(Num::Next);
501                        { at = end; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end);
502                    }
503                }
504            }
505            '0'..='9' => {
506                let end = at_next_cp_while(next, char::is_ascii_digit);
507                state = Length;
508                precision = at.slice_between(end).and_then(|num| Num::from_str(num, None));
509                { at = end; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end);
510            }
511            _ => return fallback(),
512        }
513    }
514
515    if let Length = state {
516        let c1_next1 = next.next_cp();
517        match (c, c1_next1) {
518            ('h', Some(('h', next1))) | ('l', Some(('l', next1))) => {
519                state = Type;
520                length = Some(at.slice_between(next1).unwrap());
521                { at = next1; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(next1);
522            }
523
524            ('h' | 'l' | 'L' | 'z' | 'j' | 't' | 'q', _) => {
525                state = Type;
526                length = Some(at.slice_between(next).unwrap());
527                { at = next; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(next);
528            }
529
530            ('I', _) => {
531                let end = next
532                    .at_next_cp()
533                    .and_then(|end| end.at_next_cp())
534                    .map(|end| (next.slice_between(end).unwrap(), end));
535                let end = match end {
536                    Some(("32" | "64", end)) => end,
537                    _ => next,
538                };
539                state = Type;
540                length = Some(at.slice_between(end).unwrap());
541                { at = end; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end);
542            }
543
544            _ => {
545                state = Type;
546                length = None;
547                { at = at; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(at);
548            }
549        }
550    }
551
552    if let Type = state {
553        type_ = at.slice_between(next).unwrap();
554
555        // Don't use `move_to!` here, as we *can* be at the end of the input.
556        at = next;
557    }
558
559    let _ = c; // to avoid never used value
560
561    end = at;
562    let position = InnerSpan::new(start.at, end.at);
563
564    let f = Format {
565        span: start.slice_between(end).unwrap(),
566        parameter,
567        flags,
568        width,
569        precision,
570        length,
571        type_,
572        position,
573    };
574    Some((Substitution::Format(f), end.slice_after()))
575}
576
577fn at_next_cp_while<F>(mut cur: Cur<'_>, mut pred: F) -> Cur<'_>
578where
579    F: FnMut(&char) -> bool,
580{
581    loop {
582        match cur.next_cp() {
583            Some((c, next)) if pred(&c) => {
584                cur = next;
585            }
586            _ => return cur,
587        }
588    }
589}
590
591fn is_flag(c: &char) -> bool {
592    #[allow(non_exhaustive_omitted_patterns)] match c {
    '0' | '-' | '+' | ' ' | '#' | '\'' => true,
    _ => false,
}matches!(c, '0' | '-' | '+' | ' ' | '#' | '\'')
593}
594
595#[cfg(test)]
596mod tests;