Index: clang/lib/Format/FormatToken.h =================================================================== --- clang/lib/Format/FormatToken.h +++ clang/lib/Format/FormatToken.h @@ -103,6 +103,7 @@ TYPE(UnaryOperator) \ TYPE(CSharpStringLiteral) \ TYPE(CSharpNullCoalescing) \ + TYPE(CSharpNamedArgument) \ TYPE(Unknown) enum TokenType { Index: clang/lib/Format/FormatTokenLexer.h =================================================================== --- clang/lib/Format/FormatTokenLexer.h +++ clang/lib/Format/FormatTokenLexer.h @@ -56,6 +56,7 @@ bool tryMergeCSharpDoubleQuestion(); bool tryTransformCSharpForEach(); bool tryMergeCSharpAttributeAndTarget(); + bool tryMergeCSharpNamedArgument(); bool tryMergeTokens(ArrayRef Kinds, TokenType NewType); Index: clang/lib/Format/FormatTokenLexer.cpp =================================================================== --- clang/lib/Format/FormatTokenLexer.cpp +++ clang/lib/Format/FormatTokenLexer.cpp @@ -76,6 +76,8 @@ return; if (Style.isCSharp()) { + if (tryMergeCSharpNamedArgument()) + return; if (tryMergeCSharpAttributeAndTarget()) return; if (tryMergeCSharpKeywordVariables()) @@ -184,6 +186,39 @@ return true; } +// Merge 'argName' and ':' into a single token in `foo(argName: bar)`. +bool FormatTokenLexer::tryMergeCSharpNamedArgument() { + if (Tokens.size() < 2) + return false; + auto &Colon = *(Tokens.end() - 1); + if (!Colon->is(tok::colon)) + return false; + + auto &Name = *(Tokens.end() - 2); + if (!Name->is(tok::identifier)) + return false; + + const FormatToken *CommaOrLeftParen = nullptr; + for (auto I = Tokens.rbegin() + 2, E = Tokens.rend(); I != E; ++I) { + // NB: Because previous pointers are not initialized yet, this cannot use + // Token.getPreviousNonComment. + if ((*I)->isNot(tok::comment)) { + CommaOrLeftParen = *I; + break; + } + } + + if (!CommaOrLeftParen || !CommaOrLeftParen->isOneOf(tok::l_paren, tok::comma)) + return false; + + Name->TokenText = StringRef(Name->TokenText.begin(), + Colon->TokenText.end() - Name->TokenText.begin()); + Name->ColumnWidth += Colon->ColumnWidth; + Name->Type = TT_CSharpNamedArgument; + Tokens.erase(Tokens.end() - 1); + return true; +} + // Search for verbatim or interpolated string literals @"ABC" or // $"aaaaa{abc}aaaaa" i and mark the token as TT_CSharpStringLiteral, and to // prevent splitting of @, $ and ". Index: clang/unittests/Format/FormatTestCSharp.cpp =================================================================== --- clang/unittests/Format/FormatTestCSharp.cpp +++ clang/unittests/Format/FormatTestCSharp.cpp @@ -513,7 +513,7 @@ TEST_F(FormatTestCSharp, CSharpObjectInitializers) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); - // Start code fragemnts with a comment line so that C++ raw string literals + // Start code fragments with a comment line so that C++ raw string literals // as seen are identical to expected formatted code. verifyFormat(R"(// @@ -539,5 +539,20 @@ Style); } +TEST_F(FormatTestCSharp, CSharpNamedArguments) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); + + verifyFormat(R"(// +PrintOrderDetails(orderNum: 31, productName: "Red Mug", + sellerName: "Gift Shop");)", + Style); + + // Ensure that trailing comments do not cause problems. + verifyFormat(R"(// +PrintOrderDetails(orderNum: 31, productName: "Red Mug", // comment + sellerName: "Gift Shop");)", + Style); +} + } // namespace format } // end namespace clang