From d1b412ae389e4e30706c326ddec192ffb2e272cf Mon Sep 17 00:00:00 2001 From: Jonathan Coe Date: Tue, 31 Mar 2020 18:41:39 +0100 Subject: [PATCH] [clang-format] Correct line breaks in C# generic type constraints Reviewers: krasimir Reviewed By: krasimir Subscribers: cfe-commits Tags: #clang-format, #clang Differential Revision: https://reviews.llvm.org/D77064 --- clang/lib/Format/ContinuationIndenter.cpp | 5 +++++ clang/lib/Format/TokenAnnotator.cpp | 7 ++++++- clang/lib/Format/UnwrappedLineParser.cpp | 6 ++++++ clang/unittests/Format/FormatTestCSharp.cpp | 11 ++++++++++- 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp index d2397db..ba42ba0 100644 --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -346,6 +346,11 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { Current.startsSequence(TT_SelectorName, tok::colon, tok::caret)) { return true; } + // Avoid producing inconsistent states by requiring breaks where they are not + // permitted for C# generic type constraints. + if (State.Stack.back().IsCSharpGenericTypeConstraint && + Previous.isNot(TT_CSharpGenericTypeConstraintComma)) + return false; if ((startsNextParameter(Current, Style) || Previous.is(tok::semi) || (Previous.is(TT_TemplateCloser) && Current.is(TT_StartOfName) && Style.isCpp() && diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 8f40fc7..a3cd4f42 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -1060,15 +1060,20 @@ private: } void parseCSharpGenericTypeConstraint() { + int OpenAngleBracketsCount = 0; while (CurrentToken) { if (CurrentToken->is(tok::less)) { // parseAngle is too greedy and will consume the whole line. CurrentToken->Type = TT_TemplateOpener; + ++OpenAngleBracketsCount; next(); } else if (CurrentToken->is(tok::greater)) { CurrentToken->Type = TT_TemplateCloser; + --OpenAngleBracketsCount; next(); - } else if (CurrentToken->is(tok::comma)) { + } else if (CurrentToken->is(tok::comma) && OpenAngleBracketsCount == 0) { + // We allow line breaks after GenericTypeConstraintComma's + // so do not flag commas in Generics as GenericTypeConstraintComma's. CurrentToken->Type = TT_CSharpGenericTypeConstraintComma; next(); } else if (CurrentToken->is(Keywords.kw_where)) { diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index d8202bd..1889931 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -2340,6 +2340,12 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr) { } if (FormatTok->Tok.is(tok::semi)) return; + if (Style.isCSharp() && FormatTok->is(Keywords.kw_where)) { + addUnwrappedLine(); + nextToken(); + parseCSharpGenericTypeConstraint(); + break; + } nextToken(); } } diff --git a/clang/unittests/Format/FormatTestCSharp.cpp b/clang/unittests/Format/FormatTestCSharp.cpp index f5e0bab..b0e4e76 100644 --- a/clang/unittests/Format/FormatTestCSharp.cpp +++ b/clang/unittests/Format/FormatTestCSharp.cpp @@ -564,7 +564,7 @@ var myDict = new Dictionary { TEST_F(FormatTestCSharp, CSharpArrayInitializers) { FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp); - + verifyFormat(R"(// private MySet[] setPoints = { new Point(), @@ -710,6 +710,15 @@ class ItemFactory IAnotherInterfaceStill {})", Style); + Style.ColumnLimit = 50; // Force lines to be wrapped. + verifyFormat(R"(// +class ItemFactory + where T : new(), + IAnInterface, + IAnotherInterface, + IAnotherInterfaceStill {})", + Style); + // In other languages `where` can be used as a normal identifier. // This example is in C++! verifyFormat(R"(// -- 2.7.4