about summary refs log tree commit diff
path: root/compiler/rustc_trait_selection/src
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2025-08-15 16:16:29 +1000
committerGitHub <noreply@github.com>2025-08-15 16:16:29 +1000
commit1eeb8e8b151d1da7daa73837a25dc5f7a1a7fa28 (patch)
treea8051298da3ef458d2490ebc56e462b5f91870c5 /compiler/rustc_trait_selection/src
parent324bf2b9fd8bf9661e7045c8a93f5ff0ec1a8ca5 (diff)
parent9dfee2ef35d8176bbcfb601bdf58059513049a16 (diff)
downloadrust-1eeb8e8b151d1da7daa73837a25dc5f7a1a7fa28.tar.gz
rust-1eeb8e8b151d1da7daa73837a25dc5f7a1a7fa28.zip
Rollup merge of #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 #122159.
Diffstat (limited to 'compiler/rustc_trait_selection/src')
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs40
1 files changed, 36 insertions, 4 deletions
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
index 20e425bfad1..ee7ebd508c0 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -1619,8 +1619,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         {
             let e = self.tcx.erase_regions(e);
             let f = self.tcx.erase_regions(f);
-            let expected = with_forced_trimmed_paths!(e.sort_string(self.tcx));
-            let found = with_forced_trimmed_paths!(f.sort_string(self.tcx));
+            let mut expected = with_forced_trimmed_paths!(e.sort_string(self.tcx));
+            let mut found = with_forced_trimmed_paths!(f.sort_string(self.tcx));
+            if let ObligationCauseCode::Pattern { span, .. } = cause.code()
+                && let Some(span) = span
+                && !span.from_expansion()
+                && cause.span.from_expansion()
+            {
+                // When the type error comes from a macro like `assert!()`, and we are pointing at
+                // code the user wrote the cause and effect are reversed as the expected value is
+                // what the macro expanded to.
+                (found, expected) = (expected, found);
+            }
             if expected == found {
                 label_or_note(span, terr.to_string(self.tcx));
             } else {
@@ -2143,7 +2153,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     ) -> Option<(DiagStyledString, DiagStyledString)> {
         match values {
             ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found),
-            ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found, long_ty_path),
+            ValuePairs::Terms(exp_found) => {
+                self.expected_found_str_term(cause, exp_found, long_ty_path)
+            }
             ValuePairs::Aliases(exp_found) => self.expected_found_str(exp_found),
             ValuePairs::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found),
             ValuePairs::ExistentialProjection(exp_found) => self.expected_found_str(exp_found),
@@ -2182,6 +2194,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
     fn expected_found_str_term(
         &self,
+        cause: &ObligationCause<'tcx>,
         exp_found: ty::error::ExpectedFound<ty::Term<'tcx>>,
         long_ty_path: &mut Option<PathBuf>,
     ) -> Option<(DiagStyledString, DiagStyledString)> {
@@ -2189,8 +2202,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         if exp_found.references_error() {
             return None;
         }
+        let (mut expected, mut found) = (exp_found.expected, exp_found.found);
+
+        if let ObligationCauseCode::Pattern { span, .. } = cause.code()
+            && let Some(span) = span
+            && !span.from_expansion()
+            && cause.span.from_expansion()
+        {
+            // When the type error comes from a macro like `assert!()`, and we are pointing at
+            // code the user wrote, the cause and effect are reversed as the expected value is
+            // what the macro expanded to. So if the user provided a `Type` when the macro is
+            // written in such a way that a `bool` was expected, we want to print:
+            // = note: expected `bool`
+            //            found `Type`"
+            // but as far as the compiler is concerned, after expansion what was expected was `Type`
+            // = note: expected `Type`
+            //            found `bool`"
+            // so we reverse them here to match user expectation.
+            (expected, found) = (found, expected);
+        }
 
-        Some(match (exp_found.expected.kind(), exp_found.found.kind()) {
+        Some(match (expected.kind(), found.kind()) {
             (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => {
                 let (mut exp, mut fnd) = self.cmp(expected, found);
                 // Use the terminal width as the basis to determine when to compress the printed