MyDeveloperDay updated this revision to Diff 515060.
MyDeveloperDay added a comment.
Adds additional options to give us much greater control over how we format C#
properties especially auto properties
(Submitting this for safe keeping, I need to try and minimize the if
expression, I'm sure it could be simplified into a few less clauses)
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D148467/new/
https://reviews.llvm.org/D148467
Files:
clang/docs/ClangFormatStyleOptions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Format/Format.h
clang/lib/Format/Format.cpp
clang/lib/Format/TokenAnnotator.cpp
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
@@ -42,14 +42,17 @@
return Style;
}
- static void verifyFormat(
- llvm::StringRef Code,
+ static void _verifyFormat(
+ const char *File, int Line, llvm::StringRef Code,
const FormatStyle &Style = getMicrosoftStyle(FormatStyle::LK_CSharp)) {
+ ::testing::ScopedTrace t(File, Line, ::testing::Message() << Code.str());
EXPECT_EQ(Code.str(), format(Code, Style)) << "Expected code is not stable";
EXPECT_EQ(Code.str(), format(test::messUp(Code), Style));
}
};
+#define verifyFormat(...) _verifyFormat(__FILE__, __LINE__, __VA_ARGS__)
+
TEST_F(FormatTestCSharp, CSharpClass) {
verifyFormat("public class SomeClass\n"
"{\n"
@@ -1612,5 +1615,424 @@
EXPECT_NE("", format("int where b <")); // reduced from crasher
}
+TEST_F(FormatTestCSharp, PropertyWrapping) {
+ FormatStyle Style = getMicrosoftStyle(FormatStyle::LK_CSharp);
+
+ Style.BraceWrapping.AfterCSharpProperty = false;
+ Style.AllowShortCSharpPropertiesOnASingleLine = true;
+ Style.AlwaysBreakBetweenShortCSharpProperties = false;
+ Style.BraceWrapping.AfterFunction = true;
+
+ verifyFormat("class A\n"
+ "{\n"
+ " string Foo { set; get; }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Foo { get; set; }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Foo { get; init; }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Foo\n"
+ " {\n"
+ " set {\n"
+ " val = value;\n"
+ " }\n"
+ " get {\n"
+ " return value;\n"
+ " }\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Foo\n"
+ " {\n"
+ " init {\n"
+ " val = value;\n"
+ " }\n"
+ " get {\n"
+ " return value;\n"
+ " }\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Foo\n"
+ " {\n"
+ " get;\n"
+ " set {\n"
+ " val = value;\n"
+ " }\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Foo\n"
+ " {\n"
+ " get;\n"
+ " init {\n"
+ " val = value;\n"
+ " }\n"
+ " }\n"
+ "}",
+ Style);
+
+ verifyFormat("class A\n"
+ "{\n"
+ " string Foo { private set; get; }\n"
+ "}",
+ Style);
+
+ verifyFormat("class A\n"
+ "{\n"
+ " string Foo { set; public get; }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Foo\n"
+ " {\n"
+ " get;\n"
+ " public init {\n"
+ " val = value;\n"
+ " }\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Foo\n"
+ " {\n"
+ " public init {\n"
+ " val = value;\n"
+ " }\n"
+ " get;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Foo\n"
+ " {\n"
+ " public get;\n"
+ " init {\n"
+ " val = value;\n"
+ " }\n"
+ " }\n"
+ "}",
+ Style);
+
+ verifyFormat("class A\n"
+ "{\n"
+ " string Foo\n"
+ " {\n"
+ " get;\n"
+ " public init {\n"
+ " val = value;\n"
+ " }\n"
+ " }\n"
+ "}",
+ Style);
+
+ Style.BraceWrapping.AfterCSharpProperty = true;
+ Style.AllowShortCSharpPropertiesOnASingleLine = false;
+ Style.AlwaysBreakBetweenShortCSharpProperties = true;
+ Style.BraceWrapping.AfterFunction = true;
+
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " set;\n"
+ " get;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " get;\n"
+ " set;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " get;\n"
+ " init;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " set\n"
+ " {\n"
+ " val = value;\n"
+ " }\n"
+ " get\n"
+ " {\n"
+ " return value;\n"
+ " }\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " init\n"
+ " {\n"
+ " val = value;\n"
+ " }\n"
+ " get\n"
+ " {\n"
+ " return value;\n"
+ " }\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " get;\n"
+ " set\n"
+ " {\n"
+ " val = value;\n"
+ " }\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " get;\n"
+ " init\n"
+ " {\n"
+ " val = value;\n"
+ " }\n"
+ " }\n"
+ "}",
+ Style);
+
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " private set;\n"
+ " get;\n"
+ " }\n"
+ "}",
+ Style);
+
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " set;\n"
+ " internal get;\n"
+ " }\n"
+ "}",
+ Style);
+
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar6\n"
+ " {\n"
+ " set;\n"
+ " internal get\n"
+ " {\n"
+ " return value;\n"
+ " };\n"
+ " }\n"
+ "}",
+ Style);
+
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar5\n"
+ " {\n"
+ " internal get\n"
+ " {\n"
+ " return value;\n"
+ " };\n"
+ " set;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " internal set;\n"
+ " get\n"
+ " {\n"
+ " return value;\n"
+ " };\n"
+ " }\n"
+ "}",
+ Style);
+
+ Style.BraceWrapping.AfterCSharpProperty = true;
+ Style.AllowShortCSharpPropertiesOnASingleLine = false;
+ Style.AlwaysBreakBetweenShortCSharpProperties = false;
+ Style.BraceWrapping.AfterFunction = true;
+
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " set; get;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " get; set;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " init; get;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " internal set; get;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " set; protected get;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " set;\n"
+ " protected get\n"
+ " {\n"
+ " return value;\n"
+ " }\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar\n"
+ " {\n"
+ " public get\n"
+ " {\n"
+ " return value;\n"
+ " }\n"
+ " set;\n"
+ " }\n"
+ "}",
+ Style);
+
+ Style.BraceWrapping.AfterCSharpProperty = false;
+ Style.AllowShortCSharpPropertiesOnASingleLine = false;
+ Style.AlwaysBreakBetweenShortCSharpProperties = false;
+ Style.BraceWrapping.AfterFunction = false;
+
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar1 {\n"
+ " set; get;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar1 {\n"
+ " get; set;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar1 {\n"
+ " init; get;\n"
+ " }\n"
+ "}",
+ Style);
+
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar2 {\n"
+ " public set; get;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar2 {\n"
+ " set; private get;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar3 {\n"
+ " set { value = 123 }\n"
+ " get;\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar4 {\n"
+ " get;\n"
+ " set { value = 123 }\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar4 {\n"
+ " private get;\n"
+ " set { value = 123 }\n"
+ " }\n"
+ "}",
+ Style);
+ verifyFormat("class A\n"
+ "{\n"
+ " string Bar4 {\n"
+ " get;\n"
+ " private set { value = 123 }\n"
+ " }\n"
+ "}",
+ Style);
+}
+
} // namespace format
} // end namespace clang
Index: clang/lib/Format/UnwrappedLineParser.cpp
===================================================================
--- clang/lib/Format/UnwrappedLineParser.cpp
+++ clang/lib/Format/UnwrappedLineParser.cpp
@@ -2073,8 +2073,22 @@
// Try to parse the property accessor:
// https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties
Tokens->setPosition(StoredPosition);
- if (!IsTrivialPropertyAccessor && Style.BraceWrapping.AfterFunction)
+ if ((!IsTrivialPropertyAccessor &&
+ Style.AllowShortCSharpPropertiesOnASingleLine) &&
+ Style.BraceWrapping.AfterFunction) {
addUnwrappedLine();
+ }
+
+ // Handle non short property on a single line
+ // string Foo
+ // {
+ // set;get;
+ // }
+ if (!Style.AllowShortCSharpPropertiesOnASingleLine &&
+ Style.BraceWrapping.AfterFunction) {
+ addUnwrappedLine();
+ }
+
nextToken();
do {
switch (FormatTok->Tok.getKind()) {
@@ -2087,6 +2101,16 @@
}
addUnwrappedLine();
return true;
+ case tok::semi:
+ if (!IsTrivialPropertyAccessor) {
+ ++Line->Level;
+ nextToken();
+ addUnwrappedLine();
+ --Line->Level;
+ break;
+ }
+ nextToken();
+ break;
case tok::l_brace:
++Line->Level;
parseBlock(/*MustBeDeclaration=*/true);
@@ -2107,12 +2131,24 @@
nextToken();
break;
default:
- if (FormatTok->isOneOf(Keywords.kw_get, Keywords.kw_init,
- Keywords.kw_set) &&
+ bool WrappedAlready = false;
+ if (FormatTok->isOneOf(Keywords.kw_internal, tok::kw_private,
+ tok::kw_public, tok::kw_protected) &&
!IsTrivialPropertyAccessor) {
// Non-trivial get/set needs to be on its own line.
addUnwrappedLine();
+ nextToken();
+ WrappedAlready = true;
}
+ if (FormatTok->isOneOf(Keywords.kw_get, Keywords.kw_init, Keywords.kw_set,
+ Keywords.kw_internal, tok::kw_private,
+ tok::kw_public, tok::kw_protected) &&
+ !IsTrivialPropertyAccessor) {
+ // Non-trivial get/set needs to be on its own line.
+ if (!WrappedAlready)
+ addUnwrappedLine();
+ }
+
nextToken();
}
} while (!eof());
@@ -2176,8 +2212,8 @@
// Specialization of a template with an integer parameter can contain
// arithmetic, logical, comparison and ternary operators.
//
- // FIXME: This also accepts sequences of operators that are not in the scope
- // of a template argument list.
+ // FIXME: This also accepts sequences of operators that are not in the
+ // scope of a template argument list.
//
// In a C++ lambda a template type can only occur after an arrow. We use
// this as an heuristic to distinguish between Objective-C expressions
@@ -2286,8 +2322,9 @@
// Parse a type definition.
nextToken();
- // Eat the type declaration. For braced inline object types, balance braces,
- // otherwise just parse until finding an l_brace for the function body.
+ // Eat the type declaration. For braced inline object types, balance
+ // braces, otherwise just parse until finding an l_brace for the function
+ // body.
if (FormatTok->is(tok::l_brace))
tryToParseBracedList();
else
@@ -2391,10 +2428,10 @@
}
break;
case tok::semi:
- // JavaScript (or more precisely TypeScript) can have semicolons in braced
- // lists (in so-called TypeMemberLists). Thus, the semicolon cannot be
- // used for error recovery if we have otherwise determined that this is
- // a braced list.
+ // JavaScript (or more precisely TypeScript) can have semicolons in
+ // braced lists (in so-called TypeMemberLists). Thus, the semicolon
+ // cannot be used for error recovery if we have otherwise determined
+ // that this is a braced list.
if (Style.isJavaScript()) {
nextToken();
break;
@@ -2932,8 +2969,8 @@
/*KeepBraces=*/true, /*IfKind=*/nullptr,
ManageWhitesmithsBraces);
- // Munch the semicolon after a namespace. This is more common than one would
- // think. Putting the semicolon into its own line is very ugly.
+ // Munch the semicolon after a namespace. This is more common than one
+ // would think. Putting the semicolon into its own line is very ugly.
if (FormatTok->is(tok::semi))
nextToken();
@@ -2979,7 +3016,8 @@
if (FormatTok->is(tok::l_paren)) {
parseParens();
- // If there is a class body of an anonymous class, consume that as child.
+ // If there is a class body of an anonymous class, consume that as
+ // child.
if (FormatTok->is(tok::l_brace))
parseChildBlock();
return;
@@ -3033,8 +3071,8 @@
nextToken();
if (HasParens && FormatTok->is(tok::l_paren)) {
// The type is only set for Verilog basically because we were afraid to
- // change the existing behavior for loops. See the discussion on D121756 for
- // details.
+ // change the existing behavior for loops. See the discussion on D121756
+ // for details.
if (Style.isVerilog())
FormatTok->setFinalizedType(TT_ConditionLParen);
parseParens();
@@ -3228,7 +3266,8 @@
parseRequiresExpression(RequiresToken);
return false;
case tok::l_paren:
- // Clauses and expression can start with a paren, it's unclear what we have.
+ // Clauses and expression can start with a paren, it's unclear what we
+ // have.
break;
default:
// All other tokens can only be a clause.
@@ -3290,8 +3329,8 @@
// Now we look forward and try to check if the paren content is a parameter
// list. The parameters can be cv-qualified and contain references or
// pointers.
- // So we want basically to check for TYPE NAME, but TYPE can contain all kinds
- // of stuff: typename, const, *, &, &&, ::, identifiers.
+ // So we want basically to check for TYPE NAME, but TYPE can contain all
+ // kinds of stuff: typename, const, *, &, &&, ::, identifiers.
unsigned StoredPosition = Tokens->getPosition();
FormatToken *NextToken = Tokens->getNextToken();
@@ -3367,8 +3406,8 @@
assert(RequiresToken->is(tok::kw_requires) && "'requires' expected");
// If there is no previous token, we are within a requires expression,
- // otherwise we will always have the template or function declaration in front
- // of it.
+ // otherwise we will always have the template or function declaration in
+ // front of it.
bool InRequiresExpression =
!RequiresToken->Previous ||
RequiresToken->Previous->is(TT_RequiresExpressionLBrace);
@@ -3415,21 +3454,21 @@
/// This is the body of a requires clause. It returns, when the parsing is
/// complete, or the expression is incorrect.
void UnwrappedLineParser::parseConstraintExpression() {
- // The special handling for lambdas is needed since tryToParseLambda() eats a
- // token and if a requires expression is the last part of a requires clause
- // and followed by an attribute like [[nodiscard]] the ClosesRequiresClause is
- // not set on the correct token. Thus we need to be aware if we even expect a
- // lambda to be possible.
- // template <typename T> requires requires { ... } [[nodiscard]] ...;
+ // The special handling for lambdas is needed since tryToParseLambda() eats
+ // a token and if a requires expression is the last part of a requires
+ // clause and followed by an attribute like [[nodiscard]] the
+ // ClosesRequiresClause is not set on the correct token. Thus we need to be
+ // aware if we even expect a lambda to be possible. template <typename T>
+ // requires requires { ... } [[nodiscard]] ...;
bool LambdaNextTimeAllowed = true;
- // Within lambda declarations, it is permitted to put a requires clause after
- // its template parameter list, which would place the requires clause right
- // before the parentheses of the parameters of the lambda declaration. Thus,
- // we track if we expect to see grouping parentheses at all.
- // Without this check, `requires foo<T> (T t)` in the below example would be
- // seen as the whole requires clause, accidentally eating the parameters of
- // the lambda.
+ // Within lambda declarations, it is permitted to put a requires clause
+ // after its template parameter list, which would place the requires clause
+ // right before the parentheses of the parameters of the lambda declaration.
+ // Thus, we track if we expect to see grouping parentheses at all. Without
+ // this check, `requires foo<T> (T t)` in the below example would be seen as
+ // the whole requires clause, accidentally eating the parameters of the
+ // lambda.
// [&]<typename T> requires foo<T> (T t) { ... };
bool TopLevelParensAllowed = true;
@@ -3567,9 +3606,9 @@
if (FormatTok->is(tok::kw_enum))
nextToken();
- // In TypeScript, "enum" can also be used as property name, e.g. in interface
- // declarations. An "enum" keyword followed by a colon would be a syntax
- // error and thus assume it is just an identifier.
+ // In TypeScript, "enum" can also be used as property name, e.g. in
+ // interface declarations. An "enum" keyword followed by a colon would be a
+ // syntax error and thus assume it is just an identifier.
if (Style.isJavaScript() && FormatTok->isOneOf(tok::colon, tok::question))
return false;
@@ -3833,8 +3872,8 @@
FormatToken *Previous = FormatTok->Previous;
if (!Previous ||
!(Previous->is(tok::r_paren) || Previous->isTypeOrIdentifier())) {
- // Don't try parsing a lambda if we had a closing parenthesis before,
- // it was probably a pointer to an array: int (*)[].
+ // Don't try parsing a lambda if we had a closing parenthesis
+ // before, it was probably a pointer to an array: int (*)[].
if (!tryToParseLambda())
continue;
} else {
@@ -4047,9 +4086,9 @@
if (FormatTok->is(tok::kw_default))
nextToken();
- // Consume "async function", "function" and "default function", so that these
- // get parsed as free-standing JS functions, i.e. do not require a trailing
- // semicolon.
+ // Consume "async function", "function" and "default function", so that
+ // these get parsed as free-standing JS functions, i.e. do not require a
+ // trailing semicolon.
if (FormatTok->is(Keywords.kw_async))
nextToken();
if (FormatTok->is(Keywords.kw_function)) {
@@ -4246,11 +4285,11 @@
}
void UnwrappedLineParser::parseVerilogCaseLabel() {
- // The label will get unindented in AnnotatingParser. If there are no leading
- // spaces, indent the rest here so that things inside the block will be
- // indented relative to things outside. We don't use parseLabel because we
- // don't know whether this colon is a label or a ternary expression at this
- // point.
+ // The label will get unindented in AnnotatingParser. If there are no
+ // leading spaces, indent the rest here so that things inside the block will
+ // be indented relative to things outside. We don't use parseLabel because
+ // we don't know whether this colon is a label or a ternary expression at
+ // this point.
auto OrigLevel = Line->Level;
auto FirstLine = CurrentLines->size();
if (Line->Level == 0 || (Line->InPPDirective && Line->Level <= 1))
@@ -4295,8 +4334,8 @@
Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths;
// If the current line was expanded from a macro call, we use it to
- // reconstruct an unwrapped line from the structure of the expanded unwrapped
- // line and the unexpanded token stream.
+ // reconstruct an unwrapped line from the structure of the expanded
+ // unwrapped line and the unexpanded token stream.
if (!parsingPPDirective() && !InExpansion && containsExpansion(*Line)) {
if (!Reconstruct)
Reconstruct.emplace(Line->Level, Unexpanded);
@@ -4372,10 +4411,10 @@
// section if its original column is greater or equal to the original start
// column of the line.
//
- // Define the min column token of a line as follows: if a line ends in '{' or
- // contains a '{' followed by a line comment, then the min column token is
- // that '{'. Otherwise, the min column token of the line is the first token of
- // the line.
+ // Define the min column token of a line as follows: if a line ends in '{'
+ // or contains a '{' followed by a line comment, then the min column token
+ // is that '{'. Otherwise, the min column token of the line is the first
+ // token of the line.
//
// If Line starts with a token other than a line comment, then FormatTok
// continues the comment section if its original column is greater than the
@@ -4461,14 +4500,14 @@
void UnwrappedLineParser::flushComments(bool NewlineBeforeNext) {
bool JustComments = Line->Tokens.empty();
for (FormatToken *Tok : CommentsBeforeNextToken) {
- // Line comments that belong to the same line comment section are put on the
- // same line since later we might want to reflow content between them.
+ // Line comments that belong to the same line comment section are put on
+ // the same line since later we might want to reflow content between them.
// Additional fine-grained breaking of line comment sections is controlled
- // by the class BreakableLineCommentSection in case it is desirable to keep
- // several line comment sections in the same unwrapped line.
+ // by the class BreakableLineCommentSection in case it is desirable to
+ // keep several line comment sections in the same unwrapped line.
//
- // FIXME: Consider putting separate line comment sections as children to the
- // unwrapped line instead.
+ // FIXME: Consider putting separate line comment sections as children to
+ // the unwrapped line instead.
Tok->ContinuesLineCommentSection =
continuesLineCommentSection(*Tok, *Line, CommentPragmasRegex);
if (isOnNewLine(*Tok) && JustComments && !Tok->ContinuesLineCommentSection)
@@ -4509,10 +4548,10 @@
// Whether or not a line comment token continues a line is controlled by
// the method continuesLineCommentSection, with the following caveat:
//
- // Define a trail of Comments to be a nonempty proper postfix of Comments such
- // that each comment line from the trail is aligned with the next token, if
- // the next token exists. If a trail exists, the beginning of the maximal
- // trail is marked as a start of a new comment section.
+ // Define a trail of Comments to be a nonempty proper postfix of Comments
+ // such that each comment line from the trail is aligned with the next
+ // token, if the next token exists. If a trail exists, the beginning of the
+ // maximal trail is marked as a start of a new comment section.
//
// For example in this code:
//
@@ -4521,8 +4560,8 @@
// // line 2 about b
// int b;
//
- // the two lines about b form a maximal trail, so there are two sections, the
- // first one consisting of the single comment "// line about a" and the
+ // the two lines about b form a maximal trail, so there are two sections,
+ // the first one consisting of the single comment "// line about a" and the
// second one consisting of the next two comments.
if (Comments.empty())
return;
@@ -4611,7 +4650,8 @@
Line->Level += LevelDifference;
// Comments stored before the preprocessor directive need to be output
// before the preprocessor directive, at the same level as the
- // preprocessor directive, as we consider them to apply to the directive.
+ // preprocessor directive, as we consider them to apply to the
+ // directive.
if (Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHash &&
PPBranchLevel > 0) {
Line->Level += PPBranchLevel;
@@ -4635,8 +4675,8 @@
FormatToken *ID = FormatTok;
unsigned Position = Tokens->getPosition();
- // To correctly parse the code, we need to replace the tokens of the macro
- // call with its expansion.
+ // To correctly parse the code, we need to replace the tokens of the
+ // macro call with its expansion.
auto PreCall = std::move(Line);
Line.reset(new UnwrappedLine);
bool OldInExpansion = InExpansion;
Index: clang/lib/Format/TokenAnnotator.cpp
===================================================================
--- clang/lib/Format/TokenAnnotator.cpp
+++ clang/lib/Format/TokenAnnotator.cpp
@@ -18,6 +18,7 @@
#include "clang/Basic/TokenKinds.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/Support/Debug.h"
+#include <sal.h>
#define DEBUG_TYPE "format-token-annotator"
@@ -5011,6 +5012,25 @@
return true;
}
+ // Handle `string Foo {{set {} get {}}`.
+ if (Style.isCSharp() && Style.BraceWrapping.AfterCSharpProperty &&
+ (Line.First && Line.First->isOneOf(Keywords.kw_get, Keywords.kw_set,
+ Keywords.kw_init))) {
+ return true;
+ }
+
+ // string Foo
+ // {
+ // set; interal get {};
+ // }
+ if (Style.isCSharp() && Style.BraceWrapping.AfterCSharpProperty &&
+ ((Left.isOneOf(Keywords.kw_set, Keywords.kw_get, Keywords.kw_init) &&
+ Right.is(tok::l_brace) && Left.Previous &&
+ Left.Previous->isOneOf(tok::kw_public, tok::kw_private,
+ tok::kw_protected, Keywords.kw_internal)))) {
+ return true;
+ }
+
// Don't attempt to interpret struct return types as structs.
if (Right.isNot(TT_FunctionLBrace)) {
return (Line.startsWith(tok::kw_class) &&
@@ -5020,6 +5040,90 @@
}
}
+ // Handle default `string Foo {set;get }`.
+ if (Style.isCSharp() && Style.AlwaysBreakBetweenShortCSharpProperties &&
+ Left.is(tok::semi) &&
+ Right.isOneOf(Keywords.kw_set, Keywords.kw_get, Keywords.kw_init)) {
+ return true;
+ }
+
+ // Handle non short property on a single line
+ // string Foo {
+ // set;get;
+ // }
+ if (Style.isCSharp() && !Style.BraceWrapping.AfterFunction &&
+ !Style.AllowShortCSharpPropertiesOnASingleLine) {
+ if (Left.is(tok::l_brace) &&
+ Right.isOneOf(Keywords.kw_set, Keywords.kw_get, Keywords.kw_init)) {
+ return true;
+ }
+ // string Foo {
+ // <access> set;get;
+ // }
+ if (Left.is(tok::l_brace) &&
+ Right.isOneOf(tok::kw_public, tok::kw_private, tok::kw_protected,
+ Keywords.kw_internal) &&
+ Right.Next &&
+ Right.Next->isOneOf(Keywords.kw_set, Keywords.kw_get,
+ Keywords.kw_init)) {
+ return true;
+ }
+ if (Left.is(tok::semi) && Right.is(tok::r_brace) && Left.Previous &&
+ Left.Previous->isOneOf(Keywords.kw_set, Keywords.kw_get,
+ Keywords.kw_init)) {
+ return true;
+ }
+ }
+
+ if (Style.isCSharp() && Style.BraceWrapping.AfterFunction &&
+ !Style.AllowShortCSharpPropertiesOnASingleLine) {
+ // string Foo
+ // {
+ // <access> set;get;
+ // }
+ if (Left.is(tok::l_brace) &&
+ Right.isOneOf(tok::kw_public, tok::kw_private, tok::kw_protected,
+ Keywords.kw_internal) &&
+ Right.Next &&
+ Right.Next->isOneOf(Keywords.kw_set, Keywords.kw_get,
+ Keywords.kw_init)) {
+ return true;
+ }
+ // string Foo
+ // {
+ // set; <access> get;
+ // }
+ if (Left.is(tok::semi) &&
+ Right.isOneOf(tok::kw_public, tok::kw_private, tok::kw_protected,
+ Keywords.kw_internal) &&
+ Right.Next &&
+ Right.Next->isOneOf(Keywords.kw_set, Keywords.kw_get,
+ Keywords.kw_init)) {
+ return Style.AlwaysBreakBetweenShortCSharpProperties;
+ ;
+ }
+ }
+
+ // string Foo
+ // {
+ // set; get;
+ // }
+ if (Style.isCSharp() && Style.BraceWrapping.AfterCSharpProperty &&
+ ((Left.is(tok::l_brace) &&
+ Right.isOneOf(Keywords.kw_set, Keywords.kw_get, Keywords.kw_init)))) {
+ return true;
+ }
+
+ // string Foo
+ // {
+ // set;
+ // get;
+ // }
+ if (Style.isCSharp() && Style.BraceWrapping.AfterCSharpProperty &&
+ (Left.is(tok::semi) && Right.is(tok::r_brace))) {
+ return true;
+ }
+
if (Left.is(TT_ObjCBlockLBrace) &&
Style.AllowShortBlocksOnASingleLine == FormatStyle::SBS_Never) {
return true;
Index: clang/lib/Format/Format.cpp
===================================================================
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -178,6 +178,7 @@
IO.mapOptional("AfterCaseLabel", Wrapping.AfterCaseLabel);
IO.mapOptional("AfterClass", Wrapping.AfterClass);
IO.mapOptional("AfterControlStatement", Wrapping.AfterControlStatement);
+ IO.mapOptional("AfterCSharpProperty", Wrapping.AfterCSharpProperty);
IO.mapOptional("AfterEnum", Wrapping.AfterEnum);
IO.mapOptional("AfterExternBlock", Wrapping.AfterExternBlock);
IO.mapOptional("AfterFunction", Wrapping.AfterFunction);
@@ -857,6 +858,8 @@
Style.AllowShortBlocksOnASingleLine);
IO.mapOptional("AllowShortCaseLabelsOnASingleLine",
Style.AllowShortCaseLabelsOnASingleLine);
+ IO.mapOptional("AllowShortCSharpPropertiesOnASingleLine",
+ Style.AllowShortCSharpPropertiesOnASingleLine);
IO.mapOptional("AllowShortEnumsOnASingleLine",
Style.AllowShortEnumsOnASingleLine);
IO.mapOptional("AllowShortFunctionsOnASingleLine",
@@ -873,6 +876,8 @@
Style.AlwaysBreakAfterReturnType);
IO.mapOptional("AlwaysBreakBeforeMultilineStrings",
Style.AlwaysBreakBeforeMultilineStrings);
+ IO.mapOptional("AlwaysBreakBetweenShortCSharpProperties",
+ Style.AlwaysBreakBetweenShortCSharpProperties);
IO.mapOptional("AlwaysBreakTemplateDeclarations",
Style.AlwaysBreakTemplateDeclarations);
IO.mapOptional("AttributeMacros", Style.AttributeMacros);
@@ -1174,6 +1179,7 @@
Expanded.BraceWrapping = {/*AfterCaseLabel=*/false,
/*AfterClass=*/false,
/*AfterControlStatement=*/FormatStyle::BWACS_Never,
+ /*AfterCSharpProperty=*/false,
/*AfterEnum=*/false,
/*AfterFunction=*/false,
/*AfterNamespace=*/false,
@@ -1247,6 +1253,7 @@
/*AfterCaseLabel=*/true,
/*AfterClass=*/true,
/*AfterControlStatement=*/FormatStyle::BWACS_Always,
+ /*AfterCSharpProperty=*/false,
/*AfterEnum=*/true,
/*AfterFunction=*/true,
/*AfterNamespace=*/true,
@@ -1324,6 +1331,7 @@
LLVMStyle.AllowAllParametersOfDeclarationOnNextLine = true;
LLVMStyle.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Never;
LLVMStyle.AllowShortCaseLabelsOnASingleLine = false;
+ LLVMStyle.AllowShortCSharpPropertiesOnASingleLine = true;
LLVMStyle.AllowShortEnumsOnASingleLine = true;
LLVMStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All;
LLVMStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never;
@@ -1332,29 +1340,33 @@
LLVMStyle.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None;
LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None;
LLVMStyle.AlwaysBreakBeforeMultilineStrings = false;
+ LLVMStyle.AlwaysBreakBetweenShortCSharpProperties = false;
LLVMStyle.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_MultiLine;
LLVMStyle.AttributeMacros.push_back("__capability");
LLVMStyle.BitFieldColonSpacing = FormatStyle::BFCS_Both;
LLVMStyle.BinPackArguments = true;
LLVMStyle.BinPackParameters = true;
- LLVMStyle.BraceWrapping = {/*AfterCaseLabel=*/false,
- /*AfterClass=*/false,
- /*AfterControlStatement=*/FormatStyle::BWACS_Never,
- /*AfterEnum=*/false,
- /*AfterFunction=*/false,
- /*AfterNamespace=*/false,
- /*AfterObjCDeclaration=*/false,
- /*AfterStruct=*/false,
- /*AfterUnion=*/false,
- /*AfterExternBlock=*/false,
- /*BeforeCatch=*/false,
- /*BeforeElse=*/false,
- /*BeforeLambdaBody=*/false,
- /*BeforeWhile=*/false,
- /*IndentBraces=*/false,
- /*SplitEmptyFunction=*/true,
- /*SplitEmptyRecord=*/true,
- /*SplitEmptyNamespace=*/true};
+ LLVMStyle.BraceWrapping = {
+ /*AfterCaseLabel=*/false,
+ /*AfterClass=*/false,
+ /*AfterControlStatement=*/FormatStyle::BWACS_Never,
+ /*AfterCSharpProperty=*/false,
+ /*AfterEnum=*/false,
+ /*AfterFunction=*/false,
+ /*AfterNamespace=*/false,
+ /*AfterObjCDeclaration=*/false,
+ /*AfterStruct=*/false,
+ /*AfterUnion=*/false,
+ /*AfterExternBlock=*/false,
+ /*BeforeCatch=*/false,
+ /*BeforeElse=*/false,
+ /*BeforeLambdaBody=*/false,
+ /*BeforeWhile=*/false,
+ /*IndentBraces=*/false,
+ /*SplitEmptyFunction=*/true,
+ /*SplitEmptyRecord=*/true,
+ /*SplitEmptyNamespace=*/true,
+ };
LLVMStyle.BreakAfterAttributes = FormatStyle::ABS_Never;
LLVMStyle.BreakAfterJavaFieldAnnotations = false;
LLVMStyle.BreakArrays = true;
Index: clang/include/clang/Format/Format.h
===================================================================
--- clang/include/clang/Format/Format.h
+++ clang/include/clang/Format/Format.h
@@ -538,6 +538,16 @@
/// \version 3.6
bool AllowShortCaseLabelsOnASingleLine;
+ /// If ``true``, short CSharp properties will be contracted to a single line.
+ /// \code
+ /// true: false:
+ /// public string Foo { set; get; } public string Foo {
+ /// set; get;
+ /// }
+ /// \endcode
+ /// \version 17.0
+ bool AllowShortCSharpPropertiesOnASingleLine;
+
/// Allow short enums on a single line.
/// \code
/// true:
@@ -858,6 +868,17 @@
BTDS_Yes
};
+ /// If ``true``, short CSharp properties will be contracted to a single line.
+ /// \code
+ /// true: false:
+ /// public string Foo { public string Foo {
+ /// set; set; get;
+ /// get; }
+ /// }
+ /// \endcode
+ /// \version 17.0
+ bool AlwaysBreakBetweenShortCSharpProperties;
+
/// The template declaration breaking style to use.
/// \version 3.4
BreakTemplateDeclarationsStyle AlwaysBreakTemplateDeclarations;
@@ -1002,6 +1023,7 @@
/// }
/// \endcode
bool AfterCaseLabel;
+
/// Wrap class definitions.
/// \code
/// true:
@@ -1015,6 +1037,33 @@
/// Wrap control statements (``if``/``for``/``while``/``switch``/..).
BraceWrappingAfterControlStatementStyle AfterControlStatement;
+
+ /// Wrap C# setter/getter properties.
+ /// \code
+ /// true:
+ /// string Foo {
+ /// get
+ /// {
+ /// return _foo;
+ /// }
+ /// set
+ /// {
+ /// _foo = value;
+ /// }
+ /// }
+ ///
+ /// false:
+ /// string Foo {
+ /// get {
+ /// return _foo;
+ /// }
+ /// set {
+ /// return _value
+ /// }
+ /// }
+ /// \endcode
+ bool AfterCSharpProperty;
+
/// Wrap enum definitions.
/// \code
/// true:
@@ -1205,6 +1254,28 @@
/// \endcode
///
bool SplitEmptyNamespace;
+
+ bool operator==(const BraceWrappingFlags &Other) const {
+ return AfterCaseLabel == Other.AfterCaseLabel &&
+ AfterClass == Other.AfterClass &&
+ AfterControlStatement == Other.AfterControlStatement &&
+ AfterCSharpProperty == Other.AfterCSharpProperty &&
+ AfterEnum == Other.AfterEnum &&
+ AfterFunction == Other.AfterFunction &&
+ AfterNamespace == Other.AfterNamespace &&
+ AfterObjCDeclaration == Other.AfterObjCDeclaration &&
+ AfterStruct == Other.AfterStruct &&
+ AfterUnion == Other.AfterUnion &&
+ AfterExternBlock == Other.AfterExternBlock &&
+ BeforeCatch == Other.BeforeCatch &&
+ BeforeElse == Other.BeforeElse &&
+ BeforeLambdaBody == Other.BeforeLambdaBody &&
+ BeforeWhile == Other.BeforeWhile &&
+ IndentBraces == Other.IndentBraces &&
+ SplitEmptyFunction == Other.SplitEmptyFunction &&
+ SplitEmptyRecord == Other.SplitEmptyRecord &&
+ SplitEmptyNamespace == Other.SplitEmptyNamespace;
+ }
};
/// Control of individual brace wrapping cases.
@@ -4270,6 +4341,8 @@
AllowShortBlocksOnASingleLine == R.AllowShortBlocksOnASingleLine &&
AllowShortCaseLabelsOnASingleLine ==
R.AllowShortCaseLabelsOnASingleLine &&
+ AllowShortCSharpPropertiesOnASingleLine ==
+ R.AllowShortCSharpPropertiesOnASingleLine &&
AllowShortEnumsOnASingleLine == R.AllowShortEnumsOnASingleLine &&
AllowShortFunctionsOnASingleLine ==
R.AllowShortFunctionsOnASingleLine &&
@@ -4280,6 +4353,8 @@
AlwaysBreakAfterReturnType == R.AlwaysBreakAfterReturnType &&
AlwaysBreakBeforeMultilineStrings ==
R.AlwaysBreakBeforeMultilineStrings &&
+ AlwaysBreakBetweenShortCSharpProperties ==
+ R.AlwaysBreakBetweenShortCSharpProperties &&
AlwaysBreakTemplateDeclarations ==
R.AlwaysBreakTemplateDeclarations &&
AttributeMacros == R.AttributeMacros &&
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -468,6 +468,12 @@
- Add additional Qualifier Ordering support for special cases such
as templates, requires clauses, long qualified names.
- Fix all known issues associated with ``LambdaBodyIndentation: OuterScope``.
+- Add ``AfterCSharpProperty`` style to option ``BraceWrapping`` to allow
+ newline before the opening brace of ``set;get;``.
+- Add ``AllowShortCSharpPropertiesOnASingleLine`` style to allow C# properties
+ To be on a single line.
+- Add ``AlwaysBreakBetweenShortCSharpProperties`` style to allow C# auto
+ properties to be split across multiple lines.
libclang
--------
Index: clang/docs/ClangFormatStyleOptions.rst
===================================================================
--- clang/docs/ClangFormatStyleOptions.rst
+++ clang/docs/ClangFormatStyleOptions.rst
@@ -1042,6 +1042,18 @@
+.. _AllowShortCSharpPropertiesOnASingleLine:
+
+**AllowShortCSharpPropertiesOnASingleLine** (``Boolean``) :versionbadge:`clang-format 17.0` :ref:`¶ <AllowShortCSharpPropertiesOnASingleLine>`
+ If ``true``, short CSharp properties will be contracted to a single line.
+
+ .. code-block:: c++
+
+ true: false:
+ public string Foo { set; get; } public string Foo {
+ set; get;
+ }
+
.. _AllowShortCaseLabelsOnASingleLine:
**AllowShortCaseLabelsOnASingleLine** (``Boolean``) :versionbadge:`clang-format 3.6` :ref:`¶ <AllowShortCaseLabelsOnASingleLine>`
@@ -1385,6 +1397,19 @@
"bbbb" "cccc";
"cccc";
+.. _AlwaysBreakBetweenShortCSharpProperties:
+
+**AlwaysBreakBetweenShortCSharpProperties** (``Boolean``) :versionbadge:`clang-format 17.0` :ref:`¶ <AlwaysBreakBetweenShortCSharpProperties>`
+ If ``true``, short CSharp properties will be contracted to a single line.
+
+ .. code-block:: c++
+
+ true: false:
+ public string Foo { public string Foo {
+ set; set; get;
+ get; }
+ }
+
.. _AlwaysBreakTemplateDeclarations:
**AlwaysBreakTemplateDeclarations** (``BreakTemplateDeclarationsStyle``) :versionbadge:`clang-format 3.4` :ref:`¶ <AlwaysBreakTemplateDeclarations>`
@@ -1628,6 +1653,32 @@
{}
+ * ``bool AfterCSharpProperty`` Wrap C# setter/getter properties.
+
+ .. code-block:: c++
+
+ true:
+ string Foo {
+ get
+ {
+ return _foo;
+ }
+ set
+ {
+ _foo = value;
+ }
+ }
+
+ false:
+ string Foo {
+ get {
+ return _foo;
+ }
+ set {
+ return _value
+ }
+ }
+
* ``bool AfterEnum`` Wrap enum definitions.
.. code-block:: c++
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits