mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-29 10:58:02 +00:00
Minic rustc's new format_args! expansion
This commit is contained in:
parent
c13859903c
commit
4f8767d790
7 changed files with 366 additions and 41 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -178,14 +178,14 @@ fn main() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn desugar_builtin_format_args() {
|
||||
fn desugar_builtin_format_args_before_1_89_0() {
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
//- minicore: fmt
|
||||
//- minicore: fmt_before_1_89_0
|
||||
fn main() {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", last = "!");
|
||||
builtin#format_args("\u{1b}hello {count:02} {} friends, we {are:?} {0}{last}", "fancy", orphan = (), last = "!");
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
|
@ -249,14 +249,100 @@ fn main() {
|
|||
builtin#lang(Count::Implied),
|
||||
),
|
||||
],
|
||||
unsafe {
|
||||
builtin#lang(UnsafeArg::new)()
|
||||
{
|
||||
();
|
||||
unsafe {
|
||||
builtin#lang(UnsafeArg::new)()
|
||||
}
|
||||
},
|
||||
);
|
||||
}"#]]
|
||||
.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 = "!");
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
||||
expect![[r#"
|
||||
fn main() {
|
||||
let are = "are";
|
||||
let count = 10;
|
||||
{
|
||||
let args = (&"fancy", &(), &"!", &count, &are, );
|
||||
let args = [
|
||||
builtin#lang(Argument::new_display)(
|
||||
args.3,
|
||||
), builtin#lang(Argument::new_display)(
|
||||
args.0,
|
||||
), builtin#lang(Argument::new_debug)(
|
||||
args.4,
|
||||
), builtin#lang(Argument::new_display)(
|
||||
args.2,
|
||||
),
|
||||
];
|
||||
unsafe {
|
||||
builtin#lang(Arguments::new_v1_formatted)(
|
||||
&[
|
||||
"\u{1b}hello ", " ", " friends, we ", " ", "",
|
||||
],
|
||||
&args,
|
||||
&[
|
||||
builtin#lang(Placeholder::new)(
|
||||
0usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
8u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Is)(
|
||||
2,
|
||||
),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
1usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
2usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
1usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
), builtin#lang(Placeholder::new)(
|
||||
3usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
),
|
||||
],
|
||||
)
|
||||
}
|
||||
};
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_hygiene() {
|
||||
let (db, body, def) = lower(
|
||||
|
|
@ -295,29 +381,31 @@ impl SsrError {
|
|||
expect![[r#"
|
||||
fn main() {
|
||||
_ = ra_test_fixture::error::SsrError::new(
|
||||
builtin#lang(Arguments::new_v1_formatted)(
|
||||
&[
|
||||
"Failed to resolve path `", "`",
|
||||
],
|
||||
&[
|
||||
{
|
||||
let args = [
|
||||
builtin#lang(Argument::new_display)(
|
||||
&node.text(),
|
||||
),
|
||||
],
|
||||
&[
|
||||
builtin#lang(Placeholder::new)(
|
||||
0usize,
|
||||
' ',
|
||||
builtin#lang(Alignment::Unknown),
|
||||
0u32,
|
||||
builtin#lang(Count::Implied),
|
||||
builtin#lang(Count::Implied),
|
||||
),
|
||||
],
|
||||
];
|
||||
unsafe {
|
||||
builtin#lang(UnsafeArg::new)()
|
||||
},
|
||||
),
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
}
|
||||
},
|
||||
);
|
||||
}"#]]
|
||||
.assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
|
||||
|
|
@ -327,7 +415,7 @@ impl SsrError {
|
|||
fn regression_10300() {
|
||||
let (db, body, def) = lower(
|
||||
r#"
|
||||
//- minicore: concat, panic
|
||||
//- minicore: concat, panic, fmt_before_1_89_0
|
||||
mod private {
|
||||
pub use core::concat;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue