Index: clang/lib/Format/FormatToken.h =================================================================== --- clang/lib/Format/FormatToken.h +++ clang/lib/Format/FormatToken.h @@ -104,6 +104,7 @@ TYPE(CSharpStringLiteral) \ TYPE(CSharpNullCoalescing) \ TYPE(CSharpNamedArgumentColon) \ + TYPE(CSharpNullableTypeQuestionMark) \ TYPE(Unknown) enum TokenType { Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -987,6 +987,10 @@ if (Line.MustBeDeclaration && !Contexts.back().IsExpression && Style.Language == FormatStyle::LK_JavaScript) break; + if (Style.isCSharp() && Line.MustBeDeclaration) { + Tok->Type = TT_CSharpNullableTypeQuestionMark; + break; + } parseConditional(); break; case tok::kw_template: @@ -2902,6 +2906,10 @@ if (Left.is(tok::comma) && Right.is(tok::r_square)) return Style.SpacesInSquareBrackets; + // No space before ? in nullable types. + if (Right.is(TT_CSharpNullableTypeQuestionMark)) + return false; + // space between keywords and paren e.g. "using (" if (Right.is(tok::l_paren)) if (Left.isOneOf(tok::kw_using, Keywords.kw_async, Keywords.kw_when)) Index: clang/unittests/Format/FormatTestCSharp.cpp =================================================================== --- clang/unittests/Format/FormatTestCSharp.cpp +++ clang/unittests/Format/FormatTestCSharp.cpp @@ -175,7 +175,7 @@ Style.SpaceBeforeParens = FormatStyle::SBPO_Always; verifyFormat( - "public Person(string firstName, string lastName, int? age=null)"); + "public Person(string firstName, string lastName, int? age = null)"); verifyFormat("foo () {\n" " switch (args?.Length) {}\n" @@ -603,5 +603,11 @@ verifyFormat(R"(private float[, ] Values;)", Style); } +TEST_F(FormatTestCSharp, CSharpNullableTypes) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); + + verifyFormat(R"(public float? Value;)", Style); // no space before `?`. +} + } // namespace format } // end namespace clang