1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-15 10:30:25 -08:00

Handle "noise" macros and compiler directives.

* lisp/progmodes/cc-langs.el (c-symbol-char-key): New language variable.

* lisp/progmodes/cc-vars.el (c-noise-macro-names)
(c-noise-macro-with-parens-names): New customizable variables.
(c-noise-macro-name-re, c-noise-macro-with-parens-name-re): New variables.
(c-make-noise-macro-regexps): New function.

* lisp/progmodes/cc-engine.el (c-forward-sws, c-backward-sws): Adapt to treat
members of c-noise-macro-names as whitespace.
(c-forward-noise-clause): New function.
(c-forward-keyword-prefixed-id, c-forward-type, c-forward-declarator)
(c-forward-decl-or-cast-1, c-backward-over-enum-header)
(c-guess-basic-syntax CASE 5A.3, CASE 5A.5, CASE 9A):
Handle "noise clauses" in parallel with, e.g., "hangon key clauses".

* lisp/progmodes/cc-fonts.el (c-complex-decl-matchers): Handle "noise clauses"
in parallel with "prefix-spec keywords".

* lisp/progmodes/cc-mode.el (c-mode, c++-mode, objc-mode): call
c-make-noise-macro-regexps to initialize the internal variables.

* doc/misc/cc-mode.texi ("Noise Macros"): New section documenting the new
facilities.
This commit is contained in:
Alan Mackenzie 2016-02-29 21:51:32 +00:00
parent 93bf7d5284
commit 71dc8213b1
6 changed files with 260 additions and 65 deletions

View file

