about summary refs log tree commit diff
path: root/compiler/rustc_parse
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-09-21 22:07:32 +0000
committerbors <bors@rust-lang.org>2021-09-21 22:07:32 +0000
commitd8d1d1059a7837551c0065fc060dcb806ad729ef (patch)
treead33e22ac8a336ba8f27beaaba0cd03b8b5d124c /compiler/rustc_parse
parentac2d9fc509e36d1b32513744adf58c34bcc4f43c (diff)
parenta3e6c19acf12d5995407220721f4fe28452e51da (diff)
downloadrust-d8d1d1059a7837551c0065fc060dcb806ad729ef.tar.gz
rust-d8d1d1059a7837551c0065fc060dcb806ad729ef.zip
Auto merge of #89158 - the8472:rollup-3e4ijth, r=the8472
Rollup of 12 pull requests

Successful merges:

 - #88795 (Print a note if a character literal contains a variation selector)
 - #89015 (core::ascii::escape_default: reduce struct size)
 - #89078 (Cleanup: Remove needless reference in ParentHirIterator)
 - #89086 (Stabilize `Iterator::map_while`)
 - #89096 ([bootstrap] Improve the error message when `ninja` is not found to link to installation instructions)
 - #89113 (dont `.ensure()` the `thir_abstract_const` query call in `mir_build`)
 - #89114 (Fixes a technicality regarding the size of C's `char` type)
 - #89115 (:arrow_up: rust-analyzer)
 - #89126 (Fix ICE when `indirect_structural_match` is allowed)
 - #89141 (Impl `Error` for `FromSecsError` without foreign type)
 - #89142 (Fix match for placeholder region)
 - #89147 (add case for checking const refs in check_const_value_eq)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'compiler/rustc_parse')
-rw-r--r--compiler/rustc_parse/src/lexer/unescape_error_reporting.rs61
1 files changed, 47 insertions, 14 deletions
diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
index aa6b424ce2b..cef5b3a226b 100644
--- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
+++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
@@ -3,7 +3,7 @@
 use std::iter::once;
 use std::ops::Range;
 
-use rustc_errors::{Applicability, Handler};
+use rustc_errors::{pluralize, Applicability, Handler};
 use rustc_lexer::unescape::{EscapeError, Mode};
 use rustc_span::{BytePos, Span};
 
@@ -49,24 +49,57 @@ pub(crate) fn emit_unescape_error(
                 .emit();
         }
         EscapeError::MoreThanOneChar => {
-            let (prefix, msg) = if mode.is_bytes() {
-                ("b", "if you meant to write a byte string literal, use double quotes")
-            } else {
-                ("", "if you meant to write a `str` literal, use double quotes")
-            };
+            use unicode_normalization::{char::is_combining_mark, UnicodeNormalization};
 
-            handler
-                .struct_span_err(
-                    span_with_quotes,
-                    "character literal may only contain one codepoint",
-                )
-                .span_suggestion(
+            let mut has_help = false;
+            let mut handler = handler.struct_span_err(
+                span_with_quotes,
+                "character literal may only contain one codepoint",
+            );
+
+            if lit.chars().skip(1).all(|c| is_combining_mark(c)) {
+                let escaped_marks =
+                    lit.chars().skip(1).map(|c| c.escape_default().to_string()).collect::<Vec<_>>();
+                handler.span_note(
+                    span,
+                    &format!(
+                        "this `{}` is followed by the combining mark{} `{}`",
+                        lit.chars().next().unwrap(),
+                        pluralize!(escaped_marks.len()),
+                        escaped_marks.join(""),
+                    ),
+                );
+                let normalized = lit.nfc().to_string();
+                if normalized.chars().count() == 1 {
+                    has_help = true;
+                    handler.span_suggestion(
+                        span,
+                        &format!(
+                            "consider using the normalized form `{}` of this character",
+                            normalized.chars().next().unwrap().escape_default()
+                        ),
+                        normalized,
+                        Applicability::MachineApplicable,
+                    );
+                }
+            }
+
+            if !has_help {
+                let (prefix, msg) = if mode.is_bytes() {
+                    ("b", "if you meant to write a byte string literal, use double quotes")
+                } else {
+                    ("", "if you meant to write a `str` literal, use double quotes")
+                };
+
+                handler.span_suggestion(
                     span_with_quotes,
                     msg,
                     format!("{}\"{}\"", prefix, lit),
                     Applicability::MachineApplicable,
-                )
-                .emit();
+                );
+            }
+
+            handler.emit();
         }
         EscapeError::EscapeOnlyChar => {
             let (c, char_span) = last_char();