diff options
| author | bors <bors@rust-lang.org> | 2024-07-28 14:22:27 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-07-28 14:22:27 +0000 |
| commit | 48fb66be7a657839bdf8f42ae06fc17893c2d162 (patch) | |
| tree | fee681aba06a57873e1736b19e78d97a21443dc5 /compiler/rustc_errors/src | |
| parent | 159524019f6c4ab8908dc297aeea4e5c4d1f2c89 (diff) | |
| parent | a973d3f0879c486a8485b99244f1060f69a31a33 (diff) | |
| download | rust-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.rs | 77 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/lib.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/markdown/parse.rs | 62 | ||||
| -rw-r--r-- | compiler/rustc_errors/src/markdown/tests/parse.rs | 65 |
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); +} |
