+;; Macro used inside `c-forward-decl-or-cast-1'. It ought to be a
+;; defsubst or perhaps even a defun, but it contains lots of free
+;; variables that refer to things inside `c-forward-decl-or-cast-1'.
+(defmacro c-fdoc-shift-type-backward (&optional short)
+ ;; `c-forward-decl-or-cast-1' can consume an arbitrary length list
+ ;; of types when parsing a declaration, which means that it
+ ;; sometimes consumes the identifier in the declaration as a type.
+ ;; This is used to "backtrack" and make the last type be treated as
+ ;; an identifier instead.
+ `(progn
+ ,(unless short
+ ;; These identifiers are bound only in the inner let.
+ '(setq identifier-type at-type
+ identifier-start type-start
+ got-parens nil
+ got-identifier t
+ got-suffix t
+ got-suffix-after-parens id-start
+ paren-depth 0))
+
+ (if (setq at-type (if (eq backup-at-type 'prefix)
+ t
+ backup-at-type))
+ (setq type-start backup-type-start
+ id-start backup-id-start)
+ (setq type-start start-pos
+ id-start start-pos))
+
+ ;; When these flags already are set we've found specifiers that
+ ;; unconditionally signal these attributes - backtracking doesn't
+ ;; change that. So keep them set in that case.
+ (or at-type-decl
+ (setq at-type-decl backup-at-type-decl))
+ (or maybe-typeless
+ (setq maybe-typeless backup-maybe-typeless))
+
+ ,(unless short
+ ;; This identifier is bound only in the inner let.
+ '(setq start id-start))))
+
+(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
+ ;; 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 cons where
+ ;; the car is the position of the first token in the declarator.
+ ;; 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
+ ;;
+ ;; The cdr of the return value is non-nil if a
+ ;; `c-typedef-decl-kwds' specifier is found in the declaration,
+ ;; i.e. the declared identifier(s) are types.
+ ;;
+ ;; 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-separatded declaration context (typically
+ ;; inside a function declaration arglist).
+ ;; '<> In an angle bracket arglist.
+ ;; 'arglist Some other type of arglist.
+ ;; nil Some other context or unknown context.
+ ;;
+ ;; 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 that makes the defined
+ ;; identifier(s) types.
+ at-type-decl
+ ;; Set if we've found a specifier that can start a declaration
+ ;; where there's no type.
+ maybe-typeless
+ ;; 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
+ ;; 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))
+
+ ;; 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)
+
+ ;; Look for a specifier keyword clause.
+ (when (looking-at c-prefix-spec-kwds-re)
+ (setq kwd-sym (c-keyword-sym (match-string 1)))
+ (save-excursion
+ (c-forward-keyword-clause 1)
+ (setq kwd-clause-end (point))))
+
+ (when (setq found-type (c-forward-type))
+ ;; Found a known or possible type or a prefix of a known type.
+
+ (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 kwd-sym
+ (progn
+ ;; Handle known specifier keywords and
+ ;; `c-decl-hangon-kwds' which can occur after known
+ ;; types.
+
+ (if (c-keyword-member kwd-sym 'c-decl-hangon-kwds)
+ ;; It's a hang-on keyword that can occur anywhere.
+ (progn
+ (setq at-decl-or-cast t)
+ (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)
+ (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 umambiguous
+ ;; specifier keyword and we know we're in a
+ ;; declaration.
+ (setq at-decl-or-cast t)
+
+ (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 (looking-at c-decl-hangon-key)
+ (c-forward-keyword-clause 1))
+ (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))
+ ;; 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 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)
+
+ (goto-char id-start)
+
+ ;; Skip over type decl prefix operators. (Note similar code in
+ ;; `c-font-lock-declarators'.)
+ (while (and (looking-at c-type-decl-prefix-key)
+ (if (and (c-major-mode-is 'c++-mode)
+ (match-beginning 2))
+ ;; If the second submatch matches in C++ then
+ ;; we're looking at an identifier that's a
+ ;; prefix only if it specifies a member pointer.
+ (when (setq got-identifier (c-forward-name))
+ (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))
+ (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))
+
+ ;; Skip over an identifier.
+ (or got-identifier
+ (and (looking-at c-identifier-start)
+ (setq got-identifier (c-forward-name))))
+
+ ;; Skip over type decl suffix operators.
+ (while (if (looking-at c-type-decl-suffix-key)
+
+ (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)))
+
+ ;; 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
+ (not context)))
+ (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 (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))
+
+ (setq
+ at-decl-or-cast
+ (catch 'at-decl-or-cast
+
+ (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.
+ (c-safe (goto-char (scan-lists (point) 1 paren-depth)))
+ ;; If we've found a specifier keyword then it's a
+ ;; declaration regardless.
+ (throw 'at-decl-or-cast (eq at-decl-or-cast t)))
+
+ (setq at-decl-end
+ (looking-at (cond ((eq context '<>) "[,>]")
+ (context "[,\)]")
+ (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
+
+ (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))
+ ;; Got a declaration of the form "foo bar (gnu);" where we've
+ ;; recognized "bar" as the type and "gnu" as the declarator.
+ ;; 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
+
+ (when (= (point) start)
+ ;; Got a plain list of identifiers. If a colon follows it's
+ ;; a valid label. 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 (eq (char-after) ?:)
+ ;; If we've found a specifier keyword then it's a
+ ;; declaration regardless.
+ (throw 'at-decl-or-cast (eq at-decl-or-cast t))
+ (setq backup-if-not-cast t)
+ (throw 'at-decl-or-cast t)))
+
+ (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)))
+
+ (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).
+ (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)
+ ;; 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)
+ (not context)
+ (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.
+
+ (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))
+
+ (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 afterall (or perhaps a C++ style object
+ ;; instantiation expression).
+ (throw 'at-decl-or-cast nil))))
+
+ (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))
+
+ (when (and got-identifier
+ (not context)
+ (looking-at c-after-suffixed-type-decl-key)
+ (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))
+
+ (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.)
+
+ (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.
+
+ (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))
+
+ (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
+ (when (and got-prefix-before-parens
+ at-type
+ (or at-decl-end (looking-at "=[^=]"))
+ (not context)
+ (not got-suffix))
+ ;; 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. Do not allow a suffix since it could then
+ ;; be a function call.
+ (throw 'at-decl-or-cast t))
+
+ (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).
+ (throw 'at-decl-or-cast t)))
+
+ (when (and context
+ (or got-prefix
+ (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.
+ (eq context 'decl))))
+
+ ;; 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))
+
+ (when (and c-record-type-identifiers at-type (not (eq at-type t)))
+ (let ((c-promote-possible-types t))
+ (save-excursion
+ (goto-char type-start)
+ (c-forward-type))))
+
+ (cons id-start at-type-decl))
+
+ (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 c-forward-label (&optional assume-markup preceding-token-end limit)
+ ;; Assuming that point is at the beginning of a token, check if it starts a
+ ;; label and if so move over it and return non-nil (t in default situations,
+ ;; specific symbols (see below) for interesting situations), otherwise don't
+ ;; move and return nil. "Label" here means "most things with a colon".
+ ;;
+ ;; More precisely, a "label" is regarded as one of:
+ ;; (i) a goto target like "foo:" - returns the symbol `goto-target';
+ ;; (ii) A case label - either the entire construct "case FOO:", or just the
+ ;; bare "case", should the colon be missing. We return t;
+ ;; (iii) a keyword which needs a colon, like "default:" or "private:"; We
+ ;; return t;
+ ;; (iv) One of QT's "extended" C++ variants of
+ ;; "private:"/"protected:"/"public:"/"more:" looking like "public slots:".
+ ;; Returns the symbol `qt-2kwds-colon'.
+ ;; (v) QT's construct "signals:". Returns the symbol `qt-1kwd-colon'.
+ ;; (vi) One of the keywords matched by `c-opt-extra-label-key' (without any
+ ;; colon). Currently (2006-03), this applies only to Objective C's
+ ;; keywords "@private", "@protected", and "@public". Returns t.
+ ;;
+ ;; One of the things which will NOT be recognised as a label is a bit-field
+ ;; element of a struct, something like "int foo:5".
+ ;;
+ ;; The end of the label is taken to be just after the colon, or the end of
+ ;; the first submatch in `c-opt-extra-label-key'. The point is directly
+ ;; after the end on return. The terminating char gets marked with
+ ;; `c-decl-end' to improve recognition of the following declaration or
+ ;; statement.
+ ;;
+ ;; If ASSUME-MARKUP is non-nil, it's assumed that the preceding
+ ;; label, if any, has already been marked up like that.
+ ;;
+ ;; If PRECEDING-TOKEN-END is given, it should be 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.
+ ;;
+ ;; The optional LIMIT limits the forward scan for the colon.
+ ;;
+ ;; This function records the ranges of the label symbols on
+ ;; `c-record-ref-identifiers' if `c-record-type-identifiers' (!) is
+ ;; non-nil.
+ ;;
+ ;; This function might do hidden buffer changes.
+
+ (let ((start (point))
+ label-end
+ qt-symbol-idx
+ macro-start ; if we're in one.
+ label-type)
+ (cond
+ ;; "case" or "default" (Doesn't apply to AWK).
+ ((looking-at c-label-kwds-regexp)
+ (let ((kwd-end (match-end 1)))
+ ;; Record only the keyword itself for fontification, since in
+ ;; case labels the following is a constant expression and not
+ ;; a label.
+ (when c-record-type-identifiers
+ (c-record-ref-id (cons (match-beginning 1) kwd-end)))
+
+ ;; Find the label end.
+ (goto-char kwd-end)
+ (setq label-type
+ (if (and (c-syntactic-re-search-forward
+ ;; Stop on chars that aren't allowed in expressions,
+ ;; and on operator chars that would be meaningless
+ ;; there. FIXME: This doesn't cope with ?: operators.
+ "[;{=,@]\\|\\(\\=\\|[^:]\\):\\([^:]\\|\\'\\)"
+ limit t t nil 1)
+ (match-beginning 2))
+
+ (progn ; there's a proper :
+ (goto-char (match-beginning 2)) ; just after the :
+ (c-put-c-type-property (1- (point)) 'c-decl-end)
+ t)
+
+ ;; It's an unfinished label. We consider the keyword enough
+ ;; to recognize it as a label, so that it gets fontified.
+ ;; Leave the point at the end of it, but don't put any
+ ;; `c-decl-end' marker.
+ (goto-char kwd-end)
+ t))))
+
+ ;; @private, @protected, @public, in Objective C, or similar.
+ ((and c-opt-extra-label-key
+ (looking-at c-opt-extra-label-key))
+ ;; For a `c-opt-extra-label-key' match, we record the whole
+ ;; thing for fontification. That's to get the leading '@' in
+ ;; Objective-C protection labels fontified.
+ (goto-char (match-end 1))
+ (when c-record-type-identifiers
+ (c-record-ref-id (cons (match-beginning 1) (point))))
+ (c-put-c-type-property (1- (point)) 'c-decl-end)
+ (setq label-type t))
+
+ ;; All other cases of labels.
+ ((and c-recognize-colon-labels ; nil for AWK and IDL, otherwise t.
+
+ ;; A colon label must have something before the colon.
+ (not (eq (char-after) ?:))
+
+ ;; Check that we're not after a token that can't precede a label.
+ (or
+ ;; Trivially succeeds when there's no preceding token.
+ (if preceding-token-end
+ (<= preceding-token-end (point-min))
+ (save-excursion
+ (c-backward-syntactic-ws)
+ (setq preceding-token-end (point))
+ (bobp)))
+
+ ;; Check if we're after a label, if we're after a closing
+ ;; paren that belong to statement, and with
+ ;; `c-label-prefix-re'. It's done in different order
+ ;; depending on `assume-markup' since the checks have
+ ;; different expensiveness.
+ (if assume-markup
+ (or
+ (eq (c-get-char-property (1- preceding-token-end) 'c-type)
+ 'c-decl-end)
+
+ (save-excursion
+ (goto-char (1- preceding-token-end))
+ (c-beginning-of-current-token)
+ (or (looking-at c-label-prefix-re)
+ (looking-at c-block-stmt-1-key)))
+
+ (and (eq (char-before preceding-token-end) ?\))
+ (c-after-conditional)))
+
+ (or
+ (save-excursion
+ (goto-char (1- preceding-token-end))
+ (c-beginning-of-current-token)
+ (or (looking-at c-label-prefix-re)
+ (looking-at c-block-stmt-1-key)))
+
+ (cond
+ ((eq (char-before preceding-token-end) ?\))
+ (c-after-conditional))
+
+ ((eq (char-before preceding-token-end) ?:)
+ ;; Might be after another label, so check it recursively.
+ (save-restriction
+ (save-excursion
+ (goto-char (1- preceding-token-end))
+ ;; Essentially the same as the
+ ;; `c-syntactic-re-search-forward' regexp below.
+ (setq macro-start
+ (save-excursion (and (c-beginning-of-macro)
+ (point))))
+ (if macro-start (narrow-to-region macro-start (point-max)))
+ (c-syntactic-skip-backward "^-]:?;}=*/%&|,<>!@+" nil t)
+ ;; Note: the following should work instead of the
+ ;; narrow-to-region above. Investigate why not,
+ ;; sometime. ACM, 2006-03-31.
+ ;; (c-syntactic-skip-backward "^-]:?;}=*/%&|,<>!@+"
+ ;; macro-start t)
+ (let ((pte (point))
+ ;; If the caller turned on recording for us,
+ ;; it shouldn't apply when we check the
+ ;; preceding label.
+ c-record-type-identifiers)
+ ;; A label can't start at a cpp directive. Check for
+ ;; this, since c-forward-syntactic-ws would foul up on it.
+ (unless (and c-opt-cpp-prefix (looking-at c-opt-cpp-prefix))
+ (c-forward-syntactic-ws)
+ (c-forward-label nil pte start))))))))))
+
+ ;; Point is still at the beginning of the possible label construct.
+ ;;
+ ;; Check that the next nonsymbol token is ":", or that we're in one
+ ;; of QT's "slots" declarations. Allow '(' for the sake of macro
+ ;; arguments. FIXME: Should build this regexp from the language
+ ;; constants.
+ (cond
+ ;; public: protected: private:
+ ((and
+ (c-major-mode-is 'c++-mode)
+ (search-forward-regexp
+ "\\=p\\(r\\(ivate\\|otected\\)\\|ublic\\)\\>[^_]" nil t)
+ (progn (backward-char)
+ (c-forward-syntactic-ws limit)
+ (looking-at ":\\([^:]\\|\\'\\)"))) ; A single colon.
+ (forward-char)
+ (setq label-type t))
+ ;; QT double keyword like "protected slots:" or goto target.
+ ((progn (goto-char start) nil))
+ ((when (c-syntactic-re-search-forward
+ "[ \t\n[:?;{=*/%&|,<>!@+-]" limit t t) ; not at EOB
+ (backward-char)
+ (setq label-end (point))
+ (setq qt-symbol-idx
+ (and (c-major-mode-is 'c++-mode)
+ (string-match
+ "\\(p\\(r\\(ivate\\|otected\\)\\|ublic\\)\\|more\\)\\>"
+ (buffer-substring start (point)))))
+ (c-forward-syntactic-ws limit)
+ (cond
+ ((looking-at ":\\([^:]\\|\\'\\)") ; A single colon.
+ (forward-char)
+ (setq label-type
+ (if (string= "signals" ; Special QT macro
+ (buffer-substring-no-properties start label-end))
+ 'qt-1kwd-colon
+ 'goto-target)))
+ ((and qt-symbol-idx
+ (search-forward-regexp "\\=slots\\>" limit t)
+ (progn (c-forward-syntactic-ws limit)
+ (looking-at ":\\([^:]\\|\\'\\)"))) ; A single colon
+ (forward-char)
+ (setq label-type 'qt-2kwds-colon)))))))
+
+ (save-restriction
+ (narrow-to-region start (point))
+
+ ;; Check that `c-nonlabel-token-key' doesn't match anywhere.
+ (catch 'check-label
+ (goto-char start)
+ (while (progn
+ (when (looking-at c-nonlabel-token-key)
+ (goto-char start)
+ (setq label-type nil)
+ (throw 'check-label nil))
+ (and (c-safe (c-forward-sexp)
+ (c-forward-syntactic-ws)
+ t)
+ (not (eobp)))))
+
+ ;; Record the identifiers in the label for fontification, unless
+ ;; it begins with `c-label-kwds' in which case the following
+ ;; identifiers are part of a (constant) expression that
+ ;; shouldn't be fontified.
+ (when (and c-record-type-identifiers
+ (progn (goto-char start)
+ (not (looking-at c-label-kwds-regexp))))
+ (while (c-syntactic-re-search-forward c-symbol-key nil t)
+ (c-record-ref-id (cons (match-beginning 0)
+ (match-end 0)))))
+
+ (c-put-c-type-property (1- (point-max)) 'c-decl-end)
+ (goto-char (point-max)))))
+
+ (t
+ ;; Not a label.
+ (goto-char start)))
+ label-type))
+
+(defun c-forward-objc-directive ()
+ ;; Assuming the point is at the beginning of a token, try to move
+ ;; forward to the end of the Objective-C directive that starts
+ ;; there. Return t if a directive was fully recognized, otherwise
+ ;; the point is moved as far as one could be successfully parsed and
+ ;; nil is returned.
+ ;;
+ ;; 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 (point))
+ start-char
+ (c-promote-possible-types t)
+ ;; Turn off recognition of angle bracket arglists while parsing
+ ;; types here since the protocol reference list might then be
+ ;; considered part of the preceding name or superclass-name.
+ c-recognize-<>-arglists)
+
+ (if (or
+ (when (looking-at
+ (eval-when-compile
+ (c-make-keywords-re t
+ (append (c-lang-const c-protection-kwds objc)
+ '("@end"))
+ 'objc-mode)))
+ (goto-char (match-end 1))
+ t)
+
+ (and
+ (looking-at
+ (eval-when-compile
+ (c-make-keywords-re t
+ '("@interface" "@implementation" "@protocol")
+ 'objc-mode)))
+
+ ;; Handle the name of the class itself.
+ (progn
+; (c-forward-token-2) ; 2006/1/13 This doesn't move if the token's
+; at EOB.
+ (goto-char (match-end 0))
+ (c-skip-ws-forward)
+ (c-forward-type))
+
+ (catch 'break
+ ;; Look for ": superclass-name" or "( category-name )".
+ (when (looking-at "[:\(]")
+ (setq start-char (char-after))
+ (forward-char)
+ (c-forward-syntactic-ws)
+ (unless (c-forward-type) (throw 'break nil))
+ (when (eq start-char ?\()
+ (unless (eq (char-after) ?\)) (throw 'break nil))
+ (forward-char)
+ (c-forward-syntactic-ws)))
+
+ ;; Look for a protocol reference list.
+ (if (eq (char-after) ?<)
+ (let ((c-recognize-<>-arglists t)
+ (c-parse-and-markup-<>-arglists t)
+ c-restricted-<>-arglists)
+ (c-forward-<>-arglist t))
+ t))))
+
+ (progn
+ (c-backward-syntactic-ws)
+ (c-clear-c-type-property start (1- (point)) 'c-decl-end)
+ (c-put-c-type-property (1- (point)) 'c-decl-end)
+ t)
+
+ (c-clear-c-type-property start (point) 'c-decl-end)
+ nil)))
+