exv created this revision. exv added reviewers: krasimir, MyDeveloperDay. exv requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
This adds support for the null-coalescing assignment and null-forgiving oeprators. https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-forgiving Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D101702 Files: 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
Index: clang/unittests/Format/FormatTestCSharp.cpp =================================================================== --- clang/unittests/Format/FormatTestCSharp.cpp +++ clang/unittests/Format/FormatTestCSharp.cpp @@ -358,6 +358,21 @@ verifyFormat("return _name ?? \"DEF\";"); } +TEST_F(FormatTestCSharp, CSharpNullCoalescingAssignment) { + verifyFormat("test \?\?= ABC;"); + verifyFormat("test \?\?= true;"); +} + +TEST_F(FormatTestCSharp, CSharpNullForgiving) { + verifyFormat("var test = null!;"); + verifyFormat("string test = someFunctionCall()! + \"ABC\"!"); + verifyFormat("int test = (1! + 2 + bar! + foo())!"); + verifyFormat("test \?\?= !foo!;"); + verifyFormat("test = !bar! \?\? !foo!;"); + verifyFormat("bool test = !(!true || !null! || !false && !false! && !bar()! " + "+ (!foo()))!"); +} + TEST_F(FormatTestCSharp, AttributesIndentation) { FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp); Style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -1579,24 +1579,29 @@ } } - if (Style.Language == FormatStyle::LK_JavaScript) { - if (Current.is(tok::exclaim)) { - if (Current.Previous && - (Keywords.IsJavaScriptIdentifier( - *Current.Previous, /* AcceptIdentifierName= */ true) || - Current.Previous->isOneOf( - tok::kw_namespace, tok::r_paren, tok::r_square, tok::r_brace, - Keywords.kw_type, Keywords.kw_get, Keywords.kw_set) || - Current.Previous->Tok.isLiteral())) { - Current.setType(TT_JsNonNullAssertion); - return; - } - if (Current.Next && - Current.Next->isOneOf(TT_BinaryOperator, Keywords.kw_as)) { + if ((Style.Language == FormatStyle::LK_JavaScript || Style.isCSharp()) && + Current.is(tok::exclaim)) { + if (Current.Previous) { + bool isIdentifier = + Style.Language == FormatStyle::LK_JavaScript + ? Keywords.IsJavaScriptIdentifier( + *Current.Previous, /* AcceptIdentifierName= */ true) + : Current.Previous->is(tok::identifier); + if (isIdentifier || + Current.Previous->isOneOf( + tok::kw_namespace, tok::r_paren, tok::r_square, tok::r_brace, + tok::kw_false, tok::kw_true, Keywords.kw_type, Keywords.kw_get, + Keywords.kw_set) || + Current.Previous->Tok.isLiteral()) { Current.setType(TT_JsNonNullAssertion); return; } } + if (Current.Next && + Current.Next->isOneOf(TT_BinaryOperator, Keywords.kw_as)) { + Current.setType(TT_JsNonNullAssertion); + return; + } } // Line.MightBeFunctionDecl can only be true after the parentheses of a @@ -3186,6 +3191,15 @@ if (Left.is(TT_CSharpNullCoalescing) || Right.is(TT_CSharpNullCoalescing)) return true; + // Space before and after '??='. + if (Left.is(TT_CSharpNullCoalescingAssignment) || + Right.is(TT_CSharpNullCoalescingAssignment)) + return true; + + // No space before null forgiving '!'. + if (Right.is(TT_JsNonNullAssertion)) + return false; + // No space before '?['. if (Right.is(TT_CSharpNullConditionalLSquare)) return false; Index: clang/lib/Format/FormatTokenLexer.h =================================================================== --- clang/lib/Format/FormatTokenLexer.h +++ clang/lib/Format/FormatTokenLexer.h @@ -54,7 +54,8 @@ bool tryMergeJSPrivateIdentifier(); bool tryMergeCSharpStringLiteral(); bool tryMergeCSharpKeywordVariables(); - bool tryMergeCSharpDoubleQuestion(); + bool tryMergeCSharpNullCoalescing(); + bool tryMergeCSharpNullCoalescingAssignment(); bool tryMergeCSharpNullConditional(); bool tryTransformCSharpForEach(); bool tryMergeForEach(); Index: clang/lib/Format/FormatTokenLexer.cpp =================================================================== --- clang/lib/Format/FormatTokenLexer.cpp +++ clang/lib/Format/FormatTokenLexer.cpp @@ -97,7 +97,9 @@ return; if (tryMergeCSharpStringLiteral()) return; - if (tryMergeCSharpDoubleQuestion()) + if (tryMergeCSharpNullCoalescing()) + return; + if (tryMergeCSharpNullCoalescingAssignment()) return; if (tryMergeCSharpNullConditional()) return; @@ -310,7 +312,7 @@ "param", "property", "return", "type", }; -bool FormatTokenLexer::tryMergeCSharpDoubleQuestion() { +bool FormatTokenLexer::tryMergeCSharpNullCoalescing() { if (Tokens.size() < 2) return false; auto &FirstQuestion = *(Tokens.end() - 2); @@ -327,6 +329,24 @@ return true; } +bool FormatTokenLexer::tryMergeCSharpNullCoalescingAssignment() { + if (Tokens.size() < 2) + return false; + auto &NullCoalescing = *(Tokens.end() - 2); + auto &Equal = *(Tokens.end() - 1); + if (NullCoalescing->getType() != TT_CSharpNullCoalescing || + !Equal->is(tok::equal)) + return false; + NullCoalescing->Tok.setKind(tok::equal); // no '??=' in clang tokens. + NullCoalescing->TokenText = + StringRef(NullCoalescing->TokenText.begin(), + Equal->TokenText.end() - NullCoalescing->TokenText.begin()); + NullCoalescing->ColumnWidth += Equal->ColumnWidth; + NullCoalescing->setType(TT_CSharpNullCoalescingAssignment); + Tokens.erase(Tokens.end() - 1); + return true; +} + // Merge '?[' and '?.' pairs into single tokens. bool FormatTokenLexer::tryMergeCSharpNullConditional() { if (Tokens.size() < 2) Index: clang/lib/Format/FormatToken.h =================================================================== --- clang/lib/Format/FormatToken.h +++ clang/lib/Format/FormatToken.h @@ -114,6 +114,7 @@ TYPE(CSharpNamedArgumentColon) \ TYPE(CSharpNullable) \ TYPE(CSharpNullCoalescing) \ + TYPE(CSharpNullCoalescingAssignment) \ TYPE(CSharpNullConditional) \ TYPE(CSharpNullConditionalLSquare) \ TYPE(CSharpGenericTypeConstraint) \
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits