From bbbb038654eab73624a5d51faa52f2d3f5db14b5 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Fri, 22 Jan 2016 22:31:23 +0000 Subject: [PATCH] Prevent spurious recognition of K&R argument declarations. Fixes bug #2203 * cc-engine.el (c-forward-declarator): New function. (c-in-knr-argdecl): Before recognizing a K&R argument declaration, check it is contained in the preceding arg list. * cc-fonts.el (c-font-lock-declarators): Use the new function `c-forward-declarator' in place of inline code. --- lisp/progmodes/cc-engine.el | 171 +++++++++++++++++++++++++++++++++--- lisp/progmodes/cc-fonts.el | 93 ++------------------ 2 files changed, 168 insertions(+), 96 deletions(-) diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index 63f01829845..8113446e4d3 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -6808,6 +6808,110 @@ comment at the start of cc-engine.el for more info." ;; This identifier is bound only in the inner let. '(setq start id-start)))) +(defun c-forward-declarator (&optional limit) + ;; Assuming point is at the start of a declarator, move forward over it, + ;; leaving point at the next token after it (e.g. a ) or a ; or a ,). + ;; + ;; Return a list (ID-START ID-END BRACKETS-AFTER-ID GOT-INIT), where ID-START and + ;; ID-END are the bounds of the declarator's identifier, and + ;; BRACKETS-AFTER-ID is non-nil if a [...] pair is present after the id. + ;; GOT-INIT is non-nil when the declarator is followed by "=" or "(". + ;; + ;; If no declarator is found, leave point unmoved and return nil. LIMIT is + ;; an optional limit for forward searching. + ;; + ;; Note that the global variable `c-last-identifier-range' is written to, so + ;; the caller should bind it if necessary. + + ;; Inside the following "condition form", we move forward over the + ;; declarator's identifier up as far as any opening bracket (for array + ;; size) or paren (for parameters of function-type) or brace (for + ;; array/struct initialization) or "=" or terminating delimiter + ;; (e.g. "," or ";" or "}"). + (let ((here (point)) + id-start id-end brackets-after-id paren-depth) + (or limit (setq limit (point-max))) + (if (and + (< (point) limit) + + ;; The following form moves forward over the declarator's + ;; identifier (and what precedes it), returning t. If there + ;; wasn't one, it returns nil. + (let (got-identifier) + (setq paren-depth 0) + ;; Skip over type decl prefix operators, one for each iteration + ;; of the while. These are, e.g. "*" in "int *foo" or "(" and + ;; "*" in "int (*foo) (void)" (Note similar code in + ;; `c-forward-decl-or-cast-1'.) + (while (and (looking-at c-type-decl-prefix-key) + (if (and (c-major-mode-is 'c++-mode) + (match-beginning 3)) + ;; If the third submatch matches in C++ then + ;; we're looking at an identifier that's a + ;; prefix only if it specifies a member pointer. + (progn + (setq id-start (point)) + (c-forward-name) + (if (looking-at "\\(::\\)") + ;; We only check for a trailing "::" and + ;; let the "*" that should follow be + ;; matched in the next round. + t + ;; It turned out to be the real identifier, + ;; so flag that and stop. + (setq got-identifier t) + nil)) + t)) + (if (eq (char-after) ?\() + (progn + (setq paren-depth (1+ paren-depth)) + (forward-char)) + (goto-char (match-end 1))) + (c-forward-syntactic-ws)) + + ;; If we haven't passed the identifier already, do it now. + (unless got-identifier + (setq id-start (point)) + (c-forward-name)) + (prog1 + (/= (point) here) + (save-excursion + (c-backward-syntactic-ws) + (setq id-end (point))))) + + ;; Skip out of the parens surrounding the identifier. If closing + ;; parens are missing, this form returns nil. + (or (= paren-depth 0) + (c-safe (goto-char (scan-lists (point) 1 paren-depth)))) + + (<= (point) limit) + + ;; Skip over any trailing bit, such as "__attribute__". + (progn + (when (looking-at c-decl-hangon-key) + (c-forward-keyword-clause 1)) + (<= (point) limit)) + + ;; Search syntactically to the end of the declarator (";", + ;; ",", a closing paren, eob etc) or to the beginning of an + ;; initializer or function prototype ("=" or "\\s\("). + ;; Note that square brackets are now not also treated as + ;; initializers, since this broke when there were also + ;; initializing brace lists. + (let (found) + (while + (and (setq found (c-syntactic-re-search-forward + "[;,]\\|\\s)\\|\\'\\|\\(=\\|\\s(\\)" limit t t)) + (eq (char-before) ?\[) + (c-go-up-list-forward)) + (setq brackets-after-id t)) + (backward-char) + found)) + (list id-start id-end brackets-after-id (match-beginning 1)) + + (goto-char here) + nil))) + (defun c-forward-decl-or-cast-1 (preceding-token-end context last-cast-end) ;; Move forward over a declaration or a cast if at the start of one. ;; The point is assumed to be at the start of some token. Nil is @@ -8162,14 +8266,14 @@ comment at the start of cc-engine.el for more info." ;; Return the position of the first argument declaration if point is ;; inside a K&R style argument declaration list, nil otherwise. ;; `c-recognize-knr-p' is not checked. If LIM is non-nil, it's a - ;; position that bounds the backward search for the argument list. + ;; position that bounds the backward search for the argument list. This + ;; function doesn't move point. ;; ;; Point must be within a possible K&R region, e.g. just before a top-level ;; "{". It must be outside of parens and brackets. The test can return ;; false positives otherwise. ;; ;; This function might do hidden buffer changes. - (save-excursion (save-restriction ;; If we're in a macro, our search range is restricted to it. Narrow to @@ -8178,8 +8282,12 @@ comment at the start of cc-engine.el for more info." (macro-end (save-excursion (and macro-start (c-end-of-macro) (point)))) (low-lim (max (or lim (point-min)) (or macro-start (point-min)))) before-lparen after-rparen - (pp-count-out 20)) ; Max number of paren/brace constructs before + (here (point)) + (pp-count-out 20) ; Max number of paren/brace constructs before ; we give up + ids ; List of identifiers in the parenthesised list. + id-start after-prec-token decl-or-cast decl-res + c-last-identifier-range identifier-ok) (narrow-to-region low-lim (or macro-end (point-max))) ;; Search backwards for the defun's argument list. We give up if we @@ -8198,8 +8306,12 @@ comment at the start of cc-engine.el for more info." ;; int foo (bar, baz, yuk) ;; int bar [] ; ;; int (*baz) (my_type) ; - ;; int (*) (void) (*yuk) (void) ; + ;; int (*(* yuk) (void)) (void) ; ;; { + ;; + ;; Additionally, for a knr list to be recognized: + ;; o - The identifier of each deeclarator up to and including the + ;; one "near" point must be contained in the arg list. (catch 'knr (while (> pp-count-out 0) ; go back one paren/bracket pair each time. @@ -8245,21 +8357,58 @@ comment at the start of cc-engine.el for more info." (goto-char before-lparen) (c-forward-token-2) ; to first token inside parens (and - (c-on-identifier) - (c-forward-token-2) + (setq id-start (c-on-identifier)) ; Must be at least one. (catch 'id-list - (while (eq (char-after) ?\,) + (while + (progn + (forward-char) + (c-end-of-current-token) + (push (buffer-substring-no-properties id-start + (point)) + ids) + (c-forward-syntactic-ws) + (eq (char-after) ?\,)) (c-forward-token-2) - (unless (c-on-identifier) (throw 'id-list nil)) - (c-forward-token-2)) - (eq (char-after) ?\)))))) + (unless (setq id-start (c-on-identifier)) + (throw 'id-list nil))) + (eq (char-after) ?\))))) + ;; Are all the identifiers in the k&r list up to the + ;; current one also in the argument list? + (progn + (forward-char) ; over the ) + (setq after-prec-token after-rparen) + (c-forward-syntactic-ws) + (while (and + (or (consp (setq decl-or-cast + (c-forward-decl-or-cast-1 + after-prec-token + nil ; Or 'arglist ??? + nil))) + (progn + (goto-char after-prec-token) + (c-forward-syntactic-ws) + (setq identifier-ok (eq (char-after) ?{)) + nil)) + (eq (char-after) ?\;) + (setq after-prec-token (1+ (point))) + (goto-char (car decl-or-cast)) + (setq decl-res (c-forward-declarator)) + (setq identifier-ok + (member (buffer-substring-no-properties + (car decl-res) (cadr decl-res)) + ids)) + (progn + (goto-char after-prec-token) + (prog1 (< (point) here) + (c-forward-syntactic-ws)))) + (setq identifier-ok nil)) + identifier-ok)) ;; ...Yes. We've identified the function's argument list. (throw 'knr (progn (goto-char after-rparen) (c-forward-syntactic-ws) (point))) - ;; ...No. The current parens aren't the function's arg list. (goto-char before-lparen)) diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el index 03e67a99515..1e101d12aac 100644 --- a/lisp/progmodes/cc-fonts.el +++ b/lisp/progmodes/cc-fonts.el @@ -1006,6 +1006,7 @@ casts and declarations are fontified. Used on level 2 and higher." ;;(message "c-font-lock-declarators from %s to %s" (point) limit) (c-fontify-types-and-refs ((pos (point)) next-pos id-start id-end + decl-res paren-depth id-face got-init c-last-identifier-range @@ -1015,93 +1016,15 @@ casts and declarations are fontified. Used on level 2 and higher." ;; The following `while' fontifies a single declarator id each time round. ;; It loops only when LIST is non-nil. (while - ;; Inside the following "condition form", we move forward over the - ;; declarator's identifier up as far as any opening bracket (for array - ;; size) or paren (for parameters of function-type) or brace (for - ;; array/struct initialization) or "=" or terminating delimiter - ;; (e.g. "," or ";" or "}"). - (and - pos - (< (point) limit) - - ;; The following form moves forward over the declarator's - ;; identifier (and what precedes it), returning t. If there - ;; wasn't one, it returns nil, terminating the `while'. - (let (got-identifier) - (setq paren-depth 0) - ;; Skip over type decl prefix operators, one for each iteration - ;; of the while. These are, e.g. "*" in "int *foo" or "(" and - ;; "*" in "int (*foo) (void)" (Note similar code in - ;; `c-forward-decl-or-cast-1'.) - (while (and (looking-at c-type-decl-prefix-key) - (if (and (c-major-mode-is 'c++-mode) - (match-beginning 3)) - ;; If the third submatch matches in C++ then - ;; we're looking at an identifier that's a - ;; prefix only if it specifies a member pointer. - (progn - (setq id-start (point)) - (c-forward-name) - (if (looking-at "\\(::\\)") - ;; We only check for a trailing "::" and - ;; let the "*" that should follow be - ;; matched in the next round. - t - ;; It turned out to be the real identifier, - ;; so flag that and stop. - (setq got-identifier t) - nil)) - t)) - (if (eq (char-after) ?\() - (progn - (setq paren-depth (1+ paren-depth)) - (forward-char)) - (goto-char (match-end 1))) - (c-forward-syntactic-ws)) - - ;; If we haven't passed the identifier already, do it now. - (unless got-identifier - (setq id-start (point)) - (c-forward-name)) - (setq id-end (point)) - - (/= id-end pos)) - - ;; Skip out of the parens surrounding the identifier. If closing - ;; parens are missing, this form returns nil. - (or (= paren-depth 0) - (c-safe (goto-char (scan-lists (point) 1 paren-depth)))) - - (<= (point) limit) - - ;; Skip over any trailing bit, such as "__attribute__". - (progn - (when (looking-at c-decl-hangon-key) - (c-forward-keyword-clause 1)) - (<= (point) limit)) - - ;; Search syntactically to the end of the declarator (";", - ;; ",", a closing paren, eob etc) or to the beginning of an - ;; initializer or function prototype ("=" or "\\s("). - ;; Note that square brackets are now not also treated as - ;; initializers, since this broke when there were also - ;; initializing brace lists. - (let (found) - (while - (and (setq found (c-syntactic-re-search-forward - "[;,]\\|\\s)\\|\\'\\|\\(=\\|\\s(\\)" limit t t)) - (eq (char-before) ?\[) - (c-go-up-list-forward)) - (setq brackets-after-id t)) - found)) - - (setq next-pos (match-beginning 0) - id-face (if (and (eq (char-after next-pos) ?\() - (not brackets-after-id)) + (and pos (setq decl-res (c-forward-declarator limit))) + (setq next-pos (point) + id-start (car decl-res) + id-face (if (and (eq (char-after) ?\() + (not (car (cddr decl-res)))) ; brackets-after-id 'font-lock-function-name-face 'font-lock-variable-name-face) - got-init (and (match-beginning 1) - (char-after (match-beginning 1)))) + got-init (and (cadr (cddr decl-res)) ; got-init + (char-after))) (if types ;; Register and fontify the identifier as a type. -- 2.39.5