From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Alan Mackenzie Newsgroups: gmane.emacs.bugs Subject: bug#70435: 30.0.50; cc-mode: <> are sometimes not reconized as parentheses Date: Mon, 29 Apr 2024 15:53:56 +0000 Message-ID: References: <87le5cuuyq.fsf@gmail.com> <87y18xifpb.fsf@gmail.com> Mime-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="15927"; mail-complaints-to="usenet@ciao.gmane.io" Cc: acm@muc.de, Eli Zaretskii , 70435@debbugs.gnu.org To: Herman =?UTF-8?Q?G=C3=A9za?= Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Mon Apr 29 17:55:05 2024 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1s1TLM-0003sI-WC for geb-bug-gnu-emacs@m.gmane-mx.org; Mon, 29 Apr 2024 17:55:05 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1s1TL1-0004I1-Nd; Mon, 29 Apr 2024 11:54:43 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s1TL0-0004Hj-0a for bug-gnu-emacs@gnu.org; Mon, 29 Apr 2024 11:54:42 -0400 Original-Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1s1TKz-0004kT-Lv for bug-gnu-emacs@gnu.org; Mon, 29 Apr 2024 11:54:41 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1s1TLJ-0007ID-Mi for bug-gnu-emacs@gnu.org; Mon, 29 Apr 2024 11:55:01 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Alan Mackenzie Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Mon, 29 Apr 2024 15:55:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 70435 X-GNU-PR-Package: emacs Original-Received: via spool by 70435-submit@debbugs.gnu.org id=B70435.171440606828020 (code B ref 70435); Mon, 29 Apr 2024 15:55:01 +0000 Original-Received: (at 70435) by debbugs.gnu.org; 29 Apr 2024 15:54:28 +0000 Original-Received: from localhost ([127.0.0.1]:57969 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1s1TKl-0007Hs-91 for submit@debbugs.gnu.org; Mon, 29 Apr 2024 11:54:28 -0400 Original-Received: from mail.muc.de ([193.149.48.3]:10539) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1s1TKi-0007Hm-Ny for 70435@debbugs.gnu.org; Mon, 29 Apr 2024 11:54:26 -0400 Original-Received: (qmail 69857 invoked by uid 3782); 29 Apr 2024 17:53:57 +0200 Original-Received: from muc.de (pd953a905.dip0.t-ipconnect.de [217.83.169.5]) (using STARTTLS) by colin.muc.de (tmda-ofmipd) with ESMTP; Mon, 29 Apr 2024 17:53:57 +0200 Original-Received: (qmail 16151 invoked by uid 1000); 29 Apr 2024 15:53:56 -0000 Content-Disposition: inline In-Reply-To: <87y18xifpb.fsf@gmail.com> X-Submission-Agent: TMDA/1.3.x (Ph3nix) X-Primary-Address: acm@muc.de X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:284157 Archived-At: Hello, Géza. On Sun, Apr 28, 2024 at 18:47:47 +0200, Herman, Géza wrote: > Hello Alan, > Alan Mackenzie writes: > > You've been a little less than fully explicit, but I think you're > > executing these commands in the *scratch* buffer. The first two > > lines, which are commented out in emacs-lisp-mode, are no longer > > commented out in C++ Mode. There is a whole line of garbage after > > the last end of statement marker, the (double) semicolon on line 2. > > On using ig to insert the snippet, it is hardly surprising that > > CC Mode's syntactic analysis gets confused. If you first comment > > out those first two lines (put the region around them and do C-c > > C-c), then the inserted snippet appears to get the correct syntax on > > its template markers. > > I don't think there's a bug here. If you could show ig > > producing the effect when typed inside a syntactically correct > > context, things might be different. Can you reproduce the effect in > > correct C++ code? > You're right, it seems that the example I provided wasn't the best > (this issue happens with me in real code, I tried to create a minimal > reproducible example). > If you delete the garbage from the scratch buffer, the bug doesn't > reproduce indeed. But, if you run (setq font-lock-maximum-decoration > 2) before switching to c++-mode, the issue reproduces with an empty > scratch buffer. I use this setting because font-lock runs much faster > this way, and I rely on the LSP server to do the "full" highlighting. OK, as already said, I can reproduce the bug this way. Thanks! > Sorry about the bad example, here are the fixed repro steps: > Repro: > - put the yasnippet file (included below) into > /snippets/c++-mode/something > - install yasnippet > - start emacs, scratch buffer appears > - delete the contents of the scratch buffer > - M-: (setq font-lock-maximum-decoration 2) > - M-x c++-mode > - M-x yas-minor-mode > - load snippets with "M-x yas-reload-all" > - write "ig", then press TAB to "yas-expand" the snippet > - move the cursor on the opening "<", and execute "M-x > describe-char" > - notice that it will say "syntax: . which means: punctuation" > - if you edit the buffer (like add a space somewhere), and execute > describe-char again, Emacs will say "syntax: > which means: open, > matches >", so the syntax class becomes correct. I have a fix, I think. It is actually a two line fix, removing a test from the top of a function, but it involves reindenting the entire rest of the function. Please apply the patch below, recompile cc-engine.el, then load the resulting CC Mode into a running Emacs. Please test it on your real C++ code, and let me know if the bug is actually fixed. Thanks! diff -r 072940aaeb40 cc-engine.el --- a/cc-engine.el Sun Apr 14 07:59:01 2024 +0000 +++ b/cc-engine.el Mon Apr 29 15:42:05 2024 +0000 @@ -7172,153 +7172,152 @@ ;; FIXME!!! This routine ignores the possibility of macros entirely. ;; 2010-01-29. - (when (> end beg) - ;; Extend the region (BEG END) to deal with any complicating literals. - (let* ((lit-search-beg (if (memq (char-before beg) '(?/ ?*)) - (1- beg) beg)) - (lit-search-end (if (memq (char-after end) '(?/ ?*)) - (1+ end) end)) - ;; Note we can't use c-full-pp-to-literal here, since we haven't - ;; yet applied syntax-table properties to ends of lines, etc. - (lit-search-beg-s (c-semi-pp-to-literal lit-search-beg)) - (beg-literal-beg (car (cddr lit-search-beg-s))) - (lit-search-end-s (c-semi-pp-to-literal lit-search-end)) - (end-literal-beg (car (cddr lit-search-end-s))) - (beg-literal-end (c-end-of-literal lit-search-beg-s lit-search-beg)) - (end-literal-end (c-end-of-literal lit-search-end-s lit-search-end)) - new-beg new-end search-region) - - ;; Determine any new end of literal resulting from the insertion/deletion. - (setq search-region - (if (and (eq beg-literal-beg end-literal-beg) - (eq beg-literal-end end-literal-end)) - (if beg-literal-beg - nil - (cons beg - (max end - (or beg-literal-end (point-min)) - (or end-literal-end (point-min))))) - (cons (or beg-literal-beg beg) - (max end - (or beg-literal-end (point-min)) - (or end-literal-end (point-min)))))) - - (when search-region - ;; If we've just inserted text, mask its syntaxes temporarily so that - ;; they won't interfere with the undoing of the properties on the s. - (c-save-buffer-state (syn-tab-settings syn-tab-value - swap-open-string-ends) - (unwind-protect - (progn - (when old-len - ;; Special case: If a \ has just been inserted into a - ;; string, escaping or unescaping a LF, temporarily swap - ;; the LF's syntax-table text property with that of the - ;; former end of the open string. - (goto-char end) - (when (and (eq (cadr lit-search-beg-s) 'string) - (not (eq beg-literal-end end-literal-end)) - (skip-chars-forward "\\\\") - (eq (char-after) ?\n) - (not (zerop (skip-chars-backward "\\\\")))) - (setq swap-open-string-ends t) - (if (c-get-char-property (1- beg-literal-end) - 'syntax-table) - (progn - (c-clear-char-property (1- beg-literal-end) - 'syntax-table) - (c-put-string-fence (1- end-literal-end))) - (c-put-string-fence (1- beg-literal-end)) - (c-clear-char-property (1- end-literal-end) - 'syntax-table))) - - ;; Save current settings of the 'syntax-table property in - ;; (BEG END), then splat these with the punctuation value. - (goto-char beg) - (while (setq syn-tab-value - (c-search-forward-non-nil-char-property - 'syntax-table end)) - (when (not (c-get-char-property (1- (point)) 'category)) - (push (cons (1- (point)) syn-tab-value) - syn-tab-settings))) - - (c-put-char-properties beg end 'syntax-table '(1)) - ;; If an open string's opener has just been neutralized, - ;; do the same to the terminating LF. - (when (and end-literal-end - (eq (char-before end-literal-end) ?\n) - (equal (c-get-char-property - (1- end-literal-end) 'syntax-table) - '(15))) - (push (cons (1- end-literal-end) '(15)) syn-tab-settings) - (c-put-char-property (1- end-literal-end) 'syntax-table - '(1)))) - - (let - ((beg-lit-start (progn (goto-char beg) (c-literal-start))) - beg-limit end-limit <>-pos) - ;; Locate the earliest < after the barrier before the - ;; changed region, which isn't already marked as a paren. - (goto-char (or beg-lit-start beg)) - (setq beg-limit (c-determine-limit 5000)) - - ;; Remove the syntax-table/category properties from each pertinent <...> - ;; pair. Firstly, the ones with the < before beg and > after beg.... - (goto-char (cdr search-region)) - (while (progn (c-syntactic-skip-backward "^;{}<" beg-limit) - (eq (char-before) ?<)) - (c-backward-token-2) - (when (eq (char-after) ?<) - (when (setq <>-pos (c-clear-<-pair-props-if-match-after - (car search-region))) - (setq new-end <>-pos)) - (setq new-beg (point)))) - - ;; ...Then the ones with < before end and > after end. - (goto-char (car search-region)) - (setq end-limit (c-determine-+ve-limit 5000)) - (while (and (c-syntactic-re-search-forward "[;{}>]" end-limit 'end) - (eq (char-before) ?>)) - (when (eq (char-before) ?>) - (if (and (looking-at c->-op-cont-regexp) - (not (eq (char-after) ?>))) - (goto-char (match-end 0)) - (when - (and (setq <>-pos - (c-clear->-pair-props-if-match-before - (cdr search-region) - (1- (point)))) - (or (not new-beg) - (< <>-pos new-beg))) - (setq new-beg <>-pos)) - (when (or (not new-end) (> (point) new-end)) - (setq new-end (point)))))))) - - (when old-len - (c-clear-char-properties beg end 'syntax-table) - (dolist (elt syn-tab-settings) - (if (cdr elt) - (c-put-char-property (car elt) 'syntax-table (cdr elt))))) - ;; Swap the '(15) syntax-table property on open string LFs back - ;; again. - (when swap-open-string-ends - (if (c-get-char-property (1- beg-literal-end) - 'syntax-table) - (progn - (c-clear-char-property (1- beg-literal-end) + ;; Extend the region (BEG END) to deal with any complicating literals. + (let* ((lit-search-beg (if (memq (char-before beg) '(?/ ?*)) + (1- beg) beg)) + (lit-search-end (if (memq (char-after end) '(?/ ?*)) + (1+ end) end)) + ;; Note we can't use c-full-pp-to-literal here, since we haven't + ;; yet applied syntax-table properties to ends of lines, etc. + (lit-search-beg-s (c-semi-pp-to-literal lit-search-beg)) + (beg-literal-beg (car (cddr lit-search-beg-s))) + (lit-search-end-s (c-semi-pp-to-literal lit-search-end)) + (end-literal-beg (car (cddr lit-search-end-s))) + (beg-literal-end (c-end-of-literal lit-search-beg-s lit-search-beg)) + (end-literal-end (c-end-of-literal lit-search-end-s lit-search-end)) + new-beg new-end search-region) + + ;; Determine any new end of literal resulting from the insertion/deletion. + (setq search-region + (if (and (eq beg-literal-beg end-literal-beg) + (eq beg-literal-end end-literal-end)) + (if beg-literal-beg + nil + (cons beg + (max end + (or beg-literal-end (point-min)) + (or end-literal-end (point-min))))) + (cons (or beg-literal-beg beg) + (max end + (or beg-literal-end (point-min)) + (or end-literal-end (point-min)))))) + + (when search-region + ;; If we've just inserted text, mask its syntaxes temporarily so that + ;; they won't interfere with the undoing of the properties on the s. + (c-save-buffer-state (syn-tab-settings syn-tab-value + swap-open-string-ends) + (unwind-protect + (progn + (when old-len + ;; Special case: If a \ has just been inserted into a + ;; string, escaping or unescaping a LF, temporarily swap + ;; the LF's syntax-table text property with that of the + ;; former end of the open string. + (goto-char end) + (when (and (eq (cadr lit-search-beg-s) 'string) + (not (eq beg-literal-end end-literal-end)) + (skip-chars-forward "\\\\") + (eq (char-after) ?\n) + (not (zerop (skip-chars-backward "\\\\")))) + (setq swap-open-string-ends t) + (if (c-get-char-property (1- beg-literal-end) 'syntax-table) - (c-put-string-fence (1- end-literal-end))) - (c-put-string-fence (1- beg-literal-end)) - (c-clear-char-property (1- end-literal-end) - 'syntax-table))))) - ;; Extend the fontification region, if needed. - (and new-beg - (< new-beg c-new-BEG) - (setq c-new-BEG new-beg)) - (and new-end - (> new-end c-new-END) - (setq c-new-END new-end)))))) + (progn + (c-clear-char-property (1- beg-literal-end) + 'syntax-table) + (c-put-string-fence (1- end-literal-end))) + (c-put-string-fence (1- beg-literal-end)) + (c-clear-char-property (1- end-literal-end) + 'syntax-table))) + + ;; Save current settings of the 'syntax-table property in + ;; (BEG END), then splat these with the punctuation value. + (goto-char beg) + (while (setq syn-tab-value + (c-search-forward-non-nil-char-property + 'syntax-table end)) + (when (not (c-get-char-property (1- (point)) 'category)) + (push (cons (1- (point)) syn-tab-value) + syn-tab-settings))) + + (c-put-char-properties beg end 'syntax-table '(1)) + ;; If an open string's opener has just been neutralized, + ;; do the same to the terminating LF. + (when (and end-literal-end + (eq (char-before end-literal-end) ?\n) + (equal (c-get-char-property + (1- end-literal-end) 'syntax-table) + '(15))) + (push (cons (1- end-literal-end) '(15)) syn-tab-settings) + (c-put-char-property (1- end-literal-end) 'syntax-table + '(1)))) + + (let + ((beg-lit-start (progn (goto-char beg) (c-literal-start))) + beg-limit end-limit <>-pos) + ;; Locate the earliest < after the barrier before the + ;; changed region, which isn't already marked as a paren. + (goto-char (or beg-lit-start beg)) + (setq beg-limit (c-determine-limit 5000)) + + ;; Remove the syntax-table/category properties from each pertinent <...> + ;; pair. Firstly, the ones with the < before beg and > after beg.... + (goto-char (cdr search-region)) + (while (progn (c-syntactic-skip-backward "^;{}<" beg-limit) + (eq (char-before) ?<)) + (c-backward-token-2) + (when (eq (char-after) ?<) + (when (setq <>-pos (c-clear-<-pair-props-if-match-after + (car search-region))) + (setq new-end <>-pos)) + (setq new-beg (point)))) + + ;; ...Then the ones with < before end and > after end. + (goto-char (car search-region)) + (setq end-limit (c-determine-+ve-limit 5000)) + (while (and (c-syntactic-re-search-forward "[;{}>]" end-limit 'end) + (eq (char-before) ?>)) + (when (eq (char-before) ?>) + (if (and (looking-at c->-op-cont-regexp) + (not (eq (char-after) ?>))) + (goto-char (match-end 0)) + (when + (and (setq <>-pos + (c-clear->-pair-props-if-match-before + (cdr search-region) + (1- (point)))) + (or (not new-beg) + (< <>-pos new-beg))) + (setq new-beg <>-pos)) + (when (or (not new-end) (> (point) new-end)) + (setq new-end (point)))))))) + + (when old-len + (c-clear-char-properties beg end 'syntax-table) + (dolist (elt syn-tab-settings) + (if (cdr elt) + (c-put-char-property (car elt) 'syntax-table (cdr elt))))) + ;; Swap the '(15) syntax-table property on open string LFs back + ;; again. + (when swap-open-string-ends + (if (c-get-char-property (1- beg-literal-end) + 'syntax-table) + (progn + (c-clear-char-property (1- beg-literal-end) + 'syntax-table) + (c-put-string-fence (1- end-literal-end))) + (c-put-string-fence (1- beg-literal-end)) + (c-clear-char-property (1- end-literal-end) + 'syntax-table))))) + ;; Extend the fontification region, if needed. + (and new-beg + (< new-beg c-new-BEG) + (setq c-new-BEG new-beg)) + (and new-end + (> new-end c-new-END) + (setq c-new-END new-end))))) (defun c-before-change-check-<>-operators (beg end) ;; When we're deleting text, unmark certain pairs of "< .... >" which are > Geza -- Alan Mackenzie (Nuremberg, Germany).