about summary refs log tree commit diff
path: root/compiler/rustc_errors/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-07-28 14:22:27 +0000
committerbors <bors@rust-lang.org>2024-07-28 14:22:27 +0000
commit48fb66be7a657839bdf8f42ae06fc17893c2d162 (patch)
treefee681aba06a57873e1736b19e78d97a21443dc5 /compiler/rustc_errors/src
parent159524019f6c4ab8908dc297aeea4e5c4d1f2c89 (diff)
parenta973d3f0879c486a8485b99244f1060f69a31a33 (diff)
downloadrust-48fb66be7a657839bdf8f42ae06fc17893c2d162.tar.gz
rust-48fb66be7a657839bdf8f42ae06fc17893c2d162.zip
Auto merge of #17732 - lnicola:sync-from-rust, r=lnicola
minor: sync from downstream
Diffstat (limited to 'compiler/rustc_errors/src')
-rw-r--r--compiler/rustc_errors/src/emitter.rs77
-rw-r--r--compiler/rustc_errors/src/lib.rs1
-rw-r--r--compiler/rustc_errors/src/markdown/parse.rs62
-rw-r--r--compiler/rustc_errors/src/markdown/tests/parse.rs65
4 files changed, 145 insertions, 60 deletions
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index aa47ca16676..58220c65490 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -8,7 +8,7 @@
 //! The output types are defined in `rustc_session::config::ErrorOutputType`.
 
 use rustc_span::source_map::SourceMap;
