Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-def/src/expr_store/lower.rs223
-rw-r--r--crates/hir-def/src/expr_store/tests/body.rs138
-rw-r--r--crates/ide-assists/src/handlers/term_search.rs12
-rw-r--r--crates/ide-completion/src/tests/expression.rs4
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_unsafe.rs25
-rw-r--r--crates/intern/src/symbol/symbols.rs1
-rw-r--r--crates/test-utils/src/fixture.rs4
7 files changed, 366 insertions, 41 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 4ba31c1e20..efa1374a44 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -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.
diff --git a/crates/hir-def/src/expr_store/tests/body.rs b/crates/hir-def/src/expr_store/tests/body.rs
index 29e249b07a..927e280d73 100644
--- a/crates/hir-def/src/expr_store/tests/body.rs
+++ b/crates/hir-def/src/expr_store/tests/body.rs
@@ -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,8 +249,11 @@ fn main() {
builtin#lang(Count::Implied),
),
],
- unsafe {
- builtin#lang(UnsafeArg::new)()
+ {
+ ();
+ unsafe {
+ builtin#lang(UnsafeArg::new)()
+ }
},
);
}"#]]
@@ -258,6 +261,89 @@ fn main() {
}
#[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(
r##"
@@ -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;
}
diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs
index f01b7c0bc2..019ddaf144 100644
--- a/crates/ide-assists/src/handlers/term_search.rs
+++ b/crates/ide-assists/src/handlers/term_search.rs
@@ -100,7 +100,9 @@ fn f() { let a: u128 = 1; let b: u128 = todo$0!() }"#,
fn test_complete_todo_with_msg() {
check_assist(
term_search,
- r#"//- minicore: todo, unimplemented
+ // FIXME: Since we are lacking of `super let`, term search fails due to borrowck failure.
+ // Should implement super let and remove `fmt_before_1_89_0`
+ r#"//- minicore: todo, unimplemented, fmt_before_1_89_0
fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
r#"fn f() { let a: u128 = 1; let b: u128 = a }"#,
)
@@ -110,7 +112,9 @@ fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
fn test_complete_unimplemented_with_msg() {
check_assist(
term_search,
- r#"//- minicore: todo, unimplemented
+ // FIXME: Since we are lacking of `super let`, term search fails due to borrowck failure.
+ // Should implement super let and remove `fmt_before_1_89_0`
+ r#"//- minicore: todo, unimplemented, fmt_before_1_89_0
fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
r#"fn f() { let a: u128 = 1; let b: u128 = a }"#,
)
@@ -120,7 +124,9 @@ fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
fn test_complete_unimplemented() {
check_assist(
term_search,
- r#"//- minicore: todo, unimplemented
+ // FIXME: Since we are lacking of `super let`, term search fails due to borrowck failure.
+ // Should implement super let and remove `fmt_before_1_89_0`
+ r#"//- minicore: todo, unimplemented, fmt_before_1_89_0
fn f() { let a: u128 = 1; let b: u128 = todo$0!("asd") }"#,
r#"fn f() { let a: u128 = 1; let b: u128 = a }"#,
)
diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs
index b46e4c3206..b2d18b796f 100644
--- a/crates/ide-completion/src/tests/expression.rs
+++ b/crates/ide-completion/src/tests/expression.rs
@@ -1474,20 +1474,18 @@ fn main() {
}
"#,
expect![[r#"
+ me foo() fn(&self)
sn box Box::new(expr)
sn call function(expr)
sn const const {}
sn dbg dbg!(expr)
sn dbgr dbg!(&expr)
sn deref *expr
- sn if if expr {}
sn match match expr {}
- sn not !expr
sn ref &expr
sn refm &mut expr
sn return return expr
sn unsafe unsafe {}
- sn while while expr {}
"#]],
);
}
diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 6bd5417b25..d8f6e813d8 100644
--- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -630,6 +630,17 @@ fn main() {
// Checks that we don't place orphan arguments for formatting under an unsafe block.
check_diagnostics(
r#"
+//- minicore: fmt_before_1_89_0
+fn foo() {
+ let p = 0xDEADBEEF as *const i32;
+ format_args!("", *p);
+ // ^^ error: dereference of raw pointer is unsafe and requires an unsafe function or block
+}
+ "#,
+ );
+
+ check_diagnostics(
+ r#"
//- minicore: fmt
fn foo() {
let p = 0xDEADBEEF as *const i32;
@@ -958,4 +969,18 @@ impl FooTrait for S2 {
"#,
);
}
+
+ #[test]
+ fn no_false_positive_on_format_args_since_1_89_0() {
+ check_diagnostics(
+ r#"
+//- minicore: fmt
+fn test() {
+ let foo = 10;
+ let bar = true;
+ let _x = format_args!("{} {0} {} {last}", foo, bar, last = "!");
+}
+ "#,
+ );
+ }
}
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index d5cbb7328c..adc581309d 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -496,6 +496,7 @@ define_symbols! {
vectorcall,
wasm,
win64,
+ args,
array,
boxed_slice,
completions,
diff --git a/crates/test-utils/src/fixture.rs b/crates/test-utils/src/fixture.rs
index ddc7226311..1d821e96e5 100644
--- a/crates/test-utils/src/fixture.rs
+++ b/crates/test-utils/src/fixture.rs
@@ -446,13 +446,13 @@ impl MiniCore {
}
let mut keep = true;
- for &region in active_regions.iter() {
+ for &region in &active_regions {
assert!(!region.starts_with(' '), "region marker starts with a space: {region:?}");
self.assert_valid_flag(region);
seen_regions.push(region);
keep &= self.has_flag(region);
}
- for &region in inactive_regions.iter() {
+ for &region in &inactive_regions {
assert!(!region.starts_with(' '), "region marker starts with a space: {region:?}");
self.assert_valid_flag(region);
seen_regions.push(region);