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:`¶ ` + 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:`¶ ` @@ -1385,6 +1397,19 @@ "bbbb" "cccc"; "cccc"; +.. _AlwaysBreakBetweenShortCSharpProperties: + +**AlwaysBreakBetweenShortCSharpProperties** (``Boolean``) :versionbadge:`clang-format 17.0` :ref:`¶ ` + 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:`¶ ` @@ -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++ 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/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/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/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 #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 { + // 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 + // { + // 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; 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/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 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 + // 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)` 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)` in the below example would be seen as + // the whole requires clause, accidentally eating the parameters of the + // lambda. // [&] requires foo (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/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