about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-04-21 16:22:02 +0000
committerbors <bors@rust-lang.org>2024-04-21 16:22:02 +0000
commit8ea8c7432b734161d208cf6599f0454d42e6fa58 (patch)
tree58fa3faeb41b80a76c0f85e58f018b1bc6cbfc5d
parent7a3ad0308ad32315da69d7d3be122c3788f19093 (diff)
parent04de0fff1e6b732604b1b4c2dc124d8893b7f7b0 (diff)
downloadrust-8ea8c7432b734161d208cf6599f0454d42e6fa58.tar.gz
rust-8ea8c7432b734161d208cf6599f0454d42e6fa58.zip
Auto merge of #16938 - Nilstrieb:dont-panic-tests, r=Veykril
Implement `BeginPanic` handling in const eval

for #16935, needs some figuring out of how to write these tests correctly
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs80
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs26
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs58
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs45
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html4
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram1
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs77
12 files changed, 235 insertions, 85 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
index 340e95dbc2f..f58228c45f0 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs
@@ -1869,42 +1869,45 @@ impl ExprCollector<'_> {
     ) -> ExprId {
         match count {
             Some(FormatCount::Literal(n)) => {
-                match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) {
-                    Some(count_is) => {
-                        let count_is = self.alloc_expr_desugared(Expr::Path(count_is));
-                        let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
-                            *n as u128,
-                            Some(BuiltinUint::Usize),
-                        )));
-                        self.alloc_expr_desugared(Expr::Call {
-                            callee: count_is,
-                            args: Box::new([args]),
-                            is_assignee_expr: false,
-                        })
-                    }
-                    None => self.missing_expr(),
-                }
+                let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
+                    *n as u128,
+                    Some(BuiltinUint::Usize),
+                )));
+                let count_is =
+                    match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Is]) {
+                        Some(count_is) => self.alloc_expr_desugared(Expr::Path(count_is)),
+                        None => self.missing_expr(),
+                    };
+                self.alloc_expr_desugared(Expr::Call {
+                    callee: count_is,
+                    args: Box::new([args]),
+                    is_assignee_expr: false,
+                })
             }
             Some(FormatCount::Argument(arg)) => {
                 if let Ok(arg_index) = arg.index {
                     let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
 
-                    match LangItem::FormatCount.ty_rel_path(self.db, self.krate, name![Param]) {
-                        Some(count_param) => {
-                            let count_param = self.alloc_expr_desugared(Expr::Path(count_param));
-                            let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
-                                i as u128,
-                                Some(BuiltinUint::Usize),
-                            )));
-                            self.alloc_expr_desugared(Expr::Call {
-                                callee: count_param,
-                                args: Box::new([args]),
-                                is_assignee_expr: false,
-                            })
-                        }
+                    let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
+                        i as u128,
+                        Some(BuiltinUint::Usize),
+                    )));
+                    let count_param = match LangItem::FormatCount.ty_rel_path(
+                        self.db,
+                        self.krate,
+                        name![Param],
+                    ) {
+                        Some(count_param) => self.alloc_expr_desugared(Expr::Path(count_param)),
                         None => self.missing_expr(),
-                    }
+                    };
+                    self.alloc_expr_desugared(Expr::Call {
+                        callee: count_param,
+                        args: Box::new([args]),
+                        is_assignee_expr: false,
+                    })
                 } else {
+                    // FIXME: This drops arg causing it to potentially not be resolved/type checked
+                    // when typing?
                     self.missing_expr()
                 }
             }
@@ -1925,7 +1928,8 @@ impl ExprCollector<'_> {
     fn make_argument(&mut self, arg: ExprId, ty: ArgumentType) -> ExprId {
         use ArgumentType::*;
         use FormatTrait::*;
-        match LangItem::FormatArgument.ty_rel_path(
+
+        let new_fn = match LangItem::FormatArgument.ty_rel_path(
             self.db,
             self.krate,
             match ty {
@@ -1941,16 +1945,14 @@ impl ExprCollector<'_> {
                 Usize => name![from_usize],
             },
         ) {
-            Some(new_fn) => {
-                let new_fn = self.alloc_expr_desugared(Expr::Path(new_fn));
-                self.alloc_expr_desugared(Expr::Call {
-                    callee: new_fn,
-                    args: Box::new([arg]),
-                    is_assignee_expr: false,
-                })
-            }
+            Some(new_fn) => self.alloc_expr_desugared(Expr::Path(new_fn)),
             None => self.missing_expr(),
-        }
+        };
+        self.alloc_expr_desugared(Expr::Call {
+            callee: new_fn,
+            args: Box::new([arg]),
+            is_assignee_expr: false,
+        })
     }
     // endregion: format
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
index a27ffe21675..4c8a54f7c8c 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs
@@ -318,18 +318,20 @@ fn f() {
 
     expect![[r#"
         fn f() {
-            $crate::panicking::panic_fmt(
-                builtin#lang(Arguments::new_v1_formatted)(
-                    &[
-                        "cc",
-                    ],
-                    &[],
-                    &[],
-                    unsafe {
-                        builtin#lang(UnsafeArg::new)()
-                    },
-                ),
-            );
+            {
+                $crate::panicking::panic_fmt(
+                    builtin#lang(Arguments::new_v1_formatted)(
+                        &[
+                            "cc",
+                        ],
+                        &[],
+                        &[],
+                        unsafe {
+                            builtin#lang(UnsafeArg::new)()
+                        },
+                    ),
+                );
+            };
         }"#]]
     .assert_eq(&body.pretty_print(&db, def))
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
index 0bf01b0bc6a..d99ef6679e5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_ext.rs
@@ -27,6 +27,7 @@ pub trait TyExt {
     fn is_scalar(&self) -> bool;
     fn is_floating_point(&self) -> bool;
     fn is_never(&self) -> bool;
+    fn is_str(&self) -> bool;
     fn is_unknown(&self) -> bool;
     fn contains_unknown(&self) -> bool;
     fn is_ty_var(&self) -> bool;
@@ -87,6 +88,10 @@ impl TyExt for Ty {
         matches!(self.kind(Interner), TyKind::Never)
     }
 
+    fn is_str(&self) -> bool {
+        matches!(self.kind(Interner), TyKind::Str)
+    }
+
     fn is_unknown(&self) -> bool {
         matches!(self.kind(Interner), TyKind::Error)
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
index 045ffb418c8..2de1aa30c96 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
@@ -428,6 +428,17 @@ impl MirEvalError {
         }
         Ok(())
     }
+
+    pub fn is_panic(&self) -> Option<&str> {
+        let mut err = self;
+        while let MirEvalError::InFunction(e, _) = err {
+            err = e;
+        }
+        match err {
+            MirEvalError::Panic(msg) => Some(msg),
+            _ => None,
+        }
+    }
 }
 
 impl std::fmt::Debug for MirEvalError {
@@ -1138,7 +1149,7 @@ impl Evaluator<'_> {
                 let mut ty = self.operand_ty(lhs, locals)?;
                 while let TyKind::Ref(_, _, z) = ty.kind(Interner) {
                     ty = z.clone();
-                    let size = if ty.kind(Interner) == &TyKind::Str {
+                    let size = if ty.is_str() {
                         if *op != BinOp::Eq {
                             never!("Only eq is builtin for `str`");
                         }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
index fee3dd3ada8..3438712049e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/shim.rs
@@ -49,6 +49,7 @@ impl Evaluator<'_> {
         if self.not_special_fn_cache.borrow().contains(&def) {
             return Ok(false);
         }
+
         let function_data = self.db.function_data(def);
         let is_intrinsic = match &function_data.abi {
             Some(abi) => *abi == Interned::new_str("rust-intrinsic"),
@@ -131,9 +132,7 @@ impl Evaluator<'_> {
             return Ok(true);
         }
         if let Some(it) = self.detect_lang_function(def) {
-            let arg_bytes =
-                args.iter().map(|it| Ok(it.get(self)?.to_owned())).collect::<Result<Vec<_>>>()?;
-            let result = self.exec_lang_item(it, generic_args, &arg_bytes, locals, span)?;
+            let result = self.exec_lang_item(it, generic_args, args, locals, span)?;
             destination.write_from_bytes(self, &result)?;
             return Ok(true);
         }
@@ -311,16 +310,20 @@ impl Evaluator<'_> {
 
     fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
         use LangItem::*;
-        let candidate = self.db.lang_attr(def.into())?;
+        let attrs = self.db.attrs(def.into());
+
+        if attrs.by_key("rustc_const_panic_str").exists() {
+            // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE.
+            return Some(LangItem::BeginPanic);
+        }
+
+        let candidate = attrs.by_key("lang").string_value().and_then(LangItem::from_str)?;
         // We want to execute these functions with special logic
         // `PanicFmt` is not detected here as it's redirected later.
         if [BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
             return Some(candidate);
         }
-        if self.db.attrs(def.into()).by_key("rustc_const_panic_str").exists() {
-            // `#[rustc_const_panic_str]` is treated like `lang = "begin_panic"` by rustc CTFE.
-            return Some(LangItem::BeginPanic);
-        }
+
         None
     }
 
@@ -328,18 +331,52 @@ impl Evaluator<'_> {
         &mut self,
         it: LangItem,
         generic_args: &Substitution,
-        args: &[Vec<u8>],
+        args: &[IntervalAndTy],
         locals: &Locals,
         span: MirSpan,
     ) -> Result<Vec<u8>> {
         use LangItem::*;
         let mut args = args.iter();
         match it {
-            BeginPanic => Err(MirEvalError::Panic("<unknown-panic-payload>".to_owned())),
+            BeginPanic => {
+                let mut arg = args
+                    .next()
+                    .ok_or(MirEvalError::InternalError(
+                        "argument of BeginPanic is not provided".into(),
+                    ))?
+                    .clone();
+                while let TyKind::Ref(_, _, ty) = arg.ty.kind(Interner) {
+                    if ty.is_str() {
+                        let (pointee, metadata) = arg.interval.get(self)?.split_at(self.ptr_size());
+                        let len = from_bytes!(usize, metadata);
+
+                        return {
+                            Err(MirEvalError::Panic(
+                                std::str::from_utf8(
+                                    self.read_memory(Address::from_bytes(pointee)?, len)?,
+                                )
+                                .unwrap()
+                                .to_owned(),
+                            ))
+                        };
+                    }
+                    let size = self.size_of_sized(ty, locals, "begin panic arg")?;
+                    let pointee = arg.interval.get(self)?;
+                    arg = IntervalAndTy {
+                        interval: Interval::new(Address::from_bytes(pointee)?, size),
+                        ty: ty.clone(),
+                    };
+                }
+                Err(MirEvalError::Panic(format!(
+                    "unknown-panic-payload: {:?}",
+                    arg.ty.kind(Interner)
+                )))
+            }
             SliceLen => {
                 let arg = args.next().ok_or(MirEvalError::InternalError(
                     "argument of <[T]>::len() is not provided".into(),
                 ))?;
+                let arg = arg.get(self)?;
                 let ptr_size = arg.len() / 2;
                 Ok(arg[ptr_size..].into())
             }
@@ -353,6 +390,7 @@ impl Evaluator<'_> {
                 let arg = args.next().ok_or(MirEvalError::InternalError(
                     "argument of drop_in_place is not provided".into(),
                 ))?;
+                let arg = arg.interval.get(self)?.to_owned();
                 self.run_drop_glue_deep(
                     ty.clone(),
                     locals,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
index 381522c9abe..4abbda56cbb 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
@@ -31,6 +31,7 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr
             db.trait_environment(func_id.into()),
         )
         .map_err(|e| MirEvalError::MirLowerError(func_id, e))?;
+
     let (result, output) = interpret_mir(db, body, false, None);
     result?;
     Ok((output.stdout().into_owned(), output.stderr().into_owned()))
@@ -72,6 +73,13 @@ fn check_pass_and_stdio(ra_fixture: &str, expected_stdout: &str, expected_stderr
     }
 }
 
+fn check_panic(ra_fixture: &str, expected_panic: &str) {
+    let (db, file_ids) = TestDB::with_many_files(ra_fixture);
+    let file_id = *file_ids.last().unwrap();
+    let e = eval_main(&db, file_id).unwrap_err();
+    assert_eq!(e.is_panic().unwrap_or_else(|| panic!("unexpected error: {:?}", e)), expected_panic);
+}
+
 #[test]
 fn function_with_extern_c_abi() {
     check_pass(
@@ -88,6 +96,43 @@ fn main() {
 }
 
 #[test]
+fn panic_fmt() {
+    // panic!
+    // -> panic_2021 (builtin macro redirection)
+    //   -> #[lang = "panic_fmt"] core::panicking::panic_fmt (hooked by CTFE for redirection)
+    //   -> core::panicking::const_panic_fmt
+    //     -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic)
+    //       -> Err(ConstEvalError::Panic)
+    check_panic(
+        r#"
+//- minicore: fmt, panic
+fn main() {
+    panic!("hello, world!");
+}
+    "#,
+        "hello, world!",
+    );
+}
+
+#[test]
+fn panic_display() {
+    // panic!
+    // -> panic_2021 (builtin macro redirection)
+    //   -> #[rustc_const_panic_str] core::panicking::panic_display (hooked by CTFE for builtin panic)
+    //     -> Err(ConstEvalError::Panic)
+    check_panic(
+        r#"
+//- minicore: fmt, panic
+
+fn main() {
+    panic!("{}", "hello, world!");
+}
+    "#,
+        "hello, world!",
+    );
+}
+
+#[test]
 fn drop_basic() {
     check_pass(
         r#"
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
index bb5c2b79139..cd5e95cc1e3 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/tests.rs
@@ -293,6 +293,7 @@ fn minicore_smoke_test() {
         // This should be ignored since we conditionally remove code which creates single item use with braces
         config.disabled.insert("unused_braces".to_owned());
         config.disabled.insert("unused_variables".to_owned());
+        config.disabled.insert("remove-unnecessary-else".to_owned());
         check_diagnostics_with_config(config, &source);
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index 6bbc8b380d6..0f666d40daf 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -7864,8 +7864,8 @@ impl Iterator for S {
                                 file_id: FileId(
                                     1,
                                 ),
-                                full_range: 6290..6498,
-                                focus_range: 6355..6361,
+                                full_range: 7791..7999,
+                                focus_range: 7856..7862,
                                 name: "Future",
                                 kind: Trait,
                                 container_name: "future",
@@ -7878,8 +7878,8 @@ impl Iterator for S {
                                 file_id: FileId(
                                     1,
                                 ),
-                                full_range: 7128..7594,
-                                focus_range: 7172..7180,
+                                full_range: 8629..9095,
+                                focus_range: 8673..8681,
                                 name: "Iterator",
                                 kind: Trait,
                                 container_name: "iterator",
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
index 366895ce7ec..893e3c06751 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html
@@ -49,5 +49,5 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
     <span class="keyword">let</span> <span class="variable declaration">foo</span> <span class="operator">=</span> <span class="enum_variant default_library library">Some</span><span class="parenthesis">(</span><span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
-    <span class="keyword">let</span> <span class="variable declaration">nums</span> <span class="operator">=</span> <span class="module default_library library">iter</span><span class="operator">::</span><span class="function default_library library">repeat</span><span class="parenthesis">(</span><span class="variable">foo</span><span class="operator">.</span><span class="method consuming default_library library">unwrap</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="variable declaration">nums</span> <span class="operator">=</span> <span class="module default_library library">iter</span><span class="operator">::</span><span class="function default_library library">repeat</span><span class="parenthesis">(</span><span class="variable">foo</span><span class="operator">.</span><span class="method default_library library">unwrap</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 <span class="brace">}</span></code></pre>
\ No newline at end of file
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 7a07d17b271..22706dea1fa 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -158,9 +158,9 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
 
     <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
-    <span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+    <span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="bool_literal macro">true</span><span class="comma macro">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
     <span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span>
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index e1765b25fd8..8a775dd8749 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -391,6 +391,7 @@ FormatArgsExpr =
 FormatArgsArg =
   (Name '=')? Expr
 
+# MacroCallExpr
 MacroExpr =
   MacroCall
 
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index f125792d125..0257ed9ab41 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -28,7 +28,7 @@
 //!     env: option
 //!     eq: sized
 //!     error: fmt
-//!     fmt: option, result, transmute, coerce_unsized
+//!     fmt: option, result, transmute, coerce_unsized, copy, clone, derive
 //!     fn:
 //!     from: sized
 //!     future: pin
@@ -913,11 +913,13 @@ pub mod fmt {
     }
 
     mod rt {
+        use super::*;
 
         extern "C" {
             type Opaque;
         }
 
+        #[derive(Copy, Clone)]
         #[lang = "format_argument"]
         pub struct Argument<'a> {
             value: &'a Opaque,
@@ -930,8 +932,8 @@ pub mod fmt {
                 unsafe { Argument { formatter: transmute(f), value: transmute(x) } }
             }
 
-            pub fn new_display<'b, T: Display>(x: &'b T) -> Argument<'_> {
-                Self::new(x, Display::fmt)
+            pub fn new_display<'b, T: crate::fmt::Display>(x: &'b T) -> Argument<'_> {
+                Self::new(x, crate::fmt::Display::fmt)
             }
         }
 
@@ -968,7 +970,9 @@ pub mod fmt {
                 flags: u32,
                 precision: Count,
                 width: Count,
-            ) -> Self;
+            ) -> Self {
+                Placeholder { position, fill, align, flags, precision, width }
+            }
         }
 
         #[lang = "format_unsafe_arg"]
@@ -977,10 +981,13 @@ pub mod fmt {
         }
 
         impl UnsafeArg {
-            pub unsafe fn new() -> Self;
+            pub unsafe fn new() -> Self {
+                UnsafeArg { _private: () }
+            }
         }
     }
 
+    #[derive(Copy, Clone)]
     #[lang = "format_arguments"]
     pub struct Arguments<'a> {
         pieces: &'a [&'static str],
@@ -1005,6 +1012,14 @@ pub mod fmt {
         ) -> Arguments<'a> {
             Arguments { pieces, fmt: Some(fmt), args }
         }
+
+        pub const fn as_str(&self) -> Option<&'static str> {
+            match (self.pieces, self.args) {
+                ([], []) => Some(""),
+                ([s], []) => Some(s),
+                _ => None,
+            }
+        }
     }
 
     // region:derive
@@ -1154,8 +1169,8 @@ pub mod pin {
         pointer: P,
     }
     impl<P> Pin<P> {
-        pub fn new(_pointer: P) -> Pin<P> {
-            loop {}
+        pub fn new(pointer: P) -> Pin<P> {
+            Pin { pointer }
         }
     }
     // region:deref
@@ -1356,18 +1371,48 @@ pub mod iter {
 // region:panic
 mod panic {
     pub macro panic_2021 {
-        () => (
-            $crate::panicking::panic("explicit panic")
-        ),
-        ($($t:tt)+) => (
-            $crate::panicking::panic_fmt($crate::const_format_args!($($t)+))
-        ),
+        () => ({
+            const fn panic_cold_explicit() -> ! {
+                $crate::panicking::panic_explicit()
+            }
+            panic_cold_explicit();
+        }),
+        // Special-case the single-argument case for const_panic.
+        ("{}", $arg:expr $(,)?) => ({
+            #[rustc_const_panic_str] // enforce a &&str argument in const-check and hook this by const-eval
+            #[rustc_do_not_const_check] // hooked by const-eval
+            const fn panic_cold_display<T: $crate::fmt::Display>(arg: &T) -> ! {
+                $crate::panicking::panic_display(arg)
+            }
+            panic_cold_display(&$arg);
+        }),
+        ($($t:tt)+) => ({
+            // Semicolon to prevent temporaries inside the formatting machinery from
+            // being considered alive in the caller after the panic_fmt call.
+            $crate::panicking::panic_fmt($crate::const_format_args!($($t)+));
+        }),
     }
 }
 
 mod panicking {
-    #[lang = "panic_fmt"]
-    pub const fn panic_fmt(_fmt: crate::fmt::Arguments<'_>) -> ! {
+    #[rustc_const_panic_str] // enforce a &&str argument in const-check and hook this by const-eval
+    pub const fn panic_display<T: crate::fmt::Display>(x: &T) -> ! {
+        panic_fmt(crate::format_args!("{}", *x));
+    }
+
+    // This function is used instead of panic_fmt in const eval.
+    #[lang = "const_panic_fmt"]
+    pub const fn const_panic_fmt(fmt: crate::fmt::Arguments<'_>) -> ! {
+        if let Some(msg) = fmt.as_str() {
+            // The panic_display function is hooked by const eval.
+            panic_display(&msg);
+        } else {
+            loop {}
+        }
+    }
+
+    #[lang = "panic_fmt"] // needed for const-evaluated panics
+    pub const fn panic_fmt(fmt: crate::fmt::Arguments<'_>) -> ! {
         loop {}
     }
 
@@ -1378,6 +1423,7 @@ mod panicking {
 }
 // endregion:panic
 
+#[macro_use]
 mod macros {
     // region:panic
     #[macro_export]
@@ -1470,7 +1516,6 @@ mod macros {
     }
     // endregion:unimplemented
 
-
     // region:derive
     pub(crate) mod builtin {
         #[rustc_builtin_macro]