1
Fork 0

Auto merge of #93179 - Urgau:unreachable-2021, r=m-ou-se,oli-obk

Fix invalid special casing of the unreachable! macro

This pull-request fix an invalid special casing of the `unreachable!` macro in the same way the `panic!` macro was solved, by adding two new internal only macros `unreachable_2015` and `unreachable_2021` edition dependent and turn `unreachable!` into a built-in macro that do dispatching. This logic is stolen from the `panic!` macro.

~~This pull-request also adds an internal feature `format_args_capture_non_literal` that allows capturing arguments from formatted string that expanded from macros. The original RFC #2795 mentioned this as a future possibility. This feature is [required](https://github.com/rust-lang/rust/issues/92137#issuecomment-1018630522) because of concatenation that needs to be done inside the macro:~~
```rust
$crate::concat!("internal error: entered unreachable code: ", $fmt)
```

**In summary** the new behavior for the `unreachable!` macro with this pr is:

Edition 2021:
```rust
let x = 5;
unreachable!("x is {x}");
```
```
internal error: entered unreachable code: x is 5
```

Edition <= 2018:
```rust
let x = 5;
unreachable!("x is {x}");
```
```
internal error: entered unreachable code: x is {x}
```

Also note that the change in this PR are **insta-stable** and **breaking changes** but this a considered as being a [bug](https://github.com/rust-lang/rust/issues/92137#issuecomment-998441613).
If someone could start a perf run and then a crater run this would be appreciated.

Fixes https://github.com/rust-lang/rust/issues/92137
This commit is contained in:
bors 2022-02-07 00:26:52 +00:00
commit 25b21a1d16
22 changed files with 326 additions and 80 deletions

View file

@ -49,9 +49,11 @@ impl<'tcx> LateLintPass<'tcx> for NonPanicFmt {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
if let hir::ExprKind::Call(f, [arg]) = &expr.kind {
if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() {
let f_diagnostic_name = cx.tcx.get_diagnostic_name(def_id);
if Some(def_id) == cx.tcx.lang_items().begin_panic_fn()
|| Some(def_id) == cx.tcx.lang_items().panic_fn()
|| Some(def_id) == cx.tcx.lang_items().panic_str()
|| f_diagnostic_name == Some(sym::panic_str)
{
if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
if matches!(
@ -61,6 +63,22 @@ impl<'tcx> LateLintPass<'tcx> for NonPanicFmt {
check_panic(cx, f, arg);
}
}
} else if f_diagnostic_name == Some(sym::unreachable_display) {
if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
if cx.tcx.is_diagnostic_item(sym::unreachable_2015_macro, id) {
check_panic(
cx,
f,
// This is safe because we checked above that the callee is indeed
// unreachable_display
match &arg.kind {
// Get the borrowed arg not the borrow
hir::ExprKind::AddrOf(ast::BorrowKind::Ref, _, arg) => arg,
_ => bug!("call to unreachable_display without borrow"),
},
);
}
}
}
}
}
@ -85,8 +103,8 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
return;
}
// Find the span of the argument to `panic!()`, before expansion in the
// case of `panic!(some_macro!())`.
// Find the span of the argument to `panic!()` or `unreachable!`, before expansion in the
// case of `panic!(some_macro!())` or `unreachable!(some_macro!())`.
// We don't use source_callsite(), because this `panic!(..)` might itself
// be expanded from another macro, in which case we want to stop at that
// expansion.
@ -319,6 +337,7 @@ fn panic_call<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>) -> (Span,
| sym::std_panic_macro
| sym::assert_macro
| sym::debug_assert_macro
| sym::unreachable_macro
) {
break;
}