From 130d02b62e65c5f2a434eaec63c4249e9d508487 Mon Sep 17 00:00:00 2001
From: Nick Lamb <git@tree.tlrmx.org>
Date: Sat, 14 Jan 2023 14:51:17 +0000
Subject: [PATCH] Improve E0308: suggest user meant to use byte literal, w/
 tests and fix suggested by Nilstrieb

Co-authored-by: nils <48135649+Nilstrieb@users.noreply.github.com>
---
 .../src/infer/error_reporting/mod.rs          | 16 +++++++
 .../suggestions/type-mismatch-byte-literal.rs | 18 ++++++++
 .../type-mismatch-byte-literal.stderr         | 42 +++++++++++++++++++
 3 files changed, 76 insertions(+)
 create mode 100644 tests/ui/suggestions/type-mismatch-byte-literal.rs
 create mode 100644 tests/ui/suggestions/type-mismatch-byte-literal.stderr

diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 533a3c768eb16..abd99fc74dacc 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -1923,6 +1923,22 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         (ty::Tuple(fields), _) => {
                             self.emit_tuple_wrap_err(&mut err, span, found, fields)
                         }
+                        // If a byte was expected and the found expression is a char literal
+                        // containing a single ASCII character, perhaps the user meant to write `b'c'` to
+                        // specify a byte literal
+                        (ty::Uint(ty::UintTy::U8), ty::Char) => {
+                            if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
+                                && let Some(code) = code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
+                                && code.chars().next().map_or(false, |c| c.is_ascii())
+                            {
+                                err.span_suggestion(
+                                    span,
+                                    "if you meant to write a byte literal, prefix with `b`",
+                                    format!("b'{}'", escape_literal(code)),
+                                    Applicability::MachineApplicable,
+                                );
+                            }
+                        }
                         // If a character was expected and the found expression is a string literal
                         // containing a single character, perhaps the user meant to write `'c'` to
                         // specify a character literal (issue #92479)
diff --git a/tests/ui/suggestions/type-mismatch-byte-literal.rs b/tests/ui/suggestions/type-mismatch-byte-literal.rs
new file mode 100644
index 0000000000000..34199f8c37c10
--- /dev/null
+++ b/tests/ui/suggestions/type-mismatch-byte-literal.rs
@@ -0,0 +1,18 @@
+// Tests that a suggestion is issued for type mismatch errors when a
+// u8 is expected and a char literal which is ASCII is supplied.
+
+fn foo(_t: u8) {}
+
+fn main() {
+    let _x: u8 = 'X';
+    //~^ ERROR: mismatched types [E0308]
+    //~| HELP: if you meant to write a byte literal, prefix with `b`
+
+    foo('#');
+    //~^ ERROR: mismatched types [E0308]
+    //~| HELP: if you meant to write a byte literal, prefix with `b`
+
+    // Do not issue the suggestion if the char literal isn't ASCII
+    let _t: u8 = '€';
+    //~^ ERROR: mismatched types [E0308]
+}
diff --git a/tests/ui/suggestions/type-mismatch-byte-literal.stderr b/tests/ui/suggestions/type-mismatch-byte-literal.stderr
new file mode 100644
index 0000000000000..c9c2e7498d058
--- /dev/null
+++ b/tests/ui/suggestions/type-mismatch-byte-literal.stderr
@@ -0,0 +1,42 @@
+error[E0308]: mismatched types
+  --> $DIR/type-mismatch-byte-literal.rs:7:18
+   |
+LL |     let _x: u8 = 'X';
+   |             --   ^^^ expected `u8`, found `char`
+   |             |
+   |             expected due to this
+   |
+help: if you meant to write a byte literal, prefix with `b`
+   |
+LL |     let _x: u8 = b'X';
+   |                  ~~~~
+
+error[E0308]: mismatched types
+  --> $DIR/type-mismatch-byte-literal.rs:11:9
+   |
+LL |     foo('#');
+   |     --- ^^^ expected `u8`, found `char`
+   |     |
+   |     arguments to this function are incorrect
+   |
+note: function defined here
+  --> $DIR/type-mismatch-byte-literal.rs:4:4
+   |
+LL | fn foo(_t: u8) {}
+   |    ^^^ ------
+help: if you meant to write a byte literal, prefix with `b`
+   |
+LL |     foo(b'#');
+   |         ~~~~
+
+error[E0308]: mismatched types
+  --> $DIR/type-mismatch-byte-literal.rs:16:18
+   |
+LL |     let _t: u8 = '€';
+   |             --   ^^^ expected `u8`, found `char`
+   |             |
+   |             expected due to this
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.