[clang] c3af063 - [clang-format] Handle NullCoalescing and NullConditional operators in C#
Jonathan Coe via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 2 06:02:05 PST 2020
Author: Jonathan Coe
Date: 2020-03-02T13:55:54Z
New Revision: c3af063c2bbfe5408d565ee4350c36fc1d0b2758
URL: https://github.com/llvm/llvm-project/commit/c3af063c2bbfe5408d565ee4350c36fc1d0b2758
DIFF: https://github.com/llvm/llvm-project/commit/c3af063c2bbfe5408d565ee4350c36fc1d0b2758.diff
LOG: [clang-format] Handle NullCoalescing and NullConditional operators in C#
Summary:
Disable merging of Type? into a single token.
Merge ?? ?. and ?[ into a single token.
Reviewers: krasimir, MyDeveloperDay
Reviewed By: krasimir
Subscribers: cfe-commits
Tags: #clang-format, #clang
Differential Revision: https://reviews.llvm.org/D75368
Added:
Modified:
clang/lib/Format/FormatToken.h
clang/lib/Format/FormatTokenLexer.cpp
clang/lib/Format/FormatTokenLexer.h
clang/lib/Format/TokenAnnotator.cpp
clang/unittests/Format/FormatTestCSharp.cpp
Removed:
################################################################################
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index 61c3cda89cdc..ac117840ea33 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -102,9 +102,11 @@ namespace format {
TYPE(TypenameMacro) \
TYPE(UnaryOperator) \
TYPE(CSharpStringLiteral) \
- TYPE(CSharpNullCoalescing) \
TYPE(CSharpNamedArgumentColon) \
- TYPE(CSharpNullableTypeQuestionMark) \
+ TYPE(CSharpNullable) \
+ TYPE(CSharpNullCoalescing) \
+ TYPE(CSharpNullConditional) \
+ TYPE(CSharpNullConditionalSq) \
TYPE(Unknown)
enum TokenType {
diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp
index aa8cf427d468..da73361ee3d5 100644
--- a/clang/lib/Format/FormatTokenLexer.cpp
+++ b/clang/lib/Format/FormatTokenLexer.cpp
@@ -84,7 +84,7 @@ void FormatTokenLexer::tryMergePreviousTokens() {
return;
if (tryMergeCSharpDoubleQuestion())
return;
- if (tryMergeCSharpNullConditionals())
+ if (tryMergeCSharpNullConditional())
return;
if (tryTransformCSharpForEach())
return;
@@ -319,7 +319,7 @@ bool FormatTokenLexer::tryMergeCSharpDoubleQuestion() {
auto &SecondQuestion = *(Tokens.end() - 1);
if (!FirstQuestion->is(tok::question) || !SecondQuestion->is(tok::question))
return false;
- FirstQuestion->Tok.setKind(tok::question);
+ FirstQuestion->Tok.setKind(tok::question); // no '??' in clang tokens.
FirstQuestion->TokenText = StringRef(FirstQuestion->TokenText.begin(),
SecondQuestion->TokenText.end() -
FirstQuestion->TokenText.begin());
@@ -329,6 +329,32 @@ bool FormatTokenLexer::tryMergeCSharpDoubleQuestion() {
return true;
}
+// Merge '?[' and '?.' pairs into single tokens.
+bool FormatTokenLexer::tryMergeCSharpNullConditional() {
+ if (Tokens.size() < 2)
+ return false;
+ auto &Question = *(Tokens.end() - 2);
+ auto &PeriodOrLSquare = *(Tokens.end() - 1);
+ if (!Question->is(tok::question) ||
+ !PeriodOrLSquare->isOneOf(tok::l_square, tok::period))
+ return false;
+ Question->TokenText =
+ StringRef(Question->TokenText.begin(),
+ PeriodOrLSquare->TokenText.end() - Question->TokenText.begin());
+ Question->ColumnWidth += PeriodOrLSquare->ColumnWidth;
+
+ if (PeriodOrLSquare->is(tok::l_square)) {
+ Question->Tok.setKind(tok::question); // no '?[' in clang tokens.
+ Question->Type = TT_CSharpNullConditionalSq;
+ } else {
+ Question->Tok.setKind(tok::question); // no '?.' in clang tokens.
+ Question->Type = TT_CSharpNullConditional;
+ }
+
+ Tokens.erase(Tokens.end() - 1);
+ return true;
+}
+
bool FormatTokenLexer::tryMergeCSharpKeywordVariables() {
if (Tokens.size() < 2)
return false;
@@ -348,23 +374,6 @@ bool FormatTokenLexer::tryMergeCSharpKeywordVariables() {
return true;
}
-// In C# merge the Identifier and the ? together e.g. arg?.
-bool FormatTokenLexer::tryMergeCSharpNullConditionals() {
- if (Tokens.size() < 2)
- return false;
- auto &Identifier = *(Tokens.end() - 2);
- auto &Question = *(Tokens.end() - 1);
- if (!Identifier->isOneOf(tok::r_square, tok::identifier) ||
- !Question->is(tok::question))
- return false;
- Identifier->TokenText =
- StringRef(Identifier->TokenText.begin(),
- Question->TokenText.end() - Identifier->TokenText.begin());
- Identifier->ColumnWidth += Question->ColumnWidth;
- Tokens.erase(Tokens.end() - 1);
- return true;
-}
-
// In C# transform identifier foreach into kw_foreach
bool FormatTokenLexer::tryTransformCSharpForEach() {
if (Tokens.size() < 1)
diff --git a/clang/lib/Format/FormatTokenLexer.h b/clang/lib/Format/FormatTokenLexer.h
index 4fffb36272f7..e5726fe18111 100644
--- a/clang/lib/Format/FormatTokenLexer.h
+++ b/clang/lib/Format/FormatTokenLexer.h
@@ -52,8 +52,8 @@ class FormatTokenLexer {
bool tryMergeJSPrivateIdentifier();
bool tryMergeCSharpStringLiteral();
bool tryMergeCSharpKeywordVariables();
- bool tryMergeCSharpNullConditionals();
bool tryMergeCSharpDoubleQuestion();
+ bool tryMergeCSharpNullConditional();
bool tryTransformCSharpForEach();
bool tryMergeCSharpAttributeAndTarget();
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 86a84afafb8f..e1e08686ac44 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -972,6 +972,13 @@ class AnnotatingParser {
}
break;
case tok::question:
+ if (Tok->is(TT_CSharpNullConditionalSq)) {
+ if (!parseSquare())
+ return false;
+ break;
+ }
+ if (Tok->isOneOf(TT_CSharpNullConditional, TT_CSharpNullCoalescing))
+ break;
if (Style.Language == FormatStyle::LK_JavaScript && Tok->Next &&
Tok->Next->isOneOf(tok::semi, tok::comma, tok::colon, tok::r_paren,
tok::r_brace)) {
@@ -987,9 +994,11 @@ class AnnotatingParser {
if (Line.MustBeDeclaration && !Contexts.back().IsExpression &&
Style.Language == FormatStyle::LK_JavaScript)
break;
- if (Style.isCSharp() && Line.MustBeDeclaration) {
- Tok->Type = TT_CSharpNullableTypeQuestionMark;
- break;
+ if (Style.isCSharp()) {
+ if (Line.MustBeDeclaration && !Contexts.back().IsExpression) {
+ Tok->Type = TT_CSharpNullable;
+ break;
+ }
}
parseConditional();
break;
@@ -1437,6 +1446,21 @@ class AnnotatingParser {
// The token type is already known.
return;
+ if (Style.isCSharp() && CurrentToken->is(tok::question)) {
+ if (CurrentToken->TokenText == "??") {
+ Current.Type = TT_CSharpNullCoalescing;
+ return;
+ }
+ if (CurrentToken->TokenText == "?.") {
+ Current.Type = TT_CSharpNullConditional;
+ return;
+ }
+ if (CurrentToken->TokenText == "?[") {
+ Current.Type = TT_CSharpNullConditionalSq;
+ return;
+ }
+ }
+
if (Style.Language == FormatStyle::LK_JavaScript) {
if (Current.is(tok::exclaim)) {
if (Current.Previous &&
@@ -2907,9 +2931,29 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
return Style.SpacesInSquareBrackets;
// No space before ? in nullable types.
- if (Right.is(TT_CSharpNullableTypeQuestionMark))
+ if (Right.is(TT_CSharpNullable))
+ return false;
+
+ // Require space after ? in nullable types.
+ if (Left.is(TT_CSharpNullable))
+ return true;
+
+ // No space before or after '?.'.
+ if (Left.is(TT_CSharpNullConditional) || Right.is(TT_CSharpNullConditional))
return false;
+ // Space before and after '??'.
+ if (Left.is(TT_CSharpNullCoalescing) || Right.is(TT_CSharpNullCoalescing))
+ return true;
+
+ // No space before '?['.
+ if (Right.is(TT_CSharpNullConditionalSq))
+ return false;
+
+ // Possible space inside `?[ 0 ]`.
+ if (Left.is(TT_CSharpNullConditionalSq))
+ return Style.SpacesInSquareBrackets;
+
// space between keywords and paren e.g. "using ("
if (Right.is(tok::l_paren))
if (Left.isOneOf(tok::kw_using, Keywords.kw_async, Keywords.kw_when))
diff --git a/clang/unittests/Format/FormatTestCSharp.cpp b/clang/unittests/Format/FormatTestCSharp.cpp
index 390993881fda..0bc49856375b 100644
--- a/clang/unittests/Format/FormatTestCSharp.cpp
+++ b/clang/unittests/Format/FormatTestCSharp.cpp
@@ -170,6 +170,12 @@ TEST_F(FormatTestCSharp, CSharpFatArrows) {
verifyFormat("public override string ToString() => \"{Name}\\{Age}\";");
}
+TEST_F(FormatTestCSharp, CSharpConditionalExpressions) {
+ FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
+ // conditional expression is not seen as a NullConditional.
+ verifyFormat("var y = A < B ? -1 : 1;", Style);
+}
+
TEST_F(FormatTestCSharp, CSharpNullConditional) {
FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
Style.SpaceBeforeParens = FormatStyle::SBPO_Always;
More information about the cfe-commits
mailing list