[clang] 2f9fc8d - [clang-format] Handle C# property accessors when parsing lines

Jonathan Coe via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 23 05:34:01 PDT 2020


Author: Jonathan Coe
Date: 2020-04-23T13:25:10+01:00
New Revision: 2f9fc8d9718fb19c04e169f7ba7ae26ea6a05085

URL: https://github.com/llvm/llvm-project/commit/2f9fc8d9718fb19c04e169f7ba7ae26ea6a05085
DIFF: https://github.com/llvm/llvm-project/commit/2f9fc8d9718fb19c04e169f7ba7ae26ea6a05085.diff

LOG: [clang-format] Handle C# property accessors when parsing lines

Summary:
Improve C# `{ get; set; } = default;` formatting by handling it in the UnwrappedLineParser rather than trying to merge lines later.

Remove old logic to merge lines.

Update tests as formatting output has changed (as intended).

Reviewers: krasimir, MyDeveloperDay

Reviewed By: krasimir

Subscribers: cfe-commits

Tags: #clang-format, #clang

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

Added: 
    

Modified: 
    clang/lib/Format/UnwrappedLineFormatter.cpp
    clang/lib/Format/UnwrappedLineParser.cpp
    clang/lib/Format/UnwrappedLineParser.h
    clang/unittests/Format/FormatTestCSharp.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index c16c16c4f26a..e86f9ecb4b90 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -294,13 +294,6 @@ class LineJoiner {
       }
     }
 
-    // Try to merge a CSharp property declaration like `{ get; private set }`.
-    if (Style.isCSharp()) {
-      unsigned CSPA = tryMergeCSharpPropertyAccessor(I, E, Limit);
-      if (CSPA > 0)
-        return CSPA;
-    }
-
     // Try to merge a function block with left brace unwrapped
     if (TheLine->Last->is(TT_FunctionLBrace) &&
         TheLine->First != TheLine->Last) {
@@ -430,64 +423,6 @@ class LineJoiner {
     return 0;
   }
 
-  // true for lines of the form [access-modifier] {get,set} [;]
-  bool isMergeablePropertyAccessor(const AnnotatedLine *Line) {
-    auto *Tok = Line->First;
-    if (!Tok)
-      return false;
-
-    if (Tok->isOneOf(tok::kw_public, tok::kw_protected, tok::kw_private,
-                     Keywords.kw_internal))
-      Tok = Tok->Next;
-
-    if (!Tok || (Tok->TokenText != "get" && Tok->TokenText != "set"))
-      return false;
-
-    if (!Tok->Next || Tok->Next->is(tok::semi))
-      return true;
-
-    return false;
-  }
-
-  unsigned tryMergeCSharpPropertyAccessor(
-      SmallVectorImpl<AnnotatedLine *>::const_iterator I,
-      SmallVectorImpl<AnnotatedLine *>::const_iterator E, unsigned /*Limit*/) {
-
-    auto CurrentLine = I;
-    // Does line start with `{`
-    if (!(*CurrentLine)->Last || (*CurrentLine)->Last->isNot(TT_FunctionLBrace))
-      return 0;
-    ++CurrentLine;
-
-    unsigned MergedLines = 0;
-    bool HasGetOrSet = false;
-    while (CurrentLine != E) {
-      bool LineIsGetOrSet = isMergeablePropertyAccessor(*CurrentLine);
-      HasGetOrSet = HasGetOrSet || LineIsGetOrSet;
-      if (LineIsGetOrSet) {
-        ++CurrentLine;
-        ++MergedLines;
-        continue;
-      }
-      auto *Tok = (*CurrentLine)->First;
-      if (Tok && Tok->is(tok::r_brace)) {
-        ++CurrentLine;
-        ++MergedLines;
-        // See if the next line is a default value so that we can merge `{ get;
-        // set } = 0`
-        if (CurrentLine != E && (*CurrentLine)->First &&
-            (*CurrentLine)->First->is(tok::equal)) {
-          ++MergedLines;
-        }
-        break;
-      }
-      // Not a '}' or a get/set line so do not merege lines.
-      return 0;
-    }
-
-    return HasGetOrSet ? MergedLines : 0;
-  }
-
   unsigned
   tryMergeSimplePPDirective(SmallVectorImpl<AnnotatedLine *>::const_iterator I,
                             SmallVectorImpl<AnnotatedLine *>::const_iterator E,

diff  --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 18899314512e..c84c951fcaa8 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -1334,7 +1334,7 @@ void UnwrappedLineParser::parseStructuralElement() {
         parseChildBlock();
       break;
     case tok::l_brace:
-      if (!tryToParseBracedList()) {
+      if (!tryToParsePropertyAccessor() && !tryToParseBracedList()) {
         // A block outside of parentheses must be the last part of a
         // structural element.
         // FIXME: Figure out cases where this is not true, and add projections
@@ -1487,6 +1487,75 @@ void UnwrappedLineParser::parseStructuralElement() {
   } while (!eof());
 }
 
+bool UnwrappedLineParser::tryToParsePropertyAccessor() {
+  assert(FormatTok->is(tok::l_brace));
+  if (!Style.isCSharp())
+    return false;
+  // See if it's a property accessor.
+  if (FormatTok->Previous->isNot(tok::identifier))
+    return false;
+
+  // Try to parse the property accessor braces and contents:
+  // `{ get; set; } = new MyType(defaultValue);`
+  //  ^^^^^^^^^^^^^
+  //
+  // Record the current tokenPosition so that we can advance and
+  // reset the current token. `Next` is not set yet so we need
+  // another way to advance along the token stream.
+  unsigned int StoredPosition = Tokens->getPosition();
+  FormatToken *Tok = Tokens->getNextToken();
+
+  bool HasGetOrSet = false;
+  while (!eof()) {
+    if (Tok->isOneOf(tok::semi, tok::kw_public, tok::kw_private,
+                     tok::kw_protected, Keywords.kw_internal, Keywords.kw_get,
+                     Keywords.kw_set)) {
+      if (Tok->isOneOf(Keywords.kw_get, Keywords.kw_set))
+        HasGetOrSet = true;
+      Tok = Tokens->getNextToken();
+      continue;
+    }
+    if (Tok->is(tok::r_brace))
+      break;
+    Tokens->setPosition(StoredPosition);
+    return false;
+  }
+
+  if (!HasGetOrSet) {
+    Tokens->setPosition(StoredPosition);
+    return false;
+  }
+
+  Tokens->setPosition(StoredPosition);
+  while (FormatTok->isNot(tok::r_brace)) {
+    nextToken();
+  }
+
+  // Try to parse (optional) assignment to default value:
+  // `{ get; set; } = new MyType(defaultValue);`
+  //                ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+  // There may be some very complicated expressions inside default value
+  // assignment, the simple parse block below will not handle them.
+  // The parse block below would need extending to handle opening parens etc.
+  StoredPosition = Tokens->getPosition();
+  Tok = Tokens->getNextToken();
+  bool NextTokenIsEqual = Tok->is(tok::equal);
+  Tokens->setPosition(StoredPosition);
+
+  if (NextTokenIsEqual) {
+    do {
+      nextToken();
+      if (FormatTok->is(tok::semi))
+        break;
+    } while (!eof());
+  }
+
+  // Add an unwrapped line for the whole property accessor.
+  nextToken();
+  addUnwrappedLine();
+  return true;
+}
+
 bool UnwrappedLineParser::tryToParseLambda() {
   if (!Style.isCpp()) {
     nextToken();

diff  --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h
index 42b8b51a37cc..c1fe1e3e1357 100644
--- a/clang/lib/Format/UnwrappedLineParser.h
+++ b/clang/lib/Format/UnwrappedLineParser.h
@@ -132,6 +132,7 @@ class UnwrappedLineParser {
   void parseCSharpGenericTypeConstraint();
   bool tryToParseLambda();
   bool tryToParseLambdaIntroducer();
+  bool tryToParsePropertyAccessor();
   void tryToParseJSFunction();
   void addUnwrappedLine();
   bool eof() const;

diff  --git a/clang/unittests/Format/FormatTestCSharp.cpp b/clang/unittests/Format/FormatTestCSharp.cpp
index 67571d3909bf..d37a533c3c5d 100644
--- a/clang/unittests/Format/FormatTestCSharp.cpp
+++ b/clang/unittests/Format/FormatTestCSharp.cpp
@@ -245,13 +245,11 @@ TEST_F(FormatTestCSharp, Attributes) {
                "}");
 
   verifyFormat("[TestMethod]\n"
-               "public string Host\n"
-               "{ set; get; }");
+               "public string Host { set; get; }");
 
   verifyFormat("[TestMethod(\"start\", HelpText = \"Starts the server "
                "listening on provided host\")]\n"
-               "public string Host\n"
-               "{ set; get; }");
+               "public string Host { set; get; }");
 
   verifyFormat(
       "[DllImport(\"Hello\", EntryPoint = \"hello_world\")]\n"


        


More information about the cfe-commits mailing list