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 ; Probably unnecessary (2024-09-20)
			   (looking-at c-requires-clause-key)))
		(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
			(looking-at c-requires-clause-key))
		      (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).  We also allow
		      ;; 'maybe types when nameless types can be in arglists.
		      (or (and (not c-recognize-knr-p)
			       (not c-recognize-paren-inits))
			  (memq at-type '(known found))
			  (and c-recognize-nameless-type-decls
			       (eq at-type 'maybe))))
		     ((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))))