Skip to content

Commit 6514158

Browse files
authored
Parse compound assignment operator declarations (#77943)
1 parent 46178b5 commit 6514158

31 files changed

+2746
-119
lines changed

src/Compilers/CSharp/Portable/CSharpResources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8140,4 +8140,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
81408140
<data name="ERR_OperatorMismatchOnOverride" xml:space="preserve">
81418141
<value>'{0}': cannot override inherited member '{1}' because one of them is not an operator.</value>
81428142
</data>
8143+
<data name="ERR_BadCompoundAssignmentOpArgs" xml:space="preserve">
8144+
<value>Overloaded compound assignment operator '{0}' takes one parameter</value>
8145+
</data>
81438146
</root>

src/Compilers/CSharp/Portable/Errors/ErrorCode.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2391,6 +2391,7 @@ internal enum ErrorCode
23912391
ERR_OperatorMustReturnVoid = 9503,
23922392
ERR_CloseUnimplementedInterfaceMemberOperatorMismatch = 9504,
23932393
ERR_OperatorMismatchOnOverride = 9505,
2394+
ERR_BadCompoundAssignmentOpArgs = 9506,
23942395

23952396
// Note: you will need to do the following after adding errors:
23962397
// 1) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs)

src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2508,6 +2508,7 @@ or ErrorCode.ERR_BadIncrementOpArgs
25082508
or ErrorCode.ERR_OperatorMustReturnVoid
25092509
or ErrorCode.ERR_CloseUnimplementedInterfaceMemberOperatorMismatch
25102510
or ErrorCode.ERR_OperatorMismatchOnOverride
2511+
or ErrorCode.ERR_BadCompoundAssignmentOpArgs
25112512
=> false,
25122513
};
25132514
#pragma warning restore CS8524 // The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value.

src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32021,7 +32021,18 @@ public OperatorDeclarationSyntax OperatorDeclaration(CoreSyntax.SyntaxList<Attri
3202132021
case SyntaxKind.GreaterThanEqualsToken:
3202232022
case SyntaxKind.FalseKeyword:
3202332023
case SyntaxKind.TrueKeyword:
32024-
case SyntaxKind.IsKeyword: break;
32024+
case SyntaxKind.IsKeyword:
32025+
case SyntaxKind.PlusEqualsToken:
32026+
case SyntaxKind.MinusEqualsToken:
32027+
case SyntaxKind.AsteriskEqualsToken:
32028+
case SyntaxKind.SlashEqualsToken:
32029+
case SyntaxKind.PercentEqualsToken:
32030+
case SyntaxKind.AmpersandEqualsToken:
32031+
case SyntaxKind.BarEqualsToken:
32032+
case SyntaxKind.CaretEqualsToken:
32033+
case SyntaxKind.LessThanLessThanEqualsToken:
32034+
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
32035+
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: break;
3202532036
default: throw new ArgumentException(nameof(operatorToken));
3202632037
}
3202732038
if (parameterList == null) throw new ArgumentNullException(nameof(parameterList));
@@ -32528,7 +32539,18 @@ public OperatorMemberCrefSyntax OperatorMemberCref(SyntaxToken operatorKeyword,
3252832539
case SyntaxKind.GreaterThanToken:
3252932540
case SyntaxKind.GreaterThanEqualsToken:
3253032541
case SyntaxKind.FalseKeyword:
32531-
case SyntaxKind.TrueKeyword: break;
32542+
case SyntaxKind.TrueKeyword:
32543+
case SyntaxKind.PlusEqualsToken:
32544+
case SyntaxKind.MinusEqualsToken:
32545+
case SyntaxKind.AsteriskEqualsToken:
32546+
case SyntaxKind.SlashEqualsToken:
32547+
case SyntaxKind.PercentEqualsToken:
32548+
case SyntaxKind.AmpersandEqualsToken:
32549+
case SyntaxKind.BarEqualsToken:
32550+
case SyntaxKind.CaretEqualsToken:
32551+
case SyntaxKind.LessThanLessThanEqualsToken:
32552+
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
32553+
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: break;
3253232554
default: throw new ArgumentException(nameof(operatorToken));
3253332555
}
3253432556
#endif
@@ -37329,7 +37351,18 @@ public static OperatorDeclarationSyntax OperatorDeclaration(CoreSyntax.SyntaxLis
3732937351
case SyntaxKind.GreaterThanEqualsToken:
3733037352
case SyntaxKind.FalseKeyword:
3733137353
case SyntaxKind.TrueKeyword:
37332-
case SyntaxKind.IsKeyword: break;
37354+
case SyntaxKind.IsKeyword:
37355+
case SyntaxKind.PlusEqualsToken:
37356+
case SyntaxKind.MinusEqualsToken:
37357+
case SyntaxKind.AsteriskEqualsToken:
37358+
case SyntaxKind.SlashEqualsToken:
37359+
case SyntaxKind.PercentEqualsToken:
37360+
case SyntaxKind.AmpersandEqualsToken:
37361+
case SyntaxKind.BarEqualsToken:
37362+
case SyntaxKind.CaretEqualsToken:
37363+
case SyntaxKind.LessThanLessThanEqualsToken:
37364+
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
37365+
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: break;
3733337366
default: throw new ArgumentException(nameof(operatorToken));
3733437367
}
3733537368
if (parameterList == null) throw new ArgumentNullException(nameof(parameterList));
@@ -37836,7 +37869,18 @@ public static OperatorMemberCrefSyntax OperatorMemberCref(SyntaxToken operatorKe
3783637869
case SyntaxKind.GreaterThanToken:
3783737870
case SyntaxKind.GreaterThanEqualsToken:
3783837871
case SyntaxKind.FalseKeyword:
37839-
case SyntaxKind.TrueKeyword: break;
37872+
case SyntaxKind.TrueKeyword:
37873+
case SyntaxKind.PlusEqualsToken:
37874+
case SyntaxKind.MinusEqualsToken:
37875+
case SyntaxKind.AsteriskEqualsToken:
37876+
case SyntaxKind.SlashEqualsToken:
37877+
case SyntaxKind.PercentEqualsToken:
37878+
case SyntaxKind.AmpersandEqualsToken:
37879+
case SyntaxKind.BarEqualsToken:
37880+
case SyntaxKind.CaretEqualsToken:
37881+
case SyntaxKind.LessThanLessThanEqualsToken:
37882+
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
37883+
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: break;
3784037884
default: throw new ArgumentException(nameof(operatorToken));
3784137885
}
3784237886
#endif

src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5429,7 +5429,18 @@ public static OperatorDeclarationSyntax OperatorDeclaration(SyntaxList<Attribute
54295429
case SyntaxKind.GreaterThanEqualsToken:
54305430
case SyntaxKind.FalseKeyword:
54315431
case SyntaxKind.TrueKeyword:
5432-
case SyntaxKind.IsKeyword: break;
5432+
case SyntaxKind.IsKeyword:
5433+
case SyntaxKind.PlusEqualsToken:
5434+
case SyntaxKind.MinusEqualsToken:
5435+
case SyntaxKind.AsteriskEqualsToken:
5436+
case SyntaxKind.SlashEqualsToken:
5437+
case SyntaxKind.PercentEqualsToken:
5438+
case SyntaxKind.AmpersandEqualsToken:
5439+
case SyntaxKind.BarEqualsToken:
5440+
case SyntaxKind.CaretEqualsToken:
5441+
case SyntaxKind.LessThanLessThanEqualsToken:
5442+
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
5443+
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: break;
54335444
default: throw new ArgumentException(nameof(operatorToken));
54345445
}
54355446
if (parameterList == null) throw new ArgumentNullException(nameof(parameterList));
@@ -5885,7 +5896,18 @@ public static OperatorMemberCrefSyntax OperatorMemberCref(SyntaxToken operatorKe
58855896
case SyntaxKind.GreaterThanToken:
58865897
case SyntaxKind.GreaterThanEqualsToken:
58875898
case SyntaxKind.FalseKeyword:
5888-
case SyntaxKind.TrueKeyword: break;
5899+
case SyntaxKind.TrueKeyword:
5900+
case SyntaxKind.PlusEqualsToken:
5901+
case SyntaxKind.MinusEqualsToken:
5902+
case SyntaxKind.AsteriskEqualsToken:
5903+
case SyntaxKind.SlashEqualsToken:
5904+
case SyntaxKind.PercentEqualsToken:
5905+
case SyntaxKind.AmpersandEqualsToken:
5906+
case SyntaxKind.BarEqualsToken:
5907+
case SyntaxKind.CaretEqualsToken:
5908+
case SyntaxKind.LessThanLessThanEqualsToken:
5909+
case SyntaxKind.GreaterThanGreaterThanEqualsToken:
5910+
case SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken: break;
58895911
default: throw new ArgumentException(nameof(operatorToken));
58905912
}
58915913
return (OperatorMemberCrefSyntax)Syntax.InternalSyntax.SyntaxFactory.OperatorMemberCref((Syntax.InternalSyntax.SyntaxToken)operatorKeyword.Node!, (Syntax.InternalSyntax.SyntaxToken?)checkedKeyword.Node, (Syntax.InternalSyntax.SyntaxToken)operatorToken.Node!, parameters == null ? null : (Syntax.InternalSyntax.CrefParameterListSyntax)parameters.Green).CreateRed();

