Skip to content

Commit a6d97de

Browse files
authored
[clang-format] Correctly annotate C++ alternative operators in C (#92880)
PR #90161 uncovered a bug that annotates C++ xor as UnaryOperator if followed by a binary operator. This patch fixes that and all other C++ alternative operator keywords when followed by a binary operator in C. Fixes #92688.
1 parent 511077d commit a6d97de

File tree

3 files changed

+73
-4
lines changed

3 files changed

+73
-4
lines changed

clang/lib/Format/UnwrappedLineParser.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,13 @@ void UnwrappedLineParser::readTokenWithJavaScriptASI() {
14101410
}
14111411
}
14121412

1413+
static bool isAltOperator(const FormatToken &Tok) {
1414+
return isalpha(Tok.TokenText[0]) &&
1415+
Tok.isOneOf(tok::ampamp, tok::ampequal, tok::amp, tok::pipe,
1416+
tok::tilde, tok::exclaim, tok::exclaimequal, tok::pipepipe,
1417+
tok::pipeequal, tok::caret, tok::caretequal);
1418+
}
1419+
14131420
void UnwrappedLineParser::parseStructuralElement(
14141421
const FormatToken *OpeningBrace, IfStmtKind *IfKind,
14151422
FormatToken **IfLeftBrace, bool *HasDoWhile, bool *HasLabel) {
@@ -1689,9 +1696,15 @@ void UnwrappedLineParser::parseStructuralElement(
16891696
break;
16901697
}
16911698

1692-
const bool InRequiresExpression =
1693-
OpeningBrace && OpeningBrace->is(TT_RequiresExpressionLBrace);
1694-
do {
1699+
for (const bool InRequiresExpression =
1700+
OpeningBrace && OpeningBrace->is(TT_RequiresExpressionLBrace);
1701+
!eof();) {
1702+
if (IsCpp && isAltOperator(*FormatTok)) {
1703+
if (auto *Next = Tokens->peekNextToken(/*SkipComment=*/true);
1704+
Next && Next->isBinaryOperator()) {
1705+
FormatTok->Tok.setKind(tok::identifier);
1706+
}
1707+
}
16951708
const FormatToken *Previous = FormatTok->Previous;
16961709
switch (FormatTok->Tok.getKind()) {
16971710
case tok::at:
@@ -2122,7 +2135,7 @@ void UnwrappedLineParser::parseStructuralElement(
21222135
nextToken();
21232136
break;
21242137
}
2125-
} while (!eof());
2138+
}
21262139
}
21272140

21282141
bool UnwrappedLineParser::tryToParsePropertyAccessor() {

clang/unittests/Format/FormatTest.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17340,12 +17340,14 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeAssignmentOperators) {
1734017340
verifyFormat("int a = 5;");
1734117341
verifyFormat("a += 42;");
1734217342
verifyFormat("a or_eq 8;");
17343+
verifyFormat("xor = foo;");
1734317344

1734417345
FormatStyle Spaces = getLLVMStyle();
1734517346
Spaces.SpaceBeforeAssignmentOperators = false;
1734617347
verifyFormat("int a= 5;", Spaces);
1734717348
verifyFormat("a+= 42;", Spaces);
1734817349
verifyFormat("a or_eq 8;", Spaces);
17350+
verifyFormat("xor= foo;", Spaces);
1734917351
}
1735017352

1735117353
TEST_F(FormatTest, ConfigurableSpaceBeforeColon) {

clang/unittests/Format/TokenAnnotatorTest.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3015,6 +3015,60 @@ TEST_F(TokenAnnotatorTest, SwitchExpression) {
30153015
EXPECT_TOKEN(Tokens[20], tok::arrow, TT_CaseLabelArrow);
30163016
}
30173017

3018+
TEST_F(TokenAnnotatorTest, CppAltOperatorKeywords) {
3019+
auto Tokens = annotate("a = b and c;");
3020+
ASSERT_EQ(Tokens.size(), 7u);
3021+
EXPECT_TOKEN(Tokens[3], tok::ampamp, TT_BinaryOperator);
3022+
3023+
Tokens = annotate("a = b and_eq c;");
3024+
ASSERT_EQ(Tokens.size(), 7u);
3025+
EXPECT_TOKEN(Tokens[3], tok::ampequal, TT_BinaryOperator);
3026+
3027+
Tokens = annotate("a = b bitand c;");
3028+
ASSERT_EQ(Tokens.size(), 7u);
3029+
EXPECT_TOKEN(Tokens[3], tok::amp, TT_BinaryOperator);
3030+
3031+
Tokens = annotate("a = b bitor c;");
3032+
ASSERT_EQ(Tokens.size(), 7u);
3033+
EXPECT_TOKEN(Tokens[3], tok::pipe, TT_BinaryOperator);
3034+
3035+
Tokens = annotate("a = b compl c;");
3036+
ASSERT_EQ(Tokens.size(), 7u);
3037+
EXPECT_TOKEN(Tokens[3], tok::tilde, TT_UnaryOperator);
3038+
3039+
Tokens = annotate("a = b not c;");
3040+
ASSERT_EQ(Tokens.size(), 7u);
3041+
EXPECT_TOKEN(Tokens[3], tok::exclaim, TT_UnaryOperator);
3042+
3043+
Tokens = annotate("a = b not_eq c;");
3044+
ASSERT_EQ(Tokens.size(), 7u);
3045+
EXPECT_TOKEN(Tokens[3], tok::exclaimequal, TT_BinaryOperator);
3046+
3047+
Tokens = annotate("a = b or c;");
3048+
ASSERT_EQ(Tokens.size(), 7u);
3049+
EXPECT_TOKEN(Tokens[3], tok::pipepipe, TT_BinaryOperator);
3050+
3051+
Tokens = annotate("a = b or_eq c;");
3052+
ASSERT_EQ(Tokens.size(), 7u);
3053+
EXPECT_TOKEN(Tokens[3], tok::pipeequal, TT_BinaryOperator);
3054+
3055+
Tokens = annotate("a = b xor c;");
3056+
ASSERT_EQ(Tokens.size(), 7u);
3057+
EXPECT_TOKEN(Tokens[3], tok::caret, TT_BinaryOperator);
3058+
3059+
Tokens = annotate("a = b xor_eq c;");
3060+
ASSERT_EQ(Tokens.size(), 7u);
3061+
EXPECT_TOKEN(Tokens[3], tok::caretequal, TT_BinaryOperator);
3062+
3063+
Tokens = annotate("xor = foo;");
3064+
ASSERT_EQ(Tokens.size(), 5u);
3065+
EXPECT_TOKEN(Tokens[0], tok::identifier, TT_Unknown);
3066+
3067+
Tokens = annotate("int xor = foo;");
3068+
ASSERT_EQ(Tokens.size(), 6u);
3069+
EXPECT_TOKEN(Tokens[1], tok::identifier, TT_StartOfName);
3070+
}
3071+
30183072
} // namespace
30193073
} // namespace format
30203074
} // namespace clang

0 commit comments

Comments
 (0)