diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index dc68cbc79734..6bbfdcf37f93 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -771,6 +771,7 @@ struct AdditionalKeywords { kw_unchecked = &IdentTable.get("unchecked"); kw_unsafe = &IdentTable.get("unsafe"); kw_ushort = &IdentTable.get("ushort"); + kw_when = &IdentTable.get("when"); // Keep this at the end of the constructor to make sure everything here // is @@ -787,7 +788,7 @@ struct AdditionalKeywords { kw_fixed, kw_foreach, kw_implicit, kw_in, kw_interface, kw_internal, kw_is, kw_lock, kw_null, kw_object, kw_out, kw_override, kw_params, kw_readonly, kw_ref, kw_string, kw_stackalloc, kw_sbyte, kw_sealed, - kw_uint, kw_ulong, kw_unchecked, kw_unsafe, kw_ushort, + kw_uint, kw_ulong, kw_unchecked, kw_unsafe, kw_ushort, kw_when, // Keywords from the JavaScript section. kw_as, kw_async, kw_await, kw_declare, kw_finally, kw_from, kw_function, kw_get, kw_import, kw_is, kw_let, kw_module, kw_readonly, @@ -891,6 +892,7 @@ struct AdditionalKeywords { IdentifierInfo *kw_unchecked; IdentifierInfo *kw_unsafe; IdentifierInfo *kw_ushort; + IdentifierInfo *kw_when; /// Returns \c true if \p Tok is a true JavaScript identifier, returns /// \c false if it is a keyword or a pseudo keyword. diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index d71c4f470378..04b20599638d 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -2870,21 +2870,34 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, if (Left.is(tok::numeric_constant) && Right.is(tok::percent)) return Right.WhitespaceRange.getEnd() != Right.WhitespaceRange.getBegin(); } else if (Style.isCSharp()) { + // Require spaces around '{' and before '}' unless they appear in + // interpolated strings. Interpolated strings are merged into a single token + // so cannot have spaces inserted by this function. + + // Space before { (including space within '{ {'). + if (Right.is(tok::l_brace)) + return true; + + // Spaces inside braces. + if (Left.is(tok::l_brace) && Right.isNot(tok::r_brace)) + return true; + + if (Left.isNot(tok::l_brace) && Right.is(tok::r_brace)) + return true; + + // Spaces around '=>'. + if (Left.is(TT_JsFatArrow) || Right.is(TT_JsFatArrow)) + return true; + // 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)) + if (Left.isOneOf(tok::kw_using, Keywords.kw_async, Keywords.kw_when)) return Style.SpaceBeforeParens == FormatStyle::SBPO_ControlStatements || spaceRequiredBeforeParens(Right); - // space between ']' and '{' - if (Left.is(tok::r_square) && Right.is(tok::l_brace)) - return true; - // space before '{' in "new MyType {" - if (Right.is(tok::l_brace) && Left.Previous && - Left.Previous->is(tok::kw_new)) - return true; } else if (Style.Language == FormatStyle::LK_JavaScript) { if (Left.is(TT_JsFatArrow)) return true; diff --git a/clang/unittests/Format/FormatTestCSharp.cpp b/clang/unittests/Format/FormatTestCSharp.cpp index 68a8e9fefe2d..3bdaf3d5a9f3 100644 --- a/clang/unittests/Format/FormatTestCSharp.cpp +++ b/clang/unittests/Format/FormatTestCSharp.cpp @@ -525,11 +525,11 @@ Shape[] shapes = new[] { // Omitted final `,`s will change the formatting. verifyFormat(R"(// -Shape[] shapes = new[] {new Circle {Radius = 2.7281, Colour = Colours.Red}, - new Square { - Side = 101.1, - Colour = Colours.Yellow, - }};)", +Shape[] shapes = new[] { new Circle { Radius = 2.7281, Colour = Colours.Red }, + new Square { + Side = 101.1, + Colour = Colours.Yellow, + } };)", Style); } @@ -575,5 +575,27 @@ public string Name { Style); } +TEST_F(FormatTestCSharp, CSharpSpaces) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); + Style.SpaceBeforeSquareBrackets = false; + Style.SpacesInSquareBrackets = false; + Style.SpaceBeforeCpp11BracedList = true; + Style.Cpp11BracedListStyle = false; + Style.SpacesInContainerLiterals = false; + + verifyFormat(R"(new Car { "Door", 0.1 })", Style); + verifyFormat(R"(new Car { 0.1, "Door" })", Style); + verifyFormat(R"(new string[] { "A" })", Style); + verifyFormat(R"(new string[] {})", Style); + verifyFormat(R"(new Car { someVariableName })", Style); + verifyFormat(R"(new Car { someVariableName })", Style); + verifyFormat(R"(new Dictionary { ["Key"] = "Value" };)", + Style); + verifyFormat(R"(Apply(x => x.Name, x => () => x.ID);)", Style); + verifyFormat(R"(bool[] xs = { true, true };)", Style); + verifyFormat(R"(taskContext.Factory.Run(async () => doThing(args);)", Style); + verifyFormat(R"(catch (TestException) when (innerFinallyExecuted))", Style); +} + } // namespace format } // end namespace clang