Browse Source

Add support for user-defined checked operators.

pull/3017/head
Daniel Grunwald 2 years ago
parent
commit
32fafeb9a1
  1. 46
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs
  2. 1
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  3. 12
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs
  4. 17
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  5. 60
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs
  6. 7
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  7. 21
      ICSharpCode.Decompiler/DecompilerSettings.cs
  8. 6
      ICSharpCode.Decompiler/Output/IAmbience.cs
  9. 4
      ILSpy/Languages/CSharpLanguage.cs
  10. 9
      ILSpy/Properties/Resources.Designer.cs
  11. 3
      ILSpy/Properties/Resources.resx

46
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs

@ -42,6 +42,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -42,6 +42,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return null;
}
#if CS110
public static AllOperators operator checked +(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator checked -(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator checked *(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator checked /(AllOperators a, AllOperators b)
{
return null;
}
#endif
public static AllOperators operator %(AllOperators a, AllOperators b)
{
return null;
@ -109,6 +131,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -109,6 +131,23 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
return null;
}
#if CS110
public static AllOperators operator checked -(AllOperators a)
{
return null;
}
public static AllOperators operator checked ++(AllOperators a)
{
return null;
}
public static AllOperators operator checked --(AllOperators a)
{
return null;
}
#endif
public static bool operator true(AllOperators a)
{
return false;
@ -158,6 +197,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -158,6 +197,13 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
return 0;
}
#if CS110
public static explicit operator checked int(AllOperators a)
{
return 0;
}
#endif
}
public class UseAllOperators

1
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -531,6 +531,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -531,6 +531,7 @@ namespace ICSharpCode.Decompiler.CSharp
typeSystemAstBuilder.SupportRecordClasses = settings.RecordClasses;
typeSystemAstBuilder.SupportRecordStructs = settings.RecordStructs;
typeSystemAstBuilder.SupportUnsignedRightShift = settings.UnsignedRightShift;
typeSystemAstBuilder.SupportOperatorChecked = settings.CheckedOperators;
typeSystemAstBuilder.AlwaysUseGlobal = settings.AlwaysUseGlobal;
return typeSystemAstBuilder;
}

12
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs

@ -238,6 +238,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -238,6 +238,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
astBuilder.SupportRecordClasses = (ConversionFlags & ConversionFlags.SupportRecordClasses) != 0;
astBuilder.SupportRecordStructs = (ConversionFlags & ConversionFlags.SupportRecordStructs) != 0;
astBuilder.SupportUnsignedRightShift = (ConversionFlags & ConversionFlags.SupportUnsignedRightShift) != 0;
astBuilder.SupportOperatorChecked = (ConversionFlags & ConversionFlags.SupportOperatorChecked) != 0;
return astBuilder;
}
@ -306,10 +307,19 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -306,10 +307,19 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator");
writer.Space();
var operatorType = OperatorDeclaration.GetOperatorType(member.Name);
if (operatorType.HasValue)
if (operatorType.HasValue && !((ConversionFlags & ConversionFlags.SupportOperatorChecked) == 0 && OperatorDeclaration.IsChecked(operatorType.Value)))
{
if (OperatorDeclaration.IsChecked(operatorType.Value))
{
writer.WriteToken(OperatorDeclaration.CheckedKeywordRole, "checked");
writer.Space();
}
writer.WriteToken(OperatorDeclaration.GetRole(operatorType.Value), OperatorDeclaration.GetToken(operatorType.Value));
}
else
{
writer.WriteIdentifier(node.NameToken);
}
break;
}
break;

17
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs

