Author: Jonathan Coe Date: 2020-03-23T13:38:21Z New Revision: 78e2a3c678463caeec1524baa96cdeb6fcdb48be
URL: https://github.com/llvm/llvm-project/commit/78e2a3c678463caeec1524baa96cdeb6fcdb48be DIFF: https://github.com/llvm/llvm-project/commit/78e2a3c678463caeec1524baa96cdeb6fcdb48be.diff LOG: [clang-format] Reflow long C# generic type constraints correctly Summary: Align sequential generic type constraints on a type. Indent sequential generic type constraints on different types as continuations. Do not allow '(' and '<' within a generic type constraint to open new scopes. Reviewers: krasimir Subscribers: cfe-commits, MyDeveloperDay Tags: #clang-format, #clang Differential Revision: https://reviews.llvm.org/D76597 Added: Modified: clang/lib/Format/ContinuationIndenter.cpp clang/lib/Format/ContinuationIndenter.h clang/lib/Format/TokenAnnotator.cpp clang/unittests/Format/FormatTestCSharp.cpp Removed: ################################################################################ diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp index 9a6d7877efaa..d2397dbfeb87 100644 --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -634,6 +634,7 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, State.Stack.back().NoLineBreak = true; if (Style.AlignAfterOpenBracket != FormatStyle::BAS_DontAlign && + !State.Stack.back().IsCSharpGenericTypeConstraint && Previous.opensScope() && Previous.isNot(TT_ObjCMethodExpr) && (Current.isNot(TT_LineComment) || Previous.BlockKind == BK_BracedInit)) State.Stack.back().Indent = State.Column + Spaces; @@ -715,6 +716,8 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, } else if (Previous.is(TT_InheritanceColon)) { State.Stack.back().Indent = State.Column; State.Stack.back().LastSpace = State.Column; + } else if (Current.is(TT_CSharpGenericTypeConstraintColon)) { + State.Stack.back().ColonPos = State.Column; } else if (Previous.opensScope()) { // If a function has a trailing call, indent all parameters from the // opening parenthesis. This avoids confusing indents like: @@ -924,7 +927,13 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State, unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { if (!State.NextToken || !State.NextToken->Previous) return 0; + FormatToken &Current = *State.NextToken; + + if (State.Stack.back().IsCSharpGenericTypeConstraint && + Current.isNot(TT_CSharpGenericTypeConstraint)) + return State.Stack.back().ColonPos + 2; + const FormatToken &Previous = *Current.Previous; // If we are continuing an expression, we want to use the continuation indent. unsigned ContinuationIndent = @@ -1106,9 +1115,11 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, assert(State.Stack.size()); const FormatToken &Current = *State.NextToken; + if (Current.is(TT_CSharpGenericTypeConstraint)) + State.Stack.back().IsCSharpGenericTypeConstraint = true; if (Current.isOneOf(tok::comma, TT_BinaryOperator)) State.Stack.back().NoLineBreakInOperand = false; - if (Current.is(TT_InheritanceColon)) + if (Current.isOneOf(TT_InheritanceColon, TT_CSharpGenericTypeConstraintColon)) State.Stack.back().AvoidBinPacking = true; if (Current.is(tok::lessless) && Current.isNot(TT_OverloadedOperator)) { if (State.Stack.back().FirstLessLess == 0) @@ -1329,6 +1340,11 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, if (!Current.opensScope()) return; + // Don't allow '<' or '(' in C# generic type constraints to start new scopes. + if (Current.isOneOf(tok::less, tok::l_paren) && + State.Stack.back().IsCSharpGenericTypeConstraint) + return; + if (Current.MatchingParen && Current.BlockKind == BK_Block) { moveStateToNewBlock(State); return; @@ -1393,6 +1409,7 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, (State.Line->Type == LT_ObjCDecl && ObjCBinPackProtocolList); AvoidBinPacking = + (State.Stack.back().IsCSharpGenericTypeConstraint) || (Style.Language == FormatStyle::LK_JavaScript && EndsInComma) || (State.Line->MustBeDeclaration && !BinPackDeclaration) || (!State.Line->MustBeDeclaration && !Style.BinPackArguments) || diff --git a/clang/lib/Format/ContinuationIndenter.h b/clang/lib/Format/ContinuationIndenter.h index 11df619e0f40..ab116d5468e8 100644 --- a/clang/lib/Format/ContinuationIndenter.h +++ b/clang/lib/Format/ContinuationIndenter.h @@ -208,7 +208,8 @@ struct ParenState { LastOperatorWrapped(true), ContainsLineBreak(false), ContainsUnwrappedBuilder(false), AlignColons(true), ObjCSelectorNameFound(false), HasMultipleNestedBlocks(false), - NestedBlockInlined(false), IsInsideObjCArrayLiteral(false) {} + NestedBlockInlined(false), IsInsideObjCArrayLiteral(false), + IsCSharpGenericTypeConstraint(false) {} /// \brief The token opening this parenthesis level, or nullptr if this level /// is opened by fake parenthesis. @@ -329,6 +330,8 @@ struct ParenState { /// array literal. bool IsInsideObjCArrayLiteral : 1; + bool IsCSharpGenericTypeConstraint : 1; + bool operator<(const ParenState &Other) const { if (Indent != Other.Indent) return Indent < Other.Indent; @@ -366,6 +369,8 @@ struct ParenState { return ContainsUnwrappedBuilder; if (NestedBlockInlined != Other.NestedBlockInlined) return NestedBlockInlined; + if (IsCSharpGenericTypeConstraint != Other.IsCSharpGenericTypeConstraint) + return IsCSharpGenericTypeConstraint; return false; } }; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 7193c8e6de44..685749fc31ab 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -3620,6 +3620,9 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, if (Left.isOneOf(TT_CSharpNamedArgumentColon, TT_AttributeColon) || Right.isOneOf(TT_CSharpNamedArgumentColon, TT_AttributeColon)) return false; + // Only break after commas for generic type constraints. + if (Line.First->is(TT_CSharpGenericTypeConstraint)) + return Left.is(TT_CSharpGenericTypeConstraintComma); } else if (Style.Language == FormatStyle::LK_Java) { if (Left.isOneOf(Keywords.kw_throws, Keywords.kw_extends, Keywords.kw_implements)) diff --git a/clang/unittests/Format/FormatTestCSharp.cpp b/clang/unittests/Format/FormatTestCSharp.cpp index 9746f6e15322..a0f60ce799d2 100644 --- a/clang/unittests/Format/FormatTestCSharp.cpp +++ b/clang/unittests/Format/FormatTestCSharp.cpp @@ -687,6 +687,14 @@ class Dictionary<TKey, TVal> where T : IMyInterface { doThing(); } })", Style); + + verifyFormat(R"(// +class ItemFactory<T> + where T : new(), + IAnInterface<T>, + IAnotherInterface<T>, + IAnotherInterfaceStill<T> {})", + Style); } } // namespace format _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits