Skip to content

[Bug]: treesit-query-expand writes nil as () #416

Description

@oysmuc

Bug summary

Given the same input, GNU Emacs and neomacs produce different output:

treesit-query-expand '((nil) @f)
GNU Emacs 31.1 "(nil) @f"
neomacs 0.0.19 "(()) @f"

() and nil are not the same thing in a tree-sitter query.
The tree-sitter query syntax reference says a node pattern is

a pair of parentheses containing two things: the node's type

so (()) contains no type name and matches nothing the syntax defines.

Steps to reproduce

Side-by-side comparison

emacs   --batch -Q -l treesit-nil-demo.el
neomacs --batch -Q -l treesit-nil-demo.el

The acceptance check needs a grammar for any of go, ruby or lua.

;;; treesit-nil-demo.el --- -*- lexical-binding: t; -*-

(require 'treesit)

(defvar tnd-queries
  '(((nil) @f)
    ([(false) (nil) (true)] @f)
    ([(false) (true)] @f))
  "Query sexps to pass to `treesit-query-expand'.")

(defvar tnd-dirs
  (let ((dirs (list (expand-file-name "tree-sitter/" user-emacs-directory)
                    (expand-file-name "~/.local/share/emacs/tree-sitter/")
                    (expand-file-name "~/.emacs.d/tree-sitter/"))))
    (when (boundp 'treesit-extra-load-path)
      (setq treesit-extra-load-path
            (append (seq-filter #'file-directory-p dirs) treesit-extra-load-path)))
    dirs)
  "Where this script looks for grammars, so it needs no extra flags.")

(defvar tnd-lang (seq-find #'treesit-language-available-p '(go ruby lua)))

(dolist (q tnd-queries)
  (let ((out (treesit-query-expand q)))
    (princ (format "in        %S\n" q))
    (princ (format "out       %S\n" out))
    (princ (format "accepted  %s\n\n"
                   (cond ((null tnd-lang) "(no grammar found)")
                         ((ignore-errors (treesit-query-compile tnd-lang out t) t) "yes")
                         (t "no"))))))

(princ (if tnd-lang
           (format "(grammar: %s)\n" tnd-lang)
         (format "(no grammar found; looked in %s)\n" (string-join tnd-dirs " "))))

Output, same grammar files:

** GNU Emacs 31.1
in        ((nil) @f)
out       "(nil) @f"
accepted  yes

in        ([(false) (nil) (true)] @f)
out       "[(false) (nil) (true)] @f"
accepted  yes

in        ([(false) (true)] @f)
out       "[(false) (true)] @f"
accepted  yes

(grammar: go)
** neomacs 0.0.19
in        ((nil) @f)
out       "(()) @f"
accepted  no

in        ([(false) (nil) (true)] @f)
out       "[(false) (()) (true)] @f"
accepted  no

in        ([(false) (true)] @f)
out       "[(false) (true)] @f"
accepted  yes

(grammar: go)

The inputs are identical on both builds. The outputs are not, and each
build's own output is then handed back to the same grammar: GNU's is
accepted, neomacs's is rejected. The third query, which contains no nil,
expands the same way on both and is accepted by both.

go-ts-mode, ruby-ts-mode and lua-ts-mode all match nil literals in
their font-lock rules, so on neomacs opening a .go, .rb or
.lua file gives:

Warning (treesit-font-lock-rules-mismatch): Emacs cannot compile every
font-lock rules because a mismatch between the grammar and the rules. [...]
This error can be fixed by either downgrading the grammar (tree-sitter-go)
on your system, or upgrading the major mode package.  The following are the
temporarily disabled features:

- `constant' for go.

The same grammar files work on GNU Emacs.

Expected behavior

No response

Neomacs --version output

GNU Emacs 31.1 (emacs-plus)
macOS 27.0 (Darwin 27.0.0), arm64

Neomacs 0.0.19
Git commit: b715d50bd26cceb537e905adf1cc3bfbf46fd340
Source date: 2026-09-20T12:37:58.000000000-04:00
Build: release for aarch64-apple-darwin with rustc 1.96.1
Standalone Rust binary for Neomacs (no C dependency)

Platform

macOS

Install source

Prebuilt release asset (downloaded from Releases)

Extra context

No response

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingstatus: needs-triageNot yet reviewed by a maintainer

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions