diff options
author | Paul Hoad <mydeveloperday@gmail.com> | 2019-10-04 14:16:59 +0000 |
---|---|---|
committer | Paul Hoad <mydeveloperday@gmail.com> | 2019-10-04 14:16:59 +0000 |
commit | 96aaa24db16a88715a6b8e92a4929e48d12f3835 (patch) | |
tree | 47176d7e9c88a9d22e82091bf62a3212042e31f7 | |
parent | 35019497fe483b5bf762d7ec5580585b6f50de4b (diff) | |
download | clang-96aaa24db16a88715a6b8e92a4929e48d12f3835.tar.gz |
[clang-format] [PR43531] clang-format damages "alternative representations" for operators
Summary:
https://bugs.llvm.org/show_bug.cgi?id=43531
Fix for clang-format incorrectly handles "alternative operators" as described by https://en.cppreference.com/w/cpp/language/operator_alternative
compl = ~
not = !
these are unary operators, and clang-format will remove the space between them and a numeric constant
this incorrectly formats the following code
```
int a compl 5;
int a not 5;
```
into:
```
int a compl5;
int a not5;
```
The code adds FIXME unit tests for "alternative token" representations for {} [] and # as defined by the same link, which would require a more detailed change to the FormatTokenLexer
Reviewers: klimek, reuk, owenpan, mitchell-stellar, STL_MSFT
Reviewed By: mitchell-stellar
Subscribers: cfe-commits
Tags: #clang-format, #clang-tools-extra, #clang
Differential Revision: https://reviews.llvm.org/D68332
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@373750 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/Format/TokenAnnotator.cpp | 12 | ||||
-rw-r--r-- | unittests/Format/FormatTest.cpp | 42 |
2 files changed, 53 insertions, 1 deletions
diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index 50e3d056b8..0c41723821 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -2900,9 +2900,19 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, return false; return true; } - if (Left.is(TT_UnaryOperator)) + if (Left.is(TT_UnaryOperator)) { + // The alternative operators for ~ and ! are "compl" and "not". + // If they are used instead, we do not want to combine them with + // the token to the right, unless that is a left paren. + if (!Right.is(tok::l_paren)) { + if (Left.is(tok::exclaim) && Left.TokenText == "not") + return true; + if (Left.is(tok::tilde) && Left.TokenText == "compl") + return true; + } return (Style.SpaceAfterLogicalNot && Left.is(tok::exclaim)) || Right.is(TT_BinaryOperator); + } // If the next token is a binary operator or a selector name, we have // incorrectly classified the parenthesis as a cast. FIXME: Detect correctly. diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index b770d0f26f..14ac6f9137 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -14609,6 +14609,48 @@ TEST_F(FormatTest, AmbersandInLamda) { verifyFormat("auto lambda = [&a = a]() { a = 2; };", AlignStyle); } +TEST_F(FormatTest, AlternativeOperators) { + // Test case for ensuring alternate operators are not + // combined with their right most neighbour. + verifyFormat("int a and b;"); + verifyFormat("int a and_eq b;"); + verifyFormat("int a bitand b;"); + verifyFormat("int a bitor b;"); + verifyFormat("int a compl b;"); + verifyFormat("int a not b;"); + verifyFormat("int a not_eq b;"); + verifyFormat("int a or b;"); + verifyFormat("int a xor b;"); + verifyFormat("int a xor_eq b;"); + verifyFormat("return this not_eq bitand other;"); + verifyFormat("bool operator not_eq(const X bitand other)"); + + verifyFormat("int a and 5;"); + verifyFormat("int a and_eq 5;"); + verifyFormat("int a bitand 5;"); + verifyFormat("int a bitor 5;"); + verifyFormat("int a compl 5;"); + verifyFormat("int a not 5;"); + verifyFormat("int a not_eq 5;"); + verifyFormat("int a or 5;"); + verifyFormat("int a xor 5;"); + verifyFormat("int a xor_eq 5;"); + + verifyFormat("int a compl(5);"); + verifyFormat("int a not(5);"); + + /* FIXME handle alternate tokens + * https://en.cppreference.com/w/cpp/language/operator_alternative + // alternative tokens + verifyFormat("compl foo();"); // ~foo(); + verifyFormat("foo() <%%>;"); // foo(); + verifyFormat("void foo() <%%>;"); // void foo(){} + verifyFormat("int a <:1:>;"); // int a[1];[ + verifyFormat("%:define ABC abc"); // #define ABC abc + verifyFormat("%:%:"); // ## + */ +} + } // end namespace } // end namespace format } // end namespace clang |