Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
Some((i, &self.arguments[i]))
1use rustc_data_structures::fx::FxHashMap;2use rustc_macros::{Decodable, Encodable, Walkable};3use rustc_span::{Ident, Span, Symbol};45use crate::Expr;6use crate::token::LitKind;78// Definitions:9//10// format_args!("hello {abc:.xyz$}!!", abc="world");11// └──────────────────────────────────────────────┘12// FormatArgs13//14// format_args!("hello {abc:.xyz$}!!", abc="world");15// └─────────┘16// argument17//18// format_args!("hello {abc:.xyz$}!!", abc="world");19// └───────────────────┘20// template21//22// format_args!("hello {abc:.xyz$}!!", abc="world");23// └────┘└─────────┘└┘24// pieces25//26// format_args!("hello {abc:.xyz$}!!", abc="world");27// └────┘ └┘28// literal pieces29//30// format_args!("hello {abc:.xyz$}!!", abc="world");31// └─────────┘32// placeholder33//34// format_args!("hello {abc:.xyz$}!!", abc="world");35// └─┘ └─┘36// positions (could be names, numbers, empty, or `*`)3738/// (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 it58 /// and suggestions about rewriting it.59 pub is_source_literal: bool,60}6162/// 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}7071/// 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}8283impl 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 }9293 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 index114 }115116 pub fn by_name(&self, name: Symbol) -> Option<(usize, &FormatArgument)> {117 let i = *self.names.get(&name)?;118 Some((i, &self.arguments[i]))119 }120121 pub fn by_index(&self, i: usize) -> Option<&FormatArgument> {122 (i < self.num_explicit_args).then(|| &self.arguments[i])123 }124125 pub fn unnamed_args(&self) -> &[FormatArgument] {126 &self.arguments[..self.num_unnamed_args]127 }128129 pub fn named_args(&self) -> &[FormatArgument] {130 &self.arguments[self.num_unnamed_args..self.num_explicit_args]131 }132133 pub fn explicit_args(&self) -> &[FormatArgument] {134 &self.arguments[..self.num_explicit_args]135 }136137 pub fn all_args(&self) -> &[FormatArgument] {138 &self.arguments[..]139 }140141 pub fn all_args_mut(&mut self) -> &mut Vec<FormatArgument> {142 &mut self.arguments143 }144}145146#[derive(Clone, Encodable, Decodable, Debug, Walkable)]147pub struct FormatArgument {148 pub kind: FormatArgumentKind,149 pub expr: Box<Expr>,150}151152#[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}161162impl 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}171172#[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}185186#[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}198199#[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}208209#[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}230231#[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}250251#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]252pub enum FormatSign {253 /// The `+` flag.254 Plus,255 /// The `-` flag.256 Minus,257}258259#[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}266267#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]268pub enum FormatAlignment {269 /// `{:<}`270 Left,271 /// `{:>}`272 Right,273 /// `{:^}`274 Center,275}276277#[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}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.