Subject: bug#79272: [PATCH] rust-ts-mode: handle invalid rust syntax without signaling

From: Steven Allen <[email-masked]>
Date: Tue, 19 Aug 2025 11:32:49 -0700

Tags: patch

Avoid signaling an error when encountering invalid rust syntax. The
signaled error would never be propagated to the user (caught somewhere
in the fontlocking logic, I think) but it would prevent a portion of the
buffer from being fontified.

Notes:

1. I encountered  this error in `rust-ts-mode--fontify-pattern' (and
reproduced it in the attached test). I applied the same fix to
`rust-ts-mode--fontify-scope' to be safe, but I couldn't actually
reproduce an issue there.
2. `treesit-node-parent' and `treesit-node-type' will blindly forward
nil so there's no issue calling (treesit-node-type (treesit-node-parent
node)) even if `node' has no parent.

In GNU Emacs 31.0.50 (build 1, x86_64-pc-linux-gnu, cairo version
 1.18.4) of 2025-08-16 built on Laptop
Repository revision: 68657c30983afe745f9667a0c42bdf6dd7ed4890
Repository branch: merged
Windowing system distributor 'The X.Org Foundation', version 11.0.12101018
System Description: Arch Linux

Configured using:
 'configure
 'CPPFLAGS=-I/run/user/1000/build/emacs-git/src/mps-git/build/include '
 'LDFLAGS=-L/run/user/1000/build/emacs-git/src/mps-git/build/lib -Wl,-O1
 -Wl,--sort-common -Wl,--as-needed -Wl,-z,relro -Wl,-z,now
 -Wl,-z,pack-relative-relocs -flto=auto' --prefix=/usr --sysconfdir=/etc
 --libexecdir=/usr/lib --localstatedir=/var --mandir=/usr/share/man
 --with-gameuser=:games --with-modules --without-m17n-flt
 --without-selinux --without-pop --without-gconf --disable-gc-mark-trace
 --with-mps=yes --enable-link-time-optimization
 --with-native-compilation=yes --with-xinput2 --with-x-toolkit=no
 --without-toolkit-scroll-bars --without-xaw3d --without-gsettings
 --with-cairo-xcb --without-xft --with-sound=no --with-tree-sitter
 --without-gpm --without-compress-install
 '--program-transform-name=s/\([ec]tags\)/\1.emacs/'
 'CFLAGS=-march=native -mtune=native -O3 -pipe -fno-plt -fexceptions
 -Wp,-D_FORTIFY_SOURCE=3 -Wformat -Werror=format-security
 -fstack-clash-protection -fcf-protection -fomit-frame-pointer
 -fno-math-errno -fno-trapping-math -fno-math-errno -fno-trapping-math
 -flto=auto''

>From bfb74edeaf8c94b2e5d6b2ce30e58a915348308b Mon Sep 17 00:00:00 2001
From: Steven Allen <[email-masked]>
Date: Tue, 19 Aug 2025 11:12:01 -0700
Subject: [PATCH] rust-ts-mode: handle invalid rust syntax without signaling

Don't signal an error when encountering invalid rust syntax. Without
this patch, invalid rust code would prevent a chunk of the buffer from
being highlighted.

* lisp/progmodes/rust-ts-mode.el (rust-ts-mode--fontify-scope):
(rust-ts-mode--fontify-pattern): Avoid calling `string-match-p' on nil
when a node is missing a parent.
* test/lisp/progmodes/rust-ts-mode-resources/font-lock-no-parent.rs:
Rust file that reproduces the issue.
* test/lisp/progmodes/rust-ts-mode-tests.el: Test case to reproduce the
issue.
---
 lisp/progmodes/rust-ts-mode.el                           | 9 +++++----
 .../rust-ts-mode-resources/font-lock-no-parent.rs        | 7 +++++++
 test/lisp/progmodes/rust-ts-mode-tests.el                | 7 +++++++
 3 files changed, 19 insertions(+), 4 deletions(-)
 create mode 100644 test/lisp/progmodes/rust-ts-mode-resources/font-lock-no-parent.rs

diff --git a/lisp/progmodes/rust-ts-mode.el b/lisp/progmodes/rust-ts-mode.el
index a5c217c0a4b..a98d621af65 100644
--- a/lisp/progmodes/rust-ts-mode.el
+++ b/lisp/progmodes/rust-ts-mode.el
@@ -366,7 +366,8 @@ rust-ts-mode--fontify-scope
              tail-p
              (string-match-p
               "\\`\\(?:use_list\\|call_expression\\|use_as_clause\\|use_declaration\\)\\'"
-              (treesit-node-type (treesit-node-parent (treesit-node-parent node)))))
+              (or (treesit-node-type (treesit-node-parent (treesit-node-parent node)))
+                  "no_parent")))
             nil)
            (t 'font-lock-constant-face))))
     (when face
@@ -387,9 +388,9 @@ 'rust-ts-mode--fontify-pattern
                        ,(treesit-query-compile 'rust '((identifier) @id
                                                        (shorthand_field_identifier) @id)))))
         (pcase-dolist (`(_name . ,id) captures)
-          (unless (string-match-p "\\`scoped_\\(?:type_\\)?identifier\\'"
-                                  (treesit-node-type
-                                   (treesit-node-parent id)))
+          (unless (string-match-p
+                   "\\`scoped_\\(?:type_\\)?identifier\\'"
+                   (or (treesit-node-type (treesit-node-parent id)) "no_parent"))
             (treesit-fontify-with-override
              (treesit-node-start id) (treesit-node-end id)
              'font-lock-variable-name-face override start end)))))))
diff --git a/test/lisp/progmodes/rust-ts-mode-resources/font-lock-no-parent.rs b/test/lisp/progmodes/rust-ts-mode-resources/font-lock-no-parent.rs
new file mode 100644
index 00000000000..85d0ccc9bf3
--- /dev/null
+++ b/test/lisp/progmodes/rust-ts-mode-resources/font-lock-no-parent.rs
@@ -0,0 +1,7 @@
++// intentionally invalid syntax
++const THING: [u8; 48] = [];
+
+// should recover here and highlight the text below
+trait Foo() {
+// ^ font-lock-keyword-face
+}
diff --git a/test/lisp/progmodes/rust-ts-mode-tests.el b/test/lisp/progmodes/rust-ts-mode-tests.el
index d2e28dcfbd7..32d64260a87 100644
--- a/test/lisp/progmodes/rust-ts-mode-tests.el
+++ b/test/lisp/progmodes/rust-ts-mode-tests.el
@@ -39,6 +39,13 @@ rust-ts-test-font-lock-number
     (ert-font-lock-test-file (ert-resource-file "font-lock-number.rs")
                              'rust-ts-mode)))
 
+(ert-deftest rust-ts-test-no-parent ()
+  (skip-unless (treesit-ready-p 'rust))
+  (let ((treesit-font-lock-level 4)
+        (rust-ts-mode-fontify-number-suffix-as-type t))
+    (ert-font-lock-test-file (ert-resource-file "font-lock-no-parent.rs")
+                             'rust-ts-mode)))
+
 (provide 'rust-ts-mode-tests)
 
 ;;; rust-ts-mode-tests.el ends here
-- 
2.50.1