Merge pull request #21242 from ChayimFriedman2/fmt-fix
fix: Support the new lowering of `format_args!()`
This commit is contained in:
commit
731ae4d91e
4 changed files with 1151 additions and 717 deletions
|
|
@ -2,6 +2,7 @@
|
|||
//! representation.
|
||||
|
||||
mod asm;
|
||||
mod format_args;
|
||||
mod generics;
|
||||
mod path;
|
||||
|
||||
|
|
@ -19,7 +20,7 @@ use intern::{Symbol, sym};
|
|||
use rustc_hash::FxHashMap;
|
||||
use stdx::never;
|
||||
use syntax::{
|
||||
AstNode, AstPtr, AstToken as _, SyntaxNodePtr,
|
||||
AstNode, AstPtr, SyntaxNodePtr,
|
||||
ast::{
|
||||
self, ArrayExprKind, AstChildren, BlockExpr, HasArgList, HasAttrs, HasGenericArgs,
|
||||
HasGenericParams, HasLoopBody, HasName, HasTypeBounds, IsString, RangeItem,
|
||||
|
|
@ -34,7 +35,6 @@ use crate::{
|
|||
AdtId, BlockId, BlockLoc, DefWithBodyId, FunctionId, GenericDefId, ImplId, MacroId,
|
||||
ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro,
|
||||
attrs::AttrFlags,
|
||||
builtin_type::BuiltinUint,
|
||||
db::DefDatabase,
|
||||
expr_store::{
|
||||
Body, BodySourceMap, ExprPtr, ExpressionStore, ExpressionStoreBuilder,
|
||||
|
|
@ -47,13 +47,7 @@ use crate::{
|
|||
hir::{
|
||||
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
|
||||
Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, OffsetOf, Pat, PatId,
|
||||
RecordFieldPat, RecordLitField, Statement,
|
||||
format_args::{
|
||||
self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
|
||||
FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,
|
||||
FormatPlaceholder, FormatSign, FormatTrait,
|
||||
},
|
||||
generics::GenericParams,
|
||||
RecordFieldPat, RecordLitField, Statement, generics::GenericParams,
|
||||
},
|
||||
item_scope::BuiltinShadowMode,
|
||||
item_tree::FieldsShape,
|
||||
|
|
@ -438,6 +432,8 @@ pub struct ExprCollector<'db> {
|
|||
|
||||
awaitable_context: Option<Awaitable>,
|
||||
krate: base_db::Crate,
|
||||
|
||||
name_generator_index: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -543,9 +539,16 @@ impl<'db> ExprCollector<'db> {
|
|||
current_block_legacy_macro_defs_count: FxHashMap::default(),
|
||||
outer_impl_trait: false,
|
||||
krate,
|
||||
name_generator_index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_new_name(&mut self) -> Name {
|
||||
let index = self.name_generator_index;
|
||||
self.name_generator_index += 1;
|
||||
Name::generate_new_name(index)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn lang_items(&self) -> &'db LangItems {
|
||||
self.lang_items.get_or_init(|| crate::lang_item::lang_items(self.db, self.def_map.krate()))
|
||||
|
|
@ -1645,9 +1648,8 @@ impl<'db> ExprCollector<'db> {
|
|||
/// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
|
||||
fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId {
|
||||
let try_from_output = self.lang_path(self.lang_items().TryTraitFromOutput);
|
||||
let label = self.alloc_label_desugared(Label {
|
||||
name: Name::generate_new_name(self.store.labels.len()),
|
||||
});
|
||||
let label = self.generate_new_name();
|
||||
let label = self.alloc_label_desugared(Label { name: label });
|
||||
let old_label = self.current_try_block_label.replace(label);
|
||||
|
||||
let ptr = AstPtr::new(&e).upcast();
|
||||
|
|
@ -1775,7 +1777,7 @@ impl<'db> ExprCollector<'db> {
|
|||
this.collect_expr_opt(e.loop_body().map(|it| it.into()))
|
||||
}),
|
||||
};
|
||||
let iter_name = Name::generate_new_name(self.store.exprs.len());
|
||||
let iter_name = self.generate_new_name();
|
||||
let iter_expr = self.alloc_expr(Expr::Path(Path::from(iter_name.clone())), syntax_ptr);
|
||||
let iter_expr_mut = self.alloc_expr(
|
||||
Expr::Ref { expr: iter_expr, rawness: Rawness::Ref, mutability: Mutability::Mut },
|
||||
|
|
@ -1836,7 +1838,7 @@ impl<'db> ExprCollector<'db> {
|
|||
let try_branch = self.alloc_expr(try_branch.map_or(Expr::Missing, Expr::Path), syntax_ptr);
|
||||
let expr = self
|
||||
.alloc_expr(Expr::Call { callee: try_branch, args: Box::new([operand]) }, syntax_ptr);
|
||||
let continue_name = Name::generate_new_name(self.store.bindings.len());
|
||||
let continue_name = self.generate_new_name();
|
||||
let continue_binding = self.alloc_binding(
|
||||
continue_name.clone(),
|
||||
BindingAnnotation::Unannotated,
|
||||
|
|
@ -1854,7 +1856,7 @@ impl<'db> ExprCollector<'db> {
|
|||
guard: None,
|
||||
expr: self.alloc_expr(Expr::Path(Path::from(continue_name)), syntax_ptr),
|
||||
};
|
||||
let break_name = Name::generate_new_name(self.store.bindings.len());
|
||||
let break_name = self.generate_new_name();
|
||||
let break_binding =
|
||||
self.alloc_binding(break_name.clone(), BindingAnnotation::Unannotated, HygieneId::ROOT);
|
||||
let break_bpat = self.alloc_pat_desugared(Pat::Bind { id: break_binding, subpat: None });
|
||||
|
|
@ -2609,7 +2611,6 @@ impl<'db> ExprCollector<'db> {
|
|||
}
|
||||
// endregion: labels
|
||||
|
||||
// region: format
|
||||
fn expand_macros_to_string(&mut self, expr: ast::Expr) -> Option<(ast::String, bool)> {
|
||||
let m = match expr {
|
||||
ast::Expr::MacroExpr(m) => m,
|
||||
|
|
@ -2629,676 +2630,6 @@ impl<'db> ExprCollector<'db> {
|
|||
Some((exp, false))
|
||||
}
|
||||
|
||||
fn collect_format_args(
|
||||
&mut self,
|
||||
f: ast::FormatArgsExpr,
|
||||
syntax_ptr: AstPtr<ast::Expr>,
|
||||
) -> ExprId {
|
||||
let mut args = FormatArgumentsCollector::default();
|
||||
f.args().for_each(|arg| {
|
||||
args.add(FormatArgument {
|
||||
kind: match arg.name() {
|
||||
Some(name) => FormatArgumentKind::Named(name.as_name()),
|
||||
None => FormatArgumentKind::Normal,
|
||||
},
|
||||
expr: self.collect_expr_opt(arg.expr()),
|
||||
});
|
||||
});
|
||||
let template = f.template();
|
||||
let fmt_snippet = template.as_ref().and_then(|it| match it {
|
||||
ast::Expr::Literal(literal) => match literal.kind() {
|
||||
ast::LiteralKind::String(s) => Some(s.text().to_owned()),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
});
|
||||
let mut mappings = vec![];
|
||||
let (fmt, hygiene) = match template.and_then(|template| {
|
||||
self.expand_macros_to_string(template.clone()).map(|it| (it, template))
|
||||
}) {
|
||||
Some(((s, is_direct_literal), template)) => {
|
||||
let call_ctx = self.expander.call_syntax_ctx();
|
||||
let hygiene = self.hygiene_id_for(s.syntax().text_range());
|
||||
let fmt = format_args::parse(
|
||||
&s,
|
||||
fmt_snippet,
|
||||
args,
|
||||
is_direct_literal,
|
||||
|name, range| {
|
||||
let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name)));
|
||||
if let Some(range) = range {
|
||||
self.store
|
||||
.template_map
|
||||
.get_or_insert_with(Default::default)
|
||||
.implicit_capture_to_source
|
||||
.insert(
|
||||
expr_id,
|
||||
self.expander.in_file((AstPtr::new(&template), range)),
|
||||
);
|
||||
}
|
||||
if !hygiene.is_root() {
|
||||
self.store.ident_hygiene.insert(expr_id.into(), hygiene);
|
||||
}
|
||||
expr_id
|
||||
},
|
||||
|name, span| {
|
||||
if let Some(span) = span {
|
||||
mappings.push((span, name))
|
||||
}
|
||||
},
|
||||
call_ctx,
|
||||
);
|
||||
(fmt, hygiene)
|
||||
}
|
||||
None => (
|
||||
FormatArgs {
|
||||
template: Default::default(),
|
||||
arguments: args.finish(),
|
||||
orphans: Default::default(),
|
||||
},
|
||||
HygieneId::ROOT,
|
||||
),
|
||||
};
|
||||
|
||||
// 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.iter() {
|
||||
let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
|
||||
if let Ok(index) = placeholder.argument.index {
|
||||
argmap.insert((index, ArgumentType::Format(placeholder.format_trait)));
|
||||
}
|
||||
}
|
||||
|
||||
let lit_pieces = fmt
|
||||
.template
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, piece)| {
|
||||
match piece {
|
||||
FormatArgsPiece::Literal(s) => {
|
||||
Some(self.alloc_expr_desugared(Expr::Literal(Literal::String(s.clone()))))
|
||||
}
|
||||
&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(self.alloc_expr_desugared(Expr::Literal(Literal::String(
|
||||
Symbol::empty(),
|
||||
))))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let lit_pieces =
|
||||
self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: lit_pieces }));
|
||||
let lit_pieces = self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: lit_pieces,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
});
|
||||
let format_options = {
|
||||
// Generate:
|
||||
// &[format_spec_0, format_spec_1, format_spec_2]
|
||||
let elements = fmt
|
||||
.template
|
||||
.iter()
|
||||
.filter_map(|piece| {
|
||||
let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
|
||||
Some(self.make_format_spec(placeholder, &mut argmap))
|
||||
})
|
||||
.collect();
|
||||
let array = self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements }));
|
||||
self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: array,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
})
|
||||
};
|
||||
|
||||
// Assume that rustc version >= 1.89.0 iff lang item `format_arguments` exists
|
||||
// but `format_unsafe_arg` does not
|
||||
let lang_items = self.lang_items();
|
||||
let fmt_args = lang_items.FormatArguments;
|
||||
let fmt_unsafe_arg = lang_items.FormatUnsafeArg;
|
||||
let use_format_args_since_1_89_0 = fmt_args.is_some() && fmt_unsafe_arg.is_none();
|
||||
|
||||
let idx = if use_format_args_since_1_89_0 {
|
||||
self.collect_format_args_impl(syntax_ptr, fmt, argmap, lit_pieces, format_options)
|
||||
} else {
|
||||
self.collect_format_args_before_1_89_0_impl(
|
||||
syntax_ptr,
|
||||
fmt,
|
||||
argmap,
|
||||
lit_pieces,
|
||||
format_options,
|
||||
)
|
||||
};
|
||||
|
||||
self.store
|
||||
.template_map
|
||||
.get_or_insert_with(Default::default)
|
||||
.format_args_to_captures
|
||||
.insert(idx, (hygiene, mappings));
|
||||
idx
|
||||
}
|
||||
|
||||
/// `format_args!` expansion implementation for rustc versions < `1.89.0`
|
||||
fn collect_format_args_before_1_89_0_impl(
|
||||
&mut self,
|
||||
syntax_ptr: AstPtr<ast::Expr>,
|
||||
fmt: FormatArgs,
|
||||
argmap: FxIndexSet<(usize, ArgumentType)>,
|
||||
lit_pieces: ExprId,
|
||||
format_options: ExprId,
|
||||
) -> ExprId {
|
||||
let arguments = &*fmt.arguments.arguments;
|
||||
|
||||
let args = if arguments.is_empty() {
|
||||
let expr = self
|
||||
.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: Box::default() }));
|
||||
self.alloc_expr_desugared(Expr::Ref {
|
||||
expr,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
})
|
||||
} else {
|
||||
// Generate:
|
||||
// &match (&arg0, &arg1, &…) {
|
||||
// args => [
|
||||
// <core::fmt::Argument>::new_display(args.0),
|
||||
// <core::fmt::Argument>::new_lower_hex(args.1),
|
||||
// <core::fmt::Argument>::new_debug(args.0),
|
||||
// …
|
||||
// ]
|
||||
// }
|
||||
let args = argmap
|
||||
.iter()
|
||||
.map(|&(arg_index, ty)| {
|
||||
let arg = self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: arguments[arg_index].expr,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
});
|
||||
self.make_argument(arg, ty)
|
||||
})
|
||||
.collect();
|
||||
let array =
|
||||
self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));
|
||||
self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: array,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
})
|
||||
};
|
||||
|
||||
// Generate:
|
||||
// <core::fmt::Arguments>::new_v1_formatted(
|
||||
// lit_pieces,
|
||||
// args,
|
||||
// format_options,
|
||||
// unsafe { ::core::fmt::UnsafeArg::new() }
|
||||
// )
|
||||
|
||||
let lang_items = self.lang_items();
|
||||
let new_v1_formatted = self.ty_rel_lang_path(
|
||||
lang_items.FormatArguments,
|
||||
Name::new_symbol_root(sym::new_v1_formatted),
|
||||
);
|
||||
let unsafe_arg_new =
|
||||
self.ty_rel_lang_path(lang_items.FormatUnsafeArg, Name::new_symbol_root(sym::new));
|
||||
let new_v1_formatted =
|
||||
self.alloc_expr_desugared(new_v1_formatted.map_or(Expr::Missing, Expr::Path));
|
||||
|
||||
let unsafe_arg_new =
|
||||
self.alloc_expr_desugared(unsafe_arg_new.map_or(Expr::Missing, Expr::Path));
|
||||
let unsafe_arg_new =
|
||||
self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() });
|
||||
let mut unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
|
||||
id: None,
|
||||
statements: Box::new([]),
|
||||
tail: Some(unsafe_arg_new),
|
||||
});
|
||||
if !fmt.orphans.is_empty() {
|
||||
unsafe_arg_new = self.alloc_expr_desugared(Expr::Block {
|
||||
id: None,
|
||||
// We collect the unused expressions here so that we still infer them instead of
|
||||
// dropping them out of the expression tree. We cannot store them in the `Unsafe`
|
||||
// block because then unsafe blocks within them will get a false "unused unsafe"
|
||||
// diagnostic (rustc has a notion of builtin unsafe blocks, but we don't).
|
||||
statements: fmt
|
||||
.orphans
|
||||
.into_iter()
|
||||
.map(|expr| Statement::Expr { expr, has_semi: true })
|
||||
.collect(),
|
||||
tail: Some(unsafe_arg_new),
|
||||
label: None,
|
||||
});
|
||||
}
|
||||
|
||||
self.alloc_expr(
|
||||
Expr::Call {
|
||||
callee: new_v1_formatted,
|
||||
args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),
|
||||
},
|
||||
syntax_ptr,
|
||||
)
|
||||
}
|
||||
|
||||
/// `format_args!` expansion implementation for rustc versions >= `1.89.0`,
|
||||
/// especially since [this PR](https://github.com/rust-lang/rust/pull/140748)
|
||||
fn collect_format_args_impl(
|
||||
&mut self,
|
||||
syntax_ptr: AstPtr<ast::Expr>,
|
||||
fmt: FormatArgs,
|
||||
argmap: FxIndexSet<(usize, ArgumentType)>,
|
||||
lit_pieces: ExprId,
|
||||
format_options: ExprId,
|
||||
) -> ExprId {
|
||||
let arguments = &*fmt.arguments.arguments;
|
||||
|
||||
let (let_stmts, args) = if arguments.is_empty() {
|
||||
(
|
||||
// Generate:
|
||||
// []
|
||||
vec![],
|
||||
self.alloc_expr_desugared(Expr::Array(Array::ElementList {
|
||||
elements: Box::default(),
|
||||
})),
|
||||
)
|
||||
} else if argmap.len() == 1 && arguments.len() == 1 {
|
||||
// Only one argument, so we don't need to make the `args` tuple.
|
||||
//
|
||||
// Generate:
|
||||
// super let args = [<core::fmt::Arguments>::new_display(&arg)];
|
||||
let args = argmap
|
||||
.iter()
|
||||
.map(|&(arg_index, ty)| {
|
||||
let ref_arg = self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: arguments[arg_index].expr,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
});
|
||||
self.make_argument(ref_arg, ty)
|
||||
})
|
||||
.collect();
|
||||
let args =
|
||||
self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));
|
||||
let args_name = Name::new_symbol_root(sym::args);
|
||||
let args_binding = self.alloc_binding(
|
||||
args_name.clone(),
|
||||
BindingAnnotation::Unannotated,
|
||||
HygieneId::ROOT,
|
||||
);
|
||||
let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });
|
||||
self.add_definition_to_binding(args_binding, args_pat);
|
||||
// TODO: We don't have `super let` yet.
|
||||
let let_stmt = Statement::Let {
|
||||
pat: args_pat,
|
||||
type_ref: None,
|
||||
initializer: Some(args),
|
||||
else_branch: None,
|
||||
};
|
||||
(vec![let_stmt], self.alloc_expr_desugared(Expr::Path(args_name.into())))
|
||||
} else {
|
||||
// Generate:
|
||||
// super let args = (&arg0, &arg1, &...);
|
||||
let args_name = Name::new_symbol_root(sym::args);
|
||||
let args_binding = self.alloc_binding(
|
||||
args_name.clone(),
|
||||
BindingAnnotation::Unannotated,
|
||||
HygieneId::ROOT,
|
||||
);
|
||||
let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });
|
||||
self.add_definition_to_binding(args_binding, args_pat);
|
||||
let elements = arguments
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: arg.expr,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let args_tuple = self.alloc_expr_desugared(Expr::Tuple { exprs: elements });
|
||||
// TODO: We don't have `super let` yet
|
||||
let let_stmt1 = Statement::Let {
|
||||
pat: args_pat,
|
||||
type_ref: None,
|
||||
initializer: Some(args_tuple),
|
||||
else_branch: None,
|
||||
};
|
||||
|
||||
// Generate:
|
||||
// super let args = [
|
||||
// <core::fmt::Argument>::new_display(args.0),
|
||||
// <core::fmt::Argument>::new_lower_hex(args.1),
|
||||
// <core::fmt::Argument>::new_debug(args.0),
|
||||
// …
|
||||
// ];
|
||||
let args = argmap
|
||||
.iter()
|
||||
.map(|&(arg_index, ty)| {
|
||||
let args_ident_expr =
|
||||
self.alloc_expr_desugared(Expr::Path(args_name.clone().into()));
|
||||
let arg = self.alloc_expr_desugared(Expr::Field {
|
||||
expr: args_ident_expr,
|
||||
name: Name::new_tuple_field(arg_index),
|
||||
});
|
||||
self.make_argument(arg, ty)
|
||||
})
|
||||
.collect();
|
||||
let array =
|
||||
self.alloc_expr_desugared(Expr::Array(Array::ElementList { elements: args }));
|
||||
let args_binding = self.alloc_binding(
|
||||
args_name.clone(),
|
||||
BindingAnnotation::Unannotated,
|
||||
HygieneId::ROOT,
|
||||
);
|
||||
let args_pat = self.alloc_pat_desugared(Pat::Bind { id: args_binding, subpat: None });
|
||||
self.add_definition_to_binding(args_binding, args_pat);
|
||||
let let_stmt2 = Statement::Let {
|
||||
pat: args_pat,
|
||||
type_ref: None,
|
||||
initializer: Some(array),
|
||||
else_branch: None,
|
||||
};
|
||||
(vec![let_stmt1, let_stmt2], self.alloc_expr_desugared(Expr::Path(args_name.into())))
|
||||
};
|
||||
|
||||
// Generate:
|
||||
// &args
|
||||
let args = self.alloc_expr_desugared(Expr::Ref {
|
||||
expr: args,
|
||||
rawness: Rawness::Ref,
|
||||
mutability: Mutability::Shared,
|
||||
});
|
||||
|
||||
let call_block = {
|
||||
// Generate:
|
||||
// unsafe {
|
||||
// <core::fmt::Arguments>::new_v1_formatted(
|
||||
// lit_pieces,
|
||||
// args,
|
||||
// format_options,
|
||||
// )
|
||||
// }
|
||||
|
||||
let new_v1_formatted = self.ty_rel_lang_path(
|
||||
self.lang_items().FormatArguments,
|
||||
Name::new_symbol_root(sym::new_v1_formatted),
|
||||
);
|
||||
let new_v1_formatted =
|
||||
self.alloc_expr_desugared(new_v1_formatted.map_or(Expr::Missing, Expr::Path));
|
||||
let args = [lit_pieces, args, format_options];
|
||||
let call = self
|
||||
.alloc_expr_desugared(Expr::Call { callee: new_v1_formatted, args: args.into() });
|
||||
|
||||
Expr::Unsafe { id: None, statements: Box::default(), tail: Some(call) }
|
||||
};
|
||||
|
||||
if !let_stmts.is_empty() {
|
||||
// Generate:
|
||||
// {
|
||||
// super let …
|
||||
// super let …
|
||||
// <core::fmt::Arguments>::new_…(…)
|
||||
// }
|
||||
let call = self.alloc_expr_desugared(call_block);
|
||||
self.alloc_expr(
|
||||
Expr::Block {
|
||||
id: None,
|
||||
statements: let_stmts.into(),
|
||||
tail: Some(call),
|
||||
label: None,
|
||||
},
|
||||
syntax_ptr,
|
||||
)
|
||||
} else {
|
||||
self.alloc_expr(call_block, syntax_ptr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a hir expression for a format_args placeholder specification.
|
||||
///
|
||||
/// Generates
|
||||
///
|
||||
/// ```text
|
||||
/// <core::fmt::rt::Placeholder::new(
|
||||
/// …usize, // position
|
||||
/// '…', // fill
|
||||
/// <core::fmt::rt::Alignment>::…, // alignment
|
||||
/// …u32, // flags
|
||||
/// <core::fmt::rt::Count::…>, // width
|
||||
/// <core::fmt::rt::Count::…>, // precision
|
||||
/// )
|
||||
/// ```
|
||||
fn make_format_spec(
|
||||
&mut self,
|
||||
placeholder: &FormatPlaceholder,
|
||||
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
|
||||
) -> ExprId {
|
||||
let lang_items = self.lang_items();
|
||||
let position = match placeholder.argument.index {
|
||||
Ok(arg_index) => {
|
||||
let (i, _) =
|
||||
argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
|
||||
self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
|
||||
i as u128,
|
||||
Some(BuiltinUint::Usize),
|
||||
)))
|
||||
}
|
||||
Err(_) => self.missing_expr(),
|
||||
};
|
||||
let &FormatOptions {
|
||||
ref width,
|
||||
ref precision,
|
||||
alignment,
|
||||
fill,
|
||||
sign,
|
||||
alternate,
|
||||
zero_pad,
|
||||
debug_hex,
|
||||
} = &placeholder.format_options;
|
||||
|
||||
let precision_expr = self.make_count(precision, argmap);
|
||||
let width_expr = self.make_count(width, argmap);
|
||||
|
||||
if self.krate.workspace_data(self.db).is_atleast_187() {
|
||||
// These need to match the constants in library/core/src/fmt/rt.rs.
|
||||
let align = match alignment {
|
||||
Some(FormatAlignment::Left) => 0,
|
||||
Some(FormatAlignment::Right) => 1,
|
||||
Some(FormatAlignment::Center) => 2,
|
||||
None => 3,
|
||||
};
|
||||
// This needs to match `Flag` in library/core/src/fmt/rt.rs.
|
||||
let flags = fill.unwrap_or(' ') as u32
|
||||
| ((sign == Some(FormatSign::Plus)) as u32) << 21
|
||||
| ((sign == Some(FormatSign::Minus)) as u32) << 22
|
||||
| (alternate as u32) << 23
|
||||
| (zero_pad as u32) << 24
|
||||
| ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 25
|
||||
| ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 26
|
||||
| (width.is_some() as u32) << 27
|
||||
| (precision.is_some() as u32) << 28
|
||||
| align << 29
|
||||
| 1 << 31; // Highest bit always set.
|
||||
let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
|
||||
flags as u128,
|
||||
Some(BuiltinUint::U32),
|
||||
)));
|
||||
|
||||
let position =
|
||||
RecordLitField { name: Name::new_symbol_root(sym::position), expr: position };
|
||||
let flags = RecordLitField { name: Name::new_symbol_root(sym::flags), expr: flags };
|
||||
let precision = RecordLitField {
|
||||
name: Name::new_symbol_root(sym::precision),
|
||||
expr: precision_expr,
|
||||
};
|
||||
let width =
|
||||
RecordLitField { name: Name::new_symbol_root(sym::width), expr: width_expr };
|
||||
self.alloc_expr_desugared(Expr::RecordLit {
|
||||
path: self.lang_path(lang_items.FormatPlaceholder).map(Box::new),
|
||||
fields: Box::new([position, flags, precision, width]),
|
||||
spread: None,
|
||||
})
|
||||
} else {
|
||||
let format_placeholder_new = {
|
||||
let format_placeholder_new = self.ty_rel_lang_path(
|
||||
lang_items.FormatPlaceholder,
|
||||
Name::new_symbol_root(sym::new),
|
||||
);
|
||||
match format_placeholder_new {
|
||||
Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
|
||||
None => self.missing_expr(),
|
||||
}
|
||||
};
|
||||
// This needs to match `Flag` in library/core/src/fmt/rt.rs.
|
||||
let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32)
|
||||
| (((sign == Some(FormatSign::Minus)) as u32) << 1)
|
||||
| ((alternate as u32) << 2)
|
||||
| ((zero_pad as u32) << 3)
|
||||
| (((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4)
|
||||
| (((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5);
|
||||
let flags = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
|
||||
flags as u128,
|
||||
Some(BuiltinUint::U32),
|
||||
)));
|
||||
let fill = self.alloc_expr_desugared(Expr::Literal(Literal::Char(fill.unwrap_or(' '))));
|
||||
let align = {
|
||||
let align = self.ty_rel_lang_path(
|
||||
lang_items.FormatAlignment,
|
||||
match alignment {
|
||||
Some(FormatAlignment::Left) => Name::new_symbol_root(sym::Left),
|
||||
Some(FormatAlignment::Right) => Name::new_symbol_root(sym::Right),
|
||||
Some(FormatAlignment::Center) => Name::new_symbol_root(sym::Center),
|
||||
None => Name::new_symbol_root(sym::Unknown),
|
||||
},
|
||||
);
|
||||
match align {
|
||||
Some(path) => self.alloc_expr_desugared(Expr::Path(path)),
|
||||
None => self.missing_expr(),
|
||||
}
|
||||
};
|
||||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: format_placeholder_new,
|
||||
args: Box::new([position, fill, align, flags, precision_expr, width_expr]),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a hir expression for a format_args Count.
|
||||
///
|
||||
/// Generates:
|
||||
///
|
||||
/// ```text
|
||||
/// <core::fmt::rt::Count>::Is(…)
|
||||
/// ```
|
||||
///
|
||||
/// or
|
||||
///
|
||||
/// ```text
|
||||
/// <core::fmt::rt::Count>::Param(…)
|
||||
/// ```
|
||||
///
|
||||
/// or
|
||||
///
|
||||
/// ```text
|
||||
/// <core::fmt::rt::Count>::Implied
|
||||
/// ```
|
||||
fn make_count(
|
||||
&mut self,
|
||||
count: &Option<FormatCount>,
|
||||
argmap: &mut FxIndexSet<(usize, ArgumentType)>,
|
||||
) -> ExprId {
|
||||
let lang_items = self.lang_items();
|
||||
match count {
|
||||
Some(FormatCount::Literal(n)) => {
|
||||
let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
|
||||
*n as u128,
|
||||
// FIXME: Change this to Some(BuiltinUint::U16) once we drop support for toolchains < 1.88
|
||||
None,
|
||||
)));
|
||||
let count_is = match self
|
||||
.ty_rel_lang_path(lang_items.FormatCount, Name::new_symbol_root(sym::Is))
|
||||
{
|
||||
Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)),
|
||||
None => self.missing_expr(),
|
||||
};
|
||||
self.alloc_expr_desugared(Expr::Call { callee: count_is, args: Box::new([args]) })
|
||||
}
|
||||
Some(FormatCount::Argument(arg)) => {
|
||||
if let Ok(arg_index) = arg.index {
|
||||
let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
|
||||
|
||||
let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
|
||||
i as u128,
|
||||
Some(BuiltinUint::Usize),
|
||||
)));
|
||||
let count_param = match self
|
||||
.ty_rel_lang_path(lang_items.FormatCount, Name::new_symbol_root(sym::Param))
|
||||
{
|
||||
Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
|
||||
None => self.missing_expr(),
|
||||
};
|
||||
self.alloc_expr_desugared(Expr::Call {
|
||||
callee: count_param,
|
||||
args: Box::new([args]),
|
||||
})
|
||||
} else {
|
||||
// FIXME: This drops arg causing it to potentially not be resolved/type checked
|
||||
// when typing?
|
||||
self.missing_expr()
|
||||
}
|
||||
}
|
||||
None => match self
|
||||
.ty_rel_lang_path(lang_items.FormatCount, Name::new_symbol_root(sym::Implied))
|
||||
{
|
||||
Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
|
||||
None => self.missing_expr(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a hir expression representing an argument to a format_args invocation.
|
||||
///
|
||||
/// Generates:
|
||||
///
|
||||
/// ```text
|
||||
/// <core::fmt::Argument>::new_…(arg)
|
||||
/// ```
|
||||
fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId {
|
||||
use ArgumentType::*;
|
||||
use FormatTrait::*;
|
||||
|
||||
let new_fn = match self.ty_rel_lang_path(
|
||||
self.lang_items().FormatArgument,
|
||||
Name::new_symbol_root(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,
|
||||
}),
|
||||
) {
|
||||
Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)),
|
||||
None => self.missing_expr(),
|
||||
};
|
||||
self.alloc_expr_desugared(Expr::Call { callee: new_fn, args: Box::new([arg]) })
|
||||
}
|
||||
|
||||
// endregion: format
|
||||
|
||||
fn lang_path(&self, lang: Option<impl Into<LangItemTarget>>) -> Option<Path> {
|
||||
Some(Path::LangItem(lang?.into(), None))
|
||||
}
|
||||
|
|
@ -3306,9 +2637,17 @@ impl<'db> ExprCollector<'db> {
|
|||
fn ty_rel_lang_path(
|
||||
&self,
|
||||
lang: Option<impl Into<LangItemTarget>>,
|
||||
relative_name: Name,
|
||||
relative_name: Symbol,
|
||||
) -> Option<Path> {
|
||||
Some(Path::LangItem(lang?.into(), Some(relative_name)))
|
||||
Some(Path::LangItem(lang?.into(), Some(Name::new_symbol_root(relative_name))))
|
||||
}
|
||||
|
||||
fn ty_rel_lang_path_expr(
|
||||
&self,
|
||||
lang: Option<impl Into<LangItemTarget>>,
|
||||
relative_name: Symbol,
|
||||
) -> Expr {
|
||||
self.ty_rel_lang_path(lang, relative_name).map_or(Expr::Missing, Expr::Path)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3425,12 +2764,6 @@ fn comma_follows_token(t: Option<syntax::SyntaxToken>) -> bool {
|
|||
.is_some_and(|it| it.kind() == syntax::T![,])
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
enum ArgumentType {
|
||||
Format(FormatTrait),
|
||||
Usize,
|
||||
}
|
||||
|
||||
/// This function find the AST fragment that corresponds to an `AssociatedTypeBinding` in the HIR.
|
||||
pub fn hir_assoc_type_binding_to_ast(
|
||||
segment_args: &ast::GenericArgList,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -161,9 +161,9 @@ fn main() {
|
|||
match builtin#lang(into_iter)(
|
||||
0..10,
|
||||
) {
|
||||
mut <ra@gennew>11 => loop {
|
||||
mut <ra@gennew>0 => loop {
|
||||
match builtin#lang(next)(
|
||||
&mut <ra@gennew>11,
|
||||
&mut <ra@gennew>0,
|
||||
) {
|
||||
builtin#lang(None) => break,
|
||||
builtin#lang(Some)(ident) => {
|
||||
|
|
@ -261,10 +261,10 @@ fn main() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn desugar_builtin_format_args() {
|
||||
fn desugar_builtin_format_args_before_1_93_0() {
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
//- minicore: fmt
|
||||
//- minicore: fmt_before_1_93_0
|
||||
fn main() {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
|
|
@ -343,6 +343,59 @@ fn main() {
|
|||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn desugar_builtin_format_args() {
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
//- minicore: fmt
|
||||
fn main() {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", orphan = (), last = "!");
|
||||
builtin#format_args("hello world");
|
||||
builtin#format_args("hello world", orphan = ());
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
expect![[r#"
|
||||
fn main() {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
{
|
||||
let <ra@gennew>0 = (&"fancy", &(), &"!", &count, &are, );
|
||||
let <ra@gennew>0 = [
|
||||
builtin#lang(Argument::new_display)(
|
||||
<ra@gennew>0.3,
|
||||
), builtin#lang(Argument::new_display)(
|
||||
<ra@gennew>0.0,
|
||||
), builtin#lang(Argument::new_debug)(
|
||||
<ra@gennew>0.4,
|
||||
), builtin#lang(Argument::new_display)(
|
||||
<ra@gennew>0.2,
|
||||
),
|
||||
];
|
||||
();
|
||||
unsafe {
|
||||
builtin#lang(Arguments::new)(
|
||||
"\x07\x1bhello \xc3 \x00\x00i\x02\x00\x01 \xc0\r friends, we \xc0\x01 \xc8\x01\x00\xc8\x03\x00\x00",
|
||||
&<ra@gennew>0,
|
||||
)
|
||||
}
|
||||
};
|
||||
builtin#lang(Arguments::from_str)(
|
||||
"hello world",
|
||||
);
|
||||
{
|
||||
();
|
||||
builtin#lang(Arguments::from_str)(
|
||||
"hello world",
|
||||
)
|
||||
};
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_hygiene() {
|
||||
let (db, body, def) = lower(
|
||||
|
|
@ -382,27 +435,16 @@ impl SsrError {
|
|||
fn main() {
|
||||
_ = ra_test_fixture::error::SsrError::new(
|
||||
{
|
||||
let args = [
|
||||
let <ra@gennew>0 = (&node.text(), );
|
||||
let <ra@gennew>0 = [
|
||||
builtin#lang(Argument::new_display)(
|
||||
&node.text(),
|
||||
<ra@gennew>0.0,
|
||||
),
|
||||
];
|
||||
unsafe {
|
||||
builtin#lang(Arguments::new_v1_formatted)(
|
||||
&[
|
||||
"Failed to resolve path `", "`",
|
||||
],
|
||||
&args,
|
||||
&[
|
||||
builtin#lang(Placeholder::new)(
|
||||
0usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
),
|
||||
],
|
||||
builtin#lang(Arguments::new)(
|
||||
"\x18Failed to resolve path `\xc0\x01`\x00",
|
||||
&<ra@gennew>0,
|
||||
)
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@
|
|||
//! eq: sized
|
||||
//! error: fmt
|
||||
//! fmt: option, result, transmute, coerce_unsized, copy, clone, derive
|
||||
//! fmt_before_1_89_0: fmt
|
||||
//! fmt_before_1_93_0: fmt
|
||||
//! fmt_before_1_89_0: fmt_before_1_93_0
|
||||
//! fn: sized, tuple
|
||||
//! from: sized, result
|
||||
//! future: pin
|
||||
|
|
@ -1259,6 +1260,7 @@ pub mod fmt {
|
|||
Unknown,
|
||||
}
|
||||
|
||||
// region:fmt_before_1_93_0
|
||||
#[lang = "format_count"]
|
||||
pub enum Count {
|
||||
Is(usize),
|
||||
|
|
@ -1288,6 +1290,7 @@ pub mod fmt {
|
|||
Placeholder { position, fill, align, flags, precision, width }
|
||||
}
|
||||
}
|
||||
// endregion:fmt_before_1_93_0
|
||||
|
||||
// region:fmt_before_1_89_0
|
||||
#[lang = "format_unsafe_arg"]
|
||||
|
|
@ -1303,6 +1306,7 @@ pub mod fmt {
|
|||
// endregion:fmt_before_1_89_0
|
||||
}
|
||||
|
||||
// region:fmt_before_1_93_0
|
||||
#[derive(Copy, Clone)]
|
||||
#[lang = "format_arguments"]
|
||||
pub struct Arguments<'a> {
|
||||
|
|
@ -1341,6 +1345,14 @@ pub mod fmt {
|
|||
}
|
||||
// endregion:!fmt_before_1_89_0
|
||||
|
||||
pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> {
|
||||
Self::from_str(s)
|
||||
}
|
||||
|
||||
pub const fn from_str(s: &'static str) -> Arguments<'a> {
|
||||
Arguments { pieces: &[s], fmt: None, args: &[] }
|
||||
}
|
||||
|
||||
pub const fn as_str(&self) -> Option<&'static str> {
|
||||
match (self.pieces, self.args) {
|
||||
([], []) => Some(""),
|
||||
|
|
@ -1349,6 +1361,41 @@ pub mod fmt {
|
|||
}
|
||||
}
|
||||
}
|
||||
// endregion:fmt_before_1_93_0
|
||||
|
||||
// region:!fmt_before_1_93_0
|
||||
#[lang = "format_arguments"]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Arguments<'a> {
|
||||
// This is a non-faithful representation of `core::fmt::Arguments`, because the real one
|
||||
// is too complex for minicore.
|
||||
message: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> Arguments<'a> {
|
||||
pub unsafe fn new<const N: usize, const M: usize>(
|
||||
_template: &'a [u8; N],
|
||||
_args: &'a [rt::Argument<'a>; M],
|
||||
) -> Arguments<'a> {
|
||||
Arguments { message: None }
|
||||
}
|
||||
|
||||
pub fn from_str_nonconst(s: &'static str) -> Arguments<'a> {
|
||||
Arguments { message: Some(s) }
|
||||
}
|
||||
|
||||
pub const fn from_str(s: &'static str) -> Arguments<'a> {
|
||||
Arguments { message: Some(s) }
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> Option<&'static str> {
|
||||
match self.message {
|
||||
Some(s) => unsafe { Some(&*(s as *const str)) },
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
// endregion:!fmt_before_1_93_0
|
||||
|
||||
// region:derive
|
||||
pub(crate) mod derive {
|
||||
|
|
@ -1817,7 +1864,7 @@ mod panicking {
|
|||
|
||||
#[lang = "panic"]
|
||||
pub const fn panic(expr: &'static str) -> ! {
|
||||
panic_fmt(crate::fmt::Arguments::new_const(&[expr]))
|
||||
panic_fmt(crate::fmt::Arguments::from_str(expr))
|
||||
}
|
||||
}
|
||||
// endregion:panic
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue