Skip to content

rust-analyzer backports #143794

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: beta
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 216 additions & 8 deletions src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2815,6 +2815,44 @@ 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, 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() {
Expand Down Expand Up @@ -2902,19 +2940,189 @@ 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,
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 = 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.
Expand Down
Loading
Loading