[WIP] markdown: Add a test to reproduce the parser's panic (#29479)

Backtrace of the panic in the Agent pane:
```
Thread "<unnamed>" panicked with "called `Option::unwrap()` on a `None` value" at crates/markdown/src/parser.rs:264:55
3fdbc3090d/src/crates/markdown/src/parser.rs (L264) (may not be uploaded, line may be incorrect if files modified)
   0: zed::reliability::init_panic_hook::{{closure}}
             at /home/silver/develop/zed/crates/zed/src/reliability.rs:56:29
   1: <alloc::boxed::Box<F,A> as core::ops::function::Fn<Args>>::call
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/alloc/src/boxed.rs:1990:9
      std::panicking::rust_panic_with_hook
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:839:13
   2: std::panicking::begin_panic_handler::{{closure}}
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:697:13
   3: std::sys::backtrace::__rust_end_short_backtrace
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/sys/backtrace.rs:168:18
   4: rust_begin_unwind
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:695:5
   5: core::panicking::panic_fmt
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/panicking.rs:75:14
   6: core::panicking::panic
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/panicking.rs:145:5
   7: core::option::unwrap_failed
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/option.rs:2015:5
   8: core::option::Option<T>::unwrap
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/option.rs:978:21
      markdown::parser::parse_markdown
             at /home/silver/develop/zed/crates/markdown/src/parser.rs:264:37
   9: markdown::Markdown::parse::{{closure}}
             at /home/silver/develop/zed/crates/markdown/src/markdown.rs:282:51
  10: <core::pin::Pin<P> as core::future::future::Future>::poll
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/future/future.rs:124:9
  11: async_task::raw::RawTask<F,T,S,M>::run
             at /home/silver/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/async-task-4.7.1/src/raw.rs:557:17
  12: async_task::runnable::Runnable<M>::run
             at /home/silver/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/async-task-4.7.1/src/runnable.rs:781:18
  13: gpui::platform::linux::dispatcher::LinuxDispatcher:🆕:{{closure}}::{{closure}}
             at /home/silver/develop/zed/crates/gpui/src/platform/linux/dispatcher.rs:44:25
  14: std::sys::backtrace::__rust_begin_short_backtrace
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:152:18
  15: std:🧵:Builder::spawn_unchecked_::{{closure}}::{{closure}}
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/mod.rs:559:17
  16: <core::panic::unwind_safe::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/panic/unwind_safe.rs:272:9
  17: std::panicking::try::do_call
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:587:40
  18: __rust_try
  19: std::panicking::try
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:550:19
      std::panic::catch_unwind
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:358:14
      std:🧵:Builder::spawn_unchecked_::{{closure}}
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/thread/mod.rs:557:30
  20: core::ops::function::FnOnce::call_once{{vtable.shim}}
             at /home/silver/.rustup/toolchains/1.86-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
  21: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/alloc/src/boxed.rs:1976:9
      <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/alloc/src/boxed.rs:1976:9
      std::sys::pal::unix:🧵:Thread:🆕:thread_start
             at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/sys/pal/unix/thread.rs:106:17
  22: start_thread
             at ./nptl/pthread_create.c:447:8
  23: clone3
             at ./misc/../sysdeps/unix/sysv/linux/x86_64/clone3.S:78:0

Segmentation fault
```


Release Notes:

- N/A *or* Added/Fixed/Improved ...

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
Oleksiy Syvokon 2025-04-26 18:31:06 +03:00 committed by GitHub
parent 1e47dfce79
commit bb7a5b13df
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -184,6 +184,7 @@ pub fn parse_markdown(
(range, MarkdownEvent::SubstitutedText(str.to_owned()))
}
}
#[derive(Debug)]
struct TextRange<'a> {
source_range: Range<usize>,
merged_range: Range<usize>,
@ -236,7 +237,9 @@ pub fn parse_markdown(
events.push(event_for(text, range.source_range, &range.parsed));
}
let range = ranges.peek_mut().unwrap();
let Some(range) = ranges.peek_mut() else {
continue;
};
let prefix_len = link_start_in_merged - range.merged_range.start;
if prefix_len > 0 {
let (head, tail) = range.parsed.split_at(prefix_len);
@ -251,6 +254,7 @@ pub fn parse_markdown(
}
let link_start_in_source = range.source_range.start;
let mut link_end_in_source = range.source_range.end;
let mut link_events = Vec::new();
while ranges
@ -258,23 +262,26 @@ pub fn parse_markdown(
.is_some_and(|range| range.merged_range.end <= link_end_in_merged)
{
let range = ranges.next().unwrap();
link_end_in_source = range.source_range.end;
link_events.push(event_for(text, range.source_range, &range.parsed));
}
let range = ranges.peek_mut().unwrap();
let prefix_len = link_end_in_merged - range.merged_range.start;
if prefix_len > 0 {
let (head, tail) = range.parsed.split_at(prefix_len);
link_events.push(event_for(
text,
range.source_range.start..range.source_range.start + prefix_len,
head,
));
range.parsed = CowStr::Boxed(tail.into());
range.merged_range.start += prefix_len;
range.source_range.start += prefix_len;
if let Some(range) = ranges.peek_mut() {
let prefix_len = link_end_in_merged - range.merged_range.start;
if prefix_len > 0 {
let (head, tail) = range.parsed.split_at(prefix_len);
link_events.push(event_for(
text,
range.source_range.start..range.source_range.start + prefix_len,
head,
));
range.parsed = CowStr::Boxed(tail.into());
range.merged_range.start += prefix_len;
range.source_range.start += prefix_len;
link_end_in_source = range.source_range.start;
}
}
let link_range = link_start_in_source..range.source_range.start;
let link_range = link_start_in_source..link_end_in_source;
events.push((
link_range.clone(),
@ -564,6 +571,33 @@ mod tests {
);
}
#[test]
fn test_incomplete_link() {
assert_eq!(
parse_markdown("You can use the [GitHub Search API](https://docs.github.com/en").0,
vec![
(0..62, Start(Paragraph)),
(0..16, Text),
(16..17, Text),
(17..34, Text),
(34..35, Text),
(35..36, Text),
(
36..62,
Start(Link {
link_type: LinkType::Autolink,
dest_url: "https://docs.github.com/en".into(),
title: "".into(),
id: "".into()
})
),
(36..62, Text),
(36..62, End(MarkdownTagEnd::Link)),
(0..62, End(MarkdownTagEnd::Paragraph))
],
);
}
#[test]
fn test_smart_punctuation() {
assert_eq!(