src/Compilers/CSharp/Portable/Parser/DocumentationCommentParser.cs

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,13 +1012,13 @@ private OperatorMemberCrefSyntax ParseOperatorMemberCref()
10121012

10131013
// Have to fake >>/>>> because it looks like the closing of nested type parameter lists (e.g. A<A<T>>).
10141014
// Have to fake >= so the lexer doesn't mishandle >>=.
1015-
if (operatorToken.Kind == SyntaxKind.GreaterThanToken && operatorToken.GetTrailingTriviaWidth() == 0 && CurrentToken.GetLeadingTriviaWidth() == 0)
1015+
if (operatorToken.Kind == SyntaxKind.GreaterThanToken && LanguageParser.NoTriviaBetween(operatorToken, CurrentToken))
10161016
{
10171017
if (CurrentToken.Kind == SyntaxKind.GreaterThanToken)
10181018
{
10191019
var operatorToken2 = this.EatToken();
10201020

1021-
if (operatorToken2.GetTrailingTriviaWidth() == 0 && CurrentToken.GetLeadingTriviaWidth() == 0 &&
1021+
if (LanguageParser.NoTriviaBetween(operatorToken2, CurrentToken) &&
10221022
CurrentToken.Kind is (SyntaxKind.GreaterThanToken or SyntaxKind.GreaterThanEqualsToken))
10231023
{
10241024
var operatorToken3 = this.EatToken();
@@ -1036,24 +1036,13 @@ CurrentToken.Kind is (SyntaxKind.GreaterThanToken or SyntaxKind.GreaterThanEqual
10361036
}
10371037
else
10381038
{
1039-
var nonOverloadableOperator = SyntaxFactory.Token(
1039+
operatorToken = SyntaxFactory.Token(
10401040
operatorToken.GetLeadingTrivia(),
10411041
SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken,
10421042
operatorToken.Text + operatorToken2.Text + operatorToken3.Text,
10431043
operatorToken.ValueText + operatorToken2.ValueText + operatorToken3.ValueText,
10441044
operatorToken3.GetTrailingTrivia());
1045-
1046-
operatorToken = SyntaxFactory.MissingToken(SyntaxKind.PlusToken);
1047-
1048-
// Add non-overloadable operator as skipped token.
1049-
operatorToken = AddTrailingSkippedSyntax(operatorToken, nonOverloadableOperator);
1050-
1051-
// Add an appropriate diagnostic.
1052-
const int offset = 0;
1053-
int width = nonOverloadableOperator.Width;
1054-
SyntaxDiagnosticInfo rawInfo = new SyntaxDiagnosticInfo(offset, width, ErrorCode.ERR_OvlOperatorExpected);
1055-
SyntaxDiagnosticInfo crefInfo = new SyntaxDiagnosticInfo(offset, width, ErrorCode.WRN_ErrorOverride, rawInfo, rawInfo.Code);
1056-
operatorToken = WithAdditionalDiagnostics(operatorToken, crefInfo);
1045+
operatorToken = CheckFeatureAvailability(operatorToken, MessageID.IDS_FeatureUserDefinedCompoundAssignmentOperators, forceWarning: true);
10571046
}
10581047
}
10591048
else
@@ -1079,32 +1068,76 @@ CurrentToken.Kind is (SyntaxKind.GreaterThanToken or SyntaxKind.GreaterThanEqual
10791068
else if (CurrentToken.Kind == SyntaxKind.GreaterThanEqualsToken)
10801069
{
10811070
var operatorToken2 = this.EatToken();
1082-
var nonOverloadableOperator = SyntaxFactory.Token(
1071+
operatorToken = SyntaxFactory.Token(
10831072
operatorToken.GetLeadingTrivia(),
10841073
SyntaxKind.GreaterThanGreaterThanEqualsToken,
10851074
operatorToken.Text + operatorToken2.Text,
10861075
operatorToken.ValueText + operatorToken2.ValueText,
10871076
operatorToken2.GetTrailingTrivia());
1088-
1089-
operatorToken = SyntaxFactory.MissingToken(SyntaxKind.PlusToken);
1090-
1091-
// Add non-overloadable operator as skipped token.
1092-
operatorToken = AddTrailingSkippedSyntax(operatorToken, nonOverloadableOperator);
1093-
1094-
// Add an appropriate diagnostic.
1095-
const int offset = 0;
1096-
int width = nonOverloadableOperator.Width;
1097-
SyntaxDiagnosticInfo rawInfo = new SyntaxDiagnosticInfo(offset, width, ErrorCode.ERR_OvlOperatorExpected);
1098-
SyntaxDiagnosticInfo crefInfo = new SyntaxDiagnosticInfo(offset, width, ErrorCode.WRN_ErrorOverride, rawInfo, rawInfo.Code);
1099-
operatorToken = WithAdditionalDiagnostics(operatorToken, crefInfo);
1077+
operatorToken = CheckFeatureAvailability(operatorToken, MessageID.IDS_FeatureUserDefinedCompoundAssignmentOperators, forceWarning: true);
11001078
}
11011079
}
11021080

1081+
switch (operatorToken.Kind)
1082+
{
1083+
case SyntaxKind.PlusToken:
1084+
operatorToken = tryParseCompoundAssignmentOperatorToken(operatorToken, SyntaxKind.PlusEqualsToken);
1085+
break;
1086+
case SyntaxKind.MinusToken:
1087+
operatorToken = tryParseCompoundAssignmentOperatorToken(operatorToken, SyntaxKind.MinusEqualsToken);
1088+
break;
1089+
case SyntaxKind.AsteriskToken:
1090+
operatorToken = tryParseCompoundAssignmentOperatorToken(operatorToken, SyntaxKind.AsteriskEqualsToken);
1091+
break;
1092+
case SyntaxKind.SlashToken:
1093+
operatorToken = tryParseCompoundAssignmentOperatorToken(operatorToken, SyntaxKind.SlashEqualsToken);
1094+
break;
1095+
case SyntaxKind.PercentToken:
1096+
operatorToken = tryParseCompoundAssignmentOperatorToken(operatorToken, SyntaxKind.PercentEqualsToken);
1097+
break;
1098+
case SyntaxKind.AmpersandToken:
1099+
operatorToken = tryParseCompoundAssignmentOperatorToken(operatorToken, SyntaxKind.AmpersandEqualsToken);
1100+
break;
1101+
case SyntaxKind.BarToken:
1102+
operatorToken = tryParseCompoundAssignmentOperatorToken(operatorToken, SyntaxKind.BarEqualsToken);
1103+
break;
1104+
case SyntaxKind.CaretToken:
1105+
operatorToken = tryParseCompoundAssignmentOperatorToken(operatorToken, SyntaxKind.CaretEqualsToken);
1106+
break;
1107+
case SyntaxKind.LessThanLessThanToken:
1108+
operatorToken = tryParseCompoundAssignmentOperatorToken(operatorToken, SyntaxKind.LessThanLessThanEqualsToken);
1109+
break;
1110+
case SyntaxKind.GreaterThanGreaterThanToken:
1111+
operatorToken = tryParseCompoundAssignmentOperatorToken(operatorToken, SyntaxKind.GreaterThanGreaterThanEqualsToken);
1112+
break;
1113+
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
1114+
operatorToken = tryParseCompoundAssignmentOperatorToken(operatorToken, SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken);
1115+
break;
1116+
}
1117+
11031118
Debug.Assert(SyntaxFacts.IsAnyOverloadableOperator(operatorToken.Kind));
11041119

11051120
CrefParameterListSyntax parameters = ParseCrefParameterList();
11061121

11071122
return SyntaxFactory.OperatorMemberCref(operatorKeyword, checkedKeyword, operatorToken, parameters);
1123+
1124+
SyntaxToken tryParseCompoundAssignmentOperatorToken(SyntaxToken operatorToken, SyntaxKind kind)
1125+
{
1126+
if (LanguageParser.NoTriviaBetween(operatorToken, CurrentToken) && CurrentToken.Kind == SyntaxKind.EqualsToken)
1127+
{
1128+
Debug.Assert(kind is not (SyntaxKind.GreaterThanGreaterThanEqualsToken or SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken)); // Reaching this code path is not expected
1129+
var operatorToken2 = this.EatToken();
1130+
operatorToken = SyntaxFactory.Token(
1131+
operatorToken.GetLeadingTrivia(),
1132+
kind,
1133+
operatorToken.Text + operatorToken2.Text,
1134+
operatorToken.ValueText + operatorToken2.ValueText,
1135+
operatorToken2.GetTrailingTrivia());
1136+
operatorToken = CheckFeatureAvailability(operatorToken, MessageID.IDS_FeatureUserDefinedCompoundAssignmentOperators, forceWarning: true);
1137+
}
1138+
1139+
return operatorToken;
1140+
}
11081141
}
11091142

11101143
private SyntaxToken TryEatCheckedKeyword(bool isConversion, ref SyntaxToken operatorKeyword)

0 commit comments

Comments
 (0)