Package: cc-mode;
Reported by: Mohammed Sadiq <sadiq <at> sadiqpk.org>
Date: Sat, 25 Feb 2017 10:58:01 UTC
Severity: normal
Done: Alan Mackenzie <acm <at> muc.de>
Bug is archived. No further changes may be made.
Message #11 received at 25869 <at> debbugs.gnu.org (full text, mbox):
From: Alan Mackenzie <acm <at> muc.de> To: Mohammed Sadiq <sadiq <at> sadiqpk.org> Cc: 25869 <at> debbugs.gnu.org Subject: Re: bug#25869: CC Mode 5.32.99 (C/l); Sometimes Emacs use 100% CPU on comment-dwim for a long time Date: Sun, 9 Apr 2017 14:25:15 +0000
Hello again, Mohammed. On Sun, Mar 05, 2017 at 22:11:09 +0000, Alan Mackenzie wrote: > On Sat, Feb 25, 2017 at 16:26:58 +0530, Mohammed Sadiq wrote: > > When writing code, I encountered a bug in comment-dwim, which takes 100% > > CPU for a long time, and then failing to comment the line. > > How to reproduce using the following code: > > 1. Mark some line in the first function. > > Eg: g_type_ensure (CC_TYPE_INFO_OVERVIEW); > > do C-a C-SPC C-e > > 2. Do comment-dwim > > ie, M-; > > The code that produced the issue is the following: > > Note: if I remove the array at the bottom of code, M-; begins to work > > fine. The code follows is an excerpt from gnome-control-center. > First of all, thank you for such a detailed and complete bug report. > The bug that this code triggers has been in CC Mode for quite some > while, but it takes a rather unusual combination of constructs in the C > source to trigger it. > What is happening is that the M-; first inserts "/* ". This causes all > the code up to the next terminating "*/" to become, temporarily, a > comment. One of CC Mode's functions moves forward over this comment. A > bit later on, it has cause to move backwards over it, but only moves as > far back as the "/*" which was the original start of the second comment > (but is now merely part of the first comment). This is the bug, I > think. > This might be quite tricky to fix. Give me a little time, please! > [ snip source code ] > > Thanks > [ CC Mode configuration dump appreciated, but snipped. ] OK, I think the following patch should do the trick. Would you please apply it, recompile cc-engine.el (e.g., with $ emacs -Q -batch -f batch-byte-compile cc-engine.el ), reload it (or restart Emacs), and either confirm to me that the bug has been fixed, or tell me what's still wrong about it. Thanks! diff -r 51f7a9ff5450 cc-engine.el --- a/cc-engine.el Sat Feb 25 14:39:10 2017 +0000 +++ b/cc-engine.el Sun Apr 09 14:04:38 2017 +0000 @@ -324,34 +324,41 @@ (goto-char here) nil)))))) -(defun c-end-of-macro () +(defun c-end-of-macro (&optional lim) "Go to the end of a preprocessor directive. More accurately, move the point to the end of the closest following line that doesn't end with a line continuation backslash - no check is done that the point is inside a cpp directive to begin with. +If LIM is provided, it is a limit position at which point is left +if the end of the macro doesn't occur earlier. + Note that this function might do hidden buffer changes. See the comment at the start of cc-engine.el for more info." - (if (and (cdr c-macro-cache) - (<= (point) (cdr c-macro-cache)) - (>= (point) (car c-macro-cache))) - (goto-char (cdr c-macro-cache)) - (unless (and (car c-macro-cache) - (<= (point) c-macro-cache-start-pos) - (>= (point) (car c-macro-cache))) - (setq c-macro-cache nil - c-macro-cache-start-pos nil - c-macro-cache-syntactic nil - c-macro-cache-no-comment nil)) - (while (progn - (end-of-line) - (when (and (eq (char-before) ?\\) - (not (eobp))) - (forward-char) - t))) - (when (car c-macro-cache) - (setcdr c-macro-cache (point)) - (setq c-macro-cache-syntactic nil)))) + (save-restriction + (if lim (narrow-to-region (point-min) lim)) + (if (and (cdr c-macro-cache) + (<= (point) (cdr c-macro-cache)) + (>= (point) (car c-macro-cache))) + (goto-char (cdr c-macro-cache)) + (unless (and (car c-macro-cache) + (<= (point) c-macro-cache-start-pos) + (>= (point) (car c-macro-cache))) + (setq c-macro-cache nil + c-macro-cache-start-pos nil + c-macro-cache-syntactic nil + c-macro-cache-no-comment nil)) + (while (progn + (end-of-line) + (when (and (eq (char-before) ?\\) + (not (eobp))) + (forward-char) + t))) + (when (and (car c-macro-cache) + (bolp) + (not (eq (char-before (1- (point))) ?\\))) + (setcdr c-macro-cache (point)) + (setq c-macro-cache-syntactic nil))))) (defun c-syntactic-end-of-macro () ;; Go to the end of a CPP directive, or a "safe" pos just before. @@ -1846,13 +1853,10 @@ (let (;; `rung-pos' is set to a position as early as possible in the ;; unmarked part of the simple ws region. (rung-pos (point)) next-rung-pos rung-end-pos last-put-in-sws-pos - rung-is-marked next-rung-is-marked simple-ws-end + rung-is-marked next-rung-is-marked simple-ws-end macro-start macro-end ;; `safe-start' is set when it's safe to cache the start position. - ;; It's not set if we've initially skipped over comments and line - ;; continuations since we might have gone out through the end of a - ;; macro then. This provision makes `c-forward-sws' not populate the - ;; cache in the majority of cases, but otoh is `c-backward-sws' by far - ;; more common. + ;; This is the case except when we have an unterminated block comment + ;; within a macro. safe-start) ;; Skip simple ws and do a quick check on the following character to see @@ -1928,7 +1932,33 @@ ;; Now move over any comments (x)or a CPP construct. (setq simple-ws-end (point)) - (c-forward-comments) + (setq safe-start t) + ;; Take elaborate precautions to detect an open block comment at + ;; the end of a macro. If we find one, we set `safe-start' to nil + ;; and break off any further scanning of comments. + (let ((com-begin (point)) com-end in-macro) + (when (and (c-forward-single-comment) + (setq com-end (point)) + (save-excursion + (goto-char com-begin) + (c-beginning-of-macro))) + (setq in-macro t) + (goto-char com-begin) + (if (progn (c-end-of-macro com-end) + (< (point) com-end)) + (setq safe-start nil))) + (if in-macro + (while (and safe-start + com-end (> com-end com-begin) + (setq com-begin (point)) + (when (and (c-forward-single-comment) + (setq com-end (point))) + (goto-char com-begin) + (if (progn (c-end-of-macro com-end) + (< (point) com-end)) + (setq safe-start nil)) + safe-start))) + (c-forward-comments))) (cond ((/= (point) simple-ws-end) @@ -1939,6 +1969,7 @@ ((save-excursion (and c-opt-cpp-prefix (looking-at c-opt-cpp-start) + (setq macro-start (point)) (progn (skip-chars-backward " \t") (bolp)) (or (bobp) @@ -1949,8 +1980,20 @@ (while (and (eq (char-before) ?\\) (= (forward-line 1) 0)) (end-of-line)) + (setq macro-end (point)) + ;; Check for an open block comment at the end of the macro. + (goto-char macro-start) + (let (s in-block-comment) + (while + (progn + (setq s (parse-partial-sexp (point) macro-end + nil nil s 'syntax-table)) + (< (point) macro-end)) + (setq in-block-comment + (and (elt s 4) ; in a comment + (null (elt s 7))))) ; a block comment + (if in-block-comment (setq safe-start nil))) (forward-line 1) - (setq safe-start t) ;; Don't cache at eob in case the buffer is narrowed. (not (eobp))) @@ -1958,7 +2001,6 @@ (looking-at c-noise-macro-name-re)) ;; Skip over a noise macro. (goto-char (match-end 1)) - (setq safe-start t) (not (eobp))))) ;; We've searched over a piece of non-white syntactic ws. See if this @@ -2021,8 +2063,7 @@ (if (setq rung-is-marked next-rung-is-marked) (setq rung-pos (1- (c-next-single-property-change rung-is-marked 'c-is-sws nil rung-end-pos))) - (setq rung-pos next-rung-pos)) - (setq safe-start t))) + (setq rung-pos next-rung-pos)))) ;; Make sure that the newly marked `c-in-sws' region doesn't connect to ;; another one after the point (which might occur when editing inside a -- Alan Mackenzie (Nuremberg, Germany).
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.