[clang-format] Recognize C# named argument colons as a token type
authorJonathan Coe <jbcoe@google.com>
Thu, 27 Feb 2020 13:46:43 +0000 (13:46 +0000)
committerJonathan Coe <jbcoe@google.com>
Thu, 27 Feb 2020 13:47:29 +0000 (13:47 +0000)
Summary:
No longer merge 'name' and ':' into a single token.

Ensure that line breaks cannot be placed before or after a named-argument colon.

Ensure that no space is inserted before a named-argument colon.

Reviewers: krasimir

Reviewed By: krasimir

Subscribers: cfe-commits, MyDeveloperDay

Tags: #clang-format, #clang

Differential Revision: https://reviews.llvm.org/D75244

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 6bbfdcf..bab513a 100644 (file)
@@ -103,7 +103,7 @@ namespace format {
   TYPE(UnaryOperator)                                                          \
   TYPE(CSharpStringLiteral)                                                    \
   TYPE(CSharpNullCoalescing)                                                   \
-  TYPE(CSharpNamedArgument)                                                    \
+  TYPE(CSharpNamedArgumentColon)                                               \
   TYPE(Unknown)
 
 enum TokenType {
index 8acae56..aa8cf42 100644 (file)
@@ -76,8 +76,6 @@ void FormatTokenLexer::tryMergePreviousTokens() {
     return;
 
   if (Style.isCSharp()) {
-    if (tryMergeCSharpNamedArgument())
-      return;
     if (tryMergeCSharpAttributeAndTarget())
       return;
     if (tryMergeCSharpKeywordVariables())
@@ -186,39 +184,6 @@ bool FormatTokenLexer::tryMergeJSPrivateIdentifier() {
   return true;
 }
 
-// Merge 'argName' and ':' into a single token in `foo(argName: bar)`.
-bool FormatTokenLexer::tryMergeCSharpNamedArgument() {
-  if (Tokens.size() < 2)
-    return false;
-  auto &Colon = *(Tokens.end() - 1);
-  if (!Colon->is(tok::colon))
-    return false;
-
-  auto &Name = *(Tokens.end() - 2);
-  if (!Name->is(tok::identifier))
-    return false;
-    
-  const FormatToken *CommaOrLeftParen = nullptr;
-  for (auto I = Tokens.rbegin() + 2, E = Tokens.rend(); I != E; ++I) {
-    // NB: Because previous pointers are not initialized yet, this cannot use
-    // Token.getPreviousNonComment.
-    if ((*I)->isNot(tok::comment)) {
-      CommaOrLeftParen = *I;
-      break;
-    }
-  }
-
-  if (!CommaOrLeftParen || !CommaOrLeftParen->isOneOf(tok::l_paren, tok::comma))
-    return false;
-
-  Name->TokenText = StringRef(Name->TokenText.begin(),
-                              Colon->TokenText.end() - Name->TokenText.begin());
-  Name->ColumnWidth += Colon->ColumnWidth;
-  Name->Type = TT_CSharpNamedArgument;
-  Tokens.erase(Tokens.end() - 1);
-  return true;
-}
-
 // Search for verbatim or interpolated string literals @"ABC" or
 // $"aaaaa{abc}aaaaa" i and mark the token as TT_CSharpStringLiteral, and to
 // prevent splitting of @, $ and ".
@@ -609,7 +574,7 @@ void FormatTokenLexer::handleCSharpVerbatimAndInterpolatedStrings() {
   size_t LastBreak = LiteralText.rfind('\n');
   if (LastBreak != StringRef::npos) {
     CSharpStringLiteral->IsMultiline = true;
-    unsigned StartColumn = 0; // The template tail spans the entire line.
+    unsigned StartColumn = 0;
     CSharpStringLiteral->LastLineColumnWidth = encoding::columnWidthWithTabs(
         LiteralText.substr(LastBreak + 1, LiteralText.size()), StartColumn,
         Style.TabWidth, Encoding);
index 1f930d7..4fffb36 100644 (file)
@@ -56,7 +56,6 @@ private:
   bool tryMergeCSharpDoubleQuestion();
   bool tryTransformCSharpForEach();
   bool tryMergeCSharpAttributeAndTarget();
-  bool tryMergeCSharpNamedArgument();
 
   bool tryMergeTokens(ArrayRef<tok::TokenKind> Kinds, TokenType NewType);
 
index ff5827c..fb60b8f 100644 (file)
@@ -776,6 +776,11 @@ private:
           Tok->Type = TT_JsTypeColon;
           break;
         }
+      } else if (Style.isCSharp()) {
+        if (Contexts.back().ContextKind == tok::l_paren) {
+          Tok->Type = TT_CSharpNamedArgumentColon;
+          break;
+        }
       }
       if (Contexts.back().ColonIsDictLiteral ||
           Style.Language == FormatStyle::LK_Proto ||
@@ -3052,6 +3057,8 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
       return Style.SpacesInContainerLiterals;
     if (Right.is(TT_AttributeColon))
       return false;
+    if (Right.is(TT_CSharpNamedArgumentColon))
+      return false;
     return true;
   }
   if (Left.is(TT_UnaryOperator)) {
@@ -3200,7 +3207,11 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
   if (Right.NewlinesBefore > 1 && Style.MaxEmptyLinesToKeep > 0)
     return true;
 
-  if (Style.Language == FormatStyle::LK_JavaScript) {
+  if (Style.isCSharp()) {
+    if (Right.is(TT_CSharpNamedArgumentColon) ||
+        Left.is(TT_CSharpNamedArgumentColon))
+      return false;
+  } else if (Style.Language == FormatStyle::LK_JavaScript) {
     // FIXME: This might apply to other languages and token kinds.
     if (Right.is(tok::string_literal) && Left.is(tok::plus) && Left.Previous &&
         Left.Previous->is(tok::string_literal))
@@ -3485,9 +3496,12 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
 bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
                                     const FormatToken &Right) {
   const FormatToken &Left = *Right.Previous;
-
   // Language-specific stuff.
-  if (Style.Language == FormatStyle::LK_Java) {
+  if (Style.isCSharp()) {
+    if (Left.is(TT_CSharpNamedArgumentColon) ||
+        Right.is(TT_CSharpNamedArgumentColon))
+      return false;
+  } else if (Style.Language == FormatStyle::LK_Java) {
     if (Left.isOneOf(Keywords.kw_throws, Keywords.kw_extends,
                      Keywords.kw_implements))
       return false;
index 0428de3..ec8dfff 100644 (file)
@@ -546,6 +546,8 @@ PrintOrderDetails(orderNum: 31, productName: "Red Mug",
 PrintOrderDetails(orderNum: 31, productName: "Red Mug",  // comment
                   sellerName: "Gift Shop");)",
                Style);
+
+  verifyFormat(R"(foreach (var tickCount in task.Begin(seed: 0)) {)", Style);
 }
 
 TEST_F(FormatTestCSharp, CSharpPropertyAccessors) {