Skip to content

Commit

Permalink
[clang-format] C# short ternary operator misinterpreted as a CSharpNu…
Browse files Browse the repository at this point in the history
…llable

Refactor the CSharpNullable assignment code to be a little easier to read (Honestly I don't like it when an if expression get really long and complicated).
Handle the case where '?' is actually a ternary operator.

Fixes: #58067

Reviewed By: owenpan, HazardyKnusperkeks

Differential Revision: https://reviews.llvm.org/D148473
  • Loading branch information
mydeveloperday committed Apr 18, 2023
1 parent a716ace commit 799b794
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 4 deletions.
31 changes: 27 additions & 4 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1312,11 +1312,34 @@ class AnnotatingParser {
if (Style.isCSharp()) {
// `Type?)`, `Type?>`, `Type? name;` and `Type? name =` can only be
// nullable types.

// `Type?)`, `Type?>`, `Type? name;`
if (Tok->Next &&
(Tok->Next->startsSequence(tok::question, tok::r_paren) ||
Tok->Next->startsSequence(tok::question, tok::greater) ||
Tok->Next->startsSequence(tok::question, tok::identifier,
tok::semi))) {
Tok->setType(TT_CSharpNullable);
break;
}

// `Type? name =`
if (Tok->Next && Tok->Next->is(tok::identifier) && Tok->Next->Next &&
Tok->Next->Next->is(tok::equal)) {
Tok->setType(TT_CSharpNullable);
break;
}

// Line.MustBeDeclaration will be true for `Type? name;`.
if ((!Contexts.back().IsExpression && Line.MustBeDeclaration) ||
(Tok->Next && Tok->Next->isOneOf(tok::r_paren, tok::greater)) ||
(Tok->Next && Tok->Next->is(tok::identifier) && Tok->Next->Next &&
Tok->Next->Next->is(tok::equal))) {
// But not
// cond ? "A" : "B";
// cond ? id : "B";
// cond ? cond2 ? "A" : "B" : "C";
if (!Contexts.back().IsExpression && Line.MustBeDeclaration &&
(!Tok->Next ||
!Tok->Next->isOneOf(tok::identifier, tok::string_literal) ||
!Tok->Next->Next ||
!Tok->Next->Next->isOneOf(tok::colon, tok::question))) {
Tok->setType(TT_CSharpNullable);
break;
}
Expand Down
28 changes: 28 additions & 0 deletions clang/unittests/Format/TokenAnnotatorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1724,6 +1724,34 @@ TEST_F(TokenAnnotatorTest, UnderstandsConditionParens) {
EXPECT_TOKEN(Tokens[8], tok::l_paren, TT_ConditionLParen);
}

TEST_F(TokenAnnotatorTest, CSharpNullableTypes) {
FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);

auto Tokens = annotate("int? a;", Style);
EXPECT_EQ(Tokens.size(), 5u) << Tokens;
EXPECT_TOKEN(Tokens[1], tok::question, TT_CSharpNullable);

Tokens = annotate("int? a = 1;", Style);
EXPECT_EQ(Tokens.size(), 7u) << Tokens;
EXPECT_TOKEN(Tokens[1], tok::question, TT_CSharpNullable);

Tokens = annotate("int?)", Style);
EXPECT_EQ(Tokens.size(), 4u) << Tokens;
EXPECT_TOKEN(Tokens[1], tok::question, TT_CSharpNullable);

Tokens = annotate("int?>", Style);
EXPECT_EQ(Tokens.size(), 4u) << Tokens;
EXPECT_TOKEN(Tokens[1], tok::question, TT_CSharpNullable);

Tokens = annotate("cond? id : id2", Style);
EXPECT_EQ(Tokens.size(), 6u) << Tokens;
EXPECT_TOKEN(Tokens[1], tok::question, TT_ConditionalExpr);

Tokens = annotate("cond ? cond2 ? : id1 : id2", Style);
EXPECT_EQ(Tokens.size(), 9u) << Tokens;
EXPECT_TOKEN(Tokens[1], tok::question, TT_ConditionalExpr);
}

} // namespace
} // namespace format
} // namespace clang

0 comments on commit 799b794

Please sign in to comment.