branch: elpa/d-mode
commit 627a6f8f63ac9874114e773b76b7aa5bf4e8522c
Author: Vladimir Panteleev <[email protected]>
Commit: Vladimir Panteleev <[email protected]>

    Move c-forward-decl-or-cast-1 into d-mode.el
    
    c-forward-decl-or-cast-1 is very complicated and tries to do a lot of
    things at once. Due to this, we are reaching the limit of what we can
    do with only non-invasive patching.
    
    Maintaining a copy (as d-forward-decl-or-cast-1) will allow us to
    implement support for D syntax which does not align with the logic in
    cc-mode's implementation, remove code for complicated rules which are
    not relevant to D syntax, and consolidate the ad-hoc patches
    previously applied using advice.
---
 d-mode.el | 1059 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 1056 insertions(+), 3 deletions(-)

diff --git a/d-mode.el b/d-mode.el
index 44958fb..a1d974b 100644
--- a/d-mode.el
+++ b/d-mode.el
@@ -7,7 +7,7 @@
 ;; Maintainer:  Russel Winder <[email protected]>
 ;;              Vladimir Panteleev <[email protected]>
 ;; Created:  March 2007
-;; Version:  201911071935
+;; Version:  201911081922
 ;; Keywords:  D programming language emacs cc-mode
 ;; Package-Requires: ((emacs "25.1"))
 
@@ -465,6 +465,1059 @@ operators."
 
 (defsubst d-forward-name () "Shorthand." (d-special-case-c-forward-name 
#'c-forward-name))
 
+;;----------------------------------------------------------------------------
+
+(defun d-forward-decl-or-cast-1 (preceding-token-end context last-cast-end)
+  "Modified version of `c-forward-decl-or-cast-1' for d-mode." ;; 
checkdoc-params: (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
+  ;; returned if no declaration or cast is recognized, and the point
+  ;; is clobbered in that case.
+  ;;
+  ;; If a declaration is parsed:
+  ;;
+  ;;   The point is left at the first token after the first complete
+  ;;   declarator, if there is one.  The return value is a list of 5 elements,
+  ;;   where the first is the position of the first token in the declarator.
+  ;;   (See below for the other four.)
+  ;;   Some examples:
+  ;;
+  ;;    void foo (int a, char *b) stuff ...
+  ;;     car ^                    ^ point
+  ;;    float (*a)[], b;
+  ;;      car ^     ^ point
+  ;;    unsigned int a = c_style_initializer, b;
+  ;;             car ^ ^ point
+  ;;    unsigned int a (cplusplus_style_initializer), b;
+  ;;             car ^                              ^ point (might change)
+  ;;    class Foo : public Bar {}
+  ;;      car ^   ^ point
+  ;;    class PikeClass (int a, string b) stuff ...
+  ;;      car ^                           ^ point
+  ;;    enum bool;
+  ;;     car ^   ^ point
+  ;;    enum bool flag;
+  ;;          car ^   ^ point
+  ;;     void cplusplus_function (int x) throw (Bad);
+  ;;      car ^                                     ^ point
+  ;;     Foo::Foo (int b) : Base (b) {}
+  ;; car ^                ^ point
+  ;;
+  ;;     auto foo = 5;
+  ;;      car ^   ^ point
+  ;;     auto cplusplus_11 (int a, char *b) -> decltype (bar):
+  ;;      car ^                             ^ point
+  ;;
+  ;;
+  ;;
+  ;;   The second element of the return value is non-nil when a
+  ;;   `c-typedef-decl-kwds' specifier is found in the declaration.
+  ;;   Specifically it is a dotted pair (A . B) where B is t when a
+  ;;   `c-typedef-kwds' ("typedef") is present, and A is t when some
+  ;;   other `c-typedef-decl-kwds' (e.g. class, struct, enum)
+  ;;   specifier is present.  I.e., (some of) the declared
+  ;;   identifier(s) are types.
+  ;;
+  ;;   The third element of the return value is non-nil when the declaration
+  ;;   parsed might be an expression.  The fourth element is the position of
+  ;;   the start of the type identifier.  The fifth element is t if either
+  ;;   CONTEXT was 'top, or the declaration is detected to be treated as top
+  ;;   level (e.g. with the keyword "extern").
+  ;;
+  ;; If a cast is parsed:
+  ;;
+  ;;   The point is left at the first token after the closing paren of
+  ;;   the cast.  The return value is `cast'.  Note that the start
+  ;;   position must be at the first token inside the cast parenthesis
+  ;;   to recognize it.
+  ;;
+  ;; PRECEDING-TOKEN-END is the first position after the preceding
+  ;; token, i.e. on the other side of the syntactic ws from the point.
+  ;; Use a value less than or equal to (point-min) if the point is at
+  ;; the first token in (the visible part of) the buffer.
+  ;;
+  ;; CONTEXT is a symbol that describes the context at the point:
+  ;; 'decl     In a comma-separated declaration context (typically
+  ;;           inside a function declaration arglist).
+  ;; '<>       In an angle bracket arglist.
+  ;; 'arglist  Some other type of arglist.
+  ;; 'top      Some other context and point is at the top-level (either
+  ;;           outside any braces or directly inside a class or namespace,
+  ;;           etc.)
+  ;; nil       Some other context or unknown context.  Includes
+  ;;           within the parens of an if, for, ... construct.
+  ;; 'not-decl This value is never supplied to this function.  It
+  ;;           would mean we're definitely not in a declaration.
+  ;;
+  ;; LAST-CAST-END is the first token after the closing paren of a
+  ;; preceding cast, or nil if none is known.  If
+  ;; `c-forward-decl-or-cast-1' is used in succession, it should be
+  ;; the position after the closest preceding call where a cast was
+  ;; matched.  In that case it's used to discover chains of casts like
+  ;; "(a) (b) c".
+  ;;
+  ;; This function records identifier ranges on
+  ;; `c-record-type-identifiers' and `c-record-ref-identifiers' if
+  ;; `c-record-type-identifiers' is non-nil.
+  ;;
+  ;; This function might do hidden buffer changes.
+
+  (let (;; `start-pos' is used below to point to the start of the
+       ;; first type, i.e. after any leading specifiers.  It might
+       ;; also point at the beginning of the preceding syntactic
+       ;; whitespace.
+       (start-pos (point))
+       ;; Set to the result of `c-forward-type'.
+       at-type
+       ;; The position of the first token in what we currently
+       ;; believe is the type in the declaration or cast, after any
+       ;; specifiers and their associated clauses.
+       type-start
+       ;; The position of the first token in what we currently
+       ;; believe is the declarator for the first identifier.  Set
+       ;; when the type is found, and moved forward over any
+       ;; `c-decl-hangon-kwds' and their associated clauses that
+       ;; occurs after the type.
+       id-start
+       ;; These store `at-type', `type-start' and `id-start' of the
+       ;; identifier before the one in those variables.  The previous
+       ;; identifier might turn out to be the real type in a
+       ;; declaration if the last one has to be the declarator in it.
+       ;; If `backup-at-type' is nil then the other variables have
+       ;; undefined values.
+       backup-at-type backup-type-start backup-id-start
+       ;; Set if we've found a specifier (apart from "typedef") that makes
+       ;; the defined identifier(s) types.
+       at-type-decl
+       ;; Set if we've a "typedef" keyword.
+       at-typedef
+       ;; Set if we've found a specifier that can start a declaration
+       ;; where there's no type.
+       maybe-typeless
+       ;; Save the value of kwd-sym between loops of the "Check for a
+       ;; type" loop.  Needed to distinguish a C++11 "auto" from a pre
+       ;; C++11 one.
+       prev-kwd-sym
+       ;; If a specifier is found that also can be a type prefix,
+       ;; these flags are set instead of those above.  If we need to
+       ;; back up an identifier, they are copied to the real flag
+       ;; variables.  Thus they only take effect if we fail to
+       ;; interpret it as a type.
+       backup-at-type-decl backup-maybe-typeless
+       ;; Whether we've found a declaration or a cast.  We might know
+       ;; this before we've found the type in it.  It's 'ids if we've
+       ;; found two consecutive identifiers (usually a sure sign, but
+       ;; we should allow that in labels too), and t if we've found a
+       ;; specifier keyword (a 100% sure sign).
+       at-decl-or-cast
+       ;; Set when we need to back up to parse this as a declaration
+       ;; but not as a cast.
+       backup-if-not-cast
+       ;; For casts, the return position.
+       cast-end
+       ;; Have we got a new-style C++11 "auto"?
+       new-style-auto
+       ;; Set when the symbol before `preceding-token-end' is known to
+       ;; terminate the previous construct, or when we're at point-min.
+       at-decl-start
+       ;; Set when we have encountered a keyword (e.g. "extern") which
+       ;; causes the following declaration to be treated as though top-level.
+       make-top
+       ;; Save `c-record-type-identifiers' and
+       ;; `c-record-ref-identifiers' since ranges are recorded
+       ;; speculatively and should be thrown away if it turns out
+       ;; that it isn't a declaration or cast.
+       (save-rec-type-ids c-record-type-identifiers)
+       (save-rec-ref-ids c-record-ref-identifiers)
+       ;; Set when we parse a declaration which might also be an expression,
+       ;; such as "a *b".  See CASE 16 and CASE 17.
+       maybe-expression)
+
+    (save-excursion
+      (goto-char preceding-token-end)
+      (setq at-decl-start
+           (or (bobp)
+               (let ((tok-end (point)))
+                 (c-backward-token-2)
+                 (member (buffer-substring-no-properties (point) tok-end)
+                         c-pre-start-tokens)))))
+
+    (while (c-forward-annotation)
+      (c-forward-syntactic-ws))
+
+    ;; Check for a type.  Unknown symbols are treated as possible
+    ;; types, but they could also be specifiers disguised through
+    ;; macros like __INLINE__, so we recognize both types and known
+    ;; specifiers after them too.
+    (while
+       (let* ((start (point)) kwd-sym kwd-clause-end found-type noise-start)
+
+         (cond
+         ;; Look for a specifier keyword clause.
+          ((or (and (looking-at c-make-top-level-key)
+                    (setq make-top t))
+               (looking-at c-prefix-spec-kwds-re)
+               (and (c-major-mode-is 'java-mode)
+                (looking-at "@[A-Za-z0-9]+")))
+           (save-match-data
+             (if (looking-at c-typedef-key)
+                 (setq at-typedef t)))
+           (setq kwd-sym (c-keyword-sym (match-string 1)))
+           (save-excursion
+             (c-forward-keyword-clause 1)
+             (setq kwd-clause-end (point))))
+          ((and c-opt-cpp-prefix
+                (looking-at c-noise-macro-with-parens-name-re))
+           (setq noise-start (point))
+           (c-forward-noise-clause)
+           (setq kwd-clause-end (point))))
+
+         (when (setq found-type (c-forward-type t)) ; brace-block-too
+           ;; Found a known or possible type or a prefix of a known type.
+           (when (and (c-major-mode-is 'c++-mode) ; C++11 style "auto"?
+                      (eq prev-kwd-sym (c-keyword-sym "auto"))
+                      (looking-at "[=(]")) ; FIXME!!! proper regexp.
+             (setq new-style-auto t)
+             (setq found-type nil)
+             (goto-char start))        ; position of foo in "auto foo"
+
+           (when at-type
+             ;; Got two identifiers with nothing but whitespace
+             ;; between them.  That can only happen in declarations.
+             (setq at-decl-or-cast 'ids)
+
+             (when (eq at-type 'found)
+               ;; If the previous identifier is a found type we
+               ;; record it as a real one; it might be some sort of
+               ;; alias for a prefix like "unsigned".
+               (save-excursion
+                 (goto-char type-start)
+                 (let ((c-promote-possible-types t))
+                   (c-forward-type)))))
+
+           (setq backup-at-type at-type
+                 backup-type-start type-start
+                 backup-id-start id-start
+                 at-type found-type
+                 type-start start
+                 id-start (point)
+                 ;; The previous ambiguous specifier/type turned out
+                 ;; to be a type since we've parsed another one after
+                 ;; it, so clear these backup flags.
+                 backup-at-type-decl nil
+                 backup-maybe-typeless nil))
+
+         (if (or kwd-sym noise-start)
+             (progn
+               ;; Handle known specifier keywords and
+               ;; `c-decl-hangon-kwds' which can occur after known
+               ;; types.
+
+               (if (or (c-keyword-member kwd-sym 'c-decl-hangon-kwds)
+                       noise-start)
+                   ;; It's a hang-on keyword or noise clause that can occur
+                   ;; anywhere.
+                   (progn
+                     (if at-type
+                         ;; Move the identifier start position if
+                         ;; we've passed a type.
+                         (setq id-start kwd-clause-end)
+                       ;; Otherwise treat this as a specifier and
+                       ;; move the fallback position.
+                       (setq start-pos kwd-clause-end))
+                     (goto-char kwd-clause-end))
+
+                 ;; It's an ordinary specifier so we know that
+                 ;; anything before this can't be the type.
+                 (setq backup-at-type nil
+                       start-pos kwd-clause-end)
+
+                 (if found-type
+                     ;; It's ambiguous whether this keyword is a
+                     ;; specifier or a type prefix, so set the backup
+                     ;; flags.  (It's assumed that `c-forward-type'
+                     ;; moved further than `c-forward-keyword-clause'.)
+                     (progn
+                       (when (c-keyword-member kwd-sym 'c-typedef-decl-kwds)
+                         (setq backup-at-type-decl t))
+                       (when (c-keyword-member kwd-sym 'c-typeless-decl-kwds)
+                         (setq backup-maybe-typeless t)))
+
+                   (when (c-keyword-member kwd-sym 'c-typedef-decl-kwds)
+                     ;; This test only happens after we've scanned a type.
+                     ;; So, with valid syntax, kwd-sym can't be 'typedef.
+                     (setq at-type-decl t))
+                   (when (c-keyword-member kwd-sym 'c-typeless-decl-kwds)
+                     (setq maybe-typeless t))
+
+                   ;; Haven't matched a type so it's an unambiguous
+                   ;; specifier keyword and we know we're in a
+                   ;; declaration.
+                   (setq at-decl-or-cast t)
+                   (setq prev-kwd-sym kwd-sym)
+
+                   (goto-char kwd-clause-end))))
+
+           ;; If the type isn't known we continue so that we'll jump
+           ;; over all specifiers and type identifiers.  The reason
+           ;; to do this for a known type prefix is to make things
+           ;; like "unsigned INT16" work.
+           (and found-type (not (eq found-type t))))))
+
+    (cond
+     ((eq at-type t)
+      ;; If a known type was found, we still need to skip over any
+      ;; hangon keyword clauses after it.  Otherwise it has already
+      ;; been done in the loop above.
+      (while
+         (cond ((looking-at c-decl-hangon-key)
+                (c-forward-keyword-clause 1))
+               ((and c-opt-cpp-prefix
+                     (looking-at c-noise-macro-with-parens-name-re))
+                (c-forward-noise-clause))))
+      (setq id-start (point)))
+
+     ((eq at-type 'prefix)
+      ;; A prefix type is itself a primitive type when it's not
+      ;; followed by another type.
+      (setq at-type t))
+
+     ((not at-type)
+      ;; Got no type but set things up to continue anyway to handle
+      ;; the various cases when a declaration doesn't start with a
+      ;; type.
+      (setq id-start start-pos))
+
+     ((and (eq at-type 'maybe)
+          (c-major-mode-is 'c++-mode))
+      ;; If it's C++ then check if the last "type" ends on the form
+      ;; "foo::foo" or "foo::~foo", i.e. if it's the name of a
+      ;; (con|de)structor.
+      (save-excursion
+       (let (name end-2 end-1)
+         (goto-char id-start)
+         (c-backward-syntactic-ws)
+         (setq end-2 (point))
+         (when (and
+                (c-simple-skip-symbol-backward)
+                (progn
+                  (setq name
+                        (buffer-substring-no-properties (point) end-2))
+                  ;; Cheating in the handling of syntactic ws below.
+                  (< (skip-chars-backward ":~ \t\n\r\v\f") 0))
+                (progn
+                  (setq end-1 (point))
+                  (c-simple-skip-symbol-backward))
+                (>= (point) type-start)
+                (equal (buffer-substring-no-properties (point) end-1)
+                       name)
+                (goto-char end-2)
+                (progn
+                  (c-forward-syntactic-ws)
+                  (eq (char-after) ?\()))
+           ;; It is a (con|de)structor name.  In that case the
+           ;; declaration is typeless so zap out any preceding
+           ;; identifier(s) that we might have taken as types.
+           (goto-char type-start)
+           (setq at-type nil
+                 backup-at-type nil
+                 id-start type-start))))))
+
+    ;; Check for and step over a type decl expression after the thing
+    ;; that is or might be a type.  This can't be skipped since we
+    ;; need the correct end position of the declarator for
+    ;; `max-type-decl-end-*'.
+    (let ((start (point)) (paren-depth 0) pos
+         ;; True if there's a non-open-paren match of
+         ;; `c-type-decl-prefix-key'.
+         got-prefix
+         ;; True if the declarator is surrounded by a parenthesis pair.
+         got-parens
+         ;; True if there is an identifier in the declarator.
+         got-identifier
+         ;; True if we find a number where an identifier was expected.
+         got-number
+         ;; True if there's a non-close-paren match of
+         ;; `c-type-decl-suffix-key'.
+         got-suffix
+         ;; True if there's a prefix match outside the outermost
+         ;; paren pair that surrounds the declarator.
+         got-prefix-before-parens
+         ;; True if there's a suffix match outside the outermost
+         ;; paren pair that surrounds the declarator.  The value is
+         ;; the position of the first suffix match.
+         got-suffix-after-parens
+         ;; True if we've parsed the type decl to a token that is
+         ;; known to end declarations in this context.
+         at-decl-end
+         ;; The earlier values of `at-type' and `type-start' if we've
+         ;; shifted the type backwards.
+         identifier-type identifier-start
+         ;; If `c-parse-and-markup-<>-arglists' is set we need to
+         ;; turn it off during the name skipping below to avoid
+         ;; getting `c-type' properties that might be bogus.  That
+         ;; can happen since we don't know if
+         ;; `c-restricted-<>-arglists' will be correct inside the
+         ;; arglist paren that gets entered.
+         c-parse-and-markup-<>-arglists
+         ;; Start of the identifier for which `got-identifier' was set.
+         name-start
+         ;; Position after (innermost) open parenthesis encountered in the
+         ;; prefix operators.
+         after-paren-pos)
+
+      (goto-char id-start)
+
+      ;; Skip over type decl prefix operators.  (Note similar code in
+      ;; `c-forward-declarator'.)
+      (if (and c-recognize-typeless-decls
+              (equal c-type-decl-prefix-key "\\<\\>"))
+         (when (eq (char-after) ?\()
+           (progn
+             (setq paren-depth (1+ paren-depth))
+             (forward-char)
+             (setq after-paren-pos (point))))
+       (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.
+                       (when (progn (setq pos (point))
+                                    (setq got-identifier (c-forward-name)))
+                         (setq name-start pos)
+                         (if (looking-at "\\(::\\)")
+                             ;; We only check for a trailing "::" and
+                             ;; let the "*" that should follow be
+                             ;; matched in the next round.
+                             (progn (setq got-identifier nil) t)
+                           ;; It turned out to be the real identifier,
+                           ;; so stop.
+                           nil))
+                     t))
+
+         (if (eq (char-after) ?\()
+             (progn
+               (setq paren-depth (1+ paren-depth))
+               (forward-char)
+               (setq after-paren-pos (point)))
+           (unless got-prefix-before-parens
+             (setq got-prefix-before-parens (= paren-depth 0)))
+           (setq got-prefix t)
+           (goto-char (match-end 1)))
+         (c-forward-syntactic-ws)))
+
+      (setq got-parens (> paren-depth 0))
+
+      ;; Try to skip over an identifier.
+      (or got-identifier
+         (and (looking-at c-identifier-start)
+              (setq pos (point))
+              (setq got-identifier (c-forward-name))
+              (setq name-start pos))
+         (when (looking-at "[0-9]")
+           (setq got-number t))) ; We've probably got an arithmetic expression.
+
+      ;; Skip over type decl suffix operators and trailing noise macros.
+      (while
+         (cond
+          ((and c-opt-cpp-prefix
+                (looking-at c-noise-macro-with-parens-name-re))
+           (c-forward-noise-clause))
+
+          ((and (looking-at c-type-decl-suffix-key)
+                ;; We avoid recognizing foo(bar) or foo() at top level as a
+                ;; construct here in C, since we want to recognize this as a
+                ;; typeless function declaration.
+                (not (and (c-major-mode-is 'c-mode)
+                          (or (eq context 'top) make-top)
+                          (eq (char-after) ?\)))))
+           (if (eq (char-after) ?\))
+               (when (> paren-depth 0)
+                 (setq paren-depth (1- paren-depth))
+                 (forward-char)
+                 t)
+             (when (if (save-match-data (looking-at "\\s("))
+                       (c-safe (c-forward-sexp 1) t)
+                     (goto-char (match-end 1))
+                     t)
+               (when (and (not got-suffix-after-parens)
+                          (= paren-depth 0))
+                 (setq got-suffix-after-parens (match-beginning 0)))
+               (setq got-suffix t))))
+
+          (t
+           ;; No suffix matched.  We might have matched the
+           ;; identifier as a type and the open paren of a
+           ;; function arglist as a type decl prefix.  In that
+           ;; case we should "backtrack": Reinterpret the last
+           ;; type as the identifier, move out of the arglist and
+           ;; continue searching for suffix operators.
+           ;;
+           ;; Do this even if there's no preceding type, to cope
+           ;; with old style function declarations in K&R C,
+           ;; (con|de)structors in C++ and `c-typeless-decl-kwds'
+           ;; style declarations.  That isn't applicable in an
+           ;; arglist context, though.
+           (when (and (= paren-depth 1)
+                         (not got-prefix-before-parens)
+                         (not (eq at-type t))
+                         (or backup-at-type
+                             maybe-typeless
+                             backup-maybe-typeless
+                             (when c-recognize-typeless-decls
+                               (and (memq context '(nil top))
+                                    ;; Deal with C++11's "copy-initialization"
+                                    ;; where we have <type>(<constant>), by
+                                    ;; contrasting with a typeless
+                                    ;; <name>(<type><parameter>, ...).
+                                    (save-excursion
+                                      (goto-char after-paren-pos)
+                                      (c-forward-syntactic-ws)
+                                      (or (c-forward-type)
+                                          ;; Recognize a top-level typeless
+                                          ;; function declaration in C.
+                                          (and (c-major-mode-is 'c-mode)
+                                               (or (eq context 'top) make-top)
+                                               (eq (char-after) ?\))))))))
+                         (setq pos (c-up-list-forward (point)))
+                         (eq (char-before pos) ?\)))
+                (c-fdoc-shift-type-backward)
+                (goto-char pos)
+                t)))
+
+       (c-forward-syntactic-ws))
+
+      (when (or (and new-style-auto
+                    (looking-at c-auto-ops-re))
+               (and (or maybe-typeless backup-maybe-typeless)
+                    (not got-identifier)
+                    (not got-prefix)
+                    at-type))
+       ;; Have found no identifier but `c-typeless-decl-kwds' has
+       ;; matched so we know we're inside a declaration.  The
+       ;; preceding type must be the identifier instead.
+       (c-fdoc-shift-type-backward))
+
+      ;; Prepare the "-> type;" for fontification later on.
+      (when (and new-style-auto
+                (looking-at c-haskell-op-re))
+       (save-excursion
+         (goto-char (match-end 0))
+         (c-forward-syntactic-ws)
+         (setq type-start (point))
+         (setq at-type (c-forward-type))))
+
+      ;; Move forward over any "WS" ids (like "final" or "override" in C++)
+      (while (looking-at c-type-decl-suffix-ws-ids-key)
+       (goto-char (match-end 1))
+       (c-forward-syntactic-ws))
+
+      (setq
+       at-decl-or-cast
+       (catch 'at-decl-or-cast
+
+        ;; CASE 1
+        (when (> paren-depth 0)
+          ;; Encountered something inside parens that isn't matched by
+          ;; the `c-type-decl-*' regexps, so it's not a type decl
+          ;; expression.  Try to skip out to the same paren depth to
+          ;; not confuse the cast check below.  If we don't manage this and
+          ;; `at-decl-or-cast' is 'ids we might have an expression like
+          ;; "foo bar ({ ..." which is a valid C++11 initialization.
+          (if (and (not (c-safe (goto-char (scan-lists (point) 1 
paren-depth))))
+                   (eq at-decl-or-cast 'ids))
+              (c-fdoc-shift-type-backward))
+          ;; If we've found a specifier keyword then it's a
+          ;; declaration regardless.
+          (throw 'at-decl-or-cast (memq at-decl-or-cast '(t ids))))
+
+        (setq at-decl-end
+              (looking-at (cond ((eq context '<>) "[,>]")
+                                ((not (memq context '(nil top))) "[,\)]")
+                                (t "[,;]"))))
+
+        ;; Now we've collected info about various characteristics of
+        ;; the construct we're looking at.  Below follows a decision
+        ;; tree based on that.  It's ordered to check more certain
+        ;; signs before less certain ones.
+
+        (if got-identifier
+            (progn
+
+              ;; CASE 2
+              (when (and (or at-type maybe-typeless)
+                         (not (or got-prefix got-parens)))
+                ;; Got another identifier directly after the type, so it's a
+                ;; declaration.
+                (throw 'at-decl-or-cast t))
+
+              (when (and got-parens
+                         (not got-prefix)
+                         ;; (not got-suffix-after-parens)
+                         (or backup-at-type
+                             maybe-typeless
+                             backup-maybe-typeless
+                             (eq at-decl-or-cast t)
+                             ;; Check whether we have "bar (gnu);" where we
+                             ;; are directly inside a class (etc.) called 
"bar".
+                             (save-excursion
+                               (and
+                                (progn
+                                  (goto-char name-start)
+                                  (not (memq (c-forward-type) '(nil maybe))))
+                                (progn
+                                 (goto-char id-start)
+                                 (c-directly-in-class-called-p
+                                  (buffer-substring
+                                   type-start
+                                   (progn
+                                     (goto-char type-start)
+                                     (c-forward-type)
+                                     (c-backward-syntactic-ws)
+                                     (point)))))))))
+                ;; Got a declaration of the form "foo bar (gnu);" or "bar
+                ;; (gnu);" where we've recognized "bar" as the type and "gnu"
+                ;; as the declarator, and in the latter case, checked that
+                ;; "bar (gnu)" appears directly inside the class "bar".  In
+                ;; this case it's however more likely that "bar" is the
+                ;; declarator and "gnu" a function argument or initializer
+                ;; (if `c-recognize-paren-inits' is set), since the parens
+                ;; around "gnu" would be superfluous if it's a declarator.
+                ;; Shift the type one step backward.
+                (c-fdoc-shift-type-backward)))
+
+          ;; Found no identifier.
+
+          (if backup-at-type
+              (progn
+
+                ;; CASE 3
+                (when (= (point) start)
+                  ;; Got a plain list of identifiers. If a colon follows it's
+                  ;; a valid label, or maybe a bitfield.  Otherwise the last
+                  ;; one probably is the declared identifier and we should
+                  ;; back up to the previous type, providing it isn't a cast.
+                  (if (and (eq (char-after) ?:)
+                           (not (c-major-mode-is 'java-mode)))
+                      (cond
+                       ;; If we've found a specifier keyword then it's a
+                       ;; declaration regardless.
+                       ((eq at-decl-or-cast t)
+                        (throw 'at-decl-or-cast t))
+                       ((and c-has-bitfields
+                             (eq at-decl-or-cast 'ids)) ; bitfield.
+                        (setq backup-if-not-cast t)
+                        (throw 'at-decl-or-cast t)))
+
+                    (setq backup-if-not-cast t)
+                    (throw 'at-decl-or-cast t)))
+
+                ;; CASE 4
+                (when (and got-suffix
+                           (not got-prefix)
+                           (not got-parens))
+                  ;; Got a plain list of identifiers followed by some suffix.
+                  ;; If this isn't a cast then the last identifier probably is
+                  ;; the declared one and we should back up to the previous
+                  ;; type.
+                  (setq backup-if-not-cast t)
+                  (throw 'at-decl-or-cast t)))
+
+            ;; CASE 5
+            (when (eq at-type t)
+              ;; If the type is known we know that there can't be any
+              ;; identifier somewhere else, and it's only in declarations in
+              ;; e.g. function prototypes and in casts that the identifier may
+              ;; be left out.
+              (throw 'at-decl-or-cast t))
+
+            (when (= (point) start)
+              ;; Only got a single identifier (parsed as a type so far).
+              ;; CASE 6
+              (if (and
+                   ;; Check that the identifier isn't at the start of an
+                   ;; expression.
+                   at-decl-end
+                   (cond
+                    ((eq context 'decl)
+                     ;; Inside an arglist that contains declarations.  If K&R
+                     ;; style declarations and parenthesis style initializers
+                     ;; aren't allowed then the single identifier must be a
+                     ;; type, else we require that it's known or found
+                     ;; (primitive types are handled above).
+                     (or (and (not c-recognize-knr-p)
+                              (not c-recognize-paren-inits))
+                         (memq at-type '(known found))))
+                    ((eq context '<>)
+                     ;; Inside a template arglist.  Accept known and found
+                     ;; types; other identifiers could just as well be
+                     ;; constants in C++.
+                     (memq at-type '(known found)))))
+                  (throw 'at-decl-or-cast t)
+                ;; CASE 7
+                ;; Can't be a valid declaration or cast, but if we've found a
+                ;; specifier it can't be anything else either, so treat it as
+                ;; an invalid/unfinished declaration or cast.
+                (throw 'at-decl-or-cast at-decl-or-cast))))
+
+          (if (and got-parens
+                   (not got-prefix)
+                   (memq context '(nil top))
+                   (not (eq at-type t))
+                   (or backup-at-type
+                       maybe-typeless
+                       backup-maybe-typeless
+                       (when c-recognize-typeless-decls
+                         (or (not got-suffix)
+                             (not (looking-at
+                                   c-after-suffixed-type-maybe-decl-key))))))
+              ;; Got an empty paren pair and a preceding type that probably
+              ;; really is the identifier.  Shift the type backwards to make
+              ;; the last one the identifier.  This is analogous to the
+              ;; "backtracking" done inside the `c-type-decl-suffix-key' loop
+              ;; above.
+              ;;
+              ;; Exception: In addition to the conditions in that
+              ;; "backtracking" code, do not shift backward if we're not
+              ;; looking at either `c-after-suffixed-type-decl-key' or "[;,]".
+              ;; Since there's no preceding type, the shift would mean that
+              ;; the declaration is typeless.  But if the regexp doesn't match
+              ;; then we will simply fall through in the tests below and not
+              ;; recognize it at all, so it's better to try it as an abstract
+              ;; declarator instead.
+              (c-fdoc-shift-type-backward)
+
+            ;; Still no identifier.
+            ;; CASE 8
+            (when (and got-prefix (or got-parens got-suffix))
+              ;; Require `got-prefix' together with either `got-parens' or
+              ;; `got-suffix' to recognize it as an abstract declarator:
+              ;; `got-parens' only is probably an empty function call.
+              ;; `got-suffix' only can build an ordinary expression together
+              ;; with the preceding identifier which we've taken as a type.
+              ;; We could actually accept on `got-prefix' only, but that can
+              ;; easily occur temporarily while writing an expression so we
+              ;; avoid that case anyway.  We could do a better job if we knew
+              ;; the point when the fontification was invoked.
+              (throw 'at-decl-or-cast t))
+
+            ;; CASE 9
+            (when (and at-type
+                       (not got-prefix)
+                       (not got-parens)
+                       got-suffix-after-parens
+                       (eq (char-after got-suffix-after-parens) ?\())
+              ;; Got a type, no declarator but a paren suffix. I.e. it's a
+              ;; normal function call after all (or perhaps a C++ style object
+              ;; instantiation expression).
+              (throw 'at-decl-or-cast nil))))
+
+        ;; CASE 9.5
+        (when (and (not context)       ; i.e. not at top level.
+                   (c-major-mode-is 'c++-mode)
+                   (eq at-decl-or-cast 'ids)
+                   after-paren-pos)
+          ;; We've got something like "foo bar (...)" in C++ which isn't at
+          ;; the top level.  This is probably a uniform initialization of bar
+          ;; to the contents of the parens.  In this case the declarator ends
+          ;; at the open paren.
+          (goto-char (1- after-paren-pos))
+          (throw 'at-decl-or-cast t))
+
+        ;; CASE 10
+        (when at-decl-or-cast
+          ;; By now we've located the type in the declaration that we know
+          ;; we're in.
+          (throw 'at-decl-or-cast t))
+
+        ;; CASE 11
+        (when (and got-identifier
+                   (looking-at c-after-suffixed-type-decl-key)
+                   (or (eq context 'top)
+                       make-top
+                       (and (eq context nil)
+                            (match-beginning 1)))
+                   (if (and got-parens
+                            (not got-prefix)
+                            (not got-suffix)
+                            (not (eq at-type t)))
+                       ;; Shift the type backward in the case that there's a
+                       ;; single identifier inside parens.  That can only
+                       ;; occur in K&R style function declarations so it's
+                       ;; more likely that it really is a function call.
+                       ;; Therefore we only do this after
+                       ;; `c-after-suffixed-type-decl-key' has matched.
+                       (progn (c-fdoc-shift-type-backward) t)
+                     got-suffix-after-parens))
+          ;; A declaration according to `c-after-suffixed-type-decl-key'.
+          (throw 'at-decl-or-cast t))
+
+        ;; CASE 12
+        (when (and (or got-prefix (not got-parens))
+                   (memq at-type '(t known)))
+          ;; It's a declaration if a known type precedes it and it can't be a
+          ;; function call.
+          (throw 'at-decl-or-cast t))
+
+        ;; If we get here we can't tell if this is a type decl or a normal
+        ;; expression by looking at it alone.  (That's under the assumption
+        ;; that normal expressions always can look like type decl expressions,
+        ;; which isn't really true but the cases where it doesn't hold are so
+        ;; uncommon (e.g. some placements of "const" in C++) it's not worth
+        ;; the effort to look for them.)
+
+;;;  2008-04-16: commented out the next form, to allow the function to 
recognize
+;;;  "foo (int bar)" in CC (an implicit type (in class foo) without a 
semicolon)
+;;;  as a(n almost complete) declaration, enabling it to be fontified.
+        ;; CASE 13
+        ;;     (unless (or at-decl-end (looking-at "=[^=]"))
+        ;; If this is a declaration it should end here or its initializer(*)
+        ;; should start here, so check for allowed separation tokens.  Note
+        ;; that this rule doesn't work e.g. with a K&R arglist after a
+        ;; function header.
+        ;;
+        ;; *) Don't check for C++ style initializers using parens
+        ;; since those already have been matched as suffixes.
+        ;;
+        ;; If `at-decl-or-cast' is then we've found some other sign that
+        ;; it's a declaration or cast, so then it's probably an
+        ;; invalid/unfinished one.
+        ;;       (throw 'at-decl-or-cast at-decl-or-cast))
+
+        ;; Below are tests that only should be applied when we're certain to
+        ;; not have parsed halfway through an expression.
+
+        ;; CASE 14
+        (when (memq at-type '(t known))
+          ;; The expression starts with a known type so treat it as a
+          ;; declaration.
+          (throw 'at-decl-or-cast t))
+
+        ;; CASE 15
+        (when (and (c-major-mode-is 'c++-mode)
+                   ;; In C++ we check if the identifier is a known type, since
+                   ;; (con|de)structors use the class name as identifier.
+                   ;; We've always shifted over the identifier as a type and
+                   ;; then backed up again in this case.
+                   identifier-type
+                   (or (memq identifier-type '(found known))
+                       (and (eq (char-after identifier-start) ?~)
+                            ;; `at-type' probably won't be 'found for
+                            ;; destructors since the "~" is then part of the
+                            ;; type name being checked against the list of
+                            ;; known types, so do a check without that
+                            ;; operator.
+                            (or (save-excursion
+                                  (goto-char (1+ identifier-start))
+                                  (c-forward-syntactic-ws)
+                                  (c-with-syntax-table
+                                      c-identifier-syntax-table
+                                    (looking-at c-known-type-key)))
+                                (save-excursion
+                                  (goto-char (1+ identifier-start))
+                                  ;; We have already parsed the type earlier,
+                                  ;; so it'd be possible to cache the end
+                                  ;; position instead of redoing it here, but
+                                  ;; then we'd need to keep track of another
+                                  ;; position everywhere.
+                                  (c-check-type (point)
+                                                (progn (c-forward-type)
+                                                       (point))))))))
+          (throw 'at-decl-or-cast t))
+
+        (if got-identifier
+            (progn
+              ;; CASE 16
+              (when (and got-prefix-before-parens
+                         at-type
+                         (or at-decl-end (looking-at "=[^=]"))
+                         (memq context '(nil top))
+                         (or (not got-suffix)
+                             at-decl-start))
+                ;; Got something like "foo * bar;".  Since we're not inside
+                ;; an arglist it would be a meaningless expression because
+                ;; the result isn't used.  We therefore choose to recognize
+                ;; it as a declaration.  We only allow a suffix (which makes
+                ;; the construct look like a function call) when
+                ;; `at-decl-start' provides additional evidence that we do
+                ;; have a declaration.
+                (setq maybe-expression t)
+                (throw 'at-decl-or-cast t))
+
+              ;; CASE 17
+              (when (and (or got-suffix-after-parens
+                             (looking-at "=[^=]"))
+                         (eq at-type 'found)
+                         (not (eq context 'arglist)))
+                ;; Got something like "a (*b) (c);" or "a (b) = c;".  It could
+                ;; be an odd expression or it could be a declaration.  Treat
+                ;; it as a declaration if "a" has been used as a type
+                ;; somewhere else (if it's a known type we won't get here).
+                (setq maybe-expression t)
+                (throw 'at-decl-or-cast t))
+
+              ;; CASE 17.5
+              (when (and c-asymmetry-fontification-flag
+                         got-prefix-before-parens
+                         at-type
+                         (or (not got-suffix)
+                             at-decl-start))
+                (let ((space-before-id
+                       (save-excursion
+                         (goto-char name-start)
+                         (or (bolp) (memq (char-before) '(?\  ?\t)))))
+                      (space-after-type
+                       (save-excursion
+                         (goto-char type-start)
+                         (and (c-forward-type)
+                              (progn (c-backward-syntactic-ws) t)
+                              (or (eolp)
+                                  (memq (char-after) '(?\  ?\t)))))))
+                  (when (not (eq (not space-before-id)
+                                 (not space-after-type)))
+                    (setq maybe-expression t)
+                    (throw 'at-decl-or-cast t)))))
+
+          ;; CASE 18
+          (when (and (not (memq context '(nil top)))
+                     (or (and got-prefix (not got-number))
+                         (and (eq context 'decl)
+                              (not c-recognize-paren-inits)
+                              (or got-parens got-suffix))))
+            ;; Got a type followed by an abstract declarator.  If `got-prefix'
+            ;; is set it's something like "a *" without anything after it.  If
+            ;; `got-parens' or `got-suffix' is set it's "a()", "a[]", "a()[]",
+            ;; or similar, which we accept only if the context rules out
+            ;; expressions.
+            (throw 'at-decl-or-cast t)))
+
+        ;; If we had a complete symbol table here (which rules out
+        ;; `c-found-types') we should return t due to the disambiguation rule
+        ;; (in at least C++) that anything that can be parsed as a declaration
+        ;; is a declaration.  Now we're being more defensive and prefer to
+        ;; highlight things like "foo (bar);" as a declaration only if we're
+        ;; inside an arglist that contains declarations.  Update (2017-09): We
+        ;; now recognize a top-level "foo(bar);" as a declaration in C.
+        ;; CASE 19
+        (or (eq context 'decl)
+            (and (c-major-mode-is 'c-mode)
+                 (or (eq context 'top) make-top))))))
+
+    ;; The point is now after the type decl expression.
+
+    (cond
+     ;; Check for a cast.
+     ((save-excursion
+       (and
+        c-cast-parens
+
+        ;; Should be the first type/identifier in a cast paren.
+        (> preceding-token-end (point-min))
+        (memq (char-before preceding-token-end) c-cast-parens)
+
+        ;; The closing paren should follow.
+        (progn
+          (c-forward-syntactic-ws)
+          (looking-at "\\s)"))
+
+        ;; There should be a primary expression after it.
+        (let (pos)
+          (forward-char)
+          (c-forward-syntactic-ws)
+          (setq cast-end (point))
+          (and (looking-at c-primary-expr-regexp)
+               (progn
+                 (setq pos (match-end 0))
+                 (or
+                  ;; Check if the expression begins with a prefix keyword.
+                  (match-beginning 2)
+                  (if (match-beginning 1)
+                      ;; Expression begins with an ambiguous operator.  Treat
+                      ;; it as a cast if it's a type decl or if we've
+                      ;; recognized the type somewhere else.
+                      (or at-decl-or-cast
+                          (memq at-type '(t known found)))
+                    ;; Unless it's a keyword, it's the beginning of a primary
+                    ;; expression.
+                    (not (looking-at c-keywords-regexp)))))
+               ;; If `c-primary-expr-regexp' matched a nonsymbol token, check
+               ;; that it matched a whole one so that we don't e.g. confuse
+               ;; the operator '-' with '->'.  It's ok if it matches further,
+               ;; though, since it e.g. can match the float '.5' while the
+               ;; operator regexp only matches '.'.
+               (or (not (looking-at c-nonsymbol-token-regexp))
+                   (<= (match-end 0) pos))))
+
+        ;; There should either be a cast before it or something that isn't an
+        ;; identifier or close paren.
+        (> preceding-token-end (point-min))
+        (progn
+          (goto-char (1- preceding-token-end))
+          (or (eq (point) last-cast-end)
+              (progn
+                (c-backward-syntactic-ws)
+                (if (< (skip-syntax-backward "w_") 0)
+                    ;; It's a symbol.  Accept it only if it's one of the
+                    ;; keywords that can precede an expression (without
+                    ;; surrounding parens).
+                    (looking-at c-simple-stmt-key)
+                  (and
+                   ;; Check that it isn't a close paren (block close is ok,
+                   ;; though).
+                   (not (memq (char-before) '(?\) ?\])))
+                   ;; Check that it isn't a nonsymbol identifier.
+                   (not (c-on-identifier)))))))))
+
+      ;; Handle the cast.
+      (when (and c-record-type-identifiers at-type (not (eq at-type t)))
+       (let ((c-promote-possible-types t))
+         (goto-char type-start)
+         (c-forward-type)))
+
+      (goto-char cast-end)
+      'cast)
+
+     (at-decl-or-cast
+      ;; We're at a declaration.  Highlight the type and the following
+      ;; declarators.
+
+      (when backup-if-not-cast
+       (c-fdoc-shift-type-backward t))
+
+      (when (and (eq context 'decl) (looking-at ","))
+       ;; Make sure to propagate the `c-decl-arg-start' property to
+       ;; the next argument if it's set in this one, to cope with
+       ;; interactive refontification.
+       (c-put-c-type-property (point) 'c-decl-arg-start))
+
+      ;; Record the type's coordinates in `c-record-type-identifiers' for
+      ;; later fontification.
+      (when (and c-record-type-identifiers at-type ;; (not (eq at-type t))
+                ;; There seems no reason to exclude a token from
+                ;; fontification just because it's "a known type that can't
+                ;; be a name or other expression".  2013-09-18.
+                )
+       (let ((c-promote-possible-types t))
+         (save-excursion
+           (goto-char type-start)
+           (c-forward-type))))
+
+      (list id-start
+           (and (or at-type-decl at-typedef)
+                (cons at-type-decl at-typedef))
+           maybe-expression
+           type-start
+           (or (eq context 'top) make-top)))
+
+     (t
+      ;; False alarm.  Restore the recorded ranges.
+      (setq c-record-type-identifiers save-rec-type-ids
+           c-record-ref-identifiers save-rec-ref-ids)
+      nil))))
+
+;;----------------------------------------------------------------------------
+
 (defun d-around--c-forward-decl-or-cast-1 (orig-fun &rest args)
   ;; checkdoc-params: (orig-fun args)
   "Advice function for fixing cc-mode handling of D constructors."
@@ -505,7 +1558,7 @@ operators."
    ((looking-at (d-make-keywords-re t '("else")))
     (goto-char (match-end 1))
     (c-forward-syntactic-ws)
-    (apply orig-fun args))
+    (apply #'d-forward-decl-or-cast-1 args))
 
    (t
     ;; Work around a cc-mode bug(?) in which the c-forward-annotation
@@ -518,7 +1571,7 @@ operators."
     (add-function :around (symbol-function 'c-forward-name)
                  #'d-special-case-c-forward-name)
     (unwind-protect
-       (apply orig-fun args)
+       (apply #'d-forward-decl-or-cast-1 args)
       (remove-function (symbol-function 'c-forward-name)
                       #'d-special-case-c-forward-name)))))
 

Reply via email to