Skip to content

Commit 3590366

Browse files
authored
Rollup merge of rust-lang#122661 - estebank:assert-macro-span, r=petrochenkov
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` ``` Now `assert!(val)` desugars to: ```rust match val { true => {}, _ => $crate::panic::panic_2021!(), } ``` Fix rust-lang#122159.
2 parents 1c9952f + c439a59 commit 3590366

File tree

14 files changed

+134
-50
lines changed

14 files changed

+134
-50
lines changed

compiler/rustc_builtin_macros/src/assert.rs

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
mod context;
22

3-
use rustc_ast::token::Delimiter;
3+
use rustc_ast::token::{self, Delimiter};
44
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
5-
use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp, token};
5+
use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment};
66
use rustc_ast_pretty::pprust;
77
use rustc_errors::PResult;
88
use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult};
@@ -29,7 +29,7 @@ pub(crate) fn expand_assert<'cx>(
2929

3030
// `core::panic` and `std::panic` are different macros, so we use call-site
3131
// context to pick up whichever is currently in scope.
32-
let call_site_span = cx.with_call_site_ctxt(span);
32+
let call_site_span = cx.with_call_site_ctxt(cond_expr.span);
3333

3434
let panic_path = || {
3535
if use_panic_2021(span) {
@@ -63,7 +63,7 @@ pub(crate) fn expand_assert<'cx>(
6363
}),
6464
})),
6565
);
66-
expr_if_not(cx, call_site_span, cond_expr, then, None)
66+
assert_cond_check(cx, call_site_span, cond_expr, then)
6767
}
6868
// If `generic_assert` is enabled, generates rich captured outputs
6969
//
@@ -88,26 +88,33 @@ pub(crate) fn expand_assert<'cx>(
8888
)),
8989
)],
9090
);
91-
expr_if_not(cx, call_site_span, cond_expr, then, None)
91+
assert_cond_check(cx, call_site_span, cond_expr, then)
9292
};
9393

9494
ExpandResult::Ready(MacEager::expr(expr))
9595
}
9696

97+
/// `assert!($cond_expr, $custom_message)`
9798
struct Assert {
9899
cond_expr: Box<Expr>,
99100
custom_message: Option<TokenStream>,
100101
}
101102

102-
// if !{ ... } { ... } else { ... }
103-
fn expr_if_not(
104-
cx: &ExtCtxt<'_>,
105-
span: Span,
106-
cond: Box<Expr>,
107-
then: Box<Expr>,
108-
els: Option<Box<Expr>>,
109-
) -> Box<Expr> {
110-
cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els)
103+
/// `match <cond> { true => {} _ => <then> }`
104+
fn assert_cond_check(cx: &ExtCtxt<'_>, span: Span, cond: Box<Expr>, then: Box<Expr>) -> Box<Expr> {
105+
// Instead of expanding to `if !<cond> { <then> }`, we expand to
106+
// `match <cond> { true => {} _ => <then> }`.
107+
// This allows us to always complain about mismatched types instead of "cannot apply unary
108+
// operator `!` to type `X`" when passing an invalid `<cond>`, while also allowing `<cond>` to
109+
// be `&true`.
110+
let els = cx.expr_block(cx.block(span, thin_vec![]));
111+
let mut arms = thin_vec![];
112+
arms.push(cx.arm(span, cx.pat_lit(span, cx.expr_bool(span, true)), els));
113+
arms.push(cx.arm(span, cx.pat_wild(span), then));
114+
115+
// We wrap the `match` in a statement to limit the length of any borrows introduced in the
116+
// condition.
117+
cx.expr_block(cx.block(span, [cx.stmt_expr(cx.expr_match(span, cond, arms))].into()))
111118
}
112119

