Skip to content

Commit

Permalink
Support parsing of checked user-defined operator declarations (#59504)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlekseyTs authored Feb 16, 2022
1 parent 5ea775c commit 25c6a00
Show file tree
Hide file tree
Showing 17 changed files with 1,318 additions and 215 deletions.
16 changes: 16 additions & 0 deletions docs/features/CheckedUserDefinedOperators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Checked user-defined operators
=====================================

C# should support defining `checked` variants of the following user-defined operators so that users can opt into or out of overflow behavior as appropriate:
* The `++` and `--` unary operators (https://github.com/dotnet/csharplang/blob/main/spec/expressions.md#postfix-increment-and-decrement-operators and https://github.com/dotnet/csharplang/blob/main/spec/expressions.md#prefix-increment-and-decrement-operators).
* The `-` unary operator (https://github.com/dotnet/csharplang/blob/main/spec/expressions.md#unary-minus-operator).
* The `+`, `-`, `*`, and `/` binary operators (https://github.com/dotnet/csharplang/blob/main/spec/expressions.md#arithmetic-operators).
* Explicit conversion operators.

Proposal:
- https://github.com/dotnet/csharplang/issues/4665
- https://github.com/dotnet/csharplang/blob/main/proposals/checked-user-defined-operators.md

Feature branch: https://github.com/dotnet/roslyn/tree/features/CheckedUserDefinedOperators

Test plan: https://github.com/dotnet/roslyn/issues/59458
10 changes: 5 additions & 5 deletions src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,7 @@ private OperatorMemberCrefSyntax ParseOperatorMemberCref()
{
Debug.Assert(CurrentToken.Kind == SyntaxKind.OperatorKeyword);
SyntaxToken operatorKeyword = EatToken();
SyntaxToken checkedKeyword = TryEatToken(SyntaxKind.CheckedKeyword); // PROTOTYPE(CheckedUserDefinedOperators) : consider gracefully recovering from erroneous use of 'unchecked' at this location

SyntaxToken operatorToken;

Expand Down Expand Up @@ -1067,7 +1068,7 @@ private OperatorMemberCrefSyntax ParseOperatorMemberCref()

CrefParameterListSyntax parameters = ParseCrefParameterList();

return SyntaxFactory.OperatorMemberCref(operatorKeyword, operatorToken, parameters);
return SyntaxFactory.OperatorMemberCref(operatorKeyword, checkedKeyword, operatorToken, parameters);
}

/// <summary>
Expand All @@ -1080,12 +1081,13 @@ private ConversionOperatorMemberCrefSyntax ParseConversionOperatorMemberCref()
SyntaxToken implicitOrExplicit = EatToken();

SyntaxToken operatorKeyword = EatToken(SyntaxKind.OperatorKeyword);
SyntaxToken checkedKeyword = TryEatToken(SyntaxKind.CheckedKeyword); // PROTOTYPE(CheckedUserDefinedOperators) : consider gracefully recovering from erroneous use of 'unchecked' at this location

TypeSyntax type = ParseCrefType(typeArgumentsMustBeIdentifiers: false);

CrefParameterListSyntax parameters = ParseCrefParameterList();

return SyntaxFactory.ConversionOperatorMemberCref(implicitOrExplicit, operatorKeyword, type, parameters);
return SyntaxFactory.ConversionOperatorMemberCref(implicitOrExplicit, operatorKeyword, checkedKeyword, type, parameters);
}

/// <summary>
Expand Down
22 changes: 21 additions & 1 deletion src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3305,8 +3305,23 @@ private ConversionOperatorDeclarationSyntax TryParseConversionOperatorDeclaratio

if (style.IsMissing)
{
if (this.CurrentToken.Kind != SyntaxKind.OperatorKeyword || SyntaxFacts.IsAnyOverloadableOperator(this.PeekToken(1).Kind) ||
bool possibleConversion;

if (this.CurrentToken.Kind != SyntaxKind.OperatorKeyword ||
explicitInterfaceOpt?.DotToken.IsMissing == true)
{
possibleConversion = false;
}
else if (this.PeekToken(1).Kind == SyntaxKind.CheckedKeyword) // PROTOTYPE(CheckedUserDefinedOperators) : consider gracefully recovering from erroneous use of 'unchecked' at this location
{
possibleConversion = !SyntaxFacts.IsAnyOverloadableOperator(this.PeekToken(2).Kind);
}
else
{
possibleConversion = !SyntaxFacts.IsAnyOverloadableOperator(this.PeekToken(1).Kind);
}

if (!possibleConversion)
{
this.Reset(ref point);
return null;
Expand All @@ -3332,6 +3347,7 @@ private ConversionOperatorDeclarationSyntax TryParseConversionOperatorDeclaratio
style,
explicitInterfaceOpt,
opKeyword,
checkedKeyword: null,
type,
paramList,
body: null,
Expand All @@ -3340,6 +3356,7 @@ private ConversionOperatorDeclarationSyntax TryParseConversionOperatorDeclaratio
}

opKeyword = this.EatToken(SyntaxKind.OperatorKeyword);
var checkedKeyword = this.TryEatToken(SyntaxKind.CheckedKeyword); // PROTOTYPE(CheckedUserDefinedOperators) : consider gracefully recovering from erroneous use of 'unchecked' at this location

this.Release(ref point);
point = GetResetPoint();
Expand Down Expand Up @@ -3369,6 +3386,7 @@ private ConversionOperatorDeclarationSyntax TryParseConversionOperatorDeclaratio
style,
explicitInterfaceOpt,
opKeyword,
checkedKeyword,
type,
paramList,
blockBody,
Expand Down Expand Up @@ -3463,6 +3481,7 @@ private OperatorDeclarationSyntax ParseOperatorDeclaration(
ExplicitInterfaceSpecifierSyntax explicitInterfaceOpt)
{
var opKeyword = this.EatToken(SyntaxKind.OperatorKeyword);
var checkedKeyword = this.TryEatToken(SyntaxKind.CheckedKeyword); // PROTOTYPE(CheckedUserDefinedOperators) : consider gracefully recovering from erroneous use of 'unchecked' at this location
SyntaxToken opToken;
int opTokenErrorOffset;
int opTokenErrorWidth;
Expand Down Expand Up @@ -3582,6 +3601,7 @@ private OperatorDeclarationSyntax ParseOperatorDeclaration(
type,
explicitInterfaceOpt,
opKeyword,
checkedKeyword,
opToken,
paramList,
blockBody,
Expand Down
17 changes: 17 additions & 0 deletions src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,20 @@ static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.TryStatement(Microsoft.CodeAn
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.TryStatement(Microsoft.CodeAnalysis.SyntaxToken tryKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax! block, Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.CatchClauseSyntax!> catches, Microsoft.CodeAnalysis.CSharp.Syntax.FinallyClauseSyntax? finally) -> Microsoft.CodeAnalysis.CSharp.Syntax.TryStatementSyntax!
*REMOVED*static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.TryStatement(Microsoft.CodeAnalysis.SyntaxToken tryKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax! block, Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.CatchClauseSyntax!> catches, Microsoft.CodeAnalysis.CSharp.Syntax.FinallyClauseSyntax! finally) -> Microsoft.CodeAnalysis.CSharp.Syntax.TryStatementSyntax!
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.InterpolatedStringExpression(Microsoft.CodeAnalysis.SyntaxToken stringStartToken, Microsoft.CodeAnalysis.SyntaxToken stringEndToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.InterpolatedStringExpressionSyntax!

Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorDeclarationSyntax.CheckedKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorDeclarationSyntax.Update(Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax!> attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken implicitOrExplicitKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, Microsoft.CodeAnalysis.SyntaxToken operatorKeyword, Microsoft.CodeAnalysis.SyntaxToken checkedKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax! type, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax! parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax? body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax? expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorDeclarationSyntax!
Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorDeclarationSyntax.WithCheckedKeyword(Microsoft.CodeAnalysis.SyntaxToken checkedKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorDeclarationSyntax!
Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorMemberCrefSyntax.CheckedKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorMemberCrefSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken implicitOrExplicitKeyword, Microsoft.CodeAnalysis.SyntaxToken operatorKeyword, Microsoft.CodeAnalysis.SyntaxToken checkedKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax! type, Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterListSyntax? parameters) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorMemberCrefSyntax!
Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorMemberCrefSyntax.WithCheckedKeyword(Microsoft.CodeAnalysis.SyntaxToken checkedKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorMemberCrefSyntax!
Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax.CheckedKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax.Update(Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax!> attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax! returnType, Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, Microsoft.CodeAnalysis.SyntaxToken operatorKeyword, Microsoft.CodeAnalysis.SyntaxToken checkedKeyword, Microsoft.CodeAnalysis.SyntaxToken operatorToken, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax! parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax? body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax? expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax!
Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax.WithCheckedKeyword(Microsoft.CodeAnalysis.SyntaxToken checkedKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax!
Microsoft.CodeAnalysis.CSharp.Syntax.OperatorMemberCrefSyntax.CheckedKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.OperatorMemberCrefSyntax.Update(Microsoft.CodeAnalysis.SyntaxToken operatorKeyword, Microsoft.CodeAnalysis.SyntaxToken checkedKeyword, Microsoft.CodeAnalysis.SyntaxToken operatorToken, Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterListSyntax? parameters) -> Microsoft.CodeAnalysis.CSharp.Syntax.OperatorMemberCrefSyntax!
Microsoft.CodeAnalysis.CSharp.Syntax.OperatorMemberCrefSyntax.WithCheckedKeyword(Microsoft.CodeAnalysis.SyntaxToken checkedKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.OperatorMemberCrefSyntax!
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.OperatorDeclaration(Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax!> attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax! returnType, Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, Microsoft.CodeAnalysis.SyntaxToken operatorKeyword, Microsoft.CodeAnalysis.SyntaxToken checkedKeyword, Microsoft.CodeAnalysis.SyntaxToken operatorToken, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax! parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax? body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax? expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.OperatorDeclarationSyntax!
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ConversionOperatorDeclaration(Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax!> attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken implicitOrExplicitKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier, Microsoft.CodeAnalysis.SyntaxToken operatorKeyword, Microsoft.CodeAnalysis.SyntaxToken checkedKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax! type, Microsoft.CodeAnalysis.CSharp.Syntax.ParameterListSyntax! parameterList, Microsoft.CodeAnalysis.CSharp.Syntax.BlockSyntax? body, Microsoft.CodeAnalysis.CSharp.Syntax.ArrowExpressionClauseSyntax? expressionBody, Microsoft.CodeAnalysis.SyntaxToken semicolonToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorDeclarationSyntax!
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.OperatorMemberCref(Microsoft.CodeAnalysis.SyntaxToken operatorKeyword, Microsoft.CodeAnalysis.SyntaxToken checkedKeyword, Microsoft.CodeAnalysis.SyntaxToken operatorToken, Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterListSyntax? parameters) -> Microsoft.CodeAnalysis.CSharp.Syntax.OperatorMemberCrefSyntax!
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ConversionOperatorMemberCref(Microsoft.CodeAnalysis.SyntaxToken implicitOrExplicitKeyword, Microsoft.CodeAnalysis.SyntaxToken operatorKeyword, Microsoft.CodeAnalysis.SyntaxToken checkedKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax! type, Microsoft.CodeAnalysis.CSharp.Syntax.CrefParameterListSyntax? parameters) -> Microsoft.CodeAnalysis.CSharp.Syntax.ConversionOperatorMemberCrefSyntax!
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,31 @@ public ConversionOperatorDeclarationSyntax Update(
expressionBody: expressionBody,
semicolonToken: semicolonToken);
}

public ConversionOperatorDeclarationSyntax Update(
SyntaxList<AttributeListSyntax> attributeLists,
SyntaxTokenList modifiers,
SyntaxToken implicitOrExplicitKeyword,
ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier,
SyntaxToken operatorKeyword,
TypeSyntax type,
ParameterListSyntax parameterList,
BlockSyntax? body,
ArrowExpressionClauseSyntax? expressionBody,
SyntaxToken semicolonToken)
{
return Update(
attributeLists: attributeLists,
modifiers: modifiers,
implicitOrExplicitKeyword: implicitOrExplicitKeyword,
explicitInterfaceSpecifier: explicitInterfaceSpecifier,
operatorKeyword: operatorKeyword,
checkedKeyword: this.CheckedKeyword,
type: type,
parameterList: parameterList,
body: body,
expressionBody: expressionBody,
semicolonToken: semicolonToken);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace Microsoft.CodeAnalysis.CSharp.Syntax
{
public partial class ConversionOperatorMemberCrefSyntax
{
public ConversionOperatorMemberCrefSyntax Update(SyntaxToken implicitOrExplicitKeyword, SyntaxToken operatorKeyword, TypeSyntax type, CrefParameterListSyntax? parameters)
{
return Update(
implicitOrExplicitKeyword: implicitOrExplicitKeyword,
operatorKeyword: operatorKeyword,
checkedKeyword: this.CheckedKeyword,
type: type,
parameters: parameters);
}
}
}
26 changes: 26 additions & 0 deletions src/Compilers/CSharp/Portable/Syntax/OperatorDeclarationSyntax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,31 @@ public OperatorDeclarationSyntax Update(
expressionBody: expressionBody,
semicolonToken: semicolonToken);
}

public OperatorDeclarationSyntax Update(
SyntaxList<AttributeListSyntax> attributeLists,
SyntaxTokenList modifiers,
TypeSyntax returnType,
ExplicitInterfaceSpecifierSyntax? explicitInterfaceSpecifier,
SyntaxToken operatorKeyword,
SyntaxToken operatorToken,
ParameterListSyntax parameterList,
BlockSyntax? body,
ArrowExpressionClauseSyntax? expressionBody,
SyntaxToken semicolonToken)
{
return Update(
attributeLists: attributeLists,
modifiers: modifiers,
returnType: returnType,
explicitInterfaceSpecifier: explicitInterfaceSpecifier,
operatorKeyword: operatorKeyword,
checkedKeyword: this.CheckedKeyword,
operatorToken: operatorToken,
parameterList: parameterList,
body: body,
expressionBody: expressionBody,
semicolonToken: semicolonToken);
}
}
}
18 changes: 18 additions & 0 deletions src/Compilers/CSharp/Portable/Syntax/OperatorMemberCrefSyntax.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace Microsoft.CodeAnalysis.CSharp.Syntax
{
public partial class OperatorMemberCrefSyntax
{
public OperatorMemberCrefSyntax Update(SyntaxToken operatorKeyword, SyntaxToken operatorToken, CrefParameterListSyntax? parameters)
{
return Update(
operatorKeyword: operatorKeyword,
checkedKeyword: this.CheckedKeyword,
operatorToken: operatorToken,
parameters: parameters);
}
}
}
Loading

0 comments on commit 25c6a00

Please sign in to comment.