diff options
| author | Esteban Küber <esteban@kuber.com.ar> | 2024-03-17 20:46:36 +0000 |
|---|---|---|
| committer | Esteban Küber <esteban@kuber.com.ar> | 2025-08-12 16:30:48 +0000 |
| commit | c439a59dbd275aef9bc24c7172e2111ccc3794c3 (patch) | |
| tree | 9c70517c726c8c79d9bbb9c18aedea0e9d32ac47 /compiler/rustc_builtin_macros/src/assert.rs | |
| parent | d9dba3a55476ae2da5d4e5bce8a81b341c675750 (diff) | |
| download | rust-c439a59dbd275aef9bc24c7172e2111ccc3794c3.tar.gz rust-c439a59dbd275aef9bc24c7172e2111ccc3794c3.zip | |
Change the desugaring of `assert!` for better error output
In the desugaring of `assert!`, we now expand to a `match` expression
instead of `if !cond {..}`.
The span of incorrect conditions will point only at the expression, and not
the whole `assert!` invocation.
```
error[E0308]: mismatched types
--> $DIR/issue-14091.rs:2:13
|
LL | assert!(1,1);
| ^ expected `bool`, found integer
```
We no longer mention the expression needing to implement the `Not` trait.
```
error[E0308]: mismatched types
--> $DIR/issue-14091-2.rs:15:13
|
LL | assert!(x, x);
| ^ expected `bool`, found `BytePos`
```
`assert!(val)` now desugars to:
```rust
match val {
true => {},
_ => $crate::panic::panic_2021!(),
}
```
Fix #122159.
We make some minor changes to some diagnostics to avoid span overlap on
type mismatch or inverted "expected"/"found" on type errors.
We remove some unnecessary parens from core, alloc and miri.
address review comments
Diffstat (limited to 'compiler/rustc_builtin_macros/src/assert.rs')
| -rw-r--r-- | compiler/rustc_builtin_macros/src/assert.rs | 35 |
1 files changed, 21 insertions, 14 deletions
diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index 855da5caa31..013258a1b4e 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -1,8 +1,8 @@ mod context; -use rustc_ast::token::Delimiter; +use rustc_ast::token::{self, Delimiter}; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; -use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp, token}; +use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment}; use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; @@ -29,7 +29,7 @@ pub(crate) fn expand_assert<'cx>( // `core::panic` and `std::panic` are different macros, so we use call-site // context to pick up whichever is currently in scope. - let call_site_span = cx.with_call_site_ctxt(span); + let call_site_span = cx.with_call_site_ctxt(cond_expr.span); let panic_path = || { if use_panic_2021(span) { @@ -63,7 +63,7 @@ pub(crate) fn expand_assert<'cx>( }), })), ); - expr_if_not(cx, call_site_span, cond_expr, then, None) + assert_cond_check(cx, call_site_span, cond_expr, then) } // If `generic_assert` is enabled, generates rich captured outputs // @@ -88,26 +88,33 @@ pub(crate) fn expand_assert<'cx>( )), )], ); - expr_if_not(cx, call_site_span, cond_expr, then, None) + assert_cond_check(cx, call_site_span, cond_expr, then) }; ExpandResult::Ready(MacEager::expr(expr)) } +/// `assert!($cond_expr, $custom_message)` struct Assert { cond_expr: Box<Expr>, custom_message: Option<TokenStream>, } -// if !{ ... } { ... } else { ... } -fn expr_if_not( - cx: &ExtCtxt<'_>, - span: Span, - cond: Box<Expr>, - then: Box<Expr>, - els: Option<Box<Expr>>, -) -> Box<Expr> { - cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els) +/// `match <cond> { true => {} _ => <then> }` +fn assert_cond_check(cx: &ExtCtxt<'_>, span: Span, cond: Box<Expr>, then: Box<Expr>) -> Box<Expr> { + // Instead of expanding to `if !<cond> { <then> }`, we expand to + // `match <cond> { true => {} _ => <then> }`. + // This allows us to always complain about mismatched types instead of "cannot apply unary + // operator `!` to type `X`" when passing an invalid `<cond>`, while also allowing `<cond>` to + // be `&true`. + let els = cx.expr_block(cx.block(span, thin_vec![])); + let mut arms = thin_vec![]; + arms.push(cx.arm(span, cx.pat_lit(span, cx.expr_bool(span, true)), els)); + arms.push(cx.arm(span, cx.pat_wild(span), then)); + + // We wrap the `match` in a statement to limit the length of any borrows introduced in the + // condition. + cx.expr_block(cx.block(span, [cx.stmt_expr(cx.expr_match(span, cond, arms))].into())) } fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> { |