113120
fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {

compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,8 +1620,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
16201620
{
16211621
let e = self.tcx.erase_regions(e);
16221622
let f = self.tcx.erase_regions(f);
1623-
let expected = with_forced_trimmed_paths!(e.sort_string(self.tcx));
1624-
let found = with_forced_trimmed_paths!(f.sort_string(self.tcx));
1623+
let mut expected = with_forced_trimmed_paths!(e.sort_string(self.tcx));
1624+
let mut found = with_forced_trimmed_paths!(f.sort_string(self.tcx));
1625+
if let ObligationCauseCode::Pattern { span, .. } = cause.code()
1626+
&& let Some(span) = span
1627+
&& !span.from_expansion()
1628+
&& cause.span.from_expansion()
1629+
{
1630+
// When the type error comes from a macro like `assert!()`, and we are pointing at
1631+
// code the user wrote the cause and effect are reversed as the expected value is
1632+
// what the macro expanded to.
1633+
(found, expected) = (expected, found);
1634+
}
16251635
if expected == found {
16261636
label_or_note(span, terr.to_string(self.tcx));
16271637
} else {
@@ -2144,7 +2154,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
21442154
) -> Option<(DiagStyledString, DiagStyledString)> {
21452155
match values {
21462156
ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found),
2147-
ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found, long_ty_path),
2157+
ValuePairs::Terms(exp_found) => {
2158+
self.expected_found_str_term(cause, exp_found, long_ty_path)
2159+
}
21482160
ValuePairs::Aliases(exp_found) => self.expected_found_str(exp_found),
21492161
ValuePairs::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found),
21502162
ValuePairs::ExistentialProjection(exp_found) => self.expected_found_str(exp_found),
@@ -2183,15 +2195,35 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
21832195

21842196
fn expected_found_str_term(
21852197
&self,
2198+
cause: &ObligationCause<'tcx>,
21862199
exp_found: ty::error::ExpectedFound<ty::Term<'tcx>>,
21872200
long_ty_path: &mut Option<PathBuf>,
21882201
) -> Option<(DiagStyledString, DiagStyledString)> {
21892202
let exp_found = self.resolve_vars_if_possible(exp_found);
21902203
if exp_found.references_error() {
21912204
return None;
21922205
}
2206+
let (mut expected, mut found) = (exp_found.expected, exp_found.found);
2207+
2208+
if let ObligationCauseCode::Pattern { span, .. } = cause.code()
2209+
&& let Some(span) = span
2210+
&& !span.from_expansion()
2211+
&& cause.span.from_expansion()
2212+
{
2213+
// When the type error comes from a macro like `assert!()`, and we are pointing at
2214+
// code the user wrote, the cause and effect are reversed as the expected value is
2215+
// what the macro expanded to. So if the user provided a `Type` when the macro is
2216+
// written in such a way that a `bool` was expected, we want to print:
2217+
// = note: expected `bool`
2218+
// found `Type`"
2219+
// but as far as the compiler is concerned, after expansion what was expected was `Type`
2220+
// = note: expected `Type`
2221+
// found `bool`"
2222+
// so we reverse them here to match user expectation.
2223+
(expected, found) = (found, expected);
2224+
}
21932225

2194-
Some(match (exp_found.expected.kind(), exp_found.found.kind()) {
2226+
Some(match (expected.kind(), found.kind()) {
21952227
(ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => {
21962228
let (mut exp, mut fnd) = self.cmp(expected, found);
21972229
// Use the terminal width as the basis to determine when to compress the printed

src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_ast::{BinOpKind, LitKind, RangeLimits};
1111
use rustc_data_structures::packed::Pu128;
1212
use rustc_data_structures::unhash::UnindexMap;
1313
use rustc_errors::{Applicability, Diag};
14-
use rustc_hir::{Block, Body, Expr, ExprKind, UnOp};
14+
use rustc_hir::{Body, Expr, ExprKind};
1515
use rustc_lint::{LateContext, LateLintPass};
1616
use rustc_session::declare_lint_pass;
1717
use rustc_span::source_map::Spanned;
@@ -135,12 +135,12 @@ fn assert_len_expr<'hir>(
135135
cx: &LateContext<'_>,
136136
expr: &'hir Expr<'hir>,
137137
) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> {
138-
let (cmp, asserted_len, slice_len) = if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr)
139-
&& let ExprKind::Unary(UnOp::Not, condition) = &cond.kind
140-
&& let ExprKind::Binary(bin_op, left, right) = &condition.kind
138+
let (cmp, asserted_len, slice_len) = if let Some(
139+
higher::IfLetOrMatch::Match(cond, [_, then], _)
140+
) = higher::IfLetOrMatch::parse(cx, expr)
141+
&& let ExprKind::Binary(bin_op, left, right) = &cond.kind
141142
// check if `then` block has a never type expression
142-
&& let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind
143-
&& cx.typeck_results().expr_ty(then_expr).is_never()
143+
&& cx.typeck_results().expr_ty(then.body).is_never()
144144
{
145145
len_comparison(bin_op.node, left, right)?
146146
} else if let Some((macro_call, bin_op)) = first_node_macro_backtrace(cx, expr).find_map(|macro_call| {

src/tools/clippy/tests/ui/const_is_empty.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ fn issue_13106() {
196196

197197
const {
198198
assert!(EMPTY_STR.is_empty());
199+
//~^ const_is_empty
199200
}
200201

201202
const {

src/tools/clippy/tests/ui/const_is_empty.stderr

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,16 @@ LL | let _ = val.is_empty();
158158
| ^^^^^^^^^^^^^^
159159

160160
error: this expression always evaluates to true
161-
--> tests/ui/const_is_empty.rs:202:9
161+
--> tests/ui/const_is_empty.rs:198:17
162+
|
163+
LL | assert!(EMPTY_STR.is_empty());
164+
| ^^^^^^^^^^^^^^^^^^^^
165+
166+
error: this expression always evaluates to true
167+
--> tests/ui/const_is_empty.rs:203:9
162168
|
163169
LL | EMPTY_STR.is_empty();
164170
| ^^^^^^^^^^^^^^^^^^^^
165171

166-
error: aborting due to 27 previous errors
172+
error: aborting due to 28 previous errors
167173

src/tools/clippy/tests/ui/incompatible_msrv.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![warn(clippy::incompatible_msrv)]
22
#![feature(custom_inner_attributes)]
3-
#![allow(stable_features)]
3+
#![allow(stable_features, clippy::diverging_sub_expression)]
44
#![feature(strict_provenance)] // For use in test
55
#![clippy::msrv = "1.3.0"]
66

tests/ui/codemap_tests/issue-28308.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
11
fn main() {
2-
assert!("foo");
3-
//~^ ERROR cannot apply unary operator `!`
2+
assert!("foo"); //~ ERROR mismatched types
3+
//~^ NOTE expected `bool`, found `str`
4+
//~| NOTE in this expansion of assert!
5+
let x = Some(&1);
6+
assert!(x); //~ ERROR mismatched types
7+
//~^ NOTE expected `bool`, found `Option<&{integer}>`
8+
//~| NOTE expected enum `bool`
9+
//~| NOTE in this expansion of assert!
10+
//~| NOTE in this expansion of assert!
11+
assert!(x, ""); //~ ERROR mismatched types
12+
//~^ NOTE expected `bool`, found `Option<&{integer}>`
13+
//~| NOTE expected enum `bool`
14+
//~| NOTE in this expansion of assert!
15+
//~| NOTE in this expansion of assert!
416
}
Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
1-
error[E0600]: cannot apply unary operator `!` to type `&'static str`
2-
--> $DIR/issue-28308.rs:2:5
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-28308.rs:2:13
33
|
44
LL | assert!("foo");
5-
| ^^^^^^^^^^^^^^ cannot apply unary operator `!`
5+
| ^^^^^ expected `bool`, found `str`
66

7-
error: aborting due to 1 previous error
7+
error[E0308]: mismatched types
8+
--> $DIR/issue-28308.rs:6:13
9+
|
10+
LL | assert!(x);
11+
| ^ expected `bool`, found `Option<&{integer}>`
12+
|
13+
= note: expected enum `bool`
14+
found type `Option<&{integer}>`
15+
16+
error[E0308]: mismatched types
17+
--> $DIR/issue-28308.rs:11:13
18+
|
19+
LL | assert!(x, "");
20+
| ^ expected `bool`, found `Option<&{integer}>`
21+
|
22+
= note: expected enum `bool`
23+
found type `Option<&{integer}>`
24+
25+
error: aborting due to 3 previous errors
826

9-
For more information about this error, try `rustc --explain E0600`.
27+
For more information about this error, try `rustc --explain E0308`.

tests/ui/consts/control-flow/assert.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0080]: evaluation panicked: assertion failed: false
2-
--> $DIR/assert.rs:5:15
2+
--> $DIR/assert.rs:5:23
33
|
44
LL | const _: () = assert!(false);
5-
| ^^^^^^^^^^^^^^ evaluation of `_` failed here
5+
| ^^^^^ evaluation of `_` failed here
66

77
error: aborting due to 1 previous error
88

tests/ui/generics/post_monomorphization_error_backtrace.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::<T>() == 0
2-
--> $DIR/post_monomorphization_error_backtrace.rs:6:23
2+
--> $DIR/post_monomorphization_error_backtrace.rs:6:31
33
|
44
LL | const V: () = assert!(std::mem::size_of::<T>() == 0);
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<u32>::V` failed here
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<u32>::V` failed here
66

77
note: erroneous constant encountered
88
--> $DIR/post_monomorphization_error_backtrace.rs:14:5
@@ -17,10 +17,10 @@ LL | assert_zst::<U>()
1717
| ^^^^^^^^^^^^^^^^^
1818

1919
error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::<T>() == 0
20-
--> $DIR/post_monomorphization_error_backtrace.rs:6:23
20+
--> $DIR/post_monomorphization_error_backtrace.rs:6:31
2121
|
2222
LL | const V: () = assert!(std::mem::size_of::<T>() == 0);
23-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<i32>::V` failed here
23+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::<i32>::V` failed here
2424

2525
note: erroneous constant encountered
2626
--> $DIR/post_monomorphization_error_backtrace.rs:14:5

0 commit comments

Comments
 (0)