Re: imenu parsing of ObjC classes and selectors

Alan Mackenzie <[email protected]>
Newsgroups gmane.emacs.cc-mode.general
Message-ID <[email protected]>
Hello, J.

Thanks for taking the trouble to report this bug, and thanks even more
for such a useful test file.

On Sat, Aug 08, 2026 at 09:52:13 -0400, JD Smith wrote:
> I've been working on a bunch of ObjC lately, and for the most part
> objc-mode (CC-mode v5.35.2) works well for it.  Sometimes, however, I
> notice imenu fails to parse certain class implementations correctly.
> Not sure why.  Here's an example excerpt.   

> Here's imenu's listing for this file (from imenu--make-index-alist):

> (("NSMutableArray(Emacs)" . 1882) ("-enqueue:" . 1922)
>  ("-dequeue" . 2030) ("NSData" . 19) ("NSString" . 200)
>  ("NSFont" . 1588) ("NSFont" . 1844))

> It has a number of problems:

>     • It produces spurious matches for the @end // Class (Category) lines
>     • It fails to group methods with class (aka selector in ObjC).  This does work for many cases, not sure why it sometimes fails.
>     • It misses entirely the methods under NSData and NSString.
>     • When "class method" association does work, sometimes it associates with the prior class in the file (not features in this reproducer).

This bug is an unexpected consequence of the change of ~1 year ago, such
that the character @ is now categorized as an identifier component (in
Emacs jargon, its "syntax" was changed).

So lines like:

    @end        // NSFont (Emacs)

were getting parsed as a C function with "@end" as the type and "NSFont"
as the name.  This parser has to be fast, so is not very intelligent.
Basically it's just a regexp search.

>  Happy to try any tests.  Thanks for the CC-mode family: true workhorses!

The following patch, I hope, fixes it.  It should apply cleanly to the
version of CC Mode contained within the Emacs master branch (and also
the Emacs release branch).  Would you try it out, please, on your real
Objective-C source, and let me know how well it works.  Note that some
Emacs variables' values have been changed, so it would be best to
restart Emacs after rebuilding CC Mode to ensure the obsolete values
don't remain.

[ If you want any help on applying the patch or building the patched CC
Mode afterwards, feel free to send me private email. ]



diff -r b95ae8bc0227 cc-menus.el
--- a/cc-menus.el	Tue Jun 30 11:34:01 2026 +0000
+++ b/cc-menus.el	Wed Aug 12 14:16:36 2026 +0000
@@ -307,26 +307,25 @@
    ;; Pick a token by (match-string 1)
    (car (cdr (nth 1 cc-imenu-c++-generic-expression))) ; -> index += 2
    (prog2 (setq cc-imenu-objc-generic-expression-noreturn-index 1) "")
-   "\\|"
-   ;; > General function name regexp
-   ;; Pick a token by  (match-string 3)
-   (car (cdr (nth 2 cc-imenu-c++-generic-expression))) ; -> index += 6
-   (prog2 (setq cc-imenu-objc-generic-expression-general-func-index 3) "")
    ;; > Special case for definitions using phony prototype macros like:
    ;; > `int main _PROTO( (int argc,char *argv[]) )'.
-   ;; Pick a token by  (match-string 8)
+   ;; Pick a token by  (match-string 3)
    (if cc-imenu-c-prototype-macro-regexp
        (concat
 	"\\|"
 	(car (cdr (nth 3 cc-imenu-c++-generic-expression))) ; -> index += 1
-	(prog2 (setq cc-imenu-objc-generic-expression-objc-base-index 10) "")
+	(progn (setq cc-imenu-objc-generic-expression-objc-base-index 4)
+	       (setq cc-imenu-objc-generic-expression-general-func-index 5)
+	       "")
 	)
-     (prog2 (setq cc-imenu-objc-generic-expression-objc-base-index 9) "")
+     (progn (setq cc-imenu-objc-generic-expression-objc-base-index 3)
+	    (setq cc-imenu-objc-generic-expression-general-func-index 4)
+	    "")
      "")				; -> index += 0
-   (prog2 (setq cc-imenu-objc-generic-expression-proto-index 9) "")
+   (prog2 (setq cc-imenu-objc-generic-expression-proto-index 3) "")
    ;;
    ;; For Objective-C
-   ;; Pick a token by (match-string 8 or 9)
+   ;; Pick a token by (match-string 3 or 4)
    ;;
    "\\|\\("
    "^[-+][:" c-alnum "()*_<>\n\t ]*[;{]"        ; Methods
@@ -342,7 +341,11 @@
    "\\|"
    "^@implementation[\t ]+[" c-alnum "_]+"
    "\\|"
-   "^@protocol[\t ]+[" c-alnum "_]+" "\\)")
+   "^@protocol[\t ]+[" c-alnum "_]+" "\\)"
+   "\\|"
+   ;; > General function name regexp
+   ;; Pick a token by  (match-string 4)
+   (car (cdr (nth 2 cc-imenu-c++-generic-expression)))) ; -> index += 6
   "Imenu generic expression for ObjC mode.  See `imenu-generic-expression'.")
 
 
@@ -455,7 +458,9 @@
 	 ;; C
 	 ;;
 	 ((not (eq langnum OBJC))
-	  (setq clist (cons (cons str (match-beginning langnum)) clist)))
+	  ;; Don't be fooled by @end // Classname.
+	  (when (not (eq (aref (match-string-no-properties 0) 0) ?@))
+	    (setq clist (cons (cons str (match-beginning langnum)) clist))))
 	 ;;
 	 ;; ObjC
 	 ;;
@@ -484,13 +489,16 @@
 		  str2 "@interface"))
 	   ((string= (substring  str 0 prtlen) "@protocol")
 	    (setq str (substring str prtlen)
-		  str2 "@protocol")))
-	  (setq str (cc-imenu-objc-remove-white-space str))
-	  (setq methodlist (cons (cons str2
-				       (match-beginning langnum))
-				 methodlist))
-	  (setq toplist (cons (cons str methodlist) toplist)
-		methodlist nil)))))
+		  str2 "@protocol"))
+	   (t (setq str2 nil)))
+	  (when str2
+	    (setq str (cc-imenu-objc-remove-white-space str))
+	    (setq methodlist (cons (cons str2
+					 (match-beginning langnum))
+				   methodlist))
+	    (setq toplist
+		  (cons (cons str methodlist) toplist)
+		  methodlist nil))))))
     ;; In this buffer, there is only one or zero @{interface|implementation|protocol}.
     (if (< classcount 2)
 	(let ((classname (car (car toplist)))


> JDS

-- 
Alan Mackenzie (Nuremberg, Germany).
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.