-use rustc_span::{FileLines, FileName, SourceFile, Span};
+use rustc_span::{char_width, FileLines, FileName, SourceFile, Span};
 
 use crate::snippet::{
     Annotation, AnnotationColumn, AnnotationType, Line, MultilineAnnotation, Style, StyledString,
@@ -677,10 +677,7 @@ impl HumanEmitter {
             .skip(left)
             .take_while(|ch| {
                 // Make sure that the trimming on the right will fall within the terminal width.
-                // FIXME: `unicode_width` sometimes disagrees with terminals on how wide a `char`
-                // is. For now, just accept that sometimes the code line will be longer than
-                // desired.
-                let next = unicode_width::UnicodeWidthChar::width(*ch).unwrap_or(1);
+                let next = char_width(*ch);
                 if taken + next > right - left {
                     return false;
                 }
@@ -742,11 +739,7 @@ impl HumanEmitter {
         let left = margin.left(source_string.len());
 
         // Account for unicode characters of width !=0 that were removed.
-        let left = source_string
-            .chars()
-            .take(left)
-            .map(|ch| unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1))
-            .sum();
+        let left = source_string.chars().take(left).map(|ch| char_width(ch)).sum();
 
         self.draw_line(
             buffer,
@@ -2039,7 +2032,7 @@ impl HumanEmitter {
                     let sub_len: usize =
                         if is_whitespace_addition { &part.snippet } else { part.snippet.trim() }
                             .chars()
-                            .map(|ch| unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1))
+                            .map(|ch| char_width(ch))
                             .sum();
 
                     let offset: isize = offsets
@@ -2076,11 +2069,8 @@ impl HumanEmitter {
                     }
 
                     // length of the code after substitution
-                    let full_sub_len = part
-                        .snippet
-                        .chars()
-                        .map(|ch| unicode_width::UnicodeWidthChar::width(ch).unwrap_or(1))
-                        .sum::<usize>() as isize;
+                    let full_sub_len =
+                        part.snippet.chars().map(|ch| char_width(ch)).sum::<usize>() as isize;
 
                     // length of the code to be substituted
                     let snippet_len = span_end_pos as isize - span_start_pos as isize;
@@ -2568,18 +2558,53 @@ fn num_decimal_digits(num: usize) -> usize {
 }
 
 // We replace some characters so the CLI output is always consistent and underlines aligned.
+// Keep the following list in sync with `rustc_span::char_width`.
 const OUTPUT_REPLACEMENTS: &[(char, &str)] = &[
-    ('\t', "    "),   // We do our own tab replacement
+    ('\t', "    "),    // We do our own tab replacement
     ('\u{200D}', ""), // Replace ZWJ with nothing for consistent terminal output of grapheme clusters.
-    ('\u{202A}', ""), // The following unicode text flow control characters are inconsistently
-    ('\u{202B}', ""), // supported across CLIs and can cause confusion due to the bytes on disk
-    ('\u{202D}', ""), // not corresponding to the visible source code, so we replace them always.
-    ('\u{202E}', ""),
-    ('\u{2066}', ""),
-    ('\u{2067}', ""),
-    ('\u{2068}', ""),
-    ('\u{202C}', ""),
-    ('\u{2069}', ""),
+    ('\u{202A}', "�"), // The following unicode text flow control characters are inconsistently
+    ('\u{202B}', "�"), // supported across CLIs and can cause confusion due to the bytes on disk
+    ('\u{202D}', "�"), // not corresponding to the visible source code, so we replace them always.
+    ('\u{202E}', "�"),
+    ('\u{2066}', "�"),
+    ('\u{2067}', "�"),
+    ('\u{2068}', "�"),
+    ('\u{202C}', "�"),
+    ('\u{2069}', "�"),
+    // In terminals without Unicode support the following will be garbled, but in *all* terminals
+    // the underlying codepoint will be as well. We could gate this replacement behind a "unicode
+    // support" gate.
+    ('\u{0000}', "␀"),
+    ('\u{0001}', "␁"),
+    ('\u{0002}', "␂"),
+    ('\u{0003}', "␃"),
+    ('\u{0004}', "␄"),
+    ('\u{0005}', "␅"),
+    ('\u{0006}', "␆"),
+    ('\u{0007}', "␇"),
+    ('\u{0008}', "␈"),
+    ('\u{000B}', "␋"),
+    ('\u{000C}', "␌"),
+    ('\u{000D}', "␍"),
+    ('\u{000E}', "␎"),
+    ('\u{000F}', "␏"),
+    ('\u{0010}', "␐"),
+    ('\u{0011}', "␑"),
+    ('\u{0012}', "␒"),
+    ('\u{0013}', "␓"),
+    ('\u{0014}', "␔"),
+    ('\u{0015}', "␕"),
+    ('\u{0016}', "␖"),
+    ('\u{0017}', "␗"),
+    ('\u{0018}', "␘"),
+    ('\u{0019}', "␙"),
+    ('\u{001A}', "␚"),
+    ('\u{001B}', "␛"),
+    ('\u{001C}', "␜"),
+    ('\u{001D}', "␝"),
+    ('\u{001E}', "␞"),
+    ('\u{001F}', "␟"),
+    ('\u{007F}', "␡"),
 ];
 
 fn normalize_whitespace(str: &str) -> String {
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 2086d4030f9..2a850d9303c 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -15,6 +15,7 @@
 #![feature(box_patterns)]
 #![feature(error_reporter)]
 #![feature(extract_if)]
+#![feature(if_let_guard)]
 #![feature(let_chains)]
 #![feature(negative_impls)]
 #![feature(never_type)]
diff --git a/compiler/rustc_errors/src/markdown/parse.rs b/compiler/rustc_errors/src/markdown/parse.rs
index 67e4963fddf..69e7120e714 100644
--- a/compiler/rustc_errors/src/markdown/parse.rs
+++ b/compiler/rustc_errors/src/markdown/parse.rs
@@ -10,15 +10,15 @@ const CBK: &[u8] = b"```";
 const CIL: &[u8] = b"`";
 const CMT_E: &[u8] = b"-->";
 const CMT_S: &[u8] = b"<!--";
-const EMP: &[u8] = b"_";
+const EMP_U: &[u8] = b"_";
+const EMP_A: &[u8] = b"*";
 const HDG: &[u8] = b"#";
 const LNK_CHARS: &str = "$-_.+!*'()/&?=:%";
 const LNK_E: &[u8] = b"]";
 const LNK_S: &[u8] = b"[";
-const STG: &[u8] = b"**";
+const STG_U: &[u8] = b"__";
+const STG_A: &[u8] = b"**";
 const STK: &[u8] = b"~~";
-const UL1: &[u8] = b"* ";
-const UL2: &[u8] = b"- ";
 
 /// Pattern replacements
 const REPLACEMENTS: &[(&str, &str)] = &[
@@ -100,22 +100,29 @@ fn parse_recursive<'a>(buf: &'a [u8], ctx: Context) -> MdStream<'_> {
         };
 
         let res: ParseResult<'_> = match (top_blk, prev) {
-            (_, Newline | Whitespace) if loop_buf.starts_with(CMT_S) => {
+            _ if loop_buf.starts_with(CMT_S) => {
                 parse_simple_pat(loop_buf, CMT_S, CMT_E, Po::TrimNoEsc, MdTree::Comment)
             }
             (true, Newline) if loop_buf.starts_with(CBK) => Some(parse_codeblock(loop_buf)),
-            (_, Newline | Whitespace) if loop_buf.starts_with(CIL) => parse_codeinline(loop_buf),
+            _ if loop_buf.starts_with(CIL) => parse_codeinline(loop_buf),
             (true, Newline | Whitespace) if loop_buf.starts_with(HDG) => parse_heading(loop_buf),
             (true, Newline) if loop_buf.starts_with(BRK) => {
                 Some((MdTree::HorizontalRule, parse_to_newline(loop_buf).1))
             }
-            (_, Newline | Whitespace) if loop_buf.starts_with(EMP) => {
-                parse_simple_pat(loop_buf, EMP, EMP, Po::None, MdTree::Emphasis)
+            (_, Newline) if unordered_list_start(loop_buf) => Some(parse_unordered_li(loop_buf)),
+            (_, Newline | Whitespace) if loop_buf.starts_with(STG_U) => {
+                parse_simple_pat(loop_buf, STG_U, STG_U, Po::None, MdTree::Strong)
             }
-            (_, Newline | Whitespace) if loop_buf.starts_with(STG) => {
-                parse_simple_pat(loop_buf, STG, STG, Po::None, MdTree::Strong)
+            _ if loop_buf.starts_with(STG_A) => {
+                parse_simple_pat(loop_buf, STG_A, STG_A, Po::None, MdTree::Strong)
             }
-            (_, Newline | Whitespace) if loop_buf.starts_with(STK) => {
+            (_, Newline | Whitespace) if loop_buf.starts_with(EMP_U) => {
+                parse_simple_pat(loop_buf, EMP_U, EMP_U, Po::None, MdTree::Emphasis)
+            }
+            _ if loop_buf.starts_with(EMP_A) => {
+                parse_simple_pat(loop_buf, EMP_A, EMP_A, Po::None, MdTree::Emphasis)
+            }
+            _ if loop_buf.starts_with(STK) => {
                 parse_simple_pat(loop_buf, STK, STK, Po::None, MdTree::Strikethrough)
             }
             (_, Newline | Whitespace) if loop_buf.starts_with(ANC_S) => {
@@ -130,11 +137,8 @@ fn parse_recursive<'a>(buf: &'a [u8], ctx: Context) -> MdStream<'_> {
                     _ => None,
                 }
             }
-            (_, Newline) if (loop_buf.starts_with(UL1) || loop_buf.starts_with(UL2)) => {
-                Some(parse_unordered_li(loop_buf))
-            }
             (_, Newline) if ord_list_start(loop_buf).is_some() => Some(parse_ordered_li(loop_buf)),
-            (_, Newline | Whitespace) if loop_buf.starts_with(LNK_S) => {
+            _ if loop_buf.starts_with(LNK_S) => {
                 parse_any_link(loop_buf, top_blk && prev == Prev::Newline)
             }
             (_, Escape | _) => None,
@@ -251,7 +255,6 @@ fn parse_heading(buf: &[u8]) -> ParseResult<'_> {
 
 /// Bulleted list
 fn parse_unordered_li(buf: &[u8]) -> Parsed<'_> {
-    debug_assert!(buf.starts_with(b"* ") || buf.starts_with(b"- "));
     let (txt, rest) = get_indented_section(&buf[2..]);
     let ctx = Context { top_block: false, prev: Prev::Whitespace };
     let stream = parse_recursive(trim_ascii_start(txt), ctx);
@@ -267,25 +270,28 @@ fn parse_ordered_li(buf: &[u8]) -> Parsed<'_> {
     (MdTree::OrderedListItem(num, stream), rest)
 }
 
-/// Find first line that isn't empty or doesn't start with whitespace, that will
-/// be our contents
 fn get_indented_section(buf: &[u8]) -> (&[u8], &[u8]) {
-    let mut end = buf.len();
-    for (idx, window) in buf.windows(2).enumerate() {
-        let &[ch, next_ch] = window else { unreachable!("always 2 elements") };
-        if idx >= buf.len().saturating_sub(2) && next_ch == b'\n' {
-            // End of stream
-            end = buf.len().saturating_sub(1);
-            break;
-        } else if ch == b'\n' && (!next_ch.is_ascii_whitespace() || next_ch == b'\n') {
-            end = idx;
-            break;
+    let mut lines = buf.split(|&byte| byte == b'\n');
+    let mut end = lines.next().map_or(0, |line| line.len());
+    for line in lines {
+        if let Some(first) = line.first() {
+            if unordered_list_start(line) || !first.is_ascii_whitespace() {
+                break;
+            }
         }
+        end += line.len() + 1;
     }
 
     (&buf[..end], &buf[end..])
 }
 
+fn unordered_list_start(mut buf: &[u8]) -> bool {
+    while let [b' ', rest @ ..] = buf {
+        buf = rest;
+    }
+    matches!(buf, [b'*' | b'-', b' ', ..])
+}
+
 /// Verify a valid ordered list start (e.g. `1.`) and parse it. Returns the
 /// parsed number and offset of character after the dot.
 fn ord_list_start(buf: &[u8]) -> Option<(u16, usize)> {
diff --git a/compiler/rustc_errors/src/markdown/tests/parse.rs b/compiler/rustc_errors/src/markdown/tests/parse.rs
index e39e8c89b35..e2e3f354ff6 100644
--- a/compiler/rustc_errors/src/markdown/tests/parse.rs
+++ b/compiler/rustc_errors/src/markdown/tests/parse.rs
@@ -4,13 +4,13 @@ use ParseOpt as PO;
 #[test]
 fn test_parse_simple() {
     let buf = "**abcd** rest";
-    let (t, r) = parse_simple_pat(buf.as_bytes(), STG, STG, PO::None, MdTree::Strong).unwrap();
+    let (t, r) = parse_simple_pat(buf.as_bytes(), b"**", b"**", PO::None, MdTree::Strong).unwrap();
     assert_eq!(t, MdTree::Strong("abcd"));
     assert_eq!(r, b" rest");
 
     // Escaping should fail
     let buf = r"**abcd\** rest";
-    let res = parse_simple_pat(buf.as_bytes(), STG, STG, PO::None, MdTree::Strong);
+    let res = parse_simple_pat(buf.as_bytes(), b"**", b"**", PO::None, MdTree::Strong);
     assert!(res.is_none());
 }
 
@@ -141,12 +141,12 @@ fn test_indented_section() {
     assert_eq!(str::from_utf8(r).unwrap(), "\nnot ind");
 
     let (txt, rest) = get_indented_section(IND2.as_bytes());
-    assert_eq!(str::from_utf8(txt).unwrap(), "test end of stream\n  1\n  2");
-    assert_eq!(str::from_utf8(rest).unwrap(), "\n");
+    assert_eq!(str::from_utf8(txt).unwrap(), "test end of stream\n  1\n  2\n");
+    assert_eq!(str::from_utf8(rest).unwrap(), "");
 
     let (txt, rest) = get_indented_section(IND3.as_bytes());
-    assert_eq!(str::from_utf8(txt).unwrap(), "test empty lines\n  1\n  2");
-    assert_eq!(str::from_utf8(rest).unwrap(), "\n\nnot ind");
+    assert_eq!(str::from_utf8(txt).unwrap(), "test empty lines\n  1\n  2\n");
+    assert_eq!(str::from_utf8(rest).unwrap(), "\nnot ind");
 }
 
 const HBT: &str = r"# Heading
@@ -310,3 +310,56 @@ fn test_code_at_start() {
     let res = entrypoint(CODE_STARTLINE);
     assert_eq!(res, expected);
 }
+
+#[test]
+fn test_code_in_parens() {
+    let expected =
+        vec![MdTree::PlainText("("), MdTree::CodeInline("Foo"), MdTree::PlainText(")")].into();
+    let res = entrypoint("(`Foo`)");
+    assert_eq!(res, expected);
+}
+
+const LIST_WITH_SPACE: &str = "
+para
+ * l1
+ * l2
+";
+
+#[test]
+fn test_list_with_space() {
+    let expected = vec![
+        MdTree::PlainText("para"),
+        MdTree::ParagraphBreak,
+        MdTree::UnorderedListItem(vec![MdTree::PlainText("l1")].into()),
+        MdTree::LineBreak,
+        MdTree::UnorderedListItem(vec![MdTree::PlainText("l2")].into()),
+    ]
+    .into();
+    let res = entrypoint(LIST_WITH_SPACE);
+    assert_eq!(res, expected);
+}
+
+const SNAKE_CASE: &str = "
+foo*bar*
+foo**bar**
+foo_bar_
+foo__bar__
+";
+
+#[test]
+fn test_snake_case() {
+    let expected = vec![
+        MdTree::PlainText("foo"),
+        MdTree::Emphasis("bar"),
+        MdTree::PlainText(" "),
+        MdTree::PlainText("foo"),
+        MdTree::Strong("bar"),
+        MdTree::PlainText(" "),
+        MdTree::PlainText("foo_bar_"),
+        MdTree::PlainText(" "),
+        MdTree::PlainText("foo__bar__"),
+    ]
+    .into();
+    let res = entrypoint(SNAKE_CASE);
+    assert_eq!(res, expected);
+}