From bb7a5b13df57209a36934c34f786902b30a437f8 Mon Sep 17 00:00:00 2001 From: Oleksiy Syvokon Date: Sat, 26 Apr 2025 18:31:06 +0300 Subject: [PATCH] [WIP] markdown: Add a test to reproduce the parser's panic (#29479) Backtrace of the panic in the Agent pane: ``` Thread "" panicked with "called `Option::unwrap()` on a `None` value" at crates/markdown/src/parser.rs:264:55 https://github.com/zed-industries/zed/blob/3fdbc3090d2cc5c2e24014009cccbe5e7c55d217/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: as core::ops::function::Fn>::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::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: 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::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::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::new::{{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::thread::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: 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::thread::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: as core::ops::function::FnOnce>::call_once at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/alloc/src/boxed.rs:1976:9 as core::ops::function::FnOnce>::call_once at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/alloc/src/boxed.rs:1976:9 std::sys::pal::unix::thread::Thread::new::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 --- crates/markdown/src/parser.rs | 62 +++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/crates/markdown/src/parser.rs b/crates/markdown/src/parser.rs index d039254906..ed9f64374b 100644 --- a/crates/markdown/src/parser.rs +++ b/crates/markdown/src/parser.rs @@ -184,6 +184,7 @@ pub fn parse_markdown( (range, MarkdownEvent::SubstitutedText(str.to_owned())) } } + #[derive(Debug)] struct TextRange<'a> { source_range: Range, merged_range: Range, @@ -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!(