Don't panic when a tree-sitter parse fails (#11076)

Max Brunsfeld and Conrad created

Fixes
https://zed-industries.slack.com/archives/C04S6T1T7TQ/p1714162894982749

Release Notes:
* Fixed a crash that could happen if an error occurred in a parser
provided by an extension.

Co-authored-by: Conrad <conrad@zed.dev>

Change summary

Cargo.lock                        |  2 +-
Cargo.toml                        |  2 +-
crates/language/src/syntax_map.rs | 31 +++++++++++++++++++++----------
3 files changed, 23 insertions(+), 12 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -10529,7 +10529,7 @@ dependencies = [
 [[package]]
 name = "tree-sitter"
 version = "0.20.100"
-source = "git+https://github.com/tree-sitter/tree-sitter?rev=528bcd2274814ca53711a57d71d1e3cf7abd73fe#528bcd2274814ca53711a57d71d1e3cf7abd73fe"
+source = "git+https://github.com/tree-sitter/tree-sitter?rev=7b4894ba2ae81b988846676f54c0988d4027ef4f#7b4894ba2ae81b988846676f54c0988d4027ef4f"
 dependencies = [
  "cc",
  "regex",

Cargo.toml 🔗

@@ -408,7 +408,7 @@ features = [
 ]
 
 [patch.crates-io]
-tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "528bcd2274814ca53711a57d71d1e3cf7abd73fe" }
+tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "7b4894ba2ae81b988846676f54c0988d4027ef4f" }
 # Workaround for a broken nightly build of gpui: See #7644 and revisit once 0.5.3 is released.
 pathfinder_simd = { git = "https://github.com/servo/pathfinder.git", rev = "30419d07660dc11a21e42ef4a7fa329600cff152" }
 

crates/language/src/syntax_map.rs 🔗

@@ -606,13 +606,21 @@ impl SyntaxSnapshot {
                             LogIncludedRanges(&included_ranges),
                         );
 
-                        tree = parse_text(
+                        let result = parse_text(
                             grammar,
                             text.as_rope(),
                             step_start_byte,
                             included_ranges,
                             Some(old_tree.clone()),
                         );
+                        match result {
+                            Ok(t) => tree = t,
+                            Err(e) => {
+                                log::error!("error parsing text: {:?}", e);
+                                continue;
+                            }
+                        };
+
                         changed_ranges = join_ranges(
                             invalidated_ranges
                                 .iter()
@@ -651,13 +659,20 @@ impl SyntaxSnapshot {
                             LogIncludedRanges(&included_ranges),
                         );
 
-                        tree = parse_text(
+                        let result = parse_text(
                             grammar,
                             text.as_rope(),
                             step_start_byte,
                             included_ranges,
                             None,
                         );
+                        match result {
+                            Ok(t) => tree = t,
+                            Err(e) => {
+                                log::error!("error parsing text: {:?}", e);
+                                continue;
+                            }
+                        };
                         changed_ranges = vec![step_start_byte..step_end_byte];
                     }
 
@@ -1161,16 +1176,12 @@ fn parse_text(
     start_byte: usize,
     ranges: Vec<tree_sitter::Range>,
     old_tree: Option<Tree>,
-) -> Tree {
+) -> anyhow::Result<Tree> {
     PARSER.with(|parser| {
         let mut parser = parser.borrow_mut();
         let mut chunks = text.chunks_in_range(start_byte..text.len());
-        parser
-            .set_included_ranges(&ranges)
-            .expect("overlapping ranges");
-        parser
-            .set_language(&grammar.ts_language)
-            .expect("incompatible grammar");
+        parser.set_included_ranges(&ranges)?;
+        parser.set_language(&grammar.ts_language)?;
         parser
             .parse_with(
                 &mut move |offset, _| {
@@ -1179,7 +1190,7 @@ fn parse_text(
                 },
                 old_tree.as_ref(),
             )
-            .expect("invalid language")
+            .ok_or_else(|| anyhow::anyhow!("failed to parse"))
     })
 }