rustc_ast/
format.rs

1use rustc_data_structures::fx::FxHashMap;
2use rustc_macros::{Decodable, Encodable, Walkable};
3use rustc_span::{Ident, Span, Symbol};
4
5use crate::Expr;
6use crate::token::LitKind;
7
8// Definitions:
9//
10// format_args!("hello {abc:.xyz$}!!", abc="world");
11// └──────────────────────────────────────────────┘
12//                     FormatArgs
13//
14// format_args!("hello {abc:.xyz$}!!", abc="world");
15//                                     └─────────┘
16//                                      argument
17//
18// format_args!("hello {abc:.xyz$}!!", abc="world");
19//              └───────────────────┘
20//                     template
21//
22// format_args!("hello {abc:.xyz$}!!", abc="world");
23//               └────┘└─────────┘└┘
24//                      pieces
25//
26// format_args!("hello {abc:.xyz$}!!", abc="world");
27//               └────┘           └┘
28//                   literal pieces
29//
30// format_args!("hello {abc:.xyz$}!!", abc="world");
31//                     └─────────┘
32//                     placeholder
33//
34// format_args!("hello {abc:.xyz$}!!", abc="world");
35//                      └─┘  └─┘
36//                      positions (could be names, numbers, empty, or `*`)
37
38/// (Parsed) format args.
39///
40/// Basically the "AST" for a complete `format_args!()`.
41///
42/// E.g., `format_args!("hello {name}");`.
43#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
44pub struct FormatArgs {
45    pub span: Span,
46    pub template: Vec<FormatArgsPiece>,
47    pub arguments: FormatArguments,
48    /// The raw, un-split format string literal, with no escaping or processing.
49    ///
50    /// Generally only useful for lints that care about the raw bytes the user wrote.
51    pub uncooked_fmt_str: (LitKind, Symbol),
52    /// Was the format literal written in the source?
53    /// - `format!("boo")` => true,
54    /// - `format!(concat!("b", "o", "o"))` => false,
55    /// - `format!(include_str!("boo.txt"))` => false,
56    ///
57    /// If it wasn't written in the source then we have to be careful with spans pointing into it
58    /// and suggestions about rewriting it.
59    pub is_source_literal: bool,
60}
61
62/// A piece of a format template string.
63///
64/// E.g. "hello" or "{name}".
65#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
66pub enum FormatArgsPiece {
67    Literal(Symbol),
68    Placeholder(FormatPlaceholder),
69}
70
71/// The arguments to format_args!().
72///
73/// E.g. `1, 2, name="ferris", n=3`,
74/// but also implicit captured arguments like `x` in `format_args!("{x}")`.
75#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
76pub struct FormatArguments {
77    arguments: Vec<FormatArgument>,
78    num_unnamed_args: usize,
79    num_explicit_args: usize,
80    names: FxHashMap<Symbol, usize>,
81}
82
83impl FormatArguments {
84    pub fn new() -> Self {
85        Self {
86            arguments: Vec::new(),
87            names: FxHashMap::default(),
88            num_unnamed_args: 0,
89            num_explicit_args: 0,
90        }
91    }
92
93    pub fn add(&mut self, arg: FormatArgument) -> usize {
94        let index = self.arguments.len();
95        if let Some(name) = arg.kind.ident() {
96            self.names.insert(name.name, index);
97        } else if self.names.is_empty() {
98            // Only count the unnamed args before the first named arg.
99            // (Any later ones are errors.)
100            self.num_unnamed_args += 1;
101        }
102        if !matches!(arg.kind, FormatArgumentKind::Captured(..)) {
103            // This is an explicit argument.
104            // Make sure that all arguments so far are explicit.
105            assert_eq!(
106                self.num_explicit_args,
107                self.arguments.len(),
108                "captured arguments must be added last"
109            );
110            self.num_explicit_args += 1;
111        }
112        self.arguments.push(arg);
113        index
114    }
115
116    pub fn by_name(&self, name: Symbol) -> Option<(usize, &FormatArgument)> {
117        let i = *self.names.get(&name)?;
118        Some((i, &self.arguments[i]))
119    }
120
121    pub fn by_index(&self, i: usize) -> Option<&FormatArgument> {
122        (i < self.num_explicit_args).then(|| &self.arguments[i])
123    }
124
125    pub fn unnamed_args(&self) -> &[FormatArgument] {
126        &self.arguments[..self.num_unnamed_args]
127    }
128
129    pub fn named_args(&self) -> &[FormatArgument] {
130        &self.arguments[self.num_unnamed_args..self.num_explicit_args]
131    }
132
133    pub fn explicit_args(&self) -> &[FormatArgument] {
134        &self.arguments[..self.num_explicit_args]
135    }
136
137    pub fn all_args(&self) -> &[FormatArgument] {
138        &self.arguments[..]
139    }
140
141    pub fn all_args_mut(&mut self) -> &mut Vec<FormatArgument> {
142        &mut self.arguments
143    }
144}
145
146#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
147pub struct FormatArgument {
148    pub kind: FormatArgumentKind,
149    pub expr: Box<Expr>,
150}
151
152#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
153pub enum FormatArgumentKind {
154    /// `format_args(…, arg)`
155    Normal,
156    /// `format_args(…, arg = 1)`
157    Named(Ident),
158    /// `format_args("… {arg} …")`
159    Captured(Ident),
160}
161
162impl FormatArgumentKind {
163    pub fn ident(&self) -> Option<Ident> {
164        match self {
165            &Self::Normal => None,
166            &Self::Named(id) => Some(id),
167            &Self::Captured(id) => Some(id),
168        }
169    }
170}
171
172#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, Walkable)]
173pub struct FormatPlaceholder {
174    /// Index into [`FormatArgs::arguments`].
175    pub argument: FormatArgPosition,
176    /// The span inside the format string for the full `{…}` placeholder.
177    pub span: Option<Span>,
178    /// `{}`, `{:?}`, or `{:x}`, etc.
179    #[visitable(ignore)]
180    pub format_trait: FormatTrait,
181    /// `{}` or `{:.5}` or `{:-^20}`, etc.
182    #[visitable(ignore)]
183    pub format_options: FormatOptions,
184}
185
186#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, Walkable)]
187pub struct FormatArgPosition {
188    /// Which argument this position refers to (Ok),
189    /// or would've referred to if it existed (Err).
190    #[visitable(ignore)]
191    pub index: Result<usize, usize>,
192    /// What kind of position this is. See [`FormatArgPositionKind`].
193    #[visitable(ignore)]
194    pub kind: FormatArgPositionKind,
195    /// The span of the name or number.
196    pub span: Option<Span>,
197}
198
199#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
200pub enum FormatArgPositionKind {
201    /// `{}` or `{:.*}`
202    Implicit,
203    /// `{1}` or `{:1$}` or `{:.1$}`
204    Number,
205    /// `{a}` or `{:a$}` or `{:.a$}`
206    Named,
207}
208
209#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq, Hash)]
210pub enum FormatTrait {
211    /// `{}`
212    Display,
213    /// `{:?}`
214    Debug,
215    /// `{:e}`
216    LowerExp,
217    /// `{:E}`
218    UpperExp,
219    /// `{:o}`
220    Octal,
221    /// `{:p}`
222    Pointer,
223    /// `{:b}`
224    Binary,
225    /// `{:x}`
226    LowerHex,
227    /// `{:X}`
228    UpperHex,
229}
230
231#[derive(Clone, Encodable, Decodable, Default, Debug, PartialEq, Eq)]
232pub struct FormatOptions {
233    /// The width. E.g. `{:5}` or `{:width$}`.
234    pub width: Option<FormatCount>,
235    /// The precision. E.g. `{:.5}` or `{:.precision$}`.
236    pub precision: Option<FormatCount>,
237    /// The alignment. E.g. `{:>}` or `{:<}` or `{:^}`.
238    pub alignment: Option<FormatAlignment>,
239    /// The fill character. E.g. the `.` in `{:.>10}`.
240    pub fill: Option<char>,
241    /// The `+` or `-` flag.
242    pub sign: Option<FormatSign>,
243    /// The `#` flag.
244    pub alternate: bool,
245    /// The `0` flag. E.g. the `0` in `{:02x}`.
246    pub zero_pad: bool,
247    /// The `x` or `X` flag (for `Debug` only). E.g. the `x` in `{:x?}`.
248    pub debug_hex: Option<FormatDebugHex>,
249}
250
251#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
252pub enum FormatSign {
253    /// The `+` flag.
254    Plus,
255    /// The `-` flag.
256    Minus,
257}
258
259#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
260pub enum FormatDebugHex {
261    /// The `x` flag in `{:x?}`.
262    Lower,
263    /// The `X` flag in `{:X?}`.
264    Upper,
265}
266
267#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
268pub enum FormatAlignment {
269    /// `{:<}`
270    Left,
271    /// `{:>}`
272    Right,
273    /// `{:^}`
274    Center,
275}
276
277#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
278pub enum FormatCount {
279    /// `{:5}` or `{:.5}`
280    Literal(u16),
281    /// `{:.*}`, `{:.5$}`, or `{:a$}`, etc.
282    Argument(FormatArgPosition),
283}