Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/expr_store/lower.rs')
-rw-r--r--crates/hir-def/src/expr_store/lower.rs227
1 files changed, 217 insertions, 10 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 03683ec920..efa1374a44 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -11,7 +11,7 @@ use base_db::FxIndexSet;
use cfg::CfgOptions;
use either::Either;
use hir_expand::{
- HirFileId, InFile, Intern, MacroDefId,
+ HirFileId, InFile, MacroDefId,
mod_path::tool_path,
name::{AsName, Name},
span_map::SpanMapRef,
@@ -2148,7 +2148,7 @@ impl ExprCollector<'_> {
) -> ExprId {
let block_id = self.expander.ast_id_map().ast_id_for_block(&block).map(|file_local_id| {
let ast_id = self.expander.in_file(file_local_id);
- BlockLoc { ast_id, module: self.module }.intern(self.db)
+ self.db.intern_block(BlockLoc { ast_id, module: self.module })
});
let (module, def_map) =
@@ -2815,6 +2815,51 @@ impl ExprCollector<'_> {
mutability: Mutability::Shared,
})
};
+
+ // Assume that rustc version >= 1.89.0 iff lang item `format_arguments` exists
+ // but `format_unsafe_arg` does not
+ let fmt_args =
+ || crate::lang_item::lang_item(self.db, self.module.krate(), LangItem::FormatArguments);
+ let fmt_unsafe_arg =
+ || crate::lang_item::lang_item(self.db, self.module.krate(), LangItem::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,
+ hygiene,
+ argmap,
+ lit_pieces,
+ format_options,
+ )
+ } else {
+ self.collect_format_args_before_1_89_0_impl(
+ syntax_ptr,
+ fmt,
+ argmap,
+ lit_pieces,
+ format_options,
+ )
+ };
+
+ self.source_map
+ .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() {
@@ -2902,19 +2947,181 @@ impl ExprCollector<'_> {
});
}
- let idx = self.alloc_expr(
+ self.alloc_expr(
Expr::Call {
callee: new_v1_formatted,
args: Box::new([lit_pieces, args, format_options, unsafe_arg_new]),
},
syntax_ptr,
- );
- self.source_map
- .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`,
+ /// 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,
+ hygiene: HygieneId,
+ 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, hygiene);
+ 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(Path::from(args_name))))
+ } 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, hygiene);
+ 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, hygiene);
+ 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 = LangItem::FormatArguments.ty_rel_path(
+ self.db,
+ self.module.krate(),
+ 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.