compiler/rustc_ast/src/format.rs RUST 284 lines View on github.com → Search inside
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}

Code quality findings 9

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>.
warning correctness unchecked-indexing
Some((i, &self.arguments[i]))
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>.
warning correctness unchecked-indexing
(i < self.num_explicit_args).then(|| &self.arguments[i])
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>.
warning correctness unchecked-indexing
&self.arguments[..self.num_unnamed_args]
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>.
warning correctness unchecked-indexing
&self.arguments[self.num_unnamed_args..self.num_explicit_args]
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>.
warning correctness unchecked-indexing
&self.arguments[..self.num_explicit_args]
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>.
warning correctness unchecked-indexing
&self.arguments[..]
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>.
warning correctness unchecked-indexing
/// Index into [`FormatArgs::arguments`].
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>.
warning correctness unchecked-indexing
/// What kind of position this is. See [`FormatArgPositionKind`].
Info: Including file content directly into the binary at compile time. Ensure this is intended, as it increases binary size and doesn't reflect runtime file changes.
info maintainability include-in-binary
/// - `format!(include_str!("boo.txt"))` => false,

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.