diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -112,6 +112,7 @@ TYPE(UntouchableMacroFunc) \ TYPE(CSharpStringLiteral) \ TYPE(CSharpNamedArgumentColon) \ + TYPE(CSharpNullable) \ TYPE(CSharpNullConditionalLSquare) \ TYPE(CSharpGenericTypeConstraint) \ TYPE(CSharpGenericTypeConstraintColon) \ diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -1078,7 +1078,7 @@ (Tok->Next && Tok->Next->isOneOf(tok::r_paren, tok::greater)) || (Tok->Next && Tok->Next->is(tok::identifier) && Tok->Next->Next && Tok->Next->Next->is(tok::equal))) { - Tok->setType(TT_JsTypeOptionalQuestion); + Tok->setType(TT_CSharpNullable); break; } } @@ -3161,7 +3161,7 @@ return Style.SpacesInSquareBrackets; // No space before ? in nullable types. - if (Right.is(TT_JsTypeOptionalQuestion)) + if (Right.is(TT_CSharpNullable)) return false; // No space before null forgiving '!'. @@ -3818,6 +3818,10 @@ // Only break after commas for generic type constraints. if (Line.First->is(TT_CSharpGenericTypeConstraint)) return Left.is(TT_CSharpGenericTypeConstraintComma); + // Keep nullable operators attached to their identifiers. + if (Right.is(TT_CSharpNullable)) { + return false; + } } else if (Style.Language == FormatStyle::LK_Java) { if (Left.isOneOf(Keywords.kw_throws, Keywords.kw_extends, Keywords.kw_implements)) diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -114,6 +114,7 @@ void parseNew(); void parseAccessSpecifier(); bool parseEnum(); + bool parseStructLike(); void parseConcept(); void parseRequires(); void parseRequiresExpression(unsigned int OriginalLevel); 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 @@ -1316,15 +1316,7 @@ case tok::kw_struct: case tok::kw_union: case tok::kw_class: - // parseRecord falls through and does not yet add an unwrapped line as a - // record declaration or definition can start a structural element. - parseRecord(); - // This does not apply for Java, JavaScript and C#. - if (Style.Language == FormatStyle::LK_Java || - Style.Language == FormatStyle::LK_JavaScript || Style.isCSharp()) { - if (FormatTok->is(tok::semi)) - nextToken(); - addUnwrappedLine(); + if (parseStructLike()) { return; } break; @@ -1438,6 +1430,13 @@ return; } + if (FormatTok->is(Keywords.kw_interface)) { + if (parseStructLike()) { + return; + } + break; + } + if (Style.isCpp() && FormatTok->is(TT_StatementMacro)) { parseStatementMacro(); return; @@ -2525,6 +2524,21 @@ // "} n, m;" will end up in one unwrapped line. } +bool UnwrappedLineParser::parseStructLike() { + // parseRecord falls through and does not yet add an unwrapped line as a + // record declaration or definition can start a structural element. + parseRecord(); + // This does not apply to Java, JavaScript and C#. + if (Style.Language == FormatStyle::LK_Java || + Style.Language == FormatStyle::LK_JavaScript || Style.isCSharp()) { + if (FormatTok->is(tok::semi)) + nextToken(); + addUnwrappedLine(); + return true; + } + return false; +} + namespace { // A class used to set and restore the Token position when peeking // ahead in the token source. 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 @@ -848,6 +848,21 @@ verifyFormat(R"(var x = (int?)y;)", Style); // Cast to a nullable type. verifyFormat(R"(var x = new MyContainer();)", Style); // Generics. + + verifyFormat(R"(// +public interface I { + int? Function(); +})", + Style); // Interface methods. + + Style.ColumnLimit = 10; + verifyFormat(R"(// +public VeryLongType? Function( + int arg1, + int arg2) { + // +})", + Style); // ? sticks with identifier. } TEST_F(FormatTestCSharp, CSharpArraySubscripts) {