[clang] 44ad58b - [clang-format] Improved parser for C# properties
Jonathan Coe via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 28 06:13:42 PDT 2020
Author: Jonathan Coe
Date: 2020-04-28T14:11:09+01:00
New Revision: 44ad58b9915d29eb48be4f89368be6d30d174825
URL: https://github.com/llvm/llvm-project/commit/44ad58b9915d29eb48be4f89368be6d30d174825
DIFF: https://github.com/llvm/llvm-project/commit/44ad58b9915d29eb48be4f89368be6d30d174825.diff
LOG: [clang-format] Improved parser for C# properties
Summary:
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 }
```
Reviewers: krasimir, MyDeveloperDay
Reviewed By: krasimir
Subscribers: cfe-commits
Tags: #clang-format, #clang
Differential Revision: https://reviews.llvm.org/D78915
Added:
Modified:
clang/lib/Format/UnwrappedLineParser.cpp
clang/unittests/Format/FormatTestCSharp.cpp
Removed:
################################################################################
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index fdae725d625b..4734ff16921b 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/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 @@ bool UnwrappedLineParser::tryToParsePropertyAccessor() {
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
@@ -1505,7 +1504,11 @@ bool UnwrappedLineParser::tryToParsePropertyAccessor() {
unsigned int StoredPosition = Tokens->getPosition();
FormatToken *Tok = Tokens->getNextToken();
+ // A trivial property accessor is of the form:
+ // { [ACCESS_SPECIFIER] [get]; [ACCESS_SPECIFIER] [set] }
+ // Track these as they do not require line breaks to be introduced.
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,
@@ -1515,10 +1518,9 @@ bool UnwrappedLineParser::tryToParsePropertyAccessor() {
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 +1528,51 @@ bool UnwrappedLineParser::tryToParsePropertyAccessor() {
return false;
}
+ // Try to parse the property accessor:
+ // https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties
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();
+ do {
+ switch (FormatTok->Tok.getKind()) {
+ case tok::r_brace:
nextToken();
- if (FormatTok->is(tok::semi))
+ 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;
+ case tok::equal:
+ if (FormatTok->is(TT_JsFatArrow)) {
+ ++Line->Level;
+ do {
+ nextToken();
+ } while (!eof() && FormatTok->isNot(tok::semi));
+ nextToken();
+ addUnwrappedLine();
+ --Line->Level;
break;
- } while (!eof());
- }
+ }
+ nextToken();
+ break;
+ default:
+ if (FormatTok->isOneOf(Keywords.kw_get, Keywords.kw_set) &&
+ !IsTrivialPropertyAccessor) {
+ // Non-trivial 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;
}
diff --git a/clang/unittests/Format/FormatTestCSharp.cpp b/clang/unittests/Format/FormatTestCSharp.cpp
index d37a533c3c5d..75112a786867 100644
--- a/clang/unittests/Format/FormatTestCSharp.cpp
+++ b/clang/unittests/Format/FormatTestCSharp.cpp
@@ -611,6 +611,64 @@ TEST_F(FormatTestCSharp, CSharpPropertyAccessors) {
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);
}
More information about the cfe-commits
mailing list