Discussion:
(bug) Highlighting for pointer or reference to user defined types (cc 5.33) and doxygen
Dixon Ryan (ETAS/ERS-PD2)
2017-03-10 12:27:25 UTC
Permalink
Enabled minor modes: Abbrev Auto-Composition Auto-Compression Auto-Encryption
Blink-Cursor Column-Number Company Diff-Auto-Refine Electric-Indent
File-Name-Shadow Font-Lock Global-Auto-Revert Global-Company Global-Font-Lock
Hs Irony Line-Number Mouse-Wheel Shell-Dirtrack Show-Paren Tooltip
Transient-Mark Winner Yas

(Information about these minor modes follows the major mode info.)

C++/l mode defined in `cc-mode.el':
Major mode for editing C++ code.
To submit a problem report, enter `C-c C-b' from a
c++-mode buffer. This automatically sets up a mail buffer with
version information already added. You just need to add a description
of the problem, including a reproducible test case, and send the
message.

To see what version of CC Mode you are running, enter `M-x c-version'.
Using CC Mode version 5.33


Firstly, thank you for 5.33. It brings a bunch of improvements when coding in c++11+. It feels a lot more robust but for this issue I am reporting.

I have a constructor with three arguments. The first and second arguments are not highlighted correctly. The issue is with references or pointers to user-defined types and seems to manifest mostly when there is no scope resolution involved with the user defined type - though, not always! Anyway, I have something like:

Class OneClassHere : public AnotherClassThere[]
{
/// lots of stuff
Constructor( MyType1& ref,
NS1::MyType2& ref1,
MyType3& ref2 );
/// lots of stuff
};
This is all highlighted correctly on initial entry to the file. However, when I put the cursor at [] and add a space the ref2 and its type are no longer highlighted. Then, when I click anywhere on the screen ref and its type are not highlighted. Ref1 stays highlighted. If I reload the file it is all highlighted correctly. Also, this bug only occurs when there is a newline at the end of the arguments. For example with:

Constructor(MyType1& ref, NS1::MyType2& ref1, MyType3& ref2 );

It is always hlight correctly no matter what.

I have a reproducible MyClass.h file (I then invoke c++-mode since it's a .h)

#pragma once
{}
#include <iostream>
#include "ccinc.h"

namespace OtherNs
{
class SomeForwardedClass;
}

namespace SomeType
{
class MyClass : public ccinc
{
protected:
/// \brief The The The-The The The The The The The The The The
/// The The The The The The The The The The The
/// The.
///
/// Initially \c nullptr. Set by iimplementddddR().
///
/// The The The The The The The The The The The The.
OtherNs::SomeForwardedClass* _data;

public:
MyClass( SomeType& someType,
const someOther::here& type,
SomeOtherType& ref1 );
virtual MyClass( void );
};
} //namespace

Upon load all is hlighted. Then, I move to the {} and press space. Ref1 and its type is white. Then I left click somewhere on the buffer and someType and its type is white.

IF, I remove the doxygen comment block - all those lines with /// then the problem does not happen. It therefore looks like the parsing of doxygen is causing the issue?

Thank you.
Alan Mackenzie
2017-03-12 14:13:15 UTC
Permalink
Hello, Ryan.

Thank you indeed for reporting this strange bug.

On Fri, Mar 10, 2017 at 12:27:25 +0000, Dixon Ryan (ETAS/ERS-PD2) wrote:

[ .... ]
Post by Dixon Ryan (ETAS/ERS-PD2)
Using CC Mode version 5.33
More precisely, I think you're using CC Mode version 5.33 in the master
branch of the savannah git repository. ;-)
Post by Dixon Ryan (ETAS/ERS-PD2)
Firstly, thank you for 5.33. It brings a bunch of improvements when
coding in c++11+. It feels a lot more robust but for this issue I am
reporting.
Thanks, appreciated.
Post by Dixon Ryan (ETAS/ERS-PD2)
I have a constructor with three arguments. The first and second
arguments are not highlighted correctly. The issue is with references
or pointers to user-defined types and seems to manifest mostly when
there is no scope resolution involved with the user defined type -
This has come up before, and is difficult to resolve, due to ambiguities
in the C++ language. "foo (bar * baz);" might be the declaration of a
"function" foo which takes one parameter of type bar*. It might equally
well be a function call of foo, with argument bar times baz. The
current default for CC Mode is to assume the latter. The same applies
to "foo (bar & baz);"

CC Mode works almost entirely by syntax, not semantics (it is not a
compiler), and this used to work well, before recent developments in
C++. It now works less well. It may be possible to analyse the syntax
a bit more thoroughly, possibly by looking for other clues which
indicate the construct can only be a declaration, but this will not be a
complete solution.
Post by Dixon Ryan (ETAS/ERS-PD2)
Class OneClassHere : public AnotherClassThere[]
{
/// lots of stuff
Constructor( MyType1& ref,
NS1::MyType2& ref1,
MyType3& ref2 );
/// lots of stuff
};
This is all highlighted correctly on initial entry to the file.
However, when I put the cursor at [] and add a space the ref2 and its
type are no longer highlighted. Then, when I click anywhere on the
screen ref and its type are not highlighted. Ref1 stays highlighted.
If I reload the file it is all highlighted correctly. Also, this bug
only occurs when there is a newline at the end of the arguments. For
Constructor(MyType1& ref, NS1::MyType2& ref1, MyType3& ref2 );
It is always hlight correctly no matter what.
I can confirm I can reproduce these symptoms.
Post by Dixon Ryan (ETAS/ERS-PD2)
I have a reproducible MyClass.h file (I then invoke c++-mode since it's a .h)
#pragma once
{}
#include <iostream>
#include "ccinc.h"
namespace OtherNs
{
class SomeForwardedClass;
}
namespace SomeType
{
class MyClass : public ccinc
{
/// \brief The The The-The The The The The The The The The The
/// The The The The The The The The The The The
/// The.
///
/// Initially \c nullptr. Set by iimplementddddR().
///
/// The The The The The The The The The The The The.
OtherNs::SomeForwardedClass* _data;
MyClass( SomeType& someType,
const someOther::here& type,
SomeOtherType& ref1 );
virtual MyClass( void );
};
} //namespace
Upon load all is hlighted. Then, I move to the {} and press space.
Ref1 and its type is white. Then I left click somewhere on the buffer
and someType and its type is white.
I can reproduce this, too.
Post by Dixon Ryan (ETAS/ERS-PD2)
IF, I remove the doxygen comment block - all those lines with ///
then the problem does not happen. It therefore looks like the parsing
of doxygen is causing the issue?
It's even stranger. If you remove any single long line from that
doxygen block, the problem doesn't happen. If you remove a short line
from the block the problem remains. Hmm....
Post by Dixon Ryan (ETAS/ERS-PD2)
Thank you.
Thank you, too! I'm looking at this bug at the moment, but I don't know
how long it's going to take me to diagnose. I'll be in touch again once
I've got something to report.
--
Alan Mackenzie (Nuremberg, Germany).
John Yates
2017-03-12 15:46:55 UTC
Permalink
Post by Alan Mackenzie
This has come up before, and is difficult to resolve, due to ambiguities
in the C++ language. "foo (bar * baz);" might be the declaration of a
"function" foo which takes one parameter of type bar*. It might equally
well be a function call of foo, with argument bar times baz. The
current default for CC Mode is to assume the latter. The same applies
to "foo (bar & baz);"
CC Mode works almost entirely by syntax, not semantics (it is not a
compiler), and this used to work well, before recent developments in
C++. It now works less well. It may be possible to analyse the syntax
a bit more thoroughly, possibly by looking for other clues which
indicate the construct can only be a declaration, but this will not be a
complete solution.
It occurs to me that in most code there is an important clue indicating
whether the author believes that s/he is composing a declaration or
an expression, namely symmetric vs asymmetric use of white space:

foo(bar* baz) // decl
foo(bar *baz) // decl
foo(bar*baz) // expr
foo(bar * baz) // expr

Clearly identical logic can be applied to '&'.

Admitted this is no more fundamentally correct than CC mode's purely
syntax-based approach. Still it might end up being correct more often.

/john
Alan Mackenzie
2017-03-12 16:48:29 UTC
Permalink
Hello, John.
Post by John Yates
Post by Alan Mackenzie
This has come up before, and is difficult to resolve, due to ambiguities
in the C++ language. "foo (bar * baz);" might be the declaration of a
"function" foo which takes one parameter of type bar*. It might equally
well be a function call of foo, with argument bar times baz. The
current default for CC Mode is to assume the latter. The same applies
to "foo (bar & baz);"
CC Mode works almost entirely by syntax, not semantics (it is not a
compiler), and this used to work well, before recent developments in
C++. It now works less well. It may be possible to analyse the syntax
a bit more thoroughly, possibly by looking for other clues which
indicate the construct can only be a declaration, but this will not be a
complete solution.
It occurs to me that in most code there is an important clue indicating
whether the author believes that s/he is composing a declaration or
foo(bar* baz) // decl
foo(bar *baz) // decl
foo(bar*baz) // expr
foo(bar * baz) // expr
Clearly identical logic can be applied to '&'.
Admitted this is no more fundamentally correct than CC mode's purely
syntax-based approach. Still it might end up being correct more often.
What a horrible idea! ;-) Still, it's the least bad heuristic
anybody's come up with so far. I think I'll use it.

Thanks!
Post by John Yates
/john
--
Alan Mackenzie (Nuremberg, Germany).
Dixon Ryan (ETAS/ERS-PD2)
2017-03-13 09:04:03 UTC
Permalink
Post by John Yates
foo(bar* baz) // decl
foo(bar *baz) // decl
foo(bar*baz) // expr
foo(bar * baz) // expr
Clearly identical logic can be applied to '&'.
Admitted this is no more fundamentally correct than CC mode's purely
syntax-based approach. Still it might end up being correct more often.
What a horrible idea! ;-) Still, it's the least bad heuristic anybody's come up with so far. I think I'll use it.
I feel like I have no place to say anything since I have never contributed to this project, but this determination using white space is something that I cannot really see as a strong-enough heuristic. For example, this has never applied to any code base that I know. There has never been anything in coding guidelines: when declaring thou shall do it like this: type * name | type*name, when expressing thou shall .... . However, this is the difference in c/c++: I can imagine said rule being more natural for c and I have been working in c++ for a while now. I was going to suggest assuming that it's a declaration if you are in a header file (which is not necessarily h or hpp of course) and a function call in other situations; this has its flaws too, of course. If you are in a c mode it is likely to have a function prototype etc.

I am left wondering if it is not possible for cc* to try and keep track of brace context via a stack structure or something ? For example, we know that cc* can determine a function definition; if cc* keeps track that it is still in a function definition then any foo(bar*baz); is clearly an expression. In all other cases it is a declaration?

Mit freundlichen Grüßen, all the best.
Alan Mackenzie
2017-03-22 21:22:51 UTC
Permalink
Hello, Ryan.
Post by Dixon Ryan (ETAS/ERS-PD2)
Post by Alan Mackenzie
Post by John Yates
foo(bar* baz) // decl
foo(bar *baz) // decl
foo(bar*baz) // expr
foo(bar * baz) // expr
Clearly identical logic can be applied to '&'.
Admitted this is no more fundamentally correct than CC mode's purely
syntax-based approach. Still it might end up being correct more often.
What a horrible idea! ;-) Still, it's the least bad heuristic
anybody's come up with so far. I think I'll use it.
I feel like I have no place to say anything since I have never
contributed to this project, ....
You have. You've submitted a bug report. Without bug reports, CC Mode
would be a much rougher beast than it actually is. :-)
Post by Dixon Ryan (ETAS/ERS-PD2)
.... but this determination using white space is something that I
cannot really see as a strong-enough heuristic. For example, this has
never applied to any code base that I know. There has never been
anything in coding guidelines: when declaring thou shall do it like
this: type * name | type*name, when expressing thou shall .... .
However, this is the difference in c/c++: I can imagine said rule being
more natural for c and I have been working in c++ for a while now.
In the end I've implemented this rule, but created a user option to be
able to disable it.
Post by Dixon Ryan (ETAS/ERS-PD2)
I was going to suggest assuming that it's a declaration if you are in a
header file (which is not necessarily h or hpp of course) and a
function call in other situations; this has its flaws too, of course.
If you are in a c mode it is likely to have a function prototype etc.
This wouldn't work - there are lots of declarations in non-header files
too.
Post by Dixon Ryan (ETAS/ERS-PD2)
I am left wondering if it is not possible for cc* to try and keep track
of brace context via a stack structure or something ? For example, we
know that cc* can determine a function definition; if cc* keeps track
that it is still in a function definition then any foo(bar*baz); is
clearly an expression. In all other cases it is a declaration?
I think that could be done, but it would be considerably slower. The
current way of doing fontification was first written about 15 years ago,
when PCs were much slower than now. Even so, fontification now is not as
fast as I would like: if you scroll a page with C-v, you'll likely notice
a delay whilst the fontification catches up. Auto-repeating C-v (except
in a very small window) doesn't really work. (Hence the user option
`fast-but-imprecise-scrolling'.)
Post by Dixon Ryan (ETAS/ERS-PD2)
Mit freundlichen Grüßen, all the best.
And to yourself, too!
--
Alan Mackenzie (Nuremberg, Germany).
Alan Mackenzie
2017-03-20 22:12:45 UTC
Permalink
Hello again, Michael.
Post by Alan Mackenzie
Hello, John.
Post by John Yates
It occurs to me that in most code there is an important clue indicating
whether the author believes that s/he is composing a declaration or
foo(bar* baz) // decl
foo(bar *baz) // decl
foo(bar*baz) // expr
foo(bar * baz) // expr
Clearly identical logic can be applied to '&'.
Admitted this is no more fundamentally correct than CC mode's purely
syntax-based approach. Still it might end up being correct more often.
What a horrible idea! ;-) Still, it's the least bad heuristic
anybody's come up with so far. I think I'll use it.
It is a horrible idea. At least, I think so. :/
Thank you for saying so. I've given it some extra thought.
I have been using the spaced out style for types for a while. I do this
for much the same reason you would put spaces around expression
operators: a type expression is an algebra of types, and it seems
cleaner to separate the bits of this algebra the same way you have to
separate the word based tokens like "int const".
This "asymmetry rule" would only come into play at the very end of the
function which attempts to parse a declaration, when there is still
ambiguity. I.e., something like

