diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -1482,8 +1482,8 @@ } case tok::equal: // Fat arrows (=>) have tok::TokenKind tok::equal but TokenType - // TT_FatArrow. The always start an expression or a child block if - // followed by a curly. + // TT_FatArrow. They always start an expression or a child block if + // followed by a curly brace. if (FormatTok->is(TT_FatArrow)) { nextToken(); if (FormatTok->is(tok::l_brace)) { @@ -1790,14 +1790,20 @@ bool HasError = false; // FIXME: Once we have an expression parser in the UnwrappedLineParser, - // replace this by using parseAssigmentExpression() inside. + // replace this by using parseAssignmentExpression() inside. do { if (Style.isCSharp()) { + // Fat arrows (=>) have tok::TokenKind tok::equal but TokenType + // TT_FatArrow. They always start an expression or a child block if + // followed by a curly brace. if (FormatTok->is(TT_FatArrow)) { nextToken(); - // Fat arrows can be followed by simple expressions or by child blocks - // in curly braces. if (FormatTok->is(tok::l_brace)) { + // C# may break after => if the next character is a newline. + if (Style.isCSharp() && Style.BraceWrapping.AfterFunction == true) { + // calling `addUnwrappedLine()` here causes odd parsing errors. + FormatTok->MustBreakBefore = true; + } parseChildBlock(); continue; } @@ -1927,6 +1933,12 @@ parseBracedList(); } break; + case tok::equal: + if (Style.isCSharp() && FormatTok->is(TT_FatArrow)) + parseStructuralElement(); + else + nextToken(); + break; case tok::kw_class: if (Style.Language == FormatStyle::LK_JavaScript) parseRecord(/*ParseAsExpr=*/true); diff --git a/clang/unittests/Format/FormatTestCSharp.cpp b/clang/unittests/Format/FormatTestCSharp.cpp --- a/clang/unittests/Format/FormatTestCSharp.cpp +++ b/clang/unittests/Format/FormatTestCSharp.cpp @@ -640,6 +640,122 @@ }; })", MicrosoftStyle); + + verifyFormat("void bar()\n" + "{\n" + " Function(Val, (Action)(() =>\n" + " {\n" + " lock (mylock)\n" + " {\n" + " if (true)\n" + " {\n" + " A.Remove(item);\n" + " }\n" + " }\n" + " }));\n" + "}", + MicrosoftStyle); + + verifyFormat("void baz()\n" + "{\n" + " Function(Val, (Action)(() =>\n" + " {\n" + " using (var a = new Lock())\n" + " {\n" + " if (true)\n" + " {\n" + " A.Remove(item);\n" + " }\n" + " }\n" + " }));\n" + "}", + MicrosoftStyle); + + verifyFormat("void baz()\n" + "{\n" + " Function(Val, (Action)(() =>\n" + " {\n" + " if (true)\n" + " {\n" + " A.Remove(item);\n" + " }\n" + " }));\n" + "}", + MicrosoftStyle); + + verifyFormat("void baz()\n" + "{\n" + " Function(Val, (Action)(() =>\n" + " {\n" + " do\n" + " {\n" + " A.Remove(item);\n" + " } while (true)\n" + " }));\n" + "}", + MicrosoftStyle); + + verifyFormat("void baz()\n" + "{\n" + " Function(Val, (Action)(() =>\n" + " { A.Remove(item); }));\n" + "}", + MicrosoftStyle); + + verifyFormat("void bar()\n" + "{\n" + " Function(Val, (() =>\n" + " {\n" + " lock (mylock)\n" + " {\n" + " if (true)\n" + " {\n" + " A.Remove(item);\n" + " }\n" + " }\n" + " }));\n" + "}", + MicrosoftStyle); + verifyFormat("void bar()\n" + "{\n" + " Function((() =>\n" + " {\n" + " lock (mylock)\n" + " {\n" + " if (true)\n" + " {\n" + " A.Remove(item);\n" + " }\n" + " }\n" + " }));\n" + "}", + MicrosoftStyle); + + MicrosoftStyle.IndentWidth = 2; + verifyFormat("void bar()\n" + "{\n" + " Function((() =>\n" + " {\n" + " lock (mylock)\n" + " {\n" + " if (true)\n" + " {\n" + " A.Remove(item);\n" + " }\n" + " }\n" + " }));\n" + "}", + MicrosoftStyle); + verifyFormat("void bar() {\n" + " Function((() => {\n" + " lock (mylock) {\n" + " if (true) {\n" + " A.Remove(item);\n" + " }\n" + " }\n" + " }));\n" + "}", + GoogleStyle); } TEST_F(FormatTestCSharp, CSharpObjectInitializers) {