[clang] 78e2a3c - [clang-format] Reflow long C# generic type constraints correctly
Jonathan Coe via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 23 06:41:43 PDT 2020
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
More information about the cfe-commits
mailing list