|
3 | 3 | use std::iter::once;
|
4 | 4 | use std::ops::Range;
|
5 | 5 |
|
6 |
| -use rustc_errors::{Applicability, Handler}; |
| 6 | +use rustc_errors::{pluralize, Applicability, Handler}; |
7 | 7 | use rustc_lexer::unescape::{EscapeError, Mode};
|
8 | 8 | use rustc_span::{BytePos, Span};
|
9 | 9 |
|
@@ -49,24 +49,57 @@ pub(crate) fn emit_unescape_error(
|
49 | 49 | .emit();
|
50 | 50 | }
|
51 | 51 | EscapeError::MoreThanOneChar => {
|
52 |
| - let (prefix, msg) = if mode.is_bytes() { |
53 |
| - ("b", "if you meant to write a byte string literal, use double quotes") |
54 |
| - } else { |
55 |
| - ("", "if you meant to write a `str` literal, use double quotes") |
56 |
| - }; |
| 52 | + use unicode_normalization::{char::is_combining_mark, UnicodeNormalization}; |
57 | 53 |
|
58 |
| - handler |
59 |
| - .struct_span_err( |
60 |
| - span_with_quotes, |
61 |
| - "character literal may only contain one codepoint", |
62 |
| - ) |
63 |
| - .span_suggestion( |
| 54 | + let mut has_help = false; |
| 55 | + let mut handler = handler.struct_span_err( |
| 56 | + span_with_quotes, |
| 57 | + "character literal may only contain one codepoint", |
| 58 | + ); |
| 59 | + |
| 60 | + if lit.chars().skip(1).all(|c| is_combining_mark(c)) { |
| 61 | + let escaped_marks = |
| 62 | + lit.chars().skip(1).map(|c| c.escape_default().to_string()).collect::<Vec<_>>(); |
| 63 | + handler.span_note( |
| 64 | + span, |
| 65 | + &format!( |
| 66 | + "this `{}` is followed by the combining mark{} `{}`", |
| 67 | + lit.chars().next().unwrap(), |
| 68 | + pluralize!(escaped_marks.len()), |
| 69 | + escaped_marks.join(""), |
| 70 | + ), |
| 71 | + ); |
| 72 | + let normalized = lit.nfc().to_string(); |
| 73 | + if normalized.chars().count() == 1 { |
| 74 | + has_help = true; |
| 75 | + handler.span_suggestion( |
| 76 | + span, |
| 77 | + &format!( |
| 78 | + "consider using the normalized form `{}` of this character", |
| 79 | + normalized.chars().next().unwrap().escape_default() |
| 80 | + ), |
| 81 | + normalized, |
| 82 | + Applicability::MachineApplicable, |
| 83 | + ); |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + if !has_help { |
| 88 | + let (prefix, msg) = if mode.is_bytes() { |
| 89 | + ("b", "if you meant to write a byte string literal, use double quotes") |
| 90 | + } else { |
| 91 | + ("", "if you meant to write a `str` literal, use double quotes") |
| 92 | + }; |
| 93 | + |
| 94 | + handler.span_suggestion( |
64 | 95 | span_with_quotes,
|
65 | 96 | msg,
|
66 | 97 | format!("{}\"{}\"", prefix, lit),
|
67 | 98 | Applicability::MachineApplicable,
|
68 |
| - ) |
69 |
| - .emit(); |
| 99 | + ); |
| 100 | + } |
| 101 | + |
| 102 | + handler.emit(); |
70 | 103 | }
|
71 | 104 | EscapeError::EscapeOnlyChar => {
|
72 | 105 | let (c, char_span) = last_char();
|
|
0 commit comments