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.rs | 227 |
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. |