Skip to content

Commit 73c961a

Browse files
authored
[clang-format] Fix misannotations of < in ternary expressions (llvm#100980)
Fixes llvm#100300.
1 parent 3a9ef4e commit 73c961a

File tree

2 files changed

+49
-16
lines changed

2 files changed

+49
-16
lines changed

clang/lib/Format/TokenAnnotator.cpp

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,8 @@ class AnnotatingParser {
155155
if (NonTemplateLess.count(CurrentToken->Previous) > 0)
156156
return false;
157157

158-
const FormatToken &Previous = *CurrentToken->Previous; // The '<'.
159-
if (Previous.Previous) {
158+
if (const auto &Previous = *CurrentToken->Previous; // The '<'.
159+
Previous.Previous) {
160160
if (Previous.Previous->Tok.isLiteral())
161161
return false;
162162
if (Previous.Previous->is(tok::r_brace))
@@ -176,31 +176,38 @@ class AnnotatingParser {
176176
FormatToken *Left = CurrentToken->Previous;
177177
Left->ParentBracket = Contexts.back().ContextKind;
178178
ScopedContextCreator ContextCreator(*this, tok::less, 12);
179-
180179
Contexts.back().IsExpression = false;
180+
181+
const auto *BeforeLess = Left->Previous;
182+
181183
// If there's a template keyword before the opening angle bracket, this is a
182184
// template parameter, not an argument.
183-
if (Left->Previous && Left->Previous->isNot(tok::kw_template))
185+
if (BeforeLess && BeforeLess->isNot(tok::kw_template))
184186
Contexts.back().ContextType = Context::TemplateArgument;
185187

186188
if (Style.Language == FormatStyle::LK_Java &&
187189
CurrentToken->is(tok::question)) {
188190
next();
189191
}
190192

191-
while (CurrentToken) {
193+
for (bool SeenTernaryOperator = false; CurrentToken;) {
194+
const bool InExpr = Contexts[Contexts.size() - 2].IsExpression;
192195
if (CurrentToken->is(tok::greater)) {
196+
const auto *Next = CurrentToken->Next;
193197
// Try to do a better job at looking for ">>" within the condition of
194198
// a statement. Conservatively insert spaces between consecutive ">"
195199
// tokens to prevent splitting right bitshift operators and potentially
196200
// altering program semantics. This check is overly conservative and
197201
// will prevent spaces from being inserted in select nested template
198202
// parameter cases, but should not alter program semantics.
199-
if (CurrentToken->Next && CurrentToken->Next->is(tok::greater) &&
203+
if (Next && Next->is(tok::greater) &&
200204
Left->ParentBracket != tok::less &&
201205
CurrentToken->getStartOfNonWhitespace() ==
202-
CurrentToken->Next->getStartOfNonWhitespace().getLocWithOffset(
203-
-1)) {
206+
Next->getStartOfNonWhitespace().getLocWithOffset(-1)) {
207+
return false;
208+
}
209+
if (InExpr && SeenTernaryOperator &&
210+
(!Next || !Next->isOneOf(tok::l_paren, tok::l_brace))) {
204211
return false;
205212
}
206213
Left->MatchingParen = CurrentToken;
@@ -211,14 +218,14 @@ class AnnotatingParser {
211218
// msg: < item: data >
212219
// In TT_TextProto, map<key, value> does not occur.
213220
if (Style.Language == FormatStyle::LK_TextProto ||
214-
(Style.Language == FormatStyle::LK_Proto && Left->Previous &&
215-
Left->Previous->isOneOf(TT_SelectorName, TT_DictLiteral))) {
221+
(Style.Language == FormatStyle::LK_Proto && BeforeLess &&
222+
BeforeLess->isOneOf(TT_SelectorName, TT_DictLiteral))) {
216223
CurrentToken->setType(TT_DictLiteral);
217224
} else {
218225
CurrentToken->setType(TT_TemplateCloser);
219226
CurrentToken->Tok.setLength(1);
220227
}
221-
if (CurrentToken->Next && CurrentToken->Next->Tok.isLiteral())
228+
if (Next && Next->Tok.isLiteral())
222229
return false;
223230
next();
224231
return true;
@@ -230,18 +237,21 @@ class AnnotatingParser {
230237
}
231238
if (CurrentToken->isOneOf(tok::r_paren, tok::r_square, tok::r_brace))
232239
return false;
240+
const auto &Prev = *CurrentToken->Previous;
233241
// If a && or || is found and interpreted as a binary operator, this set
234242
// of angles is likely part of something like "a < b && c > d". If the
235243
// angles are inside an expression, the ||/&& might also be a binary
236244
// operator that was misinterpreted because we are parsing template
237245
// parameters.
238246
// FIXME: This is getting out of hand, write a decent parser.
239-
if (CurrentToken->Previous->isOneOf(tok::pipepipe, tok::ampamp) &&
240-
CurrentToken->Previous->is(TT_BinaryOperator) &&
241-
Contexts[Contexts.size() - 2].IsExpression &&
242-
!Line.startsWith(tok::kw_template)) {
243-
return false;
247+
if (InExpr && !Line.startsWith(tok::kw_template) &&
248+
Prev.is(TT_BinaryOperator)) {
249+
const auto Precedence = Prev.getPrecedence();
250+
if (Precedence > prec::Conditional && Precedence < prec::Relational)
251+
return false;
244252
}
253+
if (Prev.is(TT_ConditionalExpr))
254+
SeenTernaryOperator = true;
245255
updateParameterCount(Left, CurrentToken);
246256
if (Style.Language == FormatStyle::LK_Proto) {
247257
if (FormatToken *Previous = CurrentToken->getPreviousNonComment()) {

clang/unittests/Format/TokenAnnotatorTest.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,12 +577,20 @@ TEST_F(TokenAnnotatorTest, UnderstandsTernaryInTemplate) {
577577
EXPECT_TOKEN(Tokens[7], tok::greater, TT_TemplateCloser);
578578

579579
// IsExpression = true
580+
580581
Tokens = annotate("return foo<true ? 1 : 2>();");
581582
ASSERT_EQ(Tokens.size(), 13u) << Tokens;
582583
EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
583584
EXPECT_TOKEN(Tokens[4], tok::question, TT_ConditionalExpr);
584585
EXPECT_TOKEN(Tokens[6], tok::colon, TT_ConditionalExpr);
585586
EXPECT_TOKEN(Tokens[8], tok::greater, TT_TemplateCloser);
587+
588+
Tokens = annotate("return foo<true ? 1 : 2>{};");
589+
ASSERT_EQ(Tokens.size(), 13u) << Tokens;
590+
EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
591+
EXPECT_TOKEN(Tokens[4], tok::question, TT_ConditionalExpr);
592+
EXPECT_TOKEN(Tokens[6], tok::colon, TT_ConditionalExpr);
593+
EXPECT_TOKEN(Tokens[8], tok::greater, TT_TemplateCloser);
586594
}
587595

588596
TEST_F(TokenAnnotatorTest, UnderstandsNonTemplateAngleBrackets) {
@@ -596,6 +604,21 @@ TEST_F(TokenAnnotatorTest, UnderstandsNonTemplateAngleBrackets) {
596604
EXPECT_TOKEN(Tokens[1], tok::less, TT_BinaryOperator);
597605
EXPECT_TOKEN(Tokens[7], tok::greater, TT_BinaryOperator);
598606

607+
Tokens = annotate("return A < B ? true : A > B;");
608+
ASSERT_EQ(Tokens.size(), 12u) << Tokens;
609+
EXPECT_TOKEN(Tokens[2], tok::less, TT_BinaryOperator);
610+
EXPECT_TOKEN(Tokens[8], tok::greater, TT_BinaryOperator);
611+
612+
Tokens = annotate("return A < B ? true : A > B ? false : false;");
613+
ASSERT_EQ(Tokens.size(), 16u) << Tokens;
614+
EXPECT_TOKEN(Tokens[2], tok::less, TT_BinaryOperator);
615+
EXPECT_TOKEN(Tokens[8], tok::greater, TT_BinaryOperator);
616+
617+
Tokens = annotate("return A < B ^ A > B;");
618+
ASSERT_EQ(Tokens.size(), 10u) << Tokens;
619+
EXPECT_TOKEN(Tokens[2], tok::less, TT_BinaryOperator);
620+
EXPECT_TOKEN(Tokens[6], tok::greater, TT_BinaryOperator);
621+
599622
Tokens = annotate("ratio{-1, 2} < ratio{-1, 3} == -1 / 3 > -1 / 2;");
600623
ASSERT_EQ(Tokens.size(), 27u) << Tokens;
601624
EXPECT_TOKEN(Tokens[7], tok::less, TT_BinaryOperator);

0 commit comments

Comments
 (0)