forked from OSchip/llvm-project
[clang-format] Handle C# generic type constraints
Summary: Treat each C# generic type constraint, `where T: ...`, as a line. Add C# keyword: where Add Token Types: CSharpGenericTypeConstraint, CSharpGenericTypeConstraintColon, CSharpGenericTypeConstraintComma. This patch does not wrap generic type constraints well, that will be addressed in a follow up patch. Reviewers: krasimir Reviewed By: krasimir Subscribers: cfe-commits, MyDeveloperDay Tags: #clang-format, #clang Differential Revision: https://reviews.llvm.org/D76367
This commit is contained in:
parent
0ea4fb5bb7
commit
dcbcec4822
|
@ -108,6 +108,9 @@ namespace format {
|
|||
TYPE(CSharpNullCoalescing) \
|
||||
TYPE(CSharpNullConditional) \
|
||||
TYPE(CSharpNullConditionalLSquare) \
|
||||
TYPE(CSharpGenericTypeConstraint) \
|
||||
TYPE(CSharpGenericTypeConstraintColon) \
|
||||
TYPE(CSharpGenericTypeConstraintComma) \
|
||||
TYPE(Unknown)
|
||||
|
||||
enum TokenType {
|
||||
|
@ -779,6 +782,7 @@ struct AdditionalKeywords {
|
|||
kw_unsafe = &IdentTable.get("unsafe");
|
||||
kw_ushort = &IdentTable.get("ushort");
|
||||
kw_when = &IdentTable.get("when");
|
||||
kw_where = &IdentTable.get("where");
|
||||
|
||||
// Keep this at the end of the constructor to make sure everything here
|
||||
// is
|
||||
|
@ -796,6 +800,7 @@ struct AdditionalKeywords {
|
|||
kw_is, kw_lock, kw_null, kw_object, kw_out, kw_override, kw_params,
|
||||
kw_readonly, kw_ref, kw_string, kw_stackalloc, kw_sbyte, kw_sealed,
|
||||
kw_uint, kw_ulong, kw_unchecked, kw_unsafe, kw_ushort, kw_when,
|
||||
kw_where,
|
||||
// Keywords from the JavaScript section.
|
||||
kw_as, kw_async, kw_await, kw_declare, kw_finally, kw_from,
|
||||
kw_function, kw_get, kw_import, kw_is, kw_let, kw_module, kw_readonly,
|
||||
|
@ -900,6 +905,7 @@ struct AdditionalKeywords {
|
|||
IdentifierInfo *kw_unsafe;
|
||||
IdentifierInfo *kw_ushort;
|
||||
IdentifierInfo *kw_when;
|
||||
IdentifierInfo *kw_where;
|
||||
|
||||
/// Returns \c true if \p Tok is a true JavaScript identifier, returns
|
||||
/// \c false if it is a keyword or a pseudo keyword.
|
||||
|
|
|
@ -1047,6 +1047,11 @@ private:
|
|||
Keywords.kw___has_include_next)) {
|
||||
parseHasInclude();
|
||||
}
|
||||
if (Tok->is(Keywords.kw_where) && Tok->Next &&
|
||||
Tok->Next->isNot(tok::l_paren)) {
|
||||
Tok->Type = TT_CSharpGenericTypeConstraint;
|
||||
parseCSharpGenericTypeConstraint();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -1054,6 +1059,30 @@ private:
|
|||
return true;
|
||||
}
|
||||
|
||||
void parseCSharpGenericTypeConstraint() {
|
||||
while (CurrentToken) {
|
||||
if (CurrentToken->is(tok::less)) {
|
||||
// parseAngle is too greedy and will consume the whole line.
|
||||
CurrentToken->Type = TT_TemplateOpener;
|
||||
next();
|
||||
} else if (CurrentToken->is(tok::greater)) {
|
||||
CurrentToken->Type = TT_TemplateCloser;
|
||||
next();
|
||||
} else if (CurrentToken->is(tok::comma)) {
|
||||
CurrentToken->Type = TT_CSharpGenericTypeConstraintComma;
|
||||
next();
|
||||
} else if (CurrentToken->is(Keywords.kw_where)) {
|
||||
CurrentToken->Type = TT_CSharpGenericTypeConstraint;
|
||||
next();
|
||||
} else if (CurrentToken->is(tok::colon)) {
|
||||
CurrentToken->Type = TT_CSharpGenericTypeConstraintColon;
|
||||
next();
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseIncludeDirective() {
|
||||
if (CurrentToken && CurrentToken->is(tok::less)) {
|
||||
next();
|
||||
|
@ -3299,6 +3328,8 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
|
|||
if (Right.is(TT_CSharpNamedArgumentColon) ||
|
||||
Left.is(TT_CSharpNamedArgumentColon))
|
||||
return false;
|
||||
if (Right.is(TT_CSharpGenericTypeConstraint))
|
||||
return true;
|
||||
} else if (Style.Language == FormatStyle::LK_JavaScript) {
|
||||
// FIXME: This might apply to other languages and token kinds.
|
||||
if (Right.is(tok::string_literal) && Left.is(tok::plus) && Left.Previous &&
|
||||
|
|
|
@ -64,6 +64,8 @@ public:
|
|||
}
|
||||
if (static_cast<int>(Indent) + Offset >= 0)
|
||||
Indent += Offset;
|
||||
if (Line.First->is(TT_CSharpGenericTypeConstraint))
|
||||
Indent = Line.Level * Style.IndentWidth + Style.ContinuationIndentWidth;
|
||||
}
|
||||
|
||||
/// Update the indent state given that \p Line indent should be
|
||||
|
|
|
@ -323,6 +323,24 @@ void UnwrappedLineParser::parseFile() {
|
|||
addUnwrappedLine();
|
||||
}
|
||||
|
||||
void UnwrappedLineParser::parseCSharpGenericTypeConstraint() {
|
||||
do {
|
||||
switch (FormatTok->Tok.getKind()) {
|
||||
case tok::l_brace:
|
||||
return;
|
||||
default:
|
||||
if (FormatTok->is(Keywords.kw_where)) {
|
||||
addUnwrappedLine();
|
||||
nextToken();
|
||||
parseCSharpGenericTypeConstraint();
|
||||
break;
|
||||
}
|
||||
nextToken();
|
||||
break;
|
||||
}
|
||||
} while (!eof());
|
||||
}
|
||||
|
||||
void UnwrappedLineParser::parseCSharpAttribute() {
|
||||
int UnpairedSquareBrackets = 1;
|
||||
do {
|
||||
|
@ -1344,6 +1362,12 @@ void UnwrappedLineParser::parseStructuralElement() {
|
|||
parseTryCatch();
|
||||
return;
|
||||
case tok::identifier: {
|
||||
if (Style.isCSharp() && FormatTok->is(Keywords.kw_where) &&
|
||||
Line->MustBeDeclaration) {
|
||||
addUnwrappedLine();
|
||||
parseCSharpGenericTypeConstraint();
|
||||
break;
|
||||
}
|
||||
if (FormatTok->is(TT_MacroBlockEnd)) {
|
||||
addUnwrappedLine();
|
||||
return;
|
||||
|
|
|
@ -126,6 +126,10 @@ private:
|
|||
void parseJavaScriptEs6ImportExport();
|
||||
void parseStatementMacro();
|
||||
void parseCSharpAttribute();
|
||||
// Parse a C# generic type constraint: `where T : IComparable<T>`.
|
||||
// See:
|
||||
// https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint
|
||||
void parseCSharpGenericTypeConstraint();
|
||||
bool tryToParseLambda();
|
||||
bool tryToParseLambdaIntroducer();
|
||||
void tryToParseJSFunction();
|
||||
|
|
|
@ -628,7 +628,6 @@ TEST_F(FormatTestCSharp, CSharpSpaces) {
|
|||
verifyFormat(R"(catch (TestException) when (innerFinallyExecuted))", Style);
|
||||
verifyFormat(R"(private float[,] Values;)", Style);
|
||||
verifyFormat(R"(Result this[Index x] => Foo(x);)", Style);
|
||||
verifyFormat(R"(class ItemFactory<T> where T : new() {})", Style);
|
||||
|
||||
Style.SpacesInSquareBrackets = true;
|
||||
verifyFormat(R"(private float[ , ] Values;)", Style);
|
||||
|
@ -673,5 +672,22 @@ if (someThings[i][j][k].Contains(myThing)) {
|
|||
Style);
|
||||
}
|
||||
|
||||
TEST_F(FormatTestCSharp, CSharpGenericTypeConstraints) {
|
||||
FormatStyle Style = getGoogleStyle(FormatStyle::LK_CSharp);
|
||||
|
||||
verifyFormat(R"(//
|
||||
class ItemFactory<T>
|
||||
where T : new() {})", Style);
|
||||
|
||||
verifyFormat(R"(//
|
||||
class Dictionary<TKey, TVal>
|
||||
where TKey : IComparable<TKey>
|
||||
where TVal : IMyInterface {
|
||||
public void MyMethod<T>(T t)
|
||||
where T : IMyInterface { doThing(); }
|
||||
})",
|
||||
Style);
|
||||
}
|
||||
|
||||
} // namespace format
|
||||
} // end namespace clang
|
||||
|
|
Loading…
Reference in New Issue