Index: clang/lib/Format/FormatTokenLexer.h =================================================================== --- clang/lib/Format/FormatTokenLexer.h +++ clang/lib/Format/FormatTokenLexer.h @@ -53,6 +53,7 @@ bool tryMergeCSharpKeywordVariables(); bool tryMergeCSharpNullConditionals(); bool tryMergeCSharpDoubleQuestion(); + bool tryTransformCSharpForEach(); bool tryMergeTokens(ArrayRef Kinds, TokenType NewType); Index: clang/lib/Format/FormatTokenLexer.cpp =================================================================== --- clang/lib/Format/FormatTokenLexer.cpp +++ clang/lib/Format/FormatTokenLexer.cpp @@ -80,6 +80,8 @@ return; if (tryMergeCSharpNullConditionals()) return; + if (tryTransformCSharpForEach()) + return; static const tok::TokenKind JSRightArrow[] = {tok::equal, tok::greater}; if (tryMergeTokens(JSRightArrow, TT_JsFatArrow)) return; @@ -254,6 +256,21 @@ return true; } +// In C# transform identifier foreach into kw_foreach +bool FormatTokenLexer::tryTransformCSharpForEach() { + if (Tokens.size() < 1) + return false; + auto &Identifier = *(Tokens.end() - 1); + if (!Identifier->is(tok::identifier)) + return false; + if (Identifier->TokenText != "foreach") + return false; + + Identifier->Type = TT_ForEachMacro; + Identifier->Tok.setKind(tok::kw_for); + return true; +} + bool FormatTokenLexer::tryMergeLessLess() { // Merge X,less,less,Y into X,lessless,Y unless X or Y is less. if (Tokens.size() < 3) Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -2624,10 +2624,6 @@ return Style.Language == FormatStyle::LK_JavaScript || !Left.TokenText.endswith("=*/"); if (Right.is(tok::l_paren)) { - // using (FileStream fs... - if (Style.isCSharp() && Left.is(tok::kw_using) && - Style.SpaceBeforeParens != FormatStyle::SBPO_Never) - return true; if ((Left.is(tok::r_paren) && Left.is(TT_AttributeParen)) || (Left.is(tok::r_square) && Left.is(TT_AttributeSquare))) return true; @@ -2718,7 +2714,15 @@ // and "%d %d" if (Left.is(tok::numeric_constant) && Right.is(tok::percent)) return Right.WhitespaceRange.getEnd() != Right.WhitespaceRange.getBegin(); - } else if (Style.Language == FormatStyle::LK_JavaScript || Style.isCSharp()) { + } else if (Style.isCSharp()) { + // space between type and variable e.g. Dictionary foo; + if (Left.is(TT_TemplateCloser) && Right.is(TT_StartOfName)) + return true; + // space between keywords and paren e.g. "using (" + if (Right.is(tok::l_paren)) + if (Left.is(tok::kw_using)) + return spaceRequiredBeforeParens(Left); + } else if (Style.Language == FormatStyle::LK_JavaScript) { if (Left.is(TT_JsFatArrow)) return true; // for await ( ... Index: clang/unittests/Format/FormatTestCSharp.cpp =================================================================== --- clang/unittests/Format/FormatTestCSharp.cpp +++ clang/unittests/Format/FormatTestCSharp.cpp @@ -58,6 +58,7 @@ " while (true) f();\n" " for (;;) f();\n" " if (true) f();\n" + " foreach (x in y) f();\n" " }\n" "}"); } @@ -119,13 +120,25 @@ } TEST_F(FormatTestCSharp, CSharpNullConditional) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); + Style.SpaceBeforeParens = FormatStyle::SBPO_Always; + verifyFormat( "public Person(string firstName, string lastName, int? age=null)"); - verifyFormat("switch(args?.Length)"); + verifyFormat("foo () {\n" + " switch (args?.Length) {}\n" + "}", + Style); + + verifyFormat("switch (args?.Length) {}", Style); verifyFormat("public static void Main(string[] args) { string dirPath " "= args?[0]; }"); + + Style.SpaceBeforeParens = FormatStyle::SBPO_Never; + + verifyFormat("switch(args?.Length) {}", Style); } TEST_F(FormatTestCSharp, Attributes) { @@ -168,16 +181,22 @@ TEST_F(FormatTestCSharp, CSharpUsing) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); Style.SpaceBeforeParens = FormatStyle::SBPO_Always; - verifyFormat("public void foo() {\n" + verifyFormat("public void foo () {\n" " using (StreamWriter sw = new StreamWriter (filenameA)) {}\n" "}", Style); + verifyFormat("using (StreamWriter sw = new StreamWriter (filenameB)) {}", + Style); + Style.SpaceBeforeParens = FormatStyle::SBPO_Never; verifyFormat("public void foo() {\n" " using(StreamWriter sw = new StreamWriter(filenameB)) {}\n" "}", Style); + + verifyFormat("using(StreamWriter sw = new StreamWriter(filenameB)) {}", + Style); } TEST_F(FormatTestCSharp, CSharpRegions) { @@ -195,5 +214,40 @@ verifyFormat("return _name ?? \"DEF\";"); } +TEST_F(FormatTestCSharp, CSharpSpaceBefore) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); + Style.SpaceBeforeParens = FormatStyle::SBPO_Always; + + verifyFormat("List list;", Style); + verifyFormat("Dictionary dict;", Style); + + verifyFormat("for (int i = 0; i < size (); i++) {\n" + "}", + Style); + verifyFormat("foreach (var x in y) {\n" + "}", + Style); + verifyFormat("switch (x) {}", Style); + verifyFormat("do {\n" + "} while (x);", + Style); + + Style.SpaceBeforeParens = FormatStyle::SBPO_Never; + + verifyFormat("List list;", Style); + verifyFormat("Dictionary dict;", Style); + + verifyFormat("for(int i = 0; i < size(); i++) {\n" + "}", + Style); + verifyFormat("foreach(var x in y) {\n" + "}", + Style); + verifyFormat("switch(x) {}", Style); + verifyFormat("do {\n" + "} while(x);", + Style); +} + } // namespace format } // end namespace clang