Subject: bug#35768: 27.0.50; CC-Mode problems with function definitions with macro names

From: Alan Mackenzie <[email-masked]>
Date: Sat, 18 May 2019 12:56:28 +0000

Hello, Mauro.

On Fri, May 17, 2019 at 09:40:09 -0300, Mauro Aranda wrote:

[ .... ]

> However, the following recipe exposes another problem, I think:
> 1) emacs -Q
> 2) Eval the following:
> (defun my-c-mode-hook ()
>   (setq c-noise-macro-with-parens-names (append
> c-noise-macro-with-parens-names
>                                                 '("DUMMY_1" "DUMMY_2")))
>   (c-make-noise-macro-regexps))
> (add-hook 'c-mode-hook 'my-c-mode-hook)

> 3) C-x C-f test.c
> 4) Type the following (no need to type the #define lines, that's just for
> completion)
> #define DUMMY_1(params)
> #define DUMMY_2(params)

> int DUMMY_1 (1) DUMMY_2 (2)
> foo (void)
> {
>   return 0;
> }

> 5) Observe that DUMMY_1 (1) is ignored as expected, but DUMMY_2 gets
> font-lock-type-face.  I think that's not right.

It's not right, no.

> 6) To be sure that I customized c-noise-macro-with-parens-names correctly, I
> tried a regexp search with c-noise-macro-with-parens-name-re, from the
> beginning of the buffer:
> (re-search-forward c-noise-macro-with-parens-name-re)
> That gets four hits, as it should (2 for DUMMY_1 and 2 for DUMMY_2), meaning
> that it does find DUMMY_2 as a noise macro with parens.

Just as a matter of interest, I also tried putting "DUMMY_3" into
c-noise-macro-names, and inserting it in the middle of the pertinent
line in your test file.

> Is that a bug? Or is there something else I can use to help CC Mode not get
> confused?

It's a bug.  I hope the following patch fixes it.  Would you please
apply this patch, try it out in your real code, and confirm it fixes the
bug, or tell me what's still not working.  Thanks!

diff -r 43b8aba74b73 cc-engine.el
--- a/cc-engine.el	Wed May 15 08:45:55 2019 +0000
+++ b/cc-engine.el	Sat May 18 12:45:53 2019 +0000
@@ -4500,6 +4500,30 @@
 		       (goto-char pos))))))
       (< (point) start)))
 
+(defun c-end-of-token (&optional back-limit)
+  ;; Move to the end of the token we're just before or in the middle of.
+  ;; BACK-LIMIT may be used to bound the backward search; if given it's
+  ;; assumed to be at the boundary between two tokens.  Return non-nil if the
+  ;; point is moved, nil otherwise.
+  ;;
+  ;; This function might do hidden buffer changes.
+  (let ((start (point)))
+    (cond ;; ((< (skip-syntax-backward "w_" (1- start)) 0)
+     ;;  (skip-syntax-forward "w_"))
+     ((> (skip-syntax-forward "w_") 0))
+     ((< (skip-syntax-backward ".()" back-limit) 0)
+      (while (< (point) start)
+	(if (looking-at c-nonsymbol-token-regexp)
+	    (goto-char (match-end 0))
+	  ;; `c-nonsymbol-token-regexp' should always match since
+	  ;; we've skipped backward over punctuation or paren
+	  ;; syntax, but move forward in case it doesn't so that
+	  ;; we don't leave point earlier than we started with.
+	  (forward-char))))
+     (t (if (looking-at c-nonsymbol-token-regexp)
+	    (goto-char (match-end 0)))))
+    (> (point) start)))
+
 (defun c-end-of-current-token (&optional back-limit)
   ;; Move to the end of the current token.  Do not move if not in the
   ;; middle of one.  BACK-LIMIT may be used to bound the backward
@@ -5885,9 +5909,14 @@
 	     ;; comment style has removed face properties from a construct,
 	     ;; and is relying on `c-font-lock-declarations' to add them
 	     ;; again.
-	     (and (< (point) cfd-limit)
-		  (looking-at c-doc-line-join-re)
-		  (goto-char (match-end 0)))))
+	     (cond
+	      ((looking-at c-noise-macro-name-re)
+	       (c-forward-noise-clause-not-macro-decl nil)) ; Returns t.
+	      ((looking-at c-noise-macro-with-parens-name-re)
+	       (c-forward-noise-clause-not-macro-decl t)) ; Always returns t.
+	      ((and (< (point) cfd-limit)
+		    (looking-at c-doc-line-join-re))
+	       (goto-char (match-end 0))))))
        ;; Set the position to continue at.  We can avoid going over
        ;; the comments skipped above a second time, but it's possible
        ;; that the comment skipping has taken us past `cfd-prop-match'
@@ -5916,6 +5945,8 @@
   ;; o	The first token after the end of submatch 1 in
   ;;	`c-decl-prefix-or-start-re' when that submatch matches.	 This
   ;;	submatch is typically a (L or R) brace or paren, a ;, or a ,.
+  ;;    As a special case, noise macros are skipped over and the next
+  ;;    token regarded as the spot.
   ;; o	The start of each `c-decl-prefix-or-start-re' match when
   ;;	submatch 1 doesn't match.  This is, for example, the keyword
   ;;	"class" in Pike.
@@ -7452,6 +7483,21 @@
       (c-forward-syntactic-ws))
   t)
 
+(defun c-forward-noise-clause-not-macro-decl (maybe-parens)
+  ;; Point is at a noise macro identifier, which, when MAYBE-PARENS is
+  ;; non-nil, optionally takes paren arguments.  Go forward over this name,
+  ;; and when there may be optional parens, any parenthesis expression which
+  ;; follows it, but DO NOT go over any macro declaration which may come
+  ;; between them.  Always return t.
+  (c-end-of-token)
+  (when maybe-parens
+    (let ((here (point)))
+      (c-forward-comments)
+      (if (not (and (eq (char-after) ?\()
+		    (c-go-list-forward)))
+	  (goto-char here))))
+  t)
+
 (defun c-forward-keyword-clause (match)
   ;; Submatch MATCH in the current match data is assumed to surround a
   ;; token.  If it's a keyword, move over it and any immediately
@@ -9060,7 +9106,10 @@
 	   ((and c-opt-cpp-prefix
 		 (looking-at c-noise-macro-with-parens-name-re))
 	    (setq noise-start (point))
-	    (c-forward-noise-clause)
+	    (while
+		(and
+		  (c-forward-noise-clause)
+		  (looking-at c-noise-macro-with-parens-name-re)))
 	    (setq kwd-clause-end (point))))
 
 	  (when (setq found-type (c-forward-type t)) ; brace-block-too

> Best regards,
> Mauro.

-- 
Alan Mackenzie (Nuremberg, Germany).