mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-02 10:04:23 +00:00
Expand format_args!() in rust_ast_lowering.
This commit is contained in:
parent
e83945150f
commit
a4dbcb525b
@ -3699,6 +3699,7 @@ name = "rustc_ast_pretty"
|
|||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustc_ast",
|
"rustc_ast",
|
||||||
|
"rustc_parse_format",
|
||||||
"rustc_span",
|
"rustc_span",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
//! - [`Attribute`]: Metadata associated with item.
|
//! - [`Attribute`]: Metadata associated with item.
|
||||||
//! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators.
|
//! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators.
|
||||||
|
|
||||||
|
pub use crate::format::*;
|
||||||
pub use crate::util::parser::ExprPrecedence;
|
pub use crate::util::parser::ExprPrecedence;
|
||||||
pub use GenericArgs::*;
|
pub use GenericArgs::*;
|
||||||
pub use UnsafeSource::*;
|
pub use UnsafeSource::*;
|
||||||
@ -1269,6 +1270,7 @@ impl Expr {
|
|||||||
ExprKind::Try(..) => ExprPrecedence::Try,
|
ExprKind::Try(..) => ExprPrecedence::Try,
|
||||||
ExprKind::Yield(..) => ExprPrecedence::Yield,
|
ExprKind::Yield(..) => ExprPrecedence::Yield,
|
||||||
ExprKind::Yeet(..) => ExprPrecedence::Yeet,
|
ExprKind::Yeet(..) => ExprPrecedence::Yeet,
|
||||||
|
ExprKind::FormatArgs(..) => ExprPrecedence::FormatArgs,
|
||||||
ExprKind::Err => ExprPrecedence::Err,
|
ExprKind::Err => ExprPrecedence::Err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1498,6 +1500,9 @@ pub enum ExprKind {
|
|||||||
/// with a `ByteStr` literal.
|
/// with a `ByteStr` literal.
|
||||||
IncludedBytes(Lrc<[u8]>),
|
IncludedBytes(Lrc<[u8]>),
|
||||||
|
|
||||||
|
/// A `format_args!()` expression.
|
||||||
|
FormatArgs(P<FormatArgs>),
|
||||||
|
|
||||||
/// Placeholder for an expression that wasn't syntactically well formed in some way.
|
/// Placeholder for an expression that wasn't syntactically well formed in some way.
|
||||||
Err,
|
Err,
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use rustc_ast::ptr::P;
|
use crate::ptr::P;
|
||||||
use rustc_ast::Expr;
|
use crate::Expr;
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_span::symbol::{Ident, Symbol};
|
use rustc_span::symbol::{Ident, Symbol};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
@ -39,7 +39,7 @@ use rustc_span::Span;
|
|||||||
/// Basically the "AST" for a complete `format_args!()`.
|
/// Basically the "AST" for a complete `format_args!()`.
|
||||||
///
|
///
|
||||||
/// E.g., `format_args!("hello {name}");`.
|
/// E.g., `format_args!("hello {name}");`.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||||
pub struct FormatArgs {
|
pub struct FormatArgs {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub template: Vec<FormatArgsPiece>,
|
pub template: Vec<FormatArgsPiece>,
|
||||||
@ -49,7 +49,7 @@ pub struct FormatArgs {
|
|||||||
/// A piece of a format template string.
|
/// A piece of a format template string.
|
||||||
///
|
///
|
||||||
/// E.g. "hello" or "{name}".
|
/// E.g. "hello" or "{name}".
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||||
pub enum FormatArgsPiece {
|
pub enum FormatArgsPiece {
|
||||||
Literal(Symbol),
|
Literal(Symbol),
|
||||||
Placeholder(FormatPlaceholder),
|
Placeholder(FormatPlaceholder),
|
||||||
@ -59,7 +59,7 @@ pub enum FormatArgsPiece {
|
|||||||
///
|
///
|
||||||
/// E.g. `1, 2, name="ferris", n=3`,
|
/// E.g. `1, 2, name="ferris", n=3`,
|
||||||
/// but also implicit captured arguments like `x` in `format_args!("{x}")`.
|
/// but also implicit captured arguments like `x` in `format_args!("{x}")`.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||||
pub struct FormatArguments {
|
pub struct FormatArguments {
|
||||||
arguments: Vec<FormatArgument>,
|
arguments: Vec<FormatArgument>,
|
||||||
num_unnamed_args: usize,
|
num_unnamed_args: usize,
|
||||||
@ -121,18 +121,22 @@ impl FormatArguments {
|
|||||||
&self.arguments[..self.num_explicit_args]
|
&self.arguments[..self.num_explicit_args]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_vec(self) -> Vec<FormatArgument> {
|
pub fn all_args(&self) -> &[FormatArgument] {
|
||||||
self.arguments
|
&self.arguments[..]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_args_mut(&mut self) -> &mut [FormatArgument] {
|
||||||
|
&mut self.arguments[..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||||
pub struct FormatArgument {
|
pub struct FormatArgument {
|
||||||
pub kind: FormatArgumentKind,
|
pub kind: FormatArgumentKind,
|
||||||
pub expr: P<Expr>,
|
pub expr: P<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||||
pub enum FormatArgumentKind {
|
pub enum FormatArgumentKind {
|
||||||
/// `format_args(…, arg)`
|
/// `format_args(…, arg)`
|
||||||
Normal,
|
Normal,
|
||||||
@ -152,7 +156,7 @@ impl FormatArgumentKind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
|
||||||
pub struct FormatPlaceholder {
|
pub struct FormatPlaceholder {
|
||||||
/// Index into [`FormatArgs::arguments`].
|
/// Index into [`FormatArgs::arguments`].
|
||||||
pub argument: FormatArgPosition,
|
pub argument: FormatArgPosition,
|
||||||
@ -164,7 +168,7 @@ pub struct FormatPlaceholder {
|
|||||||
pub format_options: FormatOptions,
|
pub format_options: FormatOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
|
||||||
pub struct FormatArgPosition {
|
pub struct FormatArgPosition {
|
||||||
/// Which argument this position refers to (Ok),
|
/// Which argument this position refers to (Ok),
|
||||||
/// or would've referred to if it existed (Err).
|
/// or would've referred to if it existed (Err).
|
||||||
@ -175,7 +179,7 @@ pub struct FormatArgPosition {
|
|||||||
pub span: Option<Span>,
|
pub span: Option<Span>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
|
||||||
pub enum FormatArgPositionKind {
|
pub enum FormatArgPositionKind {
|
||||||
/// `{}` or `{:.*}`
|
/// `{}` or `{:.*}`
|
||||||
Implicit,
|
Implicit,
|
||||||
@ -185,7 +189,7 @@ pub enum FormatArgPositionKind {
|
|||||||
Named,
|
Named,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum FormatTrait {
|
pub enum FormatTrait {
|
||||||
/// `{}`
|
/// `{}`
|
||||||
Display,
|
Display,
|
||||||
@ -207,7 +211,7 @@ pub enum FormatTrait {
|
|||||||
UpperHex,
|
UpperHex,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Encodable, Decodable, Default, Debug, PartialEq, Eq)]
|
||||||
pub struct FormatOptions {
|
pub struct FormatOptions {
|
||||||
/// The width. E.g. `{:5}` or `{:width$}`.
|
/// The width. E.g. `{:5}` or `{:width$}`.
|
||||||
pub width: Option<FormatCount>,
|
pub width: Option<FormatCount>,
|
||||||
@ -221,7 +225,7 @@ pub struct FormatOptions {
|
|||||||
pub flags: u32,
|
pub flags: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
|
||||||
pub enum FormatAlignment {
|
pub enum FormatAlignment {
|
||||||
/// `{:<}`
|
/// `{:<}`
|
||||||
Left,
|
Left,
|
||||||
@ -231,7 +235,7 @@ pub enum FormatAlignment {
|
|||||||
Center,
|
Center,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
|
||||||
pub enum FormatCount {
|
pub enum FormatCount {
|
||||||
/// `{:5}` or `{:.5}`
|
/// `{:5}` or `{:.5}`
|
||||||
Literal(usize),
|
Literal(usize),
|
@ -42,6 +42,7 @@ pub mod ast_traits;
|
|||||||
pub mod attr;
|
pub mod attr;
|
||||||
pub mod entry;
|
pub mod entry;
|
||||||
pub mod expand;
|
pub mod expand;
|
||||||
|
pub mod format;
|
||||||
pub mod mut_visit;
|
pub mod mut_visit;
|
||||||
pub mod node_id;
|
pub mod node_id;
|
||||||
pub mod ptr;
|
pub mod ptr;
|
||||||
@ -51,6 +52,7 @@ pub mod visit;
|
|||||||
|
|
||||||
pub use self::ast::*;
|
pub use self::ast::*;
|
||||||
pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasSpan, HasTokens};
|
pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasSpan, HasTokens};
|
||||||
|
pub use self::format::*;
|
||||||
|
|
||||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||||
|
|
||||||
|
@ -297,6 +297,10 @@ pub trait MutVisitor: Sized {
|
|||||||
fn visit_inline_asm_sym(&mut self, sym: &mut InlineAsmSym) {
|
fn visit_inline_asm_sym(&mut self, sym: &mut InlineAsmSym) {
|
||||||
noop_visit_inline_asm_sym(sym, self)
|
noop_visit_inline_asm_sym(sym, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_format_args(&mut self, fmt: &mut FormatArgs) {
|
||||||
|
noop_visit_format_args(fmt, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful
|
/// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful
|
||||||
@ -1284,6 +1288,15 @@ pub fn noop_visit_inline_asm_sym<T: MutVisitor>(
|
|||||||
vis.visit_path(path);
|
vis.visit_path(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn noop_visit_format_args<T: MutVisitor>(fmt: &mut FormatArgs, vis: &mut T) {
|
||||||
|
for arg in fmt.arguments.all_args_mut() {
|
||||||
|
if let FormatArgumentKind::Named(name) = &mut arg.kind {
|
||||||
|
vis.visit_ident(name);
|
||||||
|
}
|
||||||
|
vis.visit_expr(&mut arg.expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn noop_visit_expr<T: MutVisitor>(
|
pub fn noop_visit_expr<T: MutVisitor>(
|
||||||
Expr { kind, id, span, attrs, tokens }: &mut Expr,
|
Expr { kind, id, span, attrs, tokens }: &mut Expr,
|
||||||
vis: &mut T,
|
vis: &mut T,
|
||||||
@ -1423,6 +1436,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
|||||||
visit_opt(expr, |expr| vis.visit_expr(expr));
|
visit_opt(expr, |expr| vis.visit_expr(expr));
|
||||||
}
|
}
|
||||||
ExprKind::InlineAsm(asm) => vis.visit_inline_asm(asm),
|
ExprKind::InlineAsm(asm) => vis.visit_inline_asm(asm),
|
||||||
|
ExprKind::FormatArgs(fmt) => vis.visit_format_args(fmt),
|
||||||
ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
|
ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
|
||||||
ExprKind::Struct(se) => {
|
ExprKind::Struct(se) => {
|
||||||
let StructExpr { qself, path, fields, rest } = se.deref_mut();
|
let StructExpr { qself, path, fields, rest } = se.deref_mut();
|
||||||
|
@ -271,6 +271,7 @@ pub enum ExprPrecedence {
|
|||||||
Try,
|
Try,
|
||||||
InlineAsm,
|
InlineAsm,
|
||||||
Mac,
|
Mac,
|
||||||
|
FormatArgs,
|
||||||
|
|
||||||
Array,
|
Array,
|
||||||
Repeat,
|
Repeat,
|
||||||
@ -335,7 +336,8 @@ impl ExprPrecedence {
|
|||||||
| ExprPrecedence::Index
|
| ExprPrecedence::Index
|
||||||
| ExprPrecedence::Try
|
| ExprPrecedence::Try
|
||||||
| ExprPrecedence::InlineAsm
|
| ExprPrecedence::InlineAsm
|
||||||
| ExprPrecedence::Mac => PREC_POSTFIX,
|
| ExprPrecedence::Mac
|
||||||
|
| ExprPrecedence::FormatArgs => PREC_POSTFIX,
|
||||||
|
|
||||||
// Never need parens
|
// Never need parens
|
||||||
ExprPrecedence::Array
|
ExprPrecedence::Array
|
||||||
|
@ -242,6 +242,9 @@ pub trait Visitor<'ast>: Sized {
|
|||||||
fn visit_inline_asm(&mut self, asm: &'ast InlineAsm) {
|
fn visit_inline_asm(&mut self, asm: &'ast InlineAsm) {
|
||||||
walk_inline_asm(self, asm)
|
walk_inline_asm(self, asm)
|
||||||
}
|
}
|
||||||
|
fn visit_format_args(&mut self, fmt: &'ast FormatArgs) {
|
||||||
|
walk_format_args(self, fmt)
|
||||||
|
}
|
||||||
fn visit_inline_asm_sym(&mut self, sym: &'ast InlineAsmSym) {
|
fn visit_inline_asm_sym(&mut self, sym: &'ast InlineAsmSym) {
|
||||||
walk_inline_asm_sym(self, sym)
|
walk_inline_asm_sym(self, sym)
|
||||||
}
|
}
|
||||||
@ -756,6 +759,15 @@ pub fn walk_inline_asm_sym<'a, V: Visitor<'a>>(visitor: &mut V, sym: &'a InlineA
|
|||||||
visitor.visit_path(&sym.path, sym.id);
|
visitor.visit_path(&sym.path, sym.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn walk_format_args<'a, V: Visitor<'a>>(visitor: &mut V, fmt: &'a FormatArgs) {
|
||||||
|
for arg in fmt.arguments.all_args() {
|
||||||
|
if let FormatArgumentKind::Named(name) = arg.kind {
|
||||||
|
visitor.visit_ident(name);
|
||||||
|
}
|
||||||
|
visitor.visit_expr(&arg.expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
|
pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
|
||||||
walk_list!(visitor, visit_attribute, expression.attrs.iter());
|
walk_list!(visitor, visit_attribute, expression.attrs.iter());
|
||||||
|
|
||||||
@ -895,6 +907,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
|
|||||||
ExprKind::MacCall(mac) => visitor.visit_mac_call(mac),
|
ExprKind::MacCall(mac) => visitor.visit_mac_call(mac),
|
||||||
ExprKind::Paren(subexpression) => visitor.visit_expr(subexpression),
|
ExprKind::Paren(subexpression) => visitor.visit_expr(subexpression),
|
||||||
ExprKind::InlineAsm(asm) => visitor.visit_inline_asm(asm),
|
ExprKind::InlineAsm(asm) => visitor.visit_inline_asm(asm),
|
||||||
|
ExprKind::FormatArgs(f) => visitor.visit_format_args(f),
|
||||||
ExprKind::Yield(optional_expression) => {
|
ExprKind::Yield(optional_expression) => {
|
||||||
walk_list!(visitor, visit_expr, optional_expression);
|
walk_list!(visitor, visit_expr, optional_expression);
|
||||||
}
|
}
|
||||||
|
@ -292,6 +292,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||||||
ExprKind::InlineAsm(asm) => {
|
ExprKind::InlineAsm(asm) => {
|
||||||
hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
|
hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
|
||||||
}
|
}
|
||||||
|
ExprKind::FormatArgs(fmt) => self.lower_format_args(e.span, fmt),
|
||||||
ExprKind::Struct(se) => {
|
ExprKind::Struct(se) => {
|
||||||
let rest = match &se.rest {
|
let rest = match &se.rest {
|
||||||
StructRest::Base(e) => Some(self.lower_expr(e)),
|
StructRest::Base(e) => Some(self.lower_expr(e)),
|
||||||
|
356
compiler/rustc_ast_lowering/src/format.rs
Normal file
356
compiler/rustc_ast_lowering/src/format.rs
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
use super::LoweringContext;
|
||||||
|
use rustc_ast as ast;
|
||||||
|
use rustc_ast::visit::{self, Visitor};
|
||||||
|
use rustc_ast::*;
|
||||||
|
use rustc_data_structures::fx::FxIndexSet;
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_span::{
|
||||||
|
sym,
|
||||||
|
symbol::{kw, Ident},
|
||||||
|
Span,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
|
pub(crate) fn lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir> {
|
||||||
|
expand_format_args(self, sp, fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
|
enum ArgumentType {
|
||||||
|
Format(FormatTrait),
|
||||||
|
Usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_argument<'hir>(
|
||||||
|
ctx: &mut LoweringContext<'_, 'hir>,
|
||||||
|
sp: Span,
|
||||||
|
arg: &'hir hir::Expr<'hir>,
|
||||||
|
ty: ArgumentType,
|
||||||
|
) -> hir::Expr<'hir> {
|
||||||
|
// Generate:
|
||||||
|
// ::core::fmt::ArgumentV1::new_…(arg)
|
||||||
|
use ArgumentType::*;
|
||||||
|
use FormatTrait::*;
|
||||||
|
let new_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
|
||||||
|
sp,
|
||||||
|
hir::LangItem::FormatArgument,
|
||||||
|
match ty {
|
||||||
|
Format(Display) => sym::new_display,
|
||||||
|
Format(Debug) => sym::new_debug,
|
||||||
|
Format(LowerExp) => sym::new_lower_exp,
|
||||||
|
Format(UpperExp) => sym::new_upper_exp,
|
||||||
|
Format(Octal) => sym::new_octal,
|
||||||
|
Format(Pointer) => sym::new_pointer,
|
||||||
|
Format(Binary) => sym::new_binary,
|
||||||
|
Format(LowerHex) => sym::new_lower_hex,
|
||||||
|
Format(UpperHex) => sym::new_upper_hex,
|
||||||
|
Usize => sym::from_usize,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
ctx.expr_call_mut(sp, new_fn, std::slice::from_ref(arg))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_count<'hir>(
|
||||||
|
ctx: &mut LoweringContext<'_, 'hir>,
|
||||||
|
sp: Span,
|
||||||
|
count: &Option<FormatCount>,
|
||||||
|
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
|
||||||
|
) -> hir::Expr<'hir> {
|
||||||
|
// Generate:
|
||||||
|
// ::core::fmt::rt::v1::Count::…(…)
|
||||||
|
match count {
|
||||||
|
Some(FormatCount::Literal(n)) => {
|
||||||
|
let count_is = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
|
||||||
|
sp,
|
||||||
|
hir::LangItem::FormatCount,
|
||||||
|
sym::Is,
|
||||||
|
));
|
||||||
|
let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, *n)]);
|
||||||
|
ctx.expr_call_mut(sp, count_is, value)
|
||||||
|
}
|
||||||
|
Some(FormatCount::Argument(arg)) => {
|
||||||
|
if let Ok(arg_index) = arg.index {
|
||||||
|
let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
|
||||||
|
let count_param = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
|
||||||
|
sp,
|
||||||
|
hir::LangItem::FormatCount,
|
||||||
|
sym::Param,
|
||||||
|
));
|
||||||
|
let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, i)]);
|
||||||
|
ctx.expr_call_mut(sp, count_param, value)
|
||||||
|
} else {
|
||||||
|
ctx.expr(sp, hir::ExprKind::Err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => ctx.expr_lang_item_type_relative(sp, hir::LangItem::FormatCount, sym::Implied),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_format_spec<'hir>(
|
||||||
|
ctx: &mut LoweringContext<'_, 'hir>,
|
||||||
|
sp: Span,
|
||||||
|
placeholder: &FormatPlaceholder,
|
||||||
|
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
|
||||||
|
) -> hir::Expr<'hir> {
|
||||||
|
// Generate:
|
||||||
|
// ::core::fmt::rt::v1::Argument {
|
||||||
|
// position: 0usize,
|
||||||
|
// format: ::core::fmt::rt::v1::FormatSpec {
|
||||||
|
// fill: ' ',
|
||||||
|
// align: ::core::fmt::rt::v1::Alignment::Unknown,
|
||||||
|
// flags: 0u32,
|
||||||
|
// precision: ::core::fmt::rt::v1::Count::Implied,
|
||||||
|
// width: ::core::fmt::rt::v1::Count::Implied,
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
let position = match placeholder.argument.index {
|
||||||
|
Ok(arg_index) => {
|
||||||
|
let (i, _) =
|
||||||
|
argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
|
||||||
|
ctx.expr_usize(sp, i)
|
||||||
|
}
|
||||||
|
Err(_) => ctx.expr(sp, hir::ExprKind::Err),
|
||||||
|
};
|
||||||
|
let fill = ctx.expr_char(sp, placeholder.format_options.fill.unwrap_or(' '));
|
||||||
|
let align = ctx.expr_lang_item_type_relative(
|
||||||
|
sp,
|
||||||
|
hir::LangItem::FormatAlignment,
|
||||||
|
match placeholder.format_options.alignment {
|
||||||
|
Some(FormatAlignment::Left) => sym::Left,
|
||||||
|
Some(FormatAlignment::Right) => sym::Right,
|
||||||
|
Some(FormatAlignment::Center) => sym::Center,
|
||||||
|
None => sym::Unknown,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let flags = ctx.expr_u32(sp, placeholder.format_options.flags);
|
||||||
|
let prec = make_count(ctx, sp, &placeholder.format_options.precision, argmap);
|
||||||
|
let width = make_count(ctx, sp, &placeholder.format_options.width, argmap);
|
||||||
|
let format_placeholder_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
|
||||||
|
sp,
|
||||||
|
hir::LangItem::FormatPlaceholder,
|
||||||
|
sym::new,
|
||||||
|
));
|
||||||
|
let args = ctx.arena.alloc_from_iter([position, fill, align, flags, prec, width]);
|
||||||
|
ctx.expr_call_mut(sp, format_placeholder_new, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_format_args<'hir>(
|
||||||
|
ctx: &mut LoweringContext<'_, 'hir>,
|
||||||
|
macsp: Span,
|
||||||
|
fmt: &FormatArgs,
|
||||||
|
) -> hir::ExprKind<'hir> {
|
||||||
|
let lit_pieces =
|
||||||
|
ctx.arena.alloc_from_iter(fmt.template.iter().enumerate().filter_map(|(i, piece)| {
|
||||||
|
match piece {
|
||||||
|
&FormatArgsPiece::Literal(s) => Some(ctx.expr_str(fmt.span, s)),
|
||||||
|
&FormatArgsPiece::Placeholder(_) => {
|
||||||
|
// Inject empty string before placeholders when not already preceded by a literal piece.
|
||||||
|
if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) {
|
||||||
|
Some(ctx.expr_str(fmt.span, kw::Empty))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
let lit_pieces = ctx.expr_array_ref(fmt.span, lit_pieces);
|
||||||
|
|
||||||
|
// Whether we'll use the `Arguments::new_v1_formatted` form (true),
|
||||||
|
// or the `Arguments::new_v1` form (false).
|
||||||
|
let mut use_format_options = false;
|
||||||
|
|
||||||
|
// Create a list of all _unique_ (argument, format trait) combinations.
|
||||||
|
// E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
|
||||||
|
let mut argmap = FxIndexSet::default();
|
||||||
|
for piece in &fmt.template {
|
||||||
|
let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
|
||||||
|
if placeholder.format_options != Default::default() {
|
||||||
|
// Can't use basic form if there's any formatting options.
|
||||||
|
use_format_options = true;
|
||||||
|
}
|
||||||
|
if let Ok(index) = placeholder.argument.index {
|
||||||
|
if !argmap.insert((index, ArgumentType::Format(placeholder.format_trait))) {
|
||||||
|
// Duplicate (argument, format trait) combination,
|
||||||
|
// which we'll only put once in the args array.
|
||||||
|
use_format_options = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let format_options = use_format_options.then(|| {
|
||||||
|
// Generate:
|
||||||
|
// &[format_spec_0, format_spec_1, format_spec_2]
|
||||||
|
let elements: Vec<_> = fmt
|
||||||
|
.template
|
||||||
|
.iter()
|
||||||
|
.filter_map(|piece| {
|
||||||
|
let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
|
||||||
|
Some(make_format_spec(ctx, macsp, placeholder, &mut argmap))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements))
|
||||||
|
});
|
||||||
|
|
||||||
|
let arguments = fmt.arguments.all_args();
|
||||||
|
|
||||||
|
// If the args array contains exactly all the original arguments once,
|
||||||
|
// in order, we can use a simple array instead of a `match` construction.
|
||||||
|
// However, if there's a yield point in any argument except the first one,
|
||||||
|
// we don't do this, because an ArgumentV1 cannot be kept across yield points.
|
||||||
|
let use_simple_array = argmap.len() == arguments.len()
|
||||||
|
&& argmap.iter().enumerate().all(|(i, &(j, _))| i == j)
|
||||||
|
&& arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
|
||||||
|
|
||||||
|
let args = if use_simple_array {
|
||||||
|
// Generate:
|
||||||
|
// &[
|
||||||
|
// ::core::fmt::ArgumentV1::new_display(&arg0),
|
||||||
|
// ::core::fmt::ArgumentV1::new_lower_hex(&arg1),
|
||||||
|
// ::core::fmt::ArgumentV1::new_debug(&arg2),
|
||||||
|
// ]
|
||||||
|
let elements: Vec<_> = arguments
|
||||||
|
.iter()
|
||||||
|
.zip(argmap)
|
||||||
|
.map(|(arg, (_, ty))| {
|
||||||
|
let sp = arg.expr.span.with_ctxt(macsp.ctxt());
|
||||||
|
let arg = ctx.lower_expr(&arg.expr);
|
||||||
|
let ref_arg = ctx.arena.alloc(ctx.expr(
|
||||||
|
sp,
|
||||||
|
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg),
|
||||||
|
));
|
||||||
|
make_argument(ctx, sp, ref_arg, ty)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements))
|
||||||
|
} else {
|
||||||
|
// Generate:
|
||||||
|
// &match (&arg0, &arg1, &arg2) {
|
||||||
|
// args => [
|
||||||
|
// ::core::fmt::ArgumentV1::new_display(args.0),
|
||||||
|
// ::core::fmt::ArgumentV1::new_lower_hex(args.1),
|
||||||
|
// ::core::fmt::ArgumentV1::new_debug(args.0),
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
let args_ident = Ident::new(sym::args, macsp);
|
||||||
|
let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
|
||||||
|
let args = ctx.arena.alloc_from_iter(argmap.iter().map(|&(arg_index, ty)| {
|
||||||
|
if let Some(arg) = arguments.get(arg_index) {
|
||||||
|
let sp = arg.expr.span.with_ctxt(macsp.ctxt());
|
||||||
|
let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id);
|
||||||
|
let arg = ctx.arena.alloc(ctx.expr(
|
||||||
|
sp,
|
||||||
|
hir::ExprKind::Field(
|
||||||
|
args_ident_expr,
|
||||||
|
Ident::new(sym::integer(arg_index), macsp),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
make_argument(ctx, sp, arg, ty)
|
||||||
|
} else {
|
||||||
|
ctx.expr(macsp, hir::ExprKind::Err)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
let elements: Vec<_> = arguments
|
||||||
|
.iter()
|
||||||
|
.map(|arg| {
|
||||||
|
let arg_expr = ctx.lower_expr(&arg.expr);
|
||||||
|
ctx.expr(
|
||||||
|
arg.expr.span.with_ctxt(macsp.ctxt()),
|
||||||
|
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let args_tuple = ctx
|
||||||
|
.arena
|
||||||
|
.alloc(ctx.expr(macsp, hir::ExprKind::Tup(ctx.arena.alloc_from_iter(elements))));
|
||||||
|
let array = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
|
||||||
|
let match_arms = ctx.arena.alloc_from_iter([ctx.arm(args_pat, array)]);
|
||||||
|
let match_expr = ctx.arena.alloc(ctx.expr_match(
|
||||||
|
macsp,
|
||||||
|
args_tuple,
|
||||||
|
match_arms,
|
||||||
|
hir::MatchSource::FormatArgs,
|
||||||
|
));
|
||||||
|
ctx.expr(
|
||||||
|
macsp,
|
||||||
|
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, match_expr),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(format_options) = format_options {
|
||||||
|
// Generate:
|
||||||
|
// ::core::fmt::Arguments::new_v1_formatted(
|
||||||
|
// lit_pieces,
|
||||||
|
// args,
|
||||||
|
// format_options,
|
||||||
|
// unsafe { ::core::fmt::UnsafeArg::new() }
|
||||||
|
// )
|
||||||
|
let new_v1_formatted = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
|
||||||
|
macsp,
|
||||||
|
hir::LangItem::FormatArguments,
|
||||||
|
sym::new_v1_formatted,
|
||||||
|
));
|
||||||
|
let unsafe_arg_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
|
||||||
|
macsp,
|
||||||
|
hir::LangItem::FormatUnsafeArg,
|
||||||
|
sym::new,
|
||||||
|
));
|
||||||
|
let unsafe_arg_new_call = ctx.expr_call(macsp, unsafe_arg_new, &[]);
|
||||||
|
let hir_id = ctx.next_id();
|
||||||
|
let unsafe_arg = ctx.expr_block(ctx.arena.alloc(hir::Block {
|
||||||
|
stmts: &[],
|
||||||
|
expr: Some(unsafe_arg_new_call),
|
||||||
|
hir_id,
|
||||||
|
rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
|
||||||
|
span: macsp,
|
||||||
|
targeted_by_break: false,
|
||||||
|
}));
|
||||||
|
let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options, unsafe_arg]);
|
||||||
|
hir::ExprKind::Call(new_v1_formatted, args)
|
||||||
|
} else {
|
||||||
|
// Generate:
|
||||||
|
// ::core::fmt::Arguments::new_v1(
|
||||||
|
// lit_pieces,
|
||||||
|
// args,
|
||||||
|
// )
|
||||||
|
let new_v1 = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
|
||||||
|
macsp,
|
||||||
|
hir::LangItem::FormatArguments,
|
||||||
|
sym::new_v1,
|
||||||
|
));
|
||||||
|
let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]);
|
||||||
|
hir::ExprKind::Call(new_v1, new_args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn may_contain_yield_point(e: &ast::Expr) -> bool {
|
||||||
|
struct MayContainYieldPoint(bool);
|
||||||
|
|
||||||
|
impl Visitor<'_> for MayContainYieldPoint {
|
||||||
|
fn visit_expr(&mut self, e: &ast::Expr) {
|
||||||
|
if let ast::ExprKind::Await(_) | ast::ExprKind::Yield(_) = e.kind {
|
||||||
|
self.0 = true;
|
||||||
|
} else {
|
||||||
|
visit::walk_expr(self, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_mac_call(&mut self, _: &ast::MacCall) {
|
||||||
|
self.0 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_attribute(&mut self, _: &ast::Attribute) {
|
||||||
|
// Conservatively assume this may be a proc macro attribute in
|
||||||
|
// expression position.
|
||||||
|
self.0 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_item(&mut self, _: &ast::Item) {
|
||||||
|
// Do not recurse into nested items.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut visitor = MayContainYieldPoint(false);
|
||||||
|
visitor.visit_expr(e);
|
||||||
|
visitor.0
|
||||||
|
}
|
@ -80,6 +80,7 @@ mod asm;
|
|||||||
mod block;
|
mod block;
|
||||||
mod errors;
|
mod errors;
|
||||||
mod expr;
|
mod expr;
|
||||||
|
mod format;
|
||||||
mod index;
|
mod index;
|
||||||
mod item;
|
mod item;
|
||||||
mod lifetime_collector;
|
mod lifetime_collector;
|
||||||
|
@ -6,5 +6,6 @@ edition = "2021"
|
|||||||
[lib]
|
[lib]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rustc_span = { path = "../rustc_span" }
|
|
||||||
rustc_ast = { path = "../rustc_ast" }
|
rustc_ast = { path = "../rustc_ast" }
|
||||||
|
rustc_parse_format = { path = "../rustc_parse_format" }
|
||||||
|
rustc_span = { path = "../rustc_span" }
|
||||||
|
@ -6,6 +6,8 @@ use rustc_ast::token;
|
|||||||
use rustc_ast::util::literal::escape_byte_str_symbol;
|
use rustc_ast::util::literal::escape_byte_str_symbol;
|
||||||
use rustc_ast::util::parser::{self, AssocOp, Fixity};
|
use rustc_ast::util::parser::{self, AssocOp, Fixity};
|
||||||
use rustc_ast::{self as ast, BlockCheckMode};
|
use rustc_ast::{self as ast, BlockCheckMode};
|
||||||
|
use rustc_ast::{FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount, FormatTrait};
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
impl<'a> State<'a> {
|
impl<'a> State<'a> {
|
||||||
fn print_else(&mut self, els: Option<&ast::Expr>) {
|
fn print_else(&mut self, els: Option<&ast::Expr>) {
|
||||||
@ -528,6 +530,18 @@ impl<'a> State<'a> {
|
|||||||
self.word("asm!");
|
self.word("asm!");
|
||||||
self.print_inline_asm(a);
|
self.print_inline_asm(a);
|
||||||
}
|
}
|
||||||
|
ast::ExprKind::FormatArgs(fmt) => {
|
||||||
|
self.word("format_args!");
|
||||||
|
self.popen();
|
||||||
|
self.rbox(0, Inconsistent);
|
||||||
|
self.word(reconstruct_format_args_template_string(&fmt.template));
|
||||||
|
for arg in fmt.arguments.all_args() {
|
||||||
|
self.word_space(",");
|
||||||
|
self.print_expr(&arg.expr);
|
||||||
|
}
|
||||||
|
self.end();
|
||||||
|
self.pclose();
|
||||||
|
}
|
||||||
ast::ExprKind::MacCall(m) => self.print_mac(m),
|
ast::ExprKind::MacCall(m) => self.print_mac(m),
|
||||||
ast::ExprKind::Paren(e) => {
|
ast::ExprKind::Paren(e) => {
|
||||||
self.popen();
|
self.popen();
|
||||||
@ -627,3 +641,91 @@ impl<'a> State<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> String {
|
||||||
|
let mut template = "\"".to_string();
|
||||||
|
for piece in pieces {
|
||||||
|
match piece {
|
||||||
|
FormatArgsPiece::Literal(s) => {
|
||||||
|
for c in s.as_str().escape_debug() {
|
||||||
|
template.push(c);
|
||||||
|
if let '{' | '}' = c {
|
||||||
|
template.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FormatArgsPiece::Placeholder(p) => {
|
||||||
|
template.push('{');
|
||||||
|
let (Ok(n) | Err(n)) = p.argument.index;
|
||||||
|
write!(template, "{n}").unwrap();
|
||||||
|
if p.format_options != Default::default() || p.format_trait != FormatTrait::Display
|
||||||
|
{
|
||||||
|
template.push_str(":");
|
||||||
|
}
|
||||||
|
if let Some(fill) = p.format_options.fill {
|
||||||
|
template.push(fill);
|
||||||
|
}
|
||||||
|
match p.format_options.alignment {
|
||||||
|
Some(FormatAlignment::Left) => template.push_str("<"),
|
||||||
|
Some(FormatAlignment::Right) => template.push_str(">"),
|
||||||
|
Some(FormatAlignment::Center) => template.push_str("^"),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
let flags = p.format_options.flags;
|
||||||
|
if flags >> (rustc_parse_format::FlagSignPlus as usize) & 1 != 0 {
|
||||||
|
template.push('+');
|
||||||
|
}
|
||||||
|
if flags >> (rustc_parse_format::FlagSignMinus as usize) & 1 != 0 {
|
||||||
|
template.push('-');
|
||||||
|
}
|
||||||
|
if flags >> (rustc_parse_format::FlagAlternate as usize) & 1 != 0 {
|
||||||
|
template.push('#');
|
||||||
|
}
|
||||||
|
if flags >> (rustc_parse_format::FlagSignAwareZeroPad as usize) & 1 != 0 {
|
||||||
|
template.push('0');
|
||||||
|
}
|
||||||
|
if let Some(width) = &p.format_options.width {
|
||||||
|
match width {
|
||||||
|
FormatCount::Literal(n) => write!(template, "{n}").unwrap(),
|
||||||
|
FormatCount::Argument(FormatArgPosition {
|
||||||
|
index: Ok(n) | Err(n), ..
|
||||||
|
}) => {
|
||||||
|
write!(template, "{n}$").unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(precision) = &p.format_options.precision {
|
||||||
|
template.push('.');
|
||||||
|
match precision {
|
||||||
|
FormatCount::Literal(n) => write!(template, "{n}").unwrap(),
|
||||||
|
FormatCount::Argument(FormatArgPosition {
|
||||||
|
index: Ok(n) | Err(n), ..
|
||||||
|
}) => {
|
||||||
|
write!(template, "{n}$").unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if flags >> (rustc_parse_format::FlagDebugLowerHex as usize) & 1 != 0 {
|
||||||
|
template.push('X');
|
||||||
|
}
|
||||||
|
if flags >> (rustc_parse_format::FlagDebugUpperHex as usize) & 1 != 0 {
|
||||||
|
template.push('x');
|
||||||
|
}
|
||||||
|
template.push_str(match p.format_trait {
|
||||||
|
FormatTrait::Display => "",
|
||||||
|
FormatTrait::Debug => "?",
|
||||||
|
FormatTrait::LowerExp => "e",
|
||||||
|
FormatTrait::UpperExp => "E",
|
||||||
|
FormatTrait::Octal => "o",
|
||||||
|
FormatTrait::Pointer => "p",
|
||||||
|
FormatTrait::Binary => "b",
|
||||||
|
FormatTrait::LowerHex => "x",
|
||||||
|
FormatTrait::UpperHex => "X",
|
||||||
|
});
|
||||||
|
template.push('}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template.push('"');
|
||||||
|
template
|
||||||
|
}
|
||||||
|
@ -297,6 +297,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
|
|||||||
| ExprKind::Continue(_)
|
| ExprKind::Continue(_)
|
||||||
| ExprKind::Err
|
| ExprKind::Err
|
||||||
| ExprKind::Field(_, _)
|
| ExprKind::Field(_, _)
|
||||||
|
| ExprKind::FormatArgs(_)
|
||||||
| ExprKind::ForLoop(_, _, _, _)
|
| ExprKind::ForLoop(_, _, _, _)
|
||||||
| ExprKind::If(_, _, _)
|
| ExprKind::If(_, _, _)
|
||||||
| ExprKind::IncludedBytes(..)
|
| ExprKind::IncludedBytes(..)
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
use rustc_ast::token;
|
use rustc_ast::token;
|
||||||
use rustc_ast::tokenstream::TokenStream;
|
use rustc_ast::tokenstream::TokenStream;
|
||||||
use rustc_ast::Expr;
|
use rustc_ast::{
|
||||||
|
Expr, ExprKind, FormatAlignment, FormatArgPosition, FormatArgPositionKind, FormatArgs,
|
||||||
|
FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArguments, FormatCount,
|
||||||
|
FormatOptions, FormatPlaceholder, FormatTrait,
|
||||||
|
};
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_errors::{pluralize, Applicability, MultiSpan, PResult};
|
use rustc_errors::{pluralize, Applicability, MultiSpan, PResult};
|
||||||
use rustc_expand::base::{self, *};
|
use rustc_expand::base::{self, *};
|
||||||
@ -12,12 +16,6 @@ use rustc_span::{BytePos, InnerSpan, Span};
|
|||||||
use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
|
use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
|
||||||
use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId};
|
use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId};
|
||||||
|
|
||||||
mod ast;
|
|
||||||
use ast::*;
|
|
||||||
|
|
||||||
mod expand;
|
|
||||||
use expand::expand_parsed_format_args;
|
|
||||||
|
|
||||||
// The format_args!() macro is expanded in three steps:
|
// The format_args!() macro is expanded in three steps:
|
||||||
// 1. First, `parse_args` will parse the `(literal, arg, arg, name=arg, name=arg)` syntax,
|
// 1. First, `parse_args` will parse the `(literal, arg, arg, name=arg, name=arg)` syntax,
|
||||||
// but doesn't parse the template (the literal) itself.
|
// but doesn't parse the template (the literal) itself.
|
||||||
@ -850,7 +848,7 @@ fn expand_format_args_impl<'cx>(
|
|||||||
match parse_args(ecx, sp, tts) {
|
match parse_args(ecx, sp, tts) {
|
||||||
Ok((efmt, args)) => {
|
Ok((efmt, args)) => {
|
||||||
if let Ok(format_args) = make_format_args(ecx, efmt, args, nl) {
|
if let Ok(format_args) = make_format_args(ecx, efmt, args, nl) {
|
||||||
MacEager::expr(expand_parsed_format_args(ecx, format_args))
|
MacEager::expr(ecx.expr(sp, ExprKind::FormatArgs(P(format_args))))
|
||||||
} else {
|
} else {
|
||||||
MacEager::expr(DummyResult::raw_expr(sp, true))
|
MacEager::expr(DummyResult::raw_expr(sp, true))
|
||||||
}
|
}
|
||||||
|
@ -1,353 +0,0 @@
|
|||||||
use super::*;
|
|
||||||
use rustc_ast as ast;
|
|
||||||
use rustc_ast::visit::{self, Visitor};
|
|
||||||
use rustc_ast::{BlockCheckMode, UnsafeSource};
|
|
||||||
use rustc_data_structures::fx::FxIndexSet;
|
|
||||||
use rustc_span::{sym, symbol::kw};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
|
||||||
enum ArgumentType {
|
|
||||||
Format(FormatTrait),
|
|
||||||
Usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_argument(ecx: &ExtCtxt<'_>, sp: Span, arg: P<ast::Expr>, ty: ArgumentType) -> P<ast::Expr> {
|
|
||||||
// Generate:
|
|
||||||
// ::core::fmt::ArgumentV1::new_…(arg)
|
|
||||||
use ArgumentType::*;
|
|
||||||
use FormatTrait::*;
|
|
||||||
ecx.expr_call_global(
|
|
||||||
sp,
|
|
||||||
ecx.std_path(&[
|
|
||||||
sym::fmt,
|
|
||||||
sym::ArgumentV1,
|
|
||||||
match ty {
|
|
||||||
Format(Display) => sym::new_display,
|
|
||||||
Format(Debug) => sym::new_debug,
|
|
||||||
Format(LowerExp) => sym::new_lower_exp,
|
|
||||||
Format(UpperExp) => sym::new_upper_exp,
|
|
||||||
Format(Octal) => sym::new_octal,
|
|
||||||
Format(Pointer) => sym::new_pointer,
|
|
||||||
Format(Binary) => sym::new_binary,
|
|
||||||
Format(LowerHex) => sym::new_lower_hex,
|
|
||||||
Format(UpperHex) => sym::new_upper_hex,
|
|
||||||
Usize => sym::from_usize,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
vec![arg],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_count(
|
|
||||||
ecx: &ExtCtxt<'_>,
|
|
||||||
sp: Span,
|
|
||||||
count: &Option<FormatCount>,
|
|
||||||
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
|
|
||||||
) -> P<ast::Expr> {
|
|
||||||
// Generate:
|
|
||||||
// ::core::fmt::rt::v1::Count::…(…)
|
|
||||||
match count {
|
|
||||||
Some(FormatCount::Literal(n)) => ecx.expr_call_global(
|
|
||||||
sp,
|
|
||||||
ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Is]),
|
|
||||||
vec![ecx.expr_usize(sp, *n)],
|
|
||||||
),
|
|
||||||
Some(FormatCount::Argument(arg)) => {
|
|
||||||
if let Ok(arg_index) = arg.index {
|
|
||||||
let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
|
|
||||||
ecx.expr_call_global(
|
|
||||||
sp,
|
|
||||||
ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Param]),
|
|
||||||
vec![ecx.expr_usize(sp, i)],
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
DummyResult::raw_expr(sp, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => ecx.expr_path(ecx.path_global(
|
|
||||||
sp,
|
|
||||||
ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Implied]),
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_format_spec(
|
|
||||||
ecx: &ExtCtxt<'_>,
|
|
||||||
sp: Span,
|
|
||||||
placeholder: &FormatPlaceholder,
|
|
||||||
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
|
|
||||||
) -> P<ast::Expr> {
|
|
||||||
// Generate:
|
|
||||||
// ::core::fmt::rt::v1::Argument {
|
|
||||||
// position: 0usize,
|
|
||||||
// format: ::core::fmt::rt::v1::FormatSpec {
|
|
||||||
// fill: ' ',
|
|
||||||
// align: ::core::fmt::rt::v1::Alignment::Unknown,
|
|
||||||
// flags: 0u32,
|
|
||||||
// precision: ::core::fmt::rt::v1::Count::Implied,
|
|
||||||
// width: ::core::fmt::rt::v1::Count::Implied,
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
let position = match placeholder.argument.index {
|
|
||||||
Ok(arg_index) => {
|
|
||||||
let (i, _) =
|
|
||||||
argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
|
|
||||||
ecx.expr_usize(sp, i)
|
|
||||||
}
|
|
||||||
Err(_) => DummyResult::raw_expr(sp, true),
|
|
||||||
};
|
|
||||||
let fill = ecx.expr_char(sp, placeholder.format_options.fill.unwrap_or(' '));
|
|
||||||
let align = ecx.expr_path(ecx.path_global(
|
|
||||||
sp,
|
|
||||||
ecx.std_path(&[
|
|
||||||
sym::fmt,
|
|
||||||
sym::rt,
|
|
||||||
sym::v1,
|
|
||||||
sym::Alignment,
|
|
||||||
match placeholder.format_options.alignment {
|
|
||||||
Some(FormatAlignment::Left) => sym::Left,
|
|
||||||
Some(FormatAlignment::Right) => sym::Right,
|
|
||||||
Some(FormatAlignment::Center) => sym::Center,
|
|
||||||
None => sym::Unknown,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
));
|
|
||||||
let flags = ecx.expr_u32(sp, placeholder.format_options.flags);
|
|
||||||
let prec = make_count(ecx, sp, &placeholder.format_options.precision, argmap);
|
|
||||||
let width = make_count(ecx, sp, &placeholder.format_options.width, argmap);
|
|
||||||
ecx.expr_struct(
|
|
||||||
sp,
|
|
||||||
ecx.path_global(sp, ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Argument])),
|
|
||||||
vec![
|
|
||||||
ecx.field_imm(sp, Ident::new(sym::position, sp), position),
|
|
||||||
ecx.field_imm(
|
|
||||||
sp,
|
|
||||||
Ident::new(sym::format, sp),
|
|
||||||
ecx.expr_struct(
|
|
||||||
sp,
|
|
||||||
ecx.path_global(
|
|
||||||
sp,
|
|
||||||
ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::FormatSpec]),
|
|
||||||
),
|
|
||||||
vec![
|
|
||||||
ecx.field_imm(sp, Ident::new(sym::fill, sp), fill),
|
|
||||||
ecx.field_imm(sp, Ident::new(sym::align, sp), align),
|
|
||||||
ecx.field_imm(sp, Ident::new(sym::flags, sp), flags),
|
|
||||||
ecx.field_imm(sp, Ident::new(sym::precision, sp), prec),
|
|
||||||
ecx.field_imm(sp, Ident::new(sym::width, sp), width),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P<ast::Expr> {
|
|
||||||
let macsp = ecx.with_def_site_ctxt(ecx.call_site());
|
|
||||||
|
|
||||||
let lit_pieces = ecx.expr_array_ref(
|
|
||||||
fmt.span,
|
|
||||||
fmt.template
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(i, piece)| match piece {
|
|
||||||
&FormatArgsPiece::Literal(s) => Some(ecx.expr_str(fmt.span, s)),
|
|
||||||
&FormatArgsPiece::Placeholder(_) => {
|
|
||||||
// Inject empty string before placeholders when not already preceded by a literal piece.
|
|
||||||
if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) {
|
|
||||||
Some(ecx.expr_str(fmt.span, kw::Empty))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Whether we'll use the `Arguments::new_v1_formatted` form (true),
|
|
||||||
// or the `Arguments::new_v1` form (false).
|
|
||||||
let mut use_format_options = false;
|
|
||||||
|
|
||||||
// Create a list of all _unique_ (argument, format trait) combinations.
|
|
||||||
// E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
|
|
||||||
let mut argmap = FxIndexSet::default();
|
|
||||||
for piece in &fmt.template {
|
|
||||||
let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
|
|
||||||
if placeholder.format_options != Default::default() {
|
|
||||||
// Can't use basic form if there's any formatting options.
|
|
||||||
use_format_options = true;
|
|
||||||
}
|
|
||||||
if let Ok(index) = placeholder.argument.index {
|
|
||||||
if !argmap.insert((index, ArgumentType::Format(placeholder.format_trait))) {
|
|
||||||
// Duplicate (argument, format trait) combination,
|
|
||||||
// which we'll only put once in the args array.
|
|
||||||
use_format_options = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let format_options = use_format_options.then(|| {
|
|
||||||
// Generate:
|
|
||||||
// &[format_spec_0, format_spec_1, format_spec_2]
|
|
||||||
ecx.expr_array_ref(
|
|
||||||
macsp,
|
|
||||||
fmt.template
|
|
||||||
.iter()
|
|
||||||
.filter_map(|piece| {
|
|
||||||
let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
|
|
||||||
Some(make_format_spec(ecx, macsp, placeholder, &mut argmap))
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let arguments = fmt.arguments.into_vec();
|
|
||||||
|
|
||||||
// If the args array contains exactly all the original arguments once,
|
|
||||||
// in order, we can use a simple array instead of a `match` construction.
|
|
||||||
// However, if there's a yield point in any argument except the first one,
|
|
||||||
// we don't do this, because an ArgumentV1 cannot be kept across yield points.
|
|
||||||
let use_simple_array = argmap.len() == arguments.len()
|
|
||||||
&& argmap.iter().enumerate().all(|(i, &(j, _))| i == j)
|
|
||||||
&& arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
|
|
||||||
|
|
||||||
let args = if use_simple_array {
|
|
||||||
// Generate:
|
|
||||||
// &[
|
|
||||||
// ::core::fmt::ArgumentV1::new_display(&arg0),
|
|
||||||
// ::core::fmt::ArgumentV1::new_lower_hex(&arg1),
|
|
||||||
// ::core::fmt::ArgumentV1::new_debug(&arg2),
|
|
||||||
// ]
|
|
||||||
ecx.expr_array_ref(
|
|
||||||
macsp,
|
|
||||||
arguments
|
|
||||||
.into_iter()
|
|
||||||
.zip(argmap)
|
|
||||||
.map(|(arg, (_, ty))| {
|
|
||||||
let sp = arg.expr.span.with_ctxt(macsp.ctxt());
|
|
||||||
make_argument(ecx, sp, ecx.expr_addr_of(sp, arg.expr), ty)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// Generate:
|
|
||||||
// match (&arg0, &arg1, &arg2) {
|
|
||||||
// args => &[
|
|
||||||
// ::core::fmt::ArgumentV1::new_display(args.0),
|
|
||||||
// ::core::fmt::ArgumentV1::new_lower_hex(args.1),
|
|
||||||
// ::core::fmt::ArgumentV1::new_debug(args.0),
|
|
||||||
// ]
|
|
||||||
// }
|
|
||||||
let args_ident = Ident::new(sym::args, macsp);
|
|
||||||
let args = argmap
|
|
||||||
.iter()
|
|
||||||
.map(|&(arg_index, ty)| {
|
|
||||||
if let Some(arg) = arguments.get(arg_index) {
|
|
||||||
let sp = arg.expr.span.with_ctxt(macsp.ctxt());
|
|
||||||
make_argument(
|
|
||||||
ecx,
|
|
||||||
sp,
|
|
||||||
ecx.expr_field(
|
|
||||||
sp,
|
|
||||||
ecx.expr_ident(macsp, args_ident),
|
|
||||||
Ident::new(sym::integer(arg_index), macsp),
|
|
||||||
),
|
|
||||||
ty,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
DummyResult::raw_expr(macsp, true)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
ecx.expr_addr_of(
|
|
||||||
macsp,
|
|
||||||
ecx.expr_match(
|
|
||||||
macsp,
|
|
||||||
ecx.expr_tuple(
|
|
||||||
macsp,
|
|
||||||
arguments
|
|
||||||
.into_iter()
|
|
||||||
.map(|arg| {
|
|
||||||
ecx.expr_addr_of(arg.expr.span.with_ctxt(macsp.ctxt()), arg.expr)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
vec![ecx.arm(macsp, ecx.pat_ident(macsp, args_ident), ecx.expr_array(macsp, args))],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(format_options) = format_options {
|
|
||||||
// Generate:
|
|
||||||
// ::core::fmt::Arguments::new_v1_formatted(
|
|
||||||
// lit_pieces,
|
|
||||||
// args,
|
|
||||||
// format_options,
|
|
||||||
// unsafe { ::core::fmt::UnsafeArg::new() }
|
|
||||||
// )
|
|
||||||
ecx.expr_call_global(
|
|
||||||
macsp,
|
|
||||||
ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1_formatted]),
|
|
||||||
vec![
|
|
||||||
lit_pieces,
|
|
||||||
args,
|
|
||||||
format_options,
|
|
||||||
ecx.expr_block(P(ast::Block {
|
|
||||||
stmts: vec![ecx.stmt_expr(ecx.expr_call_global(
|
|
||||||
macsp,
|
|
||||||
ecx.std_path(&[sym::fmt, sym::UnsafeArg, sym::new]),
|
|
||||||
Vec::new(),
|
|
||||||
))],
|
|
||||||
id: ast::DUMMY_NODE_ID,
|
|
||||||
rules: BlockCheckMode::Unsafe(UnsafeSource::CompilerGenerated),
|
|
||||||
span: macsp,
|
|
||||||
tokens: None,
|
|
||||||
could_be_bare_literal: false,
|
|
||||||
})),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// Generate:
|
|
||||||
// ::core::fmt::Arguments::new_v1(
|
|
||||||
// lit_pieces,
|
|
||||||
// args,
|
|
||||||
// )
|
|
||||||
ecx.expr_call_global(
|
|
||||||
macsp,
|
|
||||||
ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1]),
|
|
||||||
vec![lit_pieces, args],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn may_contain_yield_point(e: &ast::Expr) -> bool {
|
|
||||||
struct MayContainYieldPoint(bool);
|
|
||||||
|
|
||||||
impl Visitor<'_> for MayContainYieldPoint {
|
|
||||||
fn visit_expr(&mut self, e: &ast::Expr) {
|
|
||||||
if let ast::ExprKind::Await(_) | ast::ExprKind::Yield(_) = e.kind {
|
|
||||||
self.0 = true;
|
|
||||||
} else {
|
|
||||||
visit::walk_expr(self, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_mac_call(&mut self, _: &ast::MacCall) {
|
|
||||||
self.0 = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_attribute(&mut self, _: &ast::Attribute) {
|
|
||||||
// Conservatively assume this may be a proc macro attribute in
|
|
||||||
// expression position.
|
|
||||||
self.0 = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_item(&mut self, _: &ast::Item) {
|
|
||||||
// Do not recurse into nested items.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut visitor = MayContainYieldPoint(false);
|
|
||||||
visitor.visit_expr(e);
|
|
||||||
visitor.0
|
|
||||||
}
|
|
@ -2108,6 +2108,8 @@ pub enum MatchSource {
|
|||||||
TryDesugar,
|
TryDesugar,
|
||||||
/// A desugared `<expr>.await`.
|
/// A desugared `<expr>.await`.
|
||||||
AwaitDesugar,
|
AwaitDesugar,
|
||||||
|
/// A desugared `format_args!()`.
|
||||||
|
FormatArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MatchSource {
|
impl MatchSource {
|
||||||
@ -2119,6 +2121,7 @@ impl MatchSource {
|
|||||||
ForLoopDesugar => "for",
|
ForLoopDesugar => "for",
|
||||||
TryDesugar => "?",
|
TryDesugar => "?",
|
||||||
AwaitDesugar => ".await",
|
AwaitDesugar => ".await",
|
||||||
|
FormatArgs => "format_args!()",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,9 +208,9 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
|
|||||||
// Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
|
// Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
|
||||||
// when the iterator is an uninhabited type. unreachable_code will trigger instead.
|
// when the iterator is an uninhabited type. unreachable_code will trigger instead.
|
||||||
hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
|
hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
|
||||||
hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
|
hir::MatchSource::ForLoopDesugar
|
||||||
report_arm_reachability(&cx, &report)
|
| hir::MatchSource::Normal
|
||||||
}
|
| hir::MatchSource::FormatArgs => report_arm_reachability(&cx, &report),
|
||||||
// Unreachable patterns in try and await expressions occur when one of
|
// Unreachable patterns in try and await expressions occur when one of
|
||||||
// the arms are an uninhabited type. Which is OK.
|
// the arms are an uninhabited type. Which is OK.
|
||||||
hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
|
hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
|
||||||
|
@ -48,7 +48,7 @@ impl NonConstExpr {
|
|||||||
Self::Match(TryDesugar) => &[sym::const_try],
|
Self::Match(TryDesugar) => &[sym::const_try],
|
||||||
|
|
||||||
// All other expressions are allowed.
|
// All other expressions are allowed.
|
||||||
Self::Loop(Loop | While) | Self::Match(Normal) => &[],
|
Self::Loop(Loop | While) | Self::Match(Normal | FormatArgs) => &[],
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(gates)
|
Some(gates)
|
||||||
|
@ -567,7 +567,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
|
|||||||
Box, Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
|
Box, Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
|
||||||
If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign,
|
If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign,
|
||||||
AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
|
AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
|
||||||
InlineAsm, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, IncludedBytes, Err
|
InlineAsm, FormatArgs, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, IncludedBytes, Err
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
ast_visit::walk_expr(self, e)
|
ast_visit::walk_expr(self, e)
|
||||||
|
Loading…
Reference in New Issue
Block a user