foo (bar * baz);

, which might be a declaration, or might be a function call. (And I
suppose, in recent C++s, it might even be an initialisation.) Currently,
this "bar * baz" doesn't get fontified. Under the "asymmetry rule" it
would continue not to be fontified. Only if it were written

foo (bar *baz);

or

foo (bar* baz);

, but not

foo (bar*baz);

would the "bar * baz" get fontified as a declaration. The same applies
to the "&" operator.

But somewhere, there is going to be some inventive soul who writes (at
least some of) his multiplications as

bar* baz

, perhaps conceptualising "bar*" as a sort of operator, much the way we
have `1+' in Lisp. For such users, an option to disable this "asymmetry
fontification" is really called for. Perhaps some ugly name like
`c-asymmetry-fontification-flag', set to non-nil by default.
Ah, well. To be honest, syntax highlighting is far less important to me
than proper indentation, so I won't be too upset by a change like this.
:-)

By the way, I apologise for not having tracked down the state cache
discrepancy you reported on 2016-04-11. I haven't been able to reproduce
the problem.
--
Michael Welsh Duggan
--
Alan Mackenzie (Nuremberg, Germany).
Alan Mackenzie
2017-03-22 21:42:18 UTC
Permalink
Hello again, Ryan.

On Fri, Mar 10, 2017 at 12:27:25 +0000, Dixon Ryan (ETAS/ERS-PD2) wrote:

