Function: c-forward-decl-or-cast-1
c-forward-decl-or-cast-1 is a byte-compiled function defined in
cc-engine.el.gz.
Signature
(c-forward-decl-or-cast-1 PRECEDING-TOKEN-END CONTEXT LAST-CAST-END &optional INSIDE-MACRO)
Source Code
;; Defined in /usr/src/emacs/lisp/progmodes/cc-engine.el.gz
(defun c-forward-decl-or-cast-1 (preceding-token-end context last-cast-end
&optional inside-macro)
;; 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 something
;; indicating the identifier is a type occurs in the declaration.
;; Specifically it is nil, or a three element list (A B C) where C is t
;; when context is '<> and the "identifier" is a found type, B is the
;; position of the `c-typedef-kwds' keyword ("typedef") when such 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, or the same as the first element when
;; there is no 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".
;;
;; INSIDE-MACRO is t when we definitely know we're inside a macro, nil
;; otherwise. We use it to disambiguate things like "(a) (b);", which is
;; likely a function call in a macro, but a cast outside of one.
;;
;; 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
;; The earlier value of `type-start' if we've shifted the type
;; backwards.
identifier-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
;; If we've a "typedef" keyword (?or similar), the buffer position of
;; its first character.
at-typedef
;; Set if `context' is '<> and the identifier is definitely a type, or
;; has already been recorded as a found type.
at-<>-type
;; 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. (Commented out, 2020-11-01).
;; 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
;; A list of found types in this declaration. This is an association
;; list, the car being the buffer position, the cdr being the
;; identifier.
found-type-list
;; 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
;; Set for the type when `c-forward-type' returned `maybe', and we
;; want to fontify it as a type, but aren't confident enough to enter
;; it into `c-found-types'.
unsafe-maybe)
(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 (point))))
(setq kwd-sym (c-keyword-sym (match-string 1)))
(save-excursion
(c-forward-keyword-clause 1)
(when (and (c-major-mode-is 'c++-mode)
(c-keyword-member kwd-sym 'c-<>-sexp-kwds)
(save-match-data
(and
(looking-at c-fun-name-substitute-key)
(not (eq (char-after (match-end 0)) ?_)))))
(c-forward-c++-requires-clause))
(setq kwd-clause-end (point))))
((and c-opt-cpp-prefix
(looking-at c-noise-macro-with-parens-name-re))
(setq noise-start (point))
(while
(and
(c-forward-noise-clause)
(looking-at c-noise-macro-with-parens-name-re)))
(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 (eq found-type 'no-id)
(save-excursion
(and (c-forward-name) ; over the identifier
(looking-at "[=(]")))) ; FIXME!!! proper regexp.
(setq new-style-auto t)) ; 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".
;; We postpone entering the new found type into c-found-types
;; until we are sure of it, thus preventing rapid alternation
;; of the fontification of the token throughout the buffer.
(push (cons type-start
(buffer-substring-no-properties
type-start
(save-excursion
(goto-char type-start)
(c-end-of-token)
(point))))
found-type-list))
;; Might we have a C++20 concept? i.e. template<foo bar>?
(setq at-<>-type
(and (eq context '<>)
(memq found-type '(t known prefix found))))
;; Signal a type declaration for "struct foo {".
(when (and backup-at-type-decl
(eq (char-after) ?{))
(setq at-type-decl t)))
(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 (memq found-type '(t no-id)))))))
(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))
((eq at-type 'no-id)
;; For an auto type, we assume we definitely have a type construct.
(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 a terminated argument list.
got-arglist
;; True when `got-arglist' and the token after the end of the
;; arglist is an opening brace. Used only when we have a
;; suspected typeless function name.
got-stmt-block
;; 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 prefix, such as "*" which might precede the
;; identifier in a function declaration.
got-function-name-prefix
;; 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 value of `at-type' if we've shifted the type
;; backwards.
identifier-type
;; 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 regexp-unmatchable))
(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 4))
;; If the fourth 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 (save-match-data
(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.
(save-excursion
(c-backward-syntactic-ws)
(c-simple-skip-symbol-backward)
(setq identifier-start (point)))
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)
(when (save-match-data
(looking-at c-type-decl-operator-prefix-key))
(setq got-function-name-prefix t))
(goto-char (or (match-end 1)
(match-end 2))))
(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 t))
(save-excursion
(c-simple-skip-symbol-backward)
(setq identifier-start (point)))
(progn (c-forward-syntactic-ws) t)
(setq name-start pos))
(when (looking-at "[0-9]")
(setq got-number t)) ; We probably have an arithmetic expression.
(and maybe-typeless
(or (eq at-type 'maybe)
(when (eq at-type 'found)
;; Remove the ostensible type from the found types list.
(when type-start
(let ((discard-t (assq type-start found-type-list)))
(when discard-t
(setq found-type-list
(remq discard-t found-type-list)))))
t))
;; The token which we assumed to be a type is actually the
;; identifier, and we have no explicit type.
(setq at-type nil
name-start type-start
id-start type-start
got-identifier t)
(setq identifier-start type-start)))
;; 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)
(not got-prefix)
(or (eq context 'top) make-top)
(eq (char-after) ?\))
after-paren-pos
(or (memq at-type '(nil maybe))
(not got-identifier)
(save-excursion
(goto-char after-paren-pos)
(c-forward-syntactic-ws)
;; Prevent the symbol being recorded as a type.
(let (c-record-type-identifiers)
(not (memq (c-forward-type)
'(nil maybe)))))))))
(if (eq (char-after) ?\))
(when (> paren-depth 0)
(setq paren-depth (1- paren-depth))
(forward-char)
(when (and (not got-parens)
(eq paren-depth 0))
(setq got-arglist t))
t)
(when (cond
((and (eq (char-after) ?\()
(c-safe (c-forward-sexp 1) t))
(when (eq (char-before) ?\))
(setq got-arglist t)))
((save-match-data (looking-at "\\s("))
(c-safe (c-forward-sexp 1) t))
((save-match-data
(and
(looking-at c-fun-name-substitute-key)
(not (eq (char-after (match-end 0)) ?_)))) ; C++ requires
(c-forward-c++-requires-clause))
(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))))
((and got-arglist
(eq (char-after) ?{))
(setq got-stmt-block t)
nil)
(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 0) ; ensures `after-paren-pos' is non-nil
(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)
(progn
(while
(cond
((and
c-opt-cpp-prefix
(looking-at c-noise-macro-with-parens-name-re))
(c-forward-noise-clause))
((looking-at c-decl-hangon-key)
(c-forward-keyword-clause 1))))
t)
(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) ?\))))))))
(let ((pd paren-depth))
(setq pos (point))
(catch 'pd
(while (> pd 0)
(setq pos (c-up-list-forward pos))
(when (or (null pos)
(not (eq (char-before pos) ?\))))
(throw 'pd nil))
(goto-char pos)
(setq pd (1- pd)))
t)))
(c-fdoc-shift-type-backward)
(when (and (not got-parens)
(eq paren-depth 0))
(setq got-arglist t))
t)))
(c-forward-syntactic-ws))
(when (and (not got-identifier)
(or backup-at-type
(not (memq context '(arglist decl))))
(or (and new-style-auto
(looking-at c-auto-ops-re))
(and (not got-prefix)
at-type
(or maybe-typeless backup-maybe-typeless
;; Do we have a (typeless) constructor?
(and got-stmt-block
(save-excursion
(goto-char type-start)
(and
(looking-at c-identifier-key)
(c-directly-in-class-called-p
(match-string 0)))))))))
;; 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.
(when (and got-arglist
(eq at-type 'maybe))
(setq unsafe-maybe t))
(throw 'at-decl-or-cast t))
(when (and got-parens
(or (not got-function-name-prefix)
(and (not got-suffix-after-parens)
at-decl-end))
(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
type-start
(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 nil t)
(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
;; Check for a bitfield.
(eq at-decl-or-cast 'ids)
(save-excursion
(forward-char) ; Over the :
(c-forward-syntactic-ws)
(and (looking-at "[[:alnum:]]")
(progn (c-forward-token-2)
(c-forward-syntactic-ws)
(memq (char-after) '(?\; ?,))))))
(setq backup-if-not-cast t)
(throw 'at-decl-or-cast t)))
;; If we're in declaration or template delimiters, or one
;; of a certain set of characters follows, we've got a
;; type and variable.
(if (or (memq context '(decl <>))
(memq (char-after) '(?\; ?, ?= ?\( ?{ ?:)))
(progn
(setq backup-if-not-cast t)
(throw 'at-decl-or-cast t))
;; We're probably just typing a statement.
(throw 'at-decl-or-cast nil))))
;; 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)))))
(progn
;; The user may be part way through typing a statement
;; beginning with an identifier. This makes a 'maybe
;; type in the following "declarator"'s arglist suspect.
(when (eq at-type 'maybe)
(setq unsafe-maybe t))
(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)
(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 think
;; we're in. Do we have enough evidence to promote the putative
;; type to a found type? The user may be halfway through typing
;; a statement beginning with an identifier.
(when (and (eq at-type 'maybe)
(not (eq context 'top)))
(setq unsafe-maybe t))
(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
(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 when there's "symmetrical WS" around
;; the "*" or the flag `c-asymmetry-fontification-flag' is
;; not set. 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)
(when (or (not c-asymmetry-fontification-flag)
(looking-at "=\\([^=]\\|$\\)\\|;")
(c-fdoc-assymetric-space-about-asterisk))
(when (eq at-type 'maybe)
(setq unsafe-maybe 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)
(c-fdoc-assymetric-space-about-asterisk))
(when (eq at-type 'maybe)
(setq unsafe-maybe t))
(setq maybe-expression t)
(throw 'at-decl-or-cast t)))
;; CASE 18
(when (and at-decl-end
(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.
;;
;; If we've got at-type 'maybe, we cannot confidently promote the
;; possible type to a found type.
(when (and (eq at-type 'maybe))
(setq unsafe-maybe t))
(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
(when
(or (eq context 'decl)
(and (c-major-mode-is 'c-mode)
(or (eq context 'top) make-top)))
(when (and (eq at-type 'maybe)
got-parens)
;; If we've got "foo d(bar () ...)", the d could be a typing
;; mistake, so we don't promote the 'maybe type "bar" to a 'found
;; type.
(setq unsafe-maybe t))
t))))
;; 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.
(cond
((match-beginning c-per-&*+--match)
(memq at-type '(t known found)))
((match-beginning c-per-++---match)
t)
((match-beginning c-per-\(-match)
(or
(memq at-type '(t known found))
(not inside-macro)))
(t nil))
;; 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 , or a
;; macro arglist is ok, though).
(or
(not (memq (char-before) '(?\) ?\])))
;; Have we moved back to a macro arglist?
(and c-opt-cpp-prefix
(eq (char-before) ?\))
(save-excursion
(and
(c-go-list-backward)
(let (pos)
(c-backward-syntactic-ws)
(and (setq pos (c-on-identifier))
(goto-char pos)))
(zerop (c-backward-token-2 2))
(looking-at c-opt-cpp-macro-define-start)))))
;; 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 (if (eq at-type 'maybe)
'just-one
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))
;; Enter all the found types into `c-found-types'.
(when found-type-list
(save-excursion
(let ((c-promote-possible-types t))
(dolist (ft found-type-list)
(goto-char (car ft))
(c-forward-type)))))
;; Record the type's coordinates in `c-record-type-identifiers' for
;; later fontification.
(when (and c-record-type-identifiers
(not (memq at-type '(nil no-id)))
;; 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
(if unsafe-maybe 'just-one t)))
(save-excursion
(goto-char type-start)
(c-forward-type))))
(list id-start
(and (or at-type-decl at-typedef at-<>-type)
(list at-type-decl at-typedef at-<>-type))
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))))