jbcoe created this revision.
jbcoe added reviewers: krasimir, MyDeveloperDay.
jbcoe added a project: clang-format.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

Added some examples of properties from Microsoft documentation as test cases.

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties

Configuration support will be added in a follow up patch to address whether 
automatic properties are formatted as

  Type MyType { get; set }

or

  Type MyType 
  { get; set }


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D78915

Files:
  clang/lib/Format/UnwrappedLineParser.cpp
  clang/unittests/Format/FormatTestCSharp.cpp

Index: clang/unittests/Format/FormatTestCSharp.cpp
===================================================================
--- clang/unittests/Format/FormatTestCSharp.cpp
+++ clang/unittests/Format/FormatTestCSharp.cpp
@@ -611,6 +611,64 @@
 public string Name {
   get => _name;
   set => _name = value;
+})",
+               Style);
+
+  // Examples taken from
+  // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties
+  verifyFormat(R"(
+// Expression body definitions
+public class SaleItem {
+  public decimal Price {
+    get => _cost;
+    set => _cost = value;
+  }
+})",
+               Style);
+
+  verifyFormat(R"(
+// Properties with backing fields
+class TimePeriod {
+  public double Hours {
+    get { return _seconds / 3600; }
+    set {
+      if (value < 0 || value > 24)
+        throw new ArgumentOutOfRangeException(
+            $"{nameof(value)} must be between 0 and 24.");
+      _seconds = value * 3600;
+    }
+  }
+})",
+               Style);
+
+  verifyFormat(R"(
+// Auto-implemented properties
+public class SaleItem {
+  public decimal Price { get; set; }
+})",
+               Style);
+
+  // Add column limit to wrap long lines.
+  Style.ColumnLimit = 100;
+
+  // Examples with assignment to default value.
+  verifyFormat(R"(
+// Long assignment to default value
+class MyClass {
+  public override VeryLongNamedTypeIndeed VeryLongNamedValue { get; set } =
+      VeryLongNamedTypeIndeed.Create(DefaultFirstArgument, DefaultSecondArgument,
+                                     DefaultThirdArgument);
+})",
+               Style);
+
+  verifyFormat(R"(
+// Long assignment to default value with expression body
+class MyClass {
+  public override VeryLongNamedTypeIndeed VeryLongNamedValue {
+    get => veryLongNamedField;
+    set => veryLongNamedField = value;
+  } = VeryLongNamedTypeIndeed.Create(DefaultFirstArgument, DefaultSecondArgument,
+                                     DefaultThirdArgument);
 })",
                Style);
 }
Index: clang/lib/Format/UnwrappedLineParser.cpp
===================================================================
--- clang/lib/Format/UnwrappedLineParser.cpp
+++ clang/lib/Format/UnwrappedLineParser.cpp
@@ -13,6 +13,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "UnwrappedLineParser.h"
+#include "FormatToken.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Support/raw_ostream.h"
@@ -1495,9 +1496,7 @@
   if (FormatTok->Previous->isNot(tok::identifier))
     return false;
 
-  // Try to parse the property accessor braces and contents:
-  // `{ get; set; } = new MyType(defaultValue);`
-  //  ^^^^^^^^^^^^^
+  // See if we are inside a property accessor.
   //
   // Record the current tokenPosition so that we can advance and
   // reset the current token. `Next` is not set yet so we need
@@ -1506,19 +1505,20 @@
   FormatToken *Tok = Tokens->getNextToken();
 
   bool HasGetOrSet = false;
+  bool IsTrivialPropertyAccessor = true;
   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))
+      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(Tok->isNot(tok::r_brace))
+      IsTrivialPropertyAccessor = false;
+    break;
   }
 
   if (!HasGetOrSet) {
@@ -1526,33 +1526,52 @@
     return false;
   }
 
-  Tokens->setPosition(StoredPosition);
-  while (FormatTok->isNot(tok::r_brace)) {
-    nextToken();
-  }
-
-  // Try to parse (optional) assignment to default value:
+  // Try to parse the property accessor:
   // `{ 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))
+  nextToken();
+  do {
+    switch(FormatTok->Tok.getKind()) {
+      case tok::r_brace:
+        nextToken();
+        if(FormatTok->is(tok::equal)) {
+          while(!eof() && FormatTok->isNot(tok::semi))
+            nextToken();
+          nextToken();
+        }
+        addUnwrappedLine();
+        return true;
+      case tok::l_brace:
+        ++Line->Level;
+        parseBlock(/*MustBeDeclaration=*/true);
+        addUnwrappedLine();
+        --Line->Level;
         break;
-    } while (!eof());
-  }
+      case tok::equal:
+        if(FormatTok->is(TT_JsFatArrow)) {
+          ++Line->Level;
+          do {
+            nextToken();
+          } while (!eof() && FormatTok->isNot(tok::semi));
+          nextToken();
+          addUnwrappedLine();
+          --Line->Level;
+          break;
+        }
+        //if(FormatTok->Previous->isNot(tok::r_brace))
+        //  --Line->Level;
+        nextToken();
+        break;
+      default:
+        if(FormatTok->isOneOf(Keywords.kw_get, Keywords.kw_set) && !IsTrivialPropertyAccessor) {
+          // get/set needs to be on its own line.
+          addUnwrappedLine();
+        }
+        nextToken();
+    }
+  } while(!eof());
 
-  // Add an unwrapped line for the whole property accessor.
-  nextToken();
-  addUnwrappedLine();
+  // Unreachable for well-formed code (paired '{' and '}').
   return true;
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to