@ -338,14 +338,15 @@ Line-Up Functions
* Comment Line-Up::
* Misc Line-Up::
Customizing Macros
* Macro Backslashes::
* Macros with ;::
* Noise Macros::
@end detailmenu
@end menu
@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@node Introduction, Overview, Top, Top
@comment node-name, next, previous, up
@ -6639,15 +6640,18 @@ Because a macro can expand into anything at all, near where one is
invoked @ccmode{} can only indent and fontify code heuristically.
Sometimes it gets it wrong. Usually you should try to design your
macros so that they ''look like ordinary code'' when you invoke them.
However, one situation is so common that @ccmode{} handles it
However, two situations are so common that @ccmode{} handles them
specially: that is when certain macros needn't (or mustn't) be
followed by a @samp{;}. You need to configure @ccmode{} to handle
these macros properly, see @ref{Macros with ;}.
followed by a @samp{;}, and when certain macros (or compiler
directives) expand to nothing. You need to configure @ccmode{} to
handle these macros properly, see @ref{Macros with ;} and @ref{Noise
Macros}.
@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@menu
* Macro Backslashes::
* Macros with ;::
* Noise Macros::
@end menu
@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@ -6699,7 +6703,7 @@ get aligned only when you explicitly invoke the command
@end defopt
@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@node Macros with ;, , Macro Backslashes, Custom Macros
@node Macros with ;, Noise Macros, Macro Backslashes, Custom Macros
@comment node-name, next, previous, up
@section Macros with semicolons
@cindex macros with semicolons
@ -6754,6 +6758,60 @@ return value has no meaning. This function is called by @ccmode{}'s
initialization code.
@end defun
@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@node Noise Macros, , Macros with ;, Custom Macros
@comment node-name, next, previous, up
@section Noise Macros
@cindex noise macros
@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
In @ccmode{}, @dfn{noise macros} are macros which expand to nothing,
or compiler directives (such as GCC's @code{__attribute__}) which play
no part in the syntax of the C (etc.) language. Some noise macros are
followed by arguments in parentheses (possibly optionally), others
are not.
Noise macros can easily confuse @ccmode{}'s analysis of function
headers, causing them to be mis-fontified, or even mis-indented. You
can prevent this confusion by specifying the identifiers which
constitute noise macros.
@defopt c-noise-macro-names
@vindex noise-macro-names (c-)
This variable is a list of names of noise macros which never have
parenthesized arguments. Each element is a string, and must be a
valid identifier. An element in @code{c-noise-macro-names} must not
also be in @code{c-noise-macro-with-parens-names}. Such an element is
treated as whitespace by @ccmode{}.
@end defopt
@defopt c-noise-macro-with-parens-names
@vindex noise-macro-with-parens-names (c-)
This variable is a list of names of noise macros which optionally have
arguments in parentheses. Each element of the list is a string, and
must be a valid identifier. An element in
@code{c-noise-macro-with-parens-names} must not also be in
@code{c-noise-macro-names}. For performance reasons, such an element,
together with the optional parenthesized arguments, is specially
handled, but it is only handled when used in declaration
contexts@footnote{If this restriction causes your project
difficulties, please get in touch with @email{bug-cc-mode@@gnu.org}.}.
The two compiler directives @code{__attribute__} and @code{__declspec}
have traditionally been handled specially in @ccmode{}; for example
they are fontified with font-lock-keyword-face. You don't need to
include these directives in @code{c-noise-macro-with-parens-names},
but doing so is OK.
@end defopt
@defun c-make-noise-macro-regexps
@findex make-noise-macro-regexps (c-)
Call this (non-interactive) function, which sets internal variables,
after changing the value of @code{c-noise-macro-names} or
@code{c-noise-macro-with-parens-names} (e.g. in a hook (@pxref{CC
Hooks})). This function is called by @ccmode{}'s initialization code.
@end defun
@comment !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
@node Odds and Ends, Sample Init File, Custom Macros, Top
@comment node-name, next, previous, up

View file

@ -1543,7 +1543,7 @@ comment at the start of cc-engine.el for more info."
;; two newlines with horizontal whitespace between them.
;;
;; The reason to include the first following char is to cope with
;; "rung positions" that doesn't have any ordinary whitespace. If
;; "rung positions" that don't have any ordinary whitespace. If
;; `c-is-sws' is put on a token character it does not have
;; `c-in-sws' set simultaneously. That's the only case when that
;; can occur, and the reason for not extending the `c-in-sws'
@ -1714,7 +1714,9 @@ comment at the start of cc-engine.el for more info."
;; if it's anything that can't start syntactic ws, so we can bail out
;; early in the majority of cases when there just are a few ws chars.
(skip-chars-forward " \t\n\r\f\v")
(when (looking-at c-syntactic-ws-start)
(when (or (looking-at c-syntactic-ws-start)
(and c-opt-cpp-prefix
(looking-at c-noise-macro-name-re)))
(setq rung-end-pos (min (1+ (point)) (point-max)))
(if (setq rung-is-marked (text-property-any rung-pos rung-end-pos
@ -1733,6 +1735,10 @@ comment at the start of cc-engine.el for more info."
(with-silent-modifications
(while
(progn
;; In the following while form, we move over a "ladder" and
;; following simple WS each time round the loop, appending the WS
;; onto the ladder, joining adjacent ladders, and terminating when
;; there is no more WS or we reach EOB.
(while
(when (and rung-is-marked
(get-text-property (point) 'c-in-sws))
@ -1776,6 +1782,7 @@ comment at the start of cc-engine.el for more info."
(setq rung-pos (point)
last-put-in-sws-pos rung-pos)))
;; Now move over any comments (x)or a CPP construct.
(setq simple-ws-end (point))
(c-forward-comments)
@ -1801,6 +1808,13 @@ comment at the start of cc-engine.el for more info."
(forward-line 1)
(setq safe-start t)
;; Don't cache at eob in case the buffer is narrowed.
(not (eobp)))
((and c-opt-cpp-prefix
(looking-at c-noise-macro-name-re))
;; Skip over a noise macro.
(goto-char (match-end 1))
(setq safe-start t)
(not (eobp)))))
;; We've searched over a piece of non-white syntactic ws. See if this
@ -1907,8 +1921,11 @@ comment at the start of cc-engine.el for more info."
(when (and (not (bobp))
(save-excursion
(backward-char)
(looking-at c-syntactic-ws-end)))
(or (looking-at c-syntactic-ws-end)
(and c-opt-cpp-prefix
(looking-at c-symbol-char-key)
(progn (c-beginning-of-current-token)
(looking-at c-noise-macro-name-re))))))
;; Try to find a rung position in the simple ws preceding point, so that
;; we can get a cache hit even if the last bit of the simple ws has
;; changed recently.
@ -1927,6 +1944,9 @@ comment at the start of cc-engine.el for more info."
(with-silent-modifications
(while
(progn
;; Each time round the next while form, we move back over a ladder
;; and append any simple WS preceding it, if possible joining with
;; the previous ladder.
(while
(when (and rung-is-marked
(not (bobp))
@ -2035,6 +2055,15 @@ comment at the start of cc-engine.el for more info."
;; narrowed out, and we can't risk marking the simple ws
;; at the end of it.
(goto-char next-rung-pos)
t)
((and c-opt-cpp-prefix
(save-excursion
(and (< (skip-syntax-backward "w_") 0)
(progn (setq next-rung-pos (point))
(looking-at c-noise-macro-name-re)))))
;; Skipped over a noise macro
(goto-char next-rung-pos)
t)))
;; We've searched over a piece of non-white syntactic ws. See if this
@ -5807,8 +5836,10 @@ comment at the start of cc-engine.el for more info."
`(c-forward-type)
`(c-forward-name)))
nil
(and (looking-at c-keywords-regexp)
(c-forward-keyword-clause 1))))
(cond ((looking-at c-keywords-regexp)
(c-forward-keyword-clause 1))
((looking-at c-noise-macro-with-parens-name-re)
(c-forward-noise-clause)))))
(when (memq res '(t known found prefix))
,(when (eq type 'ref)
`(when c-record-type-identifiers
@ -5830,6 +5861,17 @@ comment at the start of cc-engine.el for more info."
(c-forward-syntactic-ws)
(c-forward-keyword-prefixed-id ,type)))))
(defun c-forward-noise-clause ()
;; Point is at a c-noise-macro-with-parens-names macro identifier. Go
;; forward over this name, any parenthesis expression which follows it, and
;; any syntactic WS, ending up at the next token. If there is an unbalanced
;; paren expression, leave point at it. Always Return t.
(c-forward-token-2)
(if (and (eq (char-after) ?\()
(c-go-list-forward))
(c-forward-syntactic-ws))
t)
(defun c-forward-keyword-clause (match)
;; Submatch MATCH in the current match data is assumed to surround a
;; token. If it's a keyword, move over it and any immediately
@ -6460,6 +6502,13 @@ comment at the start of cc-engine.el for more info."
; "typedef".
(goto-char (match-end 1))
(c-forward-syntactic-ws)
(while (cond
((looking-at c-decl-hangon-key)
(c-forward-keyword-clause 1))
((looking-at c-noise-macro-with-parens-name-re)
(c-forward-noise-clause))))
(setq pos (point))
(setq name-res (c-forward-name))
@ -6852,31 +6901,38 @@ comment at the start of cc-engine.el for more info."
;; of the while. These are, e.g. "*" in "int *foo" or "(" and
;; "*" in "int (*foo) (void)" (Note similar code in
;; `c-forward-decl-or-cast-1'.)
(while (and (looking-at c-type-decl-prefix-key)
(if (and (c-major-mode-is 'c++-mode)
(match-beginning 3))
;; If the third submatch matches in C++ then
;; we're looking at an identifier that's a
;; prefix only if it specifies a member pointer.
(progn
(setq id-start (point))
(c-forward-name)
(if (looking-at "\\(::\\)")
;; We only check for a trailing "::" and
;; let the "*" that should follow be
;; matched in the next round.
t
;; It turned out to be the real identifier,
;; so flag that and stop.
(setq got-identifier t)
nil))
t))
(if (eq (char-after) ?\()
(progn
(setq paren-depth (1+ paren-depth))
(forward-char))
(goto-char (match-end 1)))
(c-forward-syntactic-ws))
(while
(cond
((looking-at c-decl-hangon-key)
(c-forward-keyword-clause 1))
((looking-at c-noise-macro-with-parens-name-re)
(c-forward-noise-clause))
((and (looking-at c-type-decl-prefix-key)
(if (and (c-major-mode-is 'c++-mode)
(match-beginning 3))
;; If the third submatch matches in C++ then
;; we're looking at an identifier that's a
;; prefix only if it specifies a member pointer.
(progn
(setq id-start (point))
(c-forward-name)
(if (looking-at "\\(::\\)")
;; We only check for a trailing "::" and
;; let the "*" that should follow be
;; matched in the next round.
t
;; It turned out to be the real identifier,
;; so flag that and stop.
(setq got-identifier t)
nil))
t))
(if (eq (char-after) ?\()
(progn
(setq paren-depth (1+ paren-depth))
(forward-char))
(goto-char (match-end 1)))
(c-forward-syntactic-ws)
t)))
;; If we haven't passed the identifier already, do it now.
(unless got-identifier
@ -6901,9 +6957,12 @@ comment at the start of cc-engine.el for more info."
;; Skip over any trailing bit, such as "__attribute__".
(progn
(when (looking-at c-decl-hangon-key)
(c-forward-keyword-clause 1))
(<= (point) limit))
(while (cond
((looking-at c-decl-hangon-key)
(c-forward-keyword-clause 1))
((looking-at c-noise-macro-with-parens-name-re)
(c-forward-noise-clause))))
(<= (point) limit))
;; Search syntactically to the end of the declarator (";",
;; ",", a closing paren, eob etc) or to the beginning of an
@ -7082,18 +7141,24 @@ comment at the start of cc-engine.el for more info."
;; 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)
(let* ((start (point)) kwd-sym kwd-clause-end found-type noise-start)
(cond
;; Look for a specifier keyword clause.
(when (or (looking-at c-prefix-spec-kwds-re) ;FIXME!!! includes auto
(and (c-major-mode-is 'java-mode)
(looking-at "@[A-Za-z0-9]+")))
(if (save-match-data (looking-at c-typedef-key))
(setq at-typedef t))
((or (looking-at c-prefix-spec-kwds-re)
(and (c-major-mode-is 'java-mode)
(looking-at "@[A-Za-z0-9]+")))
(save-match-data
(if (looking-at c-typedef-key)
(setq at-typedef t)))
(setq kwd-sym (c-keyword-sym (match-string 1)))
(save-excursion
(c-forward-keyword-clause 1)
(setq kwd-clause-end (point))))
((looking-at c-noise-macro-with-parens-name-re)
(setq noise-start (point))
(c-forward-noise-clause)
(setq kwd-clause-end (point))))
(when (setq found-type (c-forward-type t)) ; brace-block-too
;; Found a known or possible type or a prefix of a known type.
@ -7131,16 +7196,17 @@ comment at the start of cc-engine.el for more info."
backup-at-type-decl nil
backup-maybe-typeless nil))
(if kwd-sym
(if (or kwd-sym noise-start)
(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.
(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
(setq at-decl-or-cast t)
(if at-type
;; Move the identifier start position if
;; we've passed a type.
@ -7192,8 +7258,11 @@ comment at the start of cc-engine.el for more info."
;; 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))
(while
(cond ((looking-at c-decl-hangon-key)
(c-forward-keyword-clause 1))
((looking-at c-noise-macro-with-parens-name-re)
(c-forward-noise-clause))))
(setq id-start (point)))
((eq at-type 'prefix)
@ -8960,6 +9029,11 @@ comment at the start of cc-engine.el for more info."
t)
((looking-at c-after-brace-list-key) t)
((looking-at c-brace-list-key) nil)
((eq (char-after) ?\()
(and (eq (c-backward-token-2) 0)
(or (looking-at c-decl-hangon-key)
(looking-at c-noise-macro-with-parens-name-re))))
((and c-recognize-<>-arglists
(eq (char-after) ?<)
(looking-at "\\s("))
@ -10220,9 +10294,11 @@ comment at the start of cc-engine.el for more info."
;; CASE 5A.3: brace list open
((save-excursion
(c-beginning-of-decl-1 lim)
(while (looking-at c-specifier-key)
(goto-char (match-end 1))
(c-forward-syntactic-ws indent-point))
(while (cond
((looking-at c-specifier-key)
(c-forward-keyword-clause 1))
((looking-at c-noise-macro-with-parens-name-re)
(c-forward-noise-clause))))
(setq placeholder (c-point 'boi))
(or (consp special-brace-list)
(and (or (save-excursion
@ -10274,9 +10350,11 @@ comment at the start of cc-engine.el for more info."
(t
(save-excursion
(c-beginning-of-decl-1 lim)
(while (looking-at c-specifier-key)
(goto-char (match-end 1))
(c-forward-syntactic-ws indent-point))
(while (cond
((looking-at c-specifier-key)
(c-forward-keyword-clause 1))
((looking-at c-noise-macro-with-parens-name-re)
(c-forward-noise-clause))))
(c-add-syntax 'defun-open (c-point 'boi))
;; Bogus to use bol here, but it's the legacy. (Resolved,
;; 2007-11-09)
@ -10907,9 +10985,11 @@ comment at the start of cc-engine.el for more info."
(c-beginning-of-statement-1
(c-safe-position (1- containing-sexp) paren-state))
(c-forward-token-2 0)
(while (looking-at c-specifier-key)
(goto-char (match-end 1))
(c-forward-syntactic-ws))
(while (cond
((looking-at c-specifier-key)
(c-forward-keyword-clause 1))
((looking-at c-noise-macro-with-parens-name-re)
(c-forward-noise-clause))))
(c-add-syntax 'brace-list-open (c-point 'boi))))
;; CASE 9B: brace-list-close brace

View file

@ -1698,10 +1698,16 @@ on level 2 only and so aren't combined with `c-complex-decl-matchers'."
(unless (c-skip-comments-and-strings limit)
(c-forward-syntactic-ws)
;; Handle prefix declaration specifiers.
(when (or (looking-at c-prefix-spec-kwds-re)
(and (c-major-mode-is 'java-mode)
(looking-at "@[A-Za-z0-9]+")))
(c-forward-keyword-clause 1))
(while
(or
(when (or (looking-at c-prefix-spec-kwds-re)
(and (c-major-mode-is 'java-mode)
(looking-at "@[A-Za-z0-9]+")))
(c-forward-keyword-clause 1)
t)
(when (looking-at c-noise-macro-with-parens-name-re)
(c-forward-noise-clause)
t)))
,(if (c-major-mode-is 'c++-mode)
`(when (and (c-forward-type)
(eq (char-after) ?=))
@ -1827,7 +1833,7 @@ higher."
"\\)\\>"
;; Disallow various common punctuation chars that can't come
;; before the '{' of the enum list, to avoid searching too far.
"[^][{}();/#=]*"
"[^][{};/#=]*"
"{")
'((c-font-lock-declarators limit t nil)
(save-match-data

View file

@ -619,6 +619,11 @@ This is of the form that fits inside [ ] in a regexp."
objc (concat c-alnum "_$@"))
(c-lang-defvar c-symbol-chars (c-lang-const c-symbol-chars))
(c-lang-defconst c-symbol-char-key
"Regexp matching a sequence of at least one identifier character."
t (concat "[" (c-lang-const c-symbol-chars) "]+"))
(c-lang-defvar c-symbol-char-key (c-lang-const c-symbol-char-key))
(c-lang-defconst c-symbol-key
"Regexp matching identifiers and keywords (with submatch 0). Assumed
to match if `c-symbol-start' matches on the same position."

View file

@ -1493,6 +1493,7 @@ Key bindings:
abbrev-mode t)
(use-local-map c-mode-map)
(c-init-language-vars-for 'c-mode)
(c-make-noise-macro-regexps)
(c-make-macro-with-semi-re) ; matches macro names whose expansion ends with ;
(c-common-init 'c-mode)
(easy-menu-add c-c-menu)
@ -1548,6 +1549,7 @@ Key bindings:
abbrev-mode t)
(use-local-map c++-mode-map)
(c-init-language-vars-for 'c++-mode)
(c-make-noise-macro-regexps)
(c-make-macro-with-semi-re) ; matches macro names whose expansion ends with ;
(c-common-init 'c++-mode)
(easy-menu-add c-c++-menu)
@ -1601,6 +1603,7 @@ Key bindings:
abbrev-mode t)
(use-local-map objc-mode-map)
(c-init-language-vars-for 'objc-mode)
(c-make-noise-macro-regexps)
(c-make-macro-with-semi-re) ; matches macro names whose expansion ends with ;
(c-common-init 'objc-mode)
(easy-menu-add c-objc-menu)

View file

@ -1619,6 +1619,49 @@ names)."))
:type 'c-extra-types-widget
:group 'c)
(defvar c-noise-macro-with-parens-name-re nil)
(defvar c-noise-macro-name-re nil)
(defcustom c-noise-macro-names nil
"A list of names of macros which expand to nothing, or compiler extensions
like \"????\" which are syntactic noise. Such a macro/extension is complete in
itself, never having parentheses. All these names must be syntactically valid
identifiers.
If you change this variable's value, call the function
`c-make-noise-macro-regexps' to set the necessary internal variables (or do
this implicitly by reinitialising C/C++/Objc Mode on any buffer)."
:type '(repeat :tag "List of names" string)
:group 'c)
(defcustom c-noise-macro-with-parens-names nil
"A list of names of macros \(or compiler extensions like \"__attribute__\")
which optionally have arguments in parentheses, and which expand to nothing.
These are recognized by CC Mode only in declarations."
:type '(regexp :tag "List of names (possibly empty)" string)
:group 'c)
(defun c-make-noise-macro-regexps ()
;; Convert `c-noise-macro-names' and `c-noise-macro-with-parens-names' into
;; `c-noise-macro-name-re' and `c-noise-macro-with-parens-name-re'.
(setq c-noise-macro-with-parens-name-re
(cond ((null c-noise-macro-with-parens-names) "\\<\\>")
((consp c-noise-macro-with-parens-names)
(concat (regexp-opt c-noise-macro-with-parens-names t)
"\\([^[:alnum:]_$]\\|$\\)"))
((stringp c-noise-macro-with-parens-names)
(copy-sequence c-noise-macro-with-parens-names))
(t (error "c-make-noise-macro-regexps: \
c-noise-macro-with-parens-names is invalid: %s" c-noise-macro-with-parens-names))))
(setq c-noise-macro-name-re
(cond ((null c-noise-macro-names) "\\<\\>")
((consp c-noise-macro-names)
(concat (regexp-opt c-noise-macro-names t)
"\\([^[:alnum:]_$]\\|$\\)"))
((stringp c-noise-macro-names)
(copy-sequence c-noise-macro-names))
(t (error "c-make-noise-macro-regexps: \
c-noise-macro-names is invalid: %s" c-noise-macro-names)))))
;; Non-customizable variables, still part of the interface to CC Mode
(defvar c-macro-with-semi-re nil