Subject: bug#61205: 'function' in 3rd element of treesit-font-lock-feature-list

From: Dmitry Gutov <[email-masked]>
Date: Sat, 4 Feb 2023 05:36:15 +0200

On 03/02/2023 19:10, Dmitry Gutov wrote:
> On 03/02/2023 17:54, Eli Zaretskii wrote:
>>> Date: Fri, 3 Feb 2023 17:15:05 +0200
>>> Cc:[email-masked],[email-masked],[email-masked],[email-masked]
>>> From: Dmitry Gutov<[email-masked]>
>>>
>>>> Then as far as I'm concerned, this can go to level 4, but it must be
>>>> done consistently across all the *-ts modes.  So if some mode wants
>>>> 'property' to be highlighted, and wants it badly, we should IMO keep
>>>> it in C as well.
>>> Consistency is what I'm after here.
>>>
>>> c-ts-mode, as well as go-ts-mode, rust-ts-mode and typescript-ts-mode,
>>> all previously mentioned in this report, currently put it at 3.
>>>
>>> The rest put it as 4, or don't use it at all.
>> The question is: how important is this for go-ts-mode, rust-ts-mode,
>> and typescript-ts-mode?  I don't know the answer.  If the importance
>> is not high, then this should be moved to level 4.
> 
> Right. They don't seem to be particularly more important there than in 
> other modes. Or than 'function', for example.

Here's the updated patch in the meantime.

Not sure what to do with 'type' highlighting in rust-ts-mode yet. 
Additional scoping seems like will require a bunch of repetitions. 
Perhaps a :pred instruction to filter out children of a call_expression 
might work better.

diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi
index 97732b65e32..a86c12a0db7 100644
--- a/doc/emacs/display.texi
+++ b/doc/emacs/display.texi
@@ -1159,11 +1159,11 @@ Parser-based Font Lock
 This level adds fontification of keywords, strings, and data types.
 @item Level 3
 This is the default level; it adds fontification of assignments,
-numbers, properties, etc.
+numbers, etc.
 @item Level 4
 This level adds everything else that can be fontified: operators,
 delimiters, brackets, other punctuation, function names in function
-calls, variables, etc.
+calls, property look ups, variables, etc.
 @end table
 
 @vindex treesit-font-lock-feature-list
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index 390f67a8e8c..206b2e98fb3 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -774,8 +774,8 @@ c-ts-base-mode
   (setq-local treesit-font-lock-feature-list
               '(( comment definition)
                 ( keyword preprocessor string type)
-                ( assignment constant escape-sequence label literal property )
-                ( bracket delimiter error function operator variable))))
+                ( assignment constant escape-sequence label literal)
+                ( bracket delimiter error function operator property variable))))
 
 ;;;###autoload
 (define-derived-mode c-ts-mode c-ts-base-mode "C"
diff --git a/lisp/progmodes/go-ts-mode.el b/lisp/progmodes/go-ts-mode.el
index 5f3e1ea3e68..a4b64808ca2 100644
--- a/lisp/progmodes/go-ts-mode.el
+++ b/lisp/progmodes/go-ts-mode.el
@@ -119,17 +119,27 @@ go-ts-mode--font-lock-settings
    :feature 'delimiter
    '((["," "." ";" ":"]) @font-lock-delimiter-face)
 
+   :language 'go
+   :feature 'definition
+   '((function_declaration
+      name: (identifier) @font-lock-function-name-face)
+     (method_declaration
+      name: (field_identifier) @font-lock-function-name-face)
+     (method_spec
+      name: (field_identifier) @font-lock-function-name-face))
+
+   :language 'go
+   :feature 'definition
+   '((field_declaration
+      name: (field_identifier) @font-lock-property-face))
+
    :language 'go
    :feature 'function
    '((call_expression
       function: (identifier) @font-lock-function-name-face)
      (call_expression
       function: (selector_expression
-                 field: (field_identifier) @font-lock-function-name-face))
-     (function_declaration
-      name: (identifier) @font-lock-function-name-face)
-     (method_declaration
-      name: (field_identifier) @font-lock-function-name-face))
+                 field: (field_identifier) @font-lock-function-name-face)))
 
    :language 'go
    :feature 'keyword
@@ -217,11 +227,10 @@ go-ts-mode
     ;; Font-lock.
     (setq-local treesit-font-lock-settings go-ts-mode--font-lock-settings)
     (setq-local treesit-font-lock-feature-list
-                '(( comment)
+                '(( comment definition)
                   ( keyword string type)
-                  ( constant escape-sequence function label number
-                    property variable)
-                  ( bracket delimiter error operator)))
+                  ( constant escape-sequence label number)
+                  ( bracket delimiter error function operator property variable)))
 
     (treesit-major-mode-setup)))
 
diff --git a/lisp/progmodes/rust-ts-mode.el b/lisp/progmodes/rust-ts-mode.el
index e317793d211..662c286716b 100644
--- a/lisp/progmodes/rust-ts-mode.el
+++ b/lisp/progmodes/rust-ts-mode.el
@@ -155,6 +155,16 @@ rust-ts-mode--font-lock-settings
    :feature 'delimiter
    '((["," "." ";" ":" "::"]) @font-lock-delimiter-face)
 
+   :language 'rust
+   :feature 'definition
+   '((function_item name: (identifier) @font-lock-function-name-face)
+     (macro_definition "macro_rules!" @font-lock-constant-face)
+     (macro_definition (identifier) @font-lock-preprocessor-face))
+
+   :language 'rust
+   :feature 'definition
+   '((field_declaration name: (field_identifier) @font-lock-property-face))
+
    :language 'rust
    :feature 'function
    '((call_expression
@@ -164,15 +174,12 @@ rust-ts-mode--font-lock-settings
         field: (field_identifier) @font-lock-function-name-face)
        (scoped_identifier
         name: (identifier) @font-lock-function-name-face)])
-     (function_item (identifier) @font-lock-function-name-face)
      (generic_function
       function: [(identifier) @font-lock-function-name-face
                  (field_expression
                   field: (field_identifier) @font-lock-function-name-face)
                  (scoped_identifier
                   name: (identifier) @font-lock-function-name-face)])
-     (macro_definition "macro_rules!" @font-lock-constant-face)
-     (macro_definition (identifier) @font-lock-preprocessor-face)
      (macro_invocation macro: (identifier) @font-lock-preprocessor-face))
 
    :language 'rust
@@ -208,7 +215,6 @@ rust-ts-mode--font-lock-settings
      (mod_item name: (identifier) @font-lock-constant-face)
      (primitive_type) @font-lock-type-face
      (type_identifier) @font-lock-type-face
-     (scoped_identifier name: (identifier) @font-lock-type-face)
      (scoped_identifier path: (identifier) @font-lock-constant-face)
      (scoped_identifier
       (scoped_identifier
@@ -317,11 +323,11 @@ rust-ts-mode
     ;; Font-lock.
     (setq-local treesit-font-lock-settings rust-ts-mode--font-lock-settings)
     (setq-local treesit-font-lock-feature-list
-                '(( comment)
+                '(( comment definition)
                   ( keyword string)
                   ( attribute builtin constant escape-sequence
-                    function number property type variable)
-                  ( bracket delimiter error operator)))
+                    number type)
+                  ( bracket delimiter error function operator property variable)))
 
     ;; Imenu.
     (setq-local treesit-simple-imenu-settings
diff --git a/test/manual/etags/rs-src/test.rs b/test/manual/etags/rs-src/test.rs
index 081d0d7d4df..06cde0e79e2 100644
--- a/test/manual/etags/rs-src/test.rs
+++ b/test/manual/etags/rs-src/test.rs
@@ -1,5 +1,10 @@
 mod test;
 
+use std::collections::hash_map::{self, HashMap};
+
+use std::path::{self, Path, PathBuf};  // good: std is a crate name
+use crate::foo::baz::foobaz;    // good: foo is at the root of the crate
+
 enum IpAddrKind {
     V4,
     V6,
@@ -12,3 +17,69 @@ fn test1() {
 fn main() {
    test::test1();
 }
+
+fn eat_box_i32(boxed_i32: Box<i32>) {
+    println!("Destroying box that contains {}", boxed_i32);
+}
+
+// This function borrows an i32
+fn borrow_i32(borrowed_i32: &i32) {
+    println!("This int is: {}", borrowed_i32);
+}
+
+struct Val {
+    val: f64,
+}
+
+struct GenVal<T> {
+    gen_val: T,
+}
+
+// impl of Val
+impl Val {
+    fn value(&self) -> &f64 {
+        &self.val
+    }
+}
+
+// impl of GenVal for a generic type `T`
+impl<T> GenVal<T> {
+    fn value(&self) -> &T {
+        &self.gen_val
+    }
+}
+
+fn main() {
+    let x = Val { val: 3.0 };
+    let y = GenVal { gen_val: 3i32 };
+
+    println!("{}, {}", x.value(), y.value());
+}
+
+fn main() {
+    // Create a boxed i32, and a stacked i32
+    let boxed_i32 = Box::new(5_i32);
+    let stacked_i32 = 6_i32;
+
+    // Borrow the contents of the box. Ownership is not taken,
+    // so the contents can be borrowed again.
+    borrow_i32(&boxed_i32);
+    borrow_i32(&stacked_i32);
+
+    {
+        // Take a reference to the data contained inside the box
+        let _ref_to_i32: &i32 = &boxed_i32;
+
+        // Error!
+        // Can't destroy `boxed_i32` while the inner value is borrowed later in scope.
+        eat_box_i32(boxed_i32);
+        // FIXME ^ Comment out this line
+
+        // Attempt to borrow `_ref_to_i32` after inner value is destroyed
+        borrow_i32(_ref_to_i32);
+        // `_ref_to_i32` goes out of scope and is no longer borrowed.
+    }
+
+    // `boxed_i32` can now give up ownership to `eat_box` and be destroyed
+    eat_box_i32(boxed_i32);
+}