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