@ -2528,7 +2528,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -2528,7 +2528,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
StartNode(operatorDeclaration);
WriteAttributes(operatorDeclaration.Attributes);
WriteModifiers(operatorDeclaration.ModifierTokens);
if (operatorDeclaration.OperatorType == OperatorType.Explicit)
if (operatorDeclaration.OperatorType == OperatorType.Explicit || operatorDeclaration.OperatorType == OperatorType.CheckedExplicit)
{
WriteKeyword(OperatorDeclaration.ExplicitRole);
}
@ -2542,7 +2542,13 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -2542,7 +2542,13 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
}
WriteKeyword(OperatorDeclaration.OperatorKeywordRole);
Space();
if (OperatorDeclaration.IsChecked(operatorDeclaration.OperatorType))
{
WriteKeyword(OperatorDeclaration.CheckedKeywordRole);
Space();
}
if (operatorDeclaration.OperatorType == OperatorType.Explicit
|| operatorDeclaration.OperatorType == OperatorType.CheckedExplicit
|| operatorDeclaration.OperatorType == OperatorType.Implicit)
{
operatorDeclaration.ReturnType.AcceptVisitor(this);
@ -3082,7 +3088,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -3082,7 +3088,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
break;
case SymbolKind.Operator:
var opType = documentationReference.OperatorType;
if (opType == OperatorType.Explicit)
if (opType == OperatorType.Explicit || opType == OperatorType.CheckedExplicit)
{
WriteKeyword(OperatorDeclaration.ExplicitRole);
}
@ -3092,7 +3098,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor @@ -3092,7 +3098,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
}
WriteKeyword(OperatorDeclaration.OperatorKeywordRole);
Space();
if (opType == OperatorType.Explicit || opType == OperatorType.Implicit)
if (OperatorDeclaration.IsChecked(opType))
{
WriteKeyword(OperatorDeclaration.CheckedKeywordRole);
Space();
}
if (opType == OperatorType.Explicit || opType == OperatorType.Implicit || opType == OperatorType.CheckedExplicit)
{
documentationReference.ConversionOperatorReturnType.AcceptVisitor(this);
}

60
ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs

@ -37,20 +37,25 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -37,20 +37,25 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
LogicalNot,
OnesComplement,
Increment,
CheckedIncrement,
Decrement,
CheckedDecrement,
True,
False,
// Unary and Binary operators
Addition,
Subtraction,
UnaryPlus,
UnaryNegation,
CheckedUnaryNegation,
// Binary operators
Addition,
CheckedAddition,
Subtraction,
CheckedSubtraction,
Multiply,
CheckedMultiply,
Division,
CheckedDivision,
Modulus,
BitwiseAnd,
BitwiseOr,
@ -67,12 +72,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -67,12 +72,14 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
// Implicit and Explicit
Implicit,
Explicit
Explicit,
CheckedExplicit
}
public class OperatorDeclaration : EntityDeclaration
{
public static readonly TokenRole OperatorKeywordRole = new TokenRole("operator");
public static readonly TokenRole CheckedKeywordRole = new TokenRole("checked");
// Unary operators
public static readonly TokenRole LogicalNotRole = new TokenRole("!");
@ -110,19 +117,26 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -110,19 +117,26 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
static OperatorDeclaration()
{
names = new string[(int)OperatorType.Explicit + 1][];
names = new string[(int)OperatorType.CheckedExplicit + 1][];
names[(int)OperatorType.LogicalNot] = new string[] { "!", "op_LogicalNot" };
names[(int)OperatorType.OnesComplement] = new string[] { "~", "op_OnesComplement" };
names[(int)OperatorType.Increment] = new string[] { "++", "op_Increment" };
names[(int)OperatorType.CheckedIncrement] = new string[] { "++", "op_CheckedIncrement" };
names[(int)OperatorType.Decrement] = new string[] { "--", "op_Decrement" };
names[(int)OperatorType.CheckedDecrement] = new string[] { "--", "op_CheckedDecrement" };
names[(int)OperatorType.True] = new string[] { "true", "op_True" };
names[(int)OperatorType.False] = new string[] { "false", "op_False" };
names[(int)OperatorType.Addition] = new string[] { "+", "op_Addition" };
names[(int)OperatorType.Subtraction] = new string[] { "-", "op_Subtraction" };
names[(int)OperatorType.UnaryPlus] = new string[] { "+", "op_UnaryPlus" };
names[(int)OperatorType.UnaryNegation] = new string[] { "-", "op_UnaryNegation" };
names[(int)OperatorType.CheckedUnaryNegation] = new string[] { "-", "op_CheckedUnaryNegation" };
names[(int)OperatorType.Addition] = new string[] { "+", "op_Addition" };
names[(int)OperatorType.CheckedAddition] = new string[] { "+", "op_CheckedAddition" };
names[(int)OperatorType.Subtraction] = new string[] { "-", "op_Subtraction" };
names[(int)OperatorType.CheckedSubtraction] = new string[] { "-", "op_CheckedSubtraction" };
names[(int)OperatorType.Multiply] = new string[] { "*", "op_Multiply" };
names[(int)OperatorType.CheckedMultiply] = new string[] { "*", "op_CheckedMultiply" };
names[(int)OperatorType.Division] = new string[] { "/", "op_Division" };
names[(int)OperatorType.CheckedDivision] = new string[] { "/", "op_CheckedDivision" };
names[(int)OperatorType.Modulus] = new string[] { "%", "op_Modulus" };
names[(int)OperatorType.BitwiseAnd] = new string[] { "&", "op_BitwiseAnd" };
names[(int)OperatorType.BitwiseOr] = new string[] { "|", "op_BitwiseOr" };
@ -138,6 +152,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -138,6 +152,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
names[(int)OperatorType.LessThanOrEqual] = new string[] { "<=", "op_LessThanOrEqual" };
names[(int)OperatorType.Implicit] = new string[] { "implicit", "op_Implicit" };
names[(int)OperatorType.Explicit] = new string[] { "explicit", "op_Explicit" };
names[(int)OperatorType.CheckedExplicit] = new string[] { "explicit", "op_CheckedExplicit" };
}
public override SymbolKind SymbolKind {
@ -202,8 +217,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -202,8 +217,10 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
case OperatorType.OnesComplement:
return OnesComplementRole;
case OperatorType.Increment:
case OperatorType.CheckedIncrement:
return IncrementRole;
case OperatorType.Decrement:
case OperatorType.CheckedDecrement:
return DecrementRole;
case OperatorType.True:
return TrueRole;
@ -211,15 +228,20 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -211,15 +228,20 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return FalseRole;
case OperatorType.Addition:
case OperatorType.CheckedAddition:
case OperatorType.UnaryPlus:
return AdditionRole;
case OperatorType.Subtraction:
case OperatorType.CheckedSubtraction:
case OperatorType.UnaryNegation:
case OperatorType.CheckedUnaryNegation:
return SubtractionRole;
case OperatorType.Multiply:
case OperatorType.CheckedMultiply:
return MultiplyRole;
case OperatorType.Division:
case OperatorType.CheckedDivision:
return DivisionRole;
case OperatorType.Modulus:
return ModulusRole;
@ -251,6 +273,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -251,6 +273,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
case OperatorType.Implicit:
return ImplicitRole;
case OperatorType.Explicit:
case OperatorType.CheckedExplicit:
return ExplicitRole;
default:
@ -269,7 +292,26 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -269,7 +292,26 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
/// <summary>
/// Gets the token for the operator type ("+", "implicit", etc.)
/// Gets whether the operator type is a C# 11 "operator checked".
/// </summary>
public static bool IsChecked(OperatorType type)
{
return type switch {
OperatorType.CheckedAddition => true,
OperatorType.CheckedSubtraction => true,
OperatorType.CheckedMultiply => true,
OperatorType.CheckedDivision => true,
OperatorType.CheckedUnaryNegation => true,
OperatorType.CheckedIncrement => true,
OperatorType.CheckedDecrement => true,
OperatorType.CheckedExplicit => true,
_ => false,
};
}
/// <summary>
/// Gets the token for the operator type ("+", "implicit", etc.).
/// Does not include the "checked" modifier.
/// </summary>
public static string GetToken(OperatorType type)
{

7
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -230,6 +230,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -230,6 +230,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
/// </summary>
public bool SupportUnsignedRightShift { get; set; }
/// <summary>
/// Controls whether C# 11 "operator checked" is supported.
/// </summary>
public bool SupportOperatorChecked { get; set; }
/// <summary>
/// Controls whether all fully qualified type names should be prefixed with "global::".
/// </summary>
@ -2224,6 +2229,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -2224,6 +2229,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return ConvertMethod(op);
if (opType == OperatorType.UnsignedRightShift && !SupportUnsignedRightShift)
return ConvertMethod(op);
if (!SupportOperatorChecked && OperatorDeclaration.IsChecked(opType.Value))
return ConvertMethod(op);
OperatorDeclaration decl = new OperatorDeclaration();
decl.Modifiers = GetMemberModifiers(op);

21
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -155,12 +155,13 @@ namespace ICSharpCode.Decompiler @@ -155,12 +155,13 @@ namespace ICSharpCode.Decompiler
numericIntPtr = false;
utf8StringLiterals = false;
unsignedRightShift = false;
checkedOperators = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
if (parameterNullCheck || scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals || unsignedRightShift)
if (parameterNullCheck || scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals || unsignedRightShift || checkedOperators)
return CSharp.LanguageVersion.CSharp11_0;
if (fileScopedNamespaces || recordStructs)
return CSharp.LanguageVersion.CSharp10_0;
@ -1218,6 +1219,24 @@ namespace ICSharpCode.Decompiler @@ -1218,6 +1219,24 @@ namespace ICSharpCode.Decompiler
}
}
bool checkedOperators = true;
/// <summary>
/// Gets/Sets whether to use C# 11.0 user-defined checked operators.
/// </summary>
[Category("C# 11.0 / VS 2022.4")]
[Description("DecompilerSettings.CheckedOperators")]
public bool CheckedOperators {
get { return checkedOperators; }
set {
if (checkedOperators != value)
{
checkedOperators = value;
OnPropertyChanged();
}
}
}
bool showXmlDocumentation = true;
/// <summary>

6
ICSharpCode.Decompiler/Output/IAmbience.cs

@ -113,6 +113,10 @@ namespace ICSharpCode.Decompiler.Output @@ -113,6 +113,10 @@ namespace ICSharpCode.Decompiler.Output
/// Support <c>&gt;&gt;&gt;</c> as unsigned right shift operator.
/// </summary>
SupportUnsignedRightShift = 0x80000,
/// <summary>
/// Support C# 11 <c>operator checked</c>.
/// </summary>
SupportOperatorChecked = 0x100000,
StandardConversionFlags = ShowParameterNames |
ShowAccessibility |
@ -127,7 +131,7 @@ namespace ICSharpCode.Decompiler.Output @@ -127,7 +131,7 @@ namespace ICSharpCode.Decompiler.Output
ShowDefinitionKeyword |
ShowBody,
All = 0x7ffff,
All = 0x1fffff,
}
/// <summary>

4
ILSpy/Languages/CSharpLanguage.cs

@ -753,6 +753,10 @@ namespace ICSharpCode.ILSpy @@ -753,6 +753,10 @@ namespace ICSharpCode.ILSpy
{
flags |= ConversionFlags.SupportUnsignedRightShift;
}
if (settings.CheckedOperators)
{
flags |= ConversionFlags.SupportOperatorChecked;
}
if (settings.InitAccessors)
{
flags |= ConversionFlags.SupportInitAccessors;

9
ILSpy/Properties/Resources.Designer.cs generated

@ -765,6 +765,15 @@ namespace ICSharpCode.ILSpy.Properties { @@ -765,6 +765,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to User-defined checked operators.
/// </summary>
public static string DecompilerSettings_CheckedOperators {
get {
return ResourceManager.GetString("DecompilerSettings.CheckedOperators", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Decompile anonymous methods/lambdas.
/// </summary>

3
ILSpy/Properties/Resources.resx

@ -1069,4 +1069,7 @@ Do you want to continue?</value> @@ -1069,4 +1069,7 @@ Do you want to continue?</value>
<data name="_Window" xml:space="preserve">
<value>_Window</value>
</data>
<data name="DecompilerSettings.CheckedOperators" xml:space="preserve">
<value>User-defined checked operators</value>
</data>
</root>
Loading…
Cancel
Save