[ .... ]
Post by Dixon Ryan (ETAS/ERS-PD2)
I have a constructor with three arguments. The first and second
arguments are not highlighted correctly. The issue is with references
or pointers to user-defined types and seems to manifest mostly when
there is no scope resolution involved with the user defined type -
Class OneClassHere : public AnotherClassThere[]
{
/// lots of stuff
Constructor( MyType1& ref,
NS1::MyType2& ref1,
MyType3& ref2 );
/// lots of stuff
};
This is all highlighted correctly on initial entry to the file.
However, when I put the cursor at [] and add a space the ref2 and its
type are no longer highlighted. Then, when I click anywhere on the
screen ref and its type are not highlighted. Ref1 stays highlighted.
If I reload the file it is all highlighted correctly. Also, this bug
only occurs when there is a newline at the end of the arguments. For
Constructor(MyType1& ref, NS1::MyType2& ref1, MyType3& ref2 );
It is always hlight correctly no matter what.
I have a reproducible MyClass.h file (I then invoke c++-mode since it's a .h)
[ .... ]
Post by Dixon Ryan (ETAS/ERS-PD2)
Upon load all is hlighted. Then, I move to the {} and press space.
Ref1 and its type is white. Then I left click somewhere on the buffer
and someType and its type is white.
IF, I remove the doxygen comment block - all those lines with ///
then the problem does not happen. It therefore looks like the parsing
of doxygen is causing the issue?
Thank you.
OK, I think I've fixed these things, or at least most of them. Part of
the fix is implementing the "asymmetric fontification" rule that we've
discussed elsewhere on the thread. You can disable this by setting the
new user option `c-asymmetric-fontification-flag' to nil.

Here's a patch. Would you try it out, please. (In the unlikely event
you want help patching or building CC Mode, feel free to send me private
email.) Please confirm that it fixes the problems in your real code, or
alternatively let me know what's still not right.

Thanks!



diff -r 51f7a9ff5450 cc-engine.el
--- a/cc-engine.el Sat Feb 25 14:39:10 2017 +0000
+++ b/cc-engine.el Wed Mar 22 20:44:25 2017 +0000
@@ -6252,9 +6252,9 @@
(eq (char-before) ?<))
(c-backward-token-2)
(when (eq (char-after) ?<)
- (c-clear-<-pair-props-if-match-after beg)))
+ (c-clear-<-pair-props-if-match-after beg)
+ (setq new-beg (point))))
(c-forward-syntactic-ws)
- (setq new-beg (point))

;; ...Then the ones with < before end and > after end.
(goto-char (if end-lit-limits (cdr end-lit-limits) end))
@@ -6263,9 +6263,9 @@
(eq (char-before) ?>))
(c-end-of-current-token)
(when (eq (char-before) ?>)
- (c-clear->-pair-props-if-match-before end (1- (point)))))
+ (c-clear->-pair-props-if-match-before end (1- (point)))
+ (setq new-end (point))))
(c-backward-syntactic-ws)
- (setq new-end (point))

;; Extend the fontification region, if needed.
(and new-beg
@@ -8863,7 +8863,29 @@
;; 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)))
+ (throw 'at-decl-or-cast t))
+
+ ;; CASE 17.5
+ (when (and c-asymmetry-fontification-flag
+ got-prefix-before-parens
+ at-type
+ (or (not got-suffix)
+ at-decl-start))
+ (let ((space-before-id
+ (save-excursion
+ (goto-char name-start)
+ (or (bolp) (memq (char-before) '(?\ ?\t)))))
+ (space-after-type
+ (save-excursion
+ (goto-char type-start)
+ (and (c-forward-type)
+ (progn (c-backward-syntactic-ws) t)
+ (or (eolp)
+ (memq (char-after) '(?\ ?\t)))))))
+ (when (not (eq (not space-before-id)
+ (not space-after-type)))
+ (setq maybe-expression t)
+ (throw 'at-decl-or-cast t)))))

;; CASE 18
(when (and (not (memq context '(nil top)))
diff -r 51f7a9ff5450 cc-fonts.el
--- a/cc-fonts.el Sat Feb 25 14:39:10 2017 +0000
+++ b/cc-fonts.el Wed Mar 22 20:44:25 2017 +0000
@@ -1116,6 +1116,121 @@
(setq pos (point)))))) ; acts to make the `while' form continue.
nil)

+(defun c-get-fontification-context (match-pos not-front-decl &optional toplev)
+ ;; Return a cons (CONTEXT . RESTRICTED-<>-ARGLISTS) for MATCH-POS.
+ ;;
+ ;; CONTEXT is the fontification context of MATCH-POS, and is one of the
+ ;; following:
+ ;; '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 Definitely not in a declaration.
+ ;;
+ ;; RESTRICTED-<>-ARGLISTS is non-nil ......
+ (let ((type (and (> match-pos (point-min))
+ (c-get-char-property (1- match-pos) 'c-type))))
+ (cond ((not (memq (char-before match-pos) '(?\( ?, ?\[ ?< ?{)))
+ (cons (and toplev 'top) nil))
+ ;; A control flow expression or a decltype
+ ((and (eq (char-before match-pos) ?\()
+ (save-excursion
+ (goto-char match-pos)
+ (backward-char)
+ (c-backward-token-2)
+ (or (looking-at c-block-stmt-2-key)
+ (looking-at c-block-stmt-1-2-key)
+ (looking-at c-typeof-key))))
+ (cons nil t)) ; (2017-03-19): perhaps 'not-decl???
+ ;; Near BOB.
+ ((<= match-pos (point-min))
+ (cons 'arglist t))
+ ;; Got a cached hit in a declaration arglist.
+ ((eq type 'c-decl-arg-start)
+ (cons 'decl nil))
+ ;; We're inside (probably) a brace list.
+ ((eq type 'c-not-decl)
+ (cons 'not-decl nil))
+ ;; Inside a C++11 lambda function arglist.
+ ((and (c-major-mode-is 'c++-mode)
+ (eq (char-before match-pos) ?\()
+ (save-excursion
+ (goto-char match-pos)
+ (c-backward-token-2)
+ (and
+ (c-safe (goto-char (scan-sexps (point) -1)))
+ (c-looking-at-c++-lambda-capture-list))))
+ (c-put-char-property (1- match-pos) 'c-type
+ 'c-decl-arg-start)
+ (cons 'decl nil))
+ ;; We're inside a brace list.
+ ((and (eq (char-before match-pos) ?{)
+ (save-excursion
+ (goto-char (1- match-pos))
+ (consp
+ (c-looking-at-or-maybe-in-bracelist))))
+ (c-put-char-property (1- match-pos) 'c-type
+ 'c-not-decl)
+ (cons 'not-decl nil))
+ ;; We're inside an "ordinary" open brace.
+ ((eq (char-before match-pos) ?{)
+ (cons (and toplev 'top) nil))
+ ;; Inside an angle bracket arglist.
+ ((or (eq type 'c-<>-arg-sep)
+ (eq (char-before match-pos) ?<))
+ (cons '<> nil))
+ ;; Got a cached hit in some other type of arglist.
+ (type
+ (cons 'arglist t))
+ (;; (if inside-macro
+ ;; (< match-pos max-type-decl-end-before-token)
+ ;; (< match-pos max-type-decl-end))
+ not-front-decl
+ ;; The point is within the range of a previously
+ ;; encountered type decl expression, so the arglist
+ ;; is probably one that contains declarations.
+ ;; However, if `c-recognize-paren-inits' is set it
+ ;; might also be an initializer arglist.
+ ;;
+ ;; The result of this check is cached with a char
+ ;; property on the match token, so that we can look
+ ;; it up again when refontifying single lines in a
+ ;; multiline declaration.
+ (c-put-char-property (1- match-pos)
+ 'c-type 'c-decl-arg-start)
+ (cons 'decl nil))
+ ;; Got an open paren preceded by an arith operator.
+ ((and (eq (char-before match-pos) ?\()
+ (save-excursion
+ (and (zerop (c-backward-token-2 2))
+ (looking-at c-arithmetic-op-regexp))))
+ (cons nil nil)) ; (2017-03-19): perhaps 'not-decl???
+ ;; At start of a declaration inside a declaration paren.
+ ((save-excursion
+ (and (memq (char-before match-pos) '(?\( ?\,))
+ (c-go-up-list-backward match-pos)
+ (eq (char-after) ?\()
+ (let ((type (c-get-char-property (point) 'c-type)))
+ (or (memq type '(c-decl-arg-start c-decl-type-start))
+ (and
+ (progn (c-backward-syntactic-ws) t)
+ (c-back-over-compound-identifier)
+ (progn
+ (c-backward-syntactic-ws)
+ (or (bobp)
+ (progn
+ (setq type (c-get-char-property (1- (point))
+ 'c-type))
+ (memq type '(c-decl-arg-start
+ c-decl-type-start))))))))))
+ (cons 'decl nil))
+ (t (cons 'arglist t)))))
+
(defun c-font-lock-declarations (limit)
;; Fontify all the declarations, casts and labels from the point to LIMIT.
;; Assumes that strings and comments have been fontified already.
@@ -1230,95 +1345,15 @@
;; "<" for the sake of C++-style template arglists.
;; Ignore "(" when it's part of a control flow construct
;; (e.g. "for (").
- (let ((type (and (> match-pos (point-min))
- (c-get-char-property (1- match-pos) 'c-type))))
- (cond ((not (memq (char-before match-pos) '(?\( ?, ?\[ ?< ?{)))
- (setq context (and toplev 'top)
- c-restricted-<>-arglists nil))
- ;; A control flow expression or a decltype
- ((and (eq (char-before match-pos) ?\()
- (save-excursion
- (goto-char match-pos)
- (backward-char)
- (c-backward-token-2)
- (or (looking-at c-block-stmt-2-key)
- (looking-at c-block-stmt-1-2-key)
- (looking-at c-typeof-key))))
- (setq context nil
- c-restricted-<>-arglists t))
- ;; Near BOB.
- ((<= match-pos (point-min))
- (setq context 'arglist
- c-restricted-<>-arglists t))
- ;; Got a cached hit in a declaration arglist.
- ((eq type 'c-decl-arg-start)
- (setq context 'decl
- c-restricted-<>-arglists nil))
- ;; We're inside (probably) a brace list.
- ((eq type 'c-not-decl)
- (setq context 'not-decl
- c-restricted-<>-arglists nil))
- ;; Inside a C++11 lambda function arglist.
- ((and (c-major-mode-is 'c++-mode)
- (eq (char-before match-pos) ?\()
- (save-excursion
- (goto-char match-pos)
- (c-backward-token-2)
- (and
- (c-safe (goto-char (scan-sexps (point) -1)))
- (c-looking-at-c++-lambda-capture-list))))
- (setq context 'decl
- c-restricted-<>-arglists nil)
- (c-put-char-property (1- match-pos) 'c-type
- 'c-decl-arg-start))
- ;; We're inside a brace list.
- ((and (eq (char-before match-pos) ?{)
- (save-excursion
- (goto-char (1- match-pos))
- (consp
- (c-looking-at-or-maybe-in-bracelist))))
- (setq context 'not-decl
- c-restricted-<>-arglists nil)
- (c-put-char-property (1- match-pos) 'c-type
- 'c-not-decl))
- ;; We're inside an "ordinary" open brace.
- ((eq (char-before match-pos) ?{)
- (setq context (and toplev 'top)
- c-restricted-<>-arglists nil))
- ;; Inside an angle bracket arglist.
- ((or (eq type 'c-<>-arg-sep)
- (eq (char-before match-pos) ?<))
- (setq context '<>
- c-restricted-<>-arglists nil))
- ;; Got a cached hit in some other type of arglist.
- (type
- (setq context 'arglist
- c-restricted-<>-arglists t))
- ((if inside-macro
- (< match-pos max-type-decl-end-before-token)
- (< match-pos max-type-decl-end))
- ;; The point is within the range of a previously
- ;; encountered type decl expression, so the arglist
- ;; is probably one that contains declarations.
- ;; However, if `c-recognize-paren-inits' is set it
- ;; might also be an initializer arglist.
- (setq context 'decl
- c-restricted-<>-arglists nil)
- ;; The result of this check is cached with a char
- ;; property on the match token, so that we can look
- ;; it up again when refontifying single lines in a
- ;; multiline declaration.
- (c-put-char-property (1- match-pos)
- 'c-type 'c-decl-arg-start))
- ;; Got an open paren preceded by an arith operator.
- ((and (eq (char-before match-pos) ?\()
- (save-excursion
- (and (zerop (c-backward-token-2 2))
- (looking-at c-arithmetic-op-regexp))))
- (setq context nil
- c-restricted-<>-arglists nil))
- (t (setq context 'arglist
- c-restricted-<>-arglists t))))
+ (let ((got-context
+ (c-get-fontification-context
+ match-pos
+ (< match-pos (if inside-macro
+ max-type-decl-end-before-token
+ max-type-decl-end))
+ toplev)))
+ (setq context (car got-context)
+ c-restricted-<>-arglists (cdr got-context)))

;; Check we haven't missed a preceding "typedef".
(when (not (looking-at c-typedef-key))
diff -r 51f7a9ff5450 cc-mode.el
--- a/cc-mode.el Sat Feb 25 14:39:10 2017 +0000
+++ b/cc-mode.el Wed Mar 22 20:44:25 2017 +0000
@@ -1339,6 +1339,7 @@
;; This function is called indirectly from font locking stuff - either from
;; c-after-change (to prepare for after-change font-locking) or from font
;; lock context (etc.) fontification.
+ (goto-char pos)
(let ((lit-start (c-literal-start))
(new-pos pos)
capture-opener
diff -r 51f7a9ff5450 cc-vars.el
--- a/cc-vars.el Sat Feb 25 14:39:10 2017 +0000
+++ b/cc-vars.el Wed Mar 22 20:44:25 2017 +0000
@@ -1633,6 +1633,18 @@
:type 'c-extra-types-widget
:group 'c)

+(defcustom c-asymmetry-fontification-flag t
+ "Whether to fontify certain ambiguous constructs by white space asymmetry.
+
+In the fontification engine, it is sometimes impossible to determine
+whether a construct is a declaration or an expression. This happens
+particularly in C++, due to ambiguities in the language. When such a
+construct is like \"foo * bar\" or \"foo &bar\", and this variable is non-nil
+(the default), the construct will be fontified as a declaration if there is
+white space either before or after the operator, but not both."
+ :type 'boolean
+ :group 'c)
+
(defvar c-noise-macro-with-parens-name-re "\\<\\>")
(defvar c-noise-macro-name-re "\\<\\>")
--
Alan Mackenzie (Nuremberg, Germany).
Loading...