forked from OSchip/llvm-project
[clang-format] Fix C# nullable-related errors
This fixes two errors: Previously, clang-format was splitting up type identifiers from the nullable ?. This changes this behavior so that the type name sticks with the operator. Additionally, nullable operators attached to return types in interface functions were not parsed correctly. Digging deeper, it looks like interface bodies were being parsed differently than classes and structs, causing MustBeDeclaration to be incorrect for interface members. They now share the same logic. One other change is reintroducing the CSharpNullable type independent of JsTypeOptionalQuestion. Despite having a similar semantic purpose, their actual syntax differs quite a bit. Reviewed By: MyDeveloperDay, curdeius Differential Revision: https://reviews.llvm.org/D101860
This commit is contained in:
parent
a437befa8f
commit
ec725b307f
|
@ -112,6 +112,7 @@ namespace format {
|
|||
TYPE(UntouchableMacroFunc) \
|
||||
TYPE(CSharpStringLiteral) \
|
||||
TYPE(CSharpNamedArgumentColon) \
|
||||
TYPE(CSharpNullable) \
|
||||
TYPE(CSharpNullConditionalLSquare) \
|
||||
TYPE(CSharpGenericTypeConstraint) \
|
||||
TYPE(CSharpGenericTypeConstraintColon) \
|
||||
|
|
|
@ -1078,7 +1078,7 @@ private:
|
|||
(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 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line,
|
|||
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 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
|
|||
// 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))
|
||||
|
|
|
@ -1316,15 +1316,7 @@ void UnwrappedLineParser::parseStructuralElement() {
|
|||
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 @@ void UnwrappedLineParser::parseStructuralElement() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (FormatTok->is(Keywords.kw_interface)) {
|
||||
if (parseStructLike()) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (Style.isCpp() && FormatTok->is(TT_StatementMacro)) {
|
||||
parseStatementMacro();
|
||||
return;
|
||||
|
@ -2525,6 +2524,21 @@ bool UnwrappedLineParser::parseEnum() {
|
|||
// "} 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.
|
||||
|
|
|
@ -114,6 +114,7 @@ private:
|
|||
void parseNew();
|
||||
void parseAccessSpecifier();
|
||||
bool parseEnum();
|
||||
bool parseStructLike();
|
||||
void parseConcept();
|
||||
void parseRequires();
|
||||
void parseRequiresExpression(unsigned int OriginalLevel);
|
||||
|
|
|
@ -848,6 +848,21 @@ public class A {
|
|||
verifyFormat(R"(var x = (int?)y;)", Style); // Cast to a nullable type.
|
||||
|
||||
verifyFormat(R"(var x = new MyContainer<int?>();)", 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) {
|
||||
|
|
Loading…
Reference in New Issue