Browse Source

Merge pull request #2994 from icsharpcode/unsigned-right-shift

pull/3012/head
Siegfried Pammer 2 years ago committed by GitHub
parent
commit
a54c5c6ba6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  2. 106
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs
  3. 225
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Operators.cs
  4. 30
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs
  5. 1
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  6. 49
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  7. 1
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs
  8. 1
      ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs
  9. 3
      ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs
  10. 1
      ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs
  11. 21
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs
  12. 7
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs
  13. 9
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs
  14. 6
      ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs
  15. 5
      ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/OperatorDeclaration.cs
  16. 7
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  17. 2
      ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs
  18. 6
      ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs
  19. 21
      ICSharpCode.Decompiler/DecompilerSettings.cs
  20. 10
      ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs
  21. 2
      ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs
  22. 4
      ICSharpCode.Decompiler/Output/IAmbience.cs
  23. 4
      ILSpy/Languages/CSharpLanguage.cs
  24. 9
      ILSpy/Properties/Resources.Designer.cs
  25. 3
      ILSpy/Properties/Resources.resx

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -312,6 +312,12 @@ namespace ICSharpCode.Decompiler.Tests
await RunForLibrary(cscOptions: cscOptions); await RunForLibrary(cscOptions: cscOptions);
} }
[Test]
public async Task Operators([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
await RunForLibrary(cscOptions: cscOptions);
}
[Test] [Test]
public async Task Generics([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions) public async Task Generics([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{ {

106
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CompoundAssignmentTest.cs

@ -201,6 +201,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
#if CS110
public static CustomStruct operator >>>(CustomStruct lhs, int rhs)
{
throw new NotImplementedException();
}
#endif
public static CustomStruct operator &(CustomStruct lhs, CustomStruct rhs) public static CustomStruct operator &(CustomStruct lhs, CustomStruct rhs)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
@ -1685,6 +1691,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#endif #endif
} }
#if CS110
public static void ShortUnsignedRightShiftTest(short p, CustomClass c, CustomStruct2 s)
{
//X(p >>>= 5);
shortField >>>= 5;
ShortProp >>>= 5;
c.ShortField >>>= 5;
c.ShortProp >>>= 5;
s.ShortField >>>= 5;
s.ShortProp >>>= 5;
customClassField.ShortField >>>= 5;
customClassField.ShortProp >>>= 5;
otherCustomStructField.ShortField >>>= 5;
otherCustomStructField.ShortProp >>>= 5;
CustomClassProp.ShortField >>>= 5;
CustomClassProp.ShortProp >>>= 5;
GetClass().ShortField >>>= 5;
GetClass().ShortProp >>>= 5;
GetRefStruct().ShortField >>>= 5;
GetRefStruct().ShortProp >>>= 5;
GetRefShort() >>>= 5;
}
#endif
public static void ShortBitAndTest(short p, CustomClass c, CustomStruct2 s) public static void ShortBitAndTest(short p, CustomClass c, CustomStruct2 s)
{ {
//short l = 0; //short l = 0;
@ -2047,6 +2077,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#endif #endif
} }
#if CS110
public static void UshortUnsignedRightShiftTest(ushort p, CustomClass c, CustomStruct2 s)
{
//ushort l = 0;
//p >>>= 5;
//l >>>= 5;
ushortField >>>= 5;
UshortProp >>>= 5;
c.UshortField >>>= 5;
c.UshortProp >>>= 5;
s.UshortField >>>= 5;
s.UshortProp >>>= 5;
customClassField.UshortField >>>= 5;
customClassField.UshortProp >>>= 5;
otherCustomStructField.UshortField >>>= 5;
otherCustomStructField.UshortProp >>>= 5;
CustomClassProp.UshortField >>>= 5;
CustomClassProp.UshortProp >>>= 5;
GetClass().UshortField >>>= 5;
GetClass().UshortProp >>>= 5;
GetRefStruct().UshortField >>>= 5;
GetRefStruct().UshortProp >>>= 5;
GetRefUshort() >>>= 5;
}
#endif
public static void UshortBitAndTest(ushort p, CustomClass c, CustomStruct2 s) public static void UshortBitAndTest(ushort p, CustomClass c, CustomStruct2 s)
{ {
//ushort l = 0; //ushort l = 0;
@ -2409,6 +2465,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#endif #endif
} }
#if CS110
public static void IntUnsignedRightShiftTest(int p, CustomClass c, CustomStruct2 s)
{
X(p >>>= 5);
intField >>>= 5;
IntProp >>>= 5;
c.IntField >>>= 5;
c.IntProp >>>= 5;
s.IntField >>>= 5;
s.IntProp >>>= 5;
customClassField.IntField >>>= 5;
customClassField.IntProp >>>= 5;
otherCustomStructField.IntField >>>= 5;
otherCustomStructField.IntProp >>>= 5;
CustomClassProp.IntField >>>= 5;
CustomClassProp.IntProp >>>= 5;
GetClass().IntField >>>= 5;
GetClass().IntProp >>>= 5;
GetRefStruct().IntField >>>= 5;
GetRefStruct().IntProp >>>= 5;
GetRefInt() >>>= 5;
}
#endif
public static void IntBitAndTest(int p, CustomClass c, CustomStruct2 s) public static void IntBitAndTest(int p, CustomClass c, CustomStruct2 s)
{ {
//int l = 0; //int l = 0;
@ -4219,6 +4299,32 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
#endif #endif
} }
#if CS110
public static void CustomStructUnsignedRightShiftTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{
//CustomStruct l = default(CustomStruct);
//p >>>= 5;
//l >>>= 5;
customStructField >>>= 5;
CustomStructProp >>>= 5;
c.CustomStructField >>>= 5;
c.CustomStructProp >>>= 5;
s.CustomStructField >>>= 5;
s.CustomStructProp >>>= 5;
customClassField.CustomStructField >>>= 5;
customClassField.CustomStructProp >>>= 5;
otherCustomStructField.CustomStructField >>>= 5;
otherCustomStructField.CustomStructProp >>>= 5;
CustomClassProp.CustomStructField >>>= 5;
CustomClassProp.CustomStructProp >>>= 5;
GetClass().CustomStructField >>>= 5;
GetClass().CustomStructProp >>>= 5;
GetRefStruct().CustomStructField >>>= 5;
GetRefStruct().CustomStructProp >>>= 5;
GetRefCustomStruct() >>>= 5;
}
#endif
public static void CustomStructBitAndTest(CustomStruct p, CustomClass c, CustomStruct2 s) public static void CustomStructBitAndTest(CustomStruct p, CustomClass c, CustomStruct2 s)
{ {
//CustomStruct l = default(CustomStruct); //CustomStruct l = default(CustomStruct);

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

@ -0,0 +1,225 @@
// Copyright (c) Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public class AllOperators
{
public static AllOperators operator +(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator -(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator *(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator /(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator %(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator &(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator |(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator ^(AllOperators a, AllOperators b)
{
return null;
}
public static AllOperators operator <<(AllOperators a, int b)
{
return null;
}
public static AllOperators operator >>(AllOperators a, int b)
{
return null;
}
#if CS110
public static AllOperators operator >>>(AllOperators a, int b)
{
return null;
}
#endif
public static AllOperators operator ~(AllOperators a)
{
return null;
}
public static AllOperators operator !(AllOperators a)
{
return null;
}
public static AllOperators operator -(AllOperators a)
{
return null;
}
public static AllOperators operator +(AllOperators a)
{
return null;
}
public static AllOperators operator ++(AllOperators a)
{
return null;
}
public static AllOperators operator --(AllOperators a)
{
return null;
}
public static bool operator true(AllOperators a)
{
return false;
}
public static bool operator false(AllOperators a)
{
return false;
}
public static bool operator ==(AllOperators a, AllOperators b)
{
return false;
}
public static bool operator !=(AllOperators a, AllOperators b)
{
return false;
}
public static bool operator <(AllOperators a, AllOperators b)
{
return false;
}
public static bool operator >(AllOperators a, AllOperators b)
{
return false;
}
public static bool operator <=(AllOperators a, AllOperators b)
{
return false;
}
public static bool operator >=(AllOperators a, AllOperators b)
{
return false;
}
public static implicit operator AllOperators(int a)
{
return null;
}
public static explicit operator int(AllOperators a)
{
return 0;
}
}
public class UseAllOperators
{
private AllOperators a = new AllOperators();
private AllOperators b = new AllOperators();
private AllOperators c;
public void Test()
{
c = a + b;
c = a - b;
c = a * b;
c = a / b;
c = a % b;
c = a & b;
c = a | b;
c = a ^ b;
c = a << 5;
c = a >> 5;
#if CS110
c = a >>> 5;
#endif
c = ~a;
c = !a;
c = -a;
c = +a;
c = ++a;
c = --a;
if (a)
{
Console.WriteLine("a");
}
if (!a)
{
Console.WriteLine("!a");
}
if (a == b)
{
Console.WriteLine("a == b");
}
if (a != b)
{
Console.WriteLine("a != b");
}
if (a < b)
{
Console.WriteLine("a < b");
}
if (a > b)
{
Console.WriteLine("a > b");
}
if (a <= b)
{
Console.WriteLine("a <= b");
}
if (a >= b)
{
Console.WriteLine("a >= b");
}
int num = (int)a;
a = num;
}
}
}

30
ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs

@ -106,9 +106,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public uint RShiftByteWithZeroExtension(byte num) public uint RShiftByteWithZeroExtension(byte num)
{ {
// zero extend -> cast to unsigned -> unsigned shift
return (uint)num >> 8; return (uint)num >> 8;
} }
public int RShiftByteWithZeroExtensionReturnAsSigned(byte num)
{
#if CS110
// zero extend -> unsigned shift
return num >>> 8;
#else
// zero extend -> cast to unsigned -> unsigned shift -> cast to signed
return (int)((uint)num >> 8);
#endif
}
public int RShiftByteAsSByte(byte num) public int RShiftByteAsSByte(byte num)
{ {
return (sbyte)num >> 8; return (sbyte)num >> 8;
@ -121,9 +133,25 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
public uint RShiftSByteWithZeroExtension(sbyte num) public uint RShiftSByteWithZeroExtension(sbyte num)
{ {
return (uint)num >> 8; return (uint)((byte)num >> 4);
} }
public uint RShiftSByteWithSignExtension(sbyte num)
{
// sign extend -> cast to unsigned -> unsigned shift
return (uint)num >> 4;
}
public int RShiftSByteWithSignExtensionReturnAsSigned(sbyte num)
{
#if CS110
// sign extend -> unsigned shift
return num >>> 4;
#else
// sign extend -> cast to unsigned -> unsigned shift -> cast to signed
return (int)((uint)num >> 4);
#endif
}
public int RShiftSByteAsByte(sbyte num) public int RShiftSByteAsByte(sbyte num)
{ {
return (byte)num >> 8; return (byte)num >> 8;

1
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

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

49
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -1208,9 +1208,9 @@ namespace ICSharpCode.Decompiler.CSharp
case BinaryNumericOperator.BitXor: case BinaryNumericOperator.BitXor:
return HandleBinaryNumeric(inst, BinaryOperatorType.ExclusiveOr, context); return HandleBinaryNumeric(inst, BinaryOperatorType.ExclusiveOr, context);
case BinaryNumericOperator.ShiftLeft: case BinaryNumericOperator.ShiftLeft:
return HandleShift(inst, BinaryOperatorType.ShiftLeft); return HandleShift(inst, BinaryOperatorType.ShiftLeft, context);
case BinaryNumericOperator.ShiftRight: case BinaryNumericOperator.ShiftRight:
return HandleShift(inst, BinaryOperatorType.ShiftRight); return HandleShift(inst, BinaryOperatorType.ShiftRight, context);
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
@ -1729,13 +1729,14 @@ namespace ICSharpCode.Decompiler.CSharp
case BinaryOperatorType.ExclusiveOr: case BinaryOperatorType.ExclusiveOr:
case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftLeft:
case BinaryOperatorType.ShiftRight: case BinaryOperatorType.ShiftRight:
case BinaryOperatorType.UnsignedShiftRight:
return false; return false;
default: default:
return true; return true;
} }
} }
TranslatedExpression HandleShift(BinaryNumericInstruction inst, BinaryOperatorType op) TranslatedExpression HandleShift(BinaryNumericInstruction inst, BinaryOperatorType op, TranslationContext context)
{ {
var left = Translate(inst.Left); var left = Translate(inst.Left);
var right = Translate(inst.Right); var right = Translate(inst.Right);
@ -1744,10 +1745,27 @@ namespace ICSharpCode.Decompiler.CSharp
Sign sign = inst.Sign; Sign sign = inst.Sign;
var leftUType = NullableType.GetUnderlyingType(left.Type); var leftUType = NullableType.GetUnderlyingType(left.Type);
if (leftUType.IsCSharpSmallIntegerType() && sign != Sign.Unsigned && inst.UnderlyingResultType == StackType.I4) bool couldUseUnsignedRightShift = (
sign == Sign.Unsigned && op == BinaryOperatorType.ShiftRight && settings.UnsignedRightShift
&& (leftUType.IsCSharpPrimitiveIntegerType() || leftUType.IsCSharpNativeIntegerType())
// If we need to cast to unsigned anyway, don't use >>> operator.
&& context.TypeHint.GetSign() != Sign.Unsigned
);
if (leftUType.IsCSharpSmallIntegerType() && inst.UnderlyingResultType == StackType.I4 &&
(sign != Sign.Unsigned || couldUseUnsignedRightShift))
{ {
// With small integer types, C# will promote to int and perform signed shifts. // With small integer types, C# will promote to int and perform signed shifts.
// We thus don't need any casts in this case. // We thus don't need any casts in this case.
// The >>> operator also promotes to signed int, but then performs an unsigned shift.
if (sign == Sign.Unsigned)
{
op = BinaryOperatorType.UnsignedShiftRight;
}
}
else if (couldUseUnsignedRightShift && leftUType.GetSize() == inst.UnderlyingResultType.GetSize() && leftUType.GetSign() == Sign.Signed)
{
// Use C# 11 unsigned right shift operator. We don't need any casts in this case.
op = BinaryOperatorType.UnsignedShiftRight;
} }
else else
{ {
@ -1804,7 +1822,7 @@ namespace ICSharpCode.Decompiler.CSharp
else if (inst.Method.Parameters.Count == 2) else if (inst.Method.Parameters.Count == 2)
{ {
var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this); var value = Translate(inst.Value).ConvertTo(inst.Method.Parameters[1].Type, this);
AssignmentOperatorType? op = GetAssignmentOperatorTypeFromMetadataName(inst.Method.Name); AssignmentOperatorType? op = GetAssignmentOperatorTypeFromMetadataName(inst.Method.Name, settings);
Debug.Assert(op != null); Debug.Assert(op != null);
return new AssignmentExpression(target, op.Value, value) return new AssignmentExpression(target, op.Value, value)
@ -1822,7 +1840,7 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
internal static AssignmentOperatorType? GetAssignmentOperatorTypeFromMetadataName(string name) internal static AssignmentOperatorType? GetAssignmentOperatorTypeFromMetadataName(string name, DecompilerSettings settings)
{ {
switch (name) switch (name)
{ {
@ -1846,6 +1864,8 @@ namespace ICSharpCode.Decompiler.CSharp
return AssignmentOperatorType.ShiftLeft; return AssignmentOperatorType.ShiftLeft;
case "op_RightShift": case "op_RightShift":
return AssignmentOperatorType.ShiftRight; return AssignmentOperatorType.ShiftRight;
case "op_UnsignedRightShift" when settings.UnsignedRightShift:
return AssignmentOperatorType.UnsignedShiftRight;
default: default:
return null; return null;
} }
@ -1887,7 +1907,22 @@ namespace ICSharpCode.Decompiler.CSharp
case BinaryNumericOperator.ShiftLeft: case BinaryNumericOperator.ShiftLeft:
return HandleCompoundShift(inst, AssignmentOperatorType.ShiftLeft); return HandleCompoundShift(inst, AssignmentOperatorType.ShiftLeft);
case BinaryNumericOperator.ShiftRight: case BinaryNumericOperator.ShiftRight:
return HandleCompoundShift(inst, AssignmentOperatorType.ShiftRight); if (inst.Sign == Sign.Unsigned && inst.Type.GetSign() == Sign.Signed)
{
Debug.Assert(settings.UnsignedRightShift);
return HandleCompoundShift(inst, AssignmentOperatorType.UnsignedShiftRight);
}
else if (inst.Sign == Sign.Unsigned && inst.Type.IsCSharpSmallIntegerType() && settings.UnsignedRightShift)
{
// For small unsigned integer types promoted to signed int, the sign bit will be zero,
// so there is no difference between signed and unsigned shift.
// However the IL still indicates which C# operator was used, so preserve that if the setting allows us to.
return HandleCompoundShift(inst, AssignmentOperatorType.UnsignedShiftRight);
}
else
{
return HandleCompoundShift(inst, AssignmentOperatorType.ShiftRight);
}
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }

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

@ -237,6 +237,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
astBuilder.SupportInitAccessors = (ConversionFlags & ConversionFlags.SupportInitAccessors) != 0; astBuilder.SupportInitAccessors = (ConversionFlags & ConversionFlags.SupportInitAccessors) != 0;
astBuilder.SupportRecordClasses = (ConversionFlags & ConversionFlags.SupportRecordClasses) != 0; astBuilder.SupportRecordClasses = (ConversionFlags & ConversionFlags.SupportRecordClasses) != 0;
astBuilder.SupportRecordStructs = (ConversionFlags & ConversionFlags.SupportRecordStructs) != 0; astBuilder.SupportRecordStructs = (ConversionFlags & ConversionFlags.SupportRecordStructs) != 0;
astBuilder.SupportUnsignedRightShift = (ConversionFlags & ConversionFlags.SupportUnsignedRightShift) != 0;
return astBuilder; return astBuilder;
} }

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

@ -826,6 +826,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
break; break;
case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftLeft:
case BinaryOperatorType.ShiftRight: case BinaryOperatorType.ShiftRight:
case BinaryOperatorType.UnsignedShiftRight:
spacePolicy = policy.SpaceAroundShiftOperator; spacePolicy = policy.SpaceAroundShiftOperator;
break; break;
case BinaryOperatorType.NullCoalescing: case BinaryOperatorType.NullCoalescing:

3
ICSharpCode.Decompiler/CSharp/OutputVisitor/GenericGrammarAmbiguityVisitor.cs

@ -92,6 +92,9 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
case BinaryOperatorType.ShiftRight when genericNestingLevel >= 2: case BinaryOperatorType.ShiftRight when genericNestingLevel >= 2:
genericNestingLevel -= 2; genericNestingLevel -= 2;
break; break;
case BinaryOperatorType.UnsignedShiftRight when genericNestingLevel >= 3:
genericNestingLevel -= 3;
break;
default: default:
return true; // stop visiting, no ambiguity found return true; // stop visiting, no ambiguity found
} }

1
ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs

@ -122,6 +122,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor
return PrecedenceLevel.Additive; return PrecedenceLevel.Additive;
case BinaryOperatorType.ShiftLeft: case BinaryOperatorType.ShiftLeft:
case BinaryOperatorType.ShiftRight: case BinaryOperatorType.ShiftRight:
case BinaryOperatorType.UnsignedShiftRight:
return PrecedenceLevel.Shift; return PrecedenceLevel.Shift;
case BinaryOperatorType.GreaterThan: case BinaryOperatorType.GreaterThan:
case BinaryOperatorType.GreaterThanOrEqual: case BinaryOperatorType.GreaterThanOrEqual:

21
ICSharpCode.Decompiler/CSharp/Resolver/CSharpOperators.cs

@ -674,6 +674,27 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
} }
} }
} }
OperatorMethod[]? unsignedShiftRightOperators;
public OperatorMethod[] UnsignedShiftRightOperators {
get {
OperatorMethod[]? ops = LazyInit.VolatileRead(ref unsignedShiftRightOperators);
if (ops != null)
{
return ops;
}
else
{
return LazyInit.GetOrSet(ref unsignedShiftRightOperators, Lift(
new LambdaBinaryOperatorMethod<int, int>(this, (a, b) => (int)((uint)a >> b)),
new LambdaBinaryOperatorMethod<uint, int>(this, (a, b) => a >> b),
new LambdaBinaryOperatorMethod<long, int>(this, (a, b) => (long)((ulong)a >> b)),
new LambdaBinaryOperatorMethod<ulong, int>(this, (a, b) => a >> b)
));
}
}
}
#endregion #endregion
#region Equality operators #region Equality operators

7
ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs

@ -685,7 +685,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
{ {
isNullable = true; isNullable = true;
} }
if (op == BinaryOperatorType.ShiftLeft || op == BinaryOperatorType.ShiftRight) if (op == BinaryOperatorType.ShiftLeft || op == BinaryOperatorType.ShiftRight || op == BinaryOperatorType.UnsignedShiftRight)
{ {
// special case: the shift operators allow "var x = null << null", producing int?. // special case: the shift operators allow "var x = null << null", producing int?.
if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null) if (lhsType.Kind == TypeKind.Null && rhsType.Kind == TypeKind.Null)
@ -805,6 +805,9 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
case BinaryOperatorType.ShiftRight: case BinaryOperatorType.ShiftRight:
methodGroup = operators.ShiftRightOperators; methodGroup = operators.ShiftRightOperators;
break; break;
case BinaryOperatorType.UnsignedShiftRight:
methodGroup = operators.UnsignedShiftRightOperators;
break;
case BinaryOperatorType.Equality: case BinaryOperatorType.Equality:
case BinaryOperatorType.InEquality: case BinaryOperatorType.InEquality:
case BinaryOperatorType.LessThan: case BinaryOperatorType.LessThan:
@ -1256,6 +1259,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
return "op_LeftShift"; return "op_LeftShift";
case BinaryOperatorType.ShiftRight: case BinaryOperatorType.ShiftRight:
return "op_RightShift"; return "op_RightShift";
case BinaryOperatorType.UnsignedShiftRight:
return "op_UnsignedRightShift";
case BinaryOperatorType.Equality: case BinaryOperatorType.Equality:
return "op_Equality"; return "op_Equality";
case BinaryOperatorType.InEquality: case BinaryOperatorType.InEquality:

9
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/AssignmentExpression.cs

@ -47,6 +47,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public readonly static TokenRole ModulusRole = new TokenRole("%="); public readonly static TokenRole ModulusRole = new TokenRole("%=");
public readonly static TokenRole ShiftLeftRole = new TokenRole("<<="); public readonly static TokenRole ShiftLeftRole = new TokenRole("<<=");
public readonly static TokenRole ShiftRightRole = new TokenRole(">>="); public readonly static TokenRole ShiftRightRole = new TokenRole(">>=");
public readonly static TokenRole UnsignedShiftRightRole = new TokenRole(">>>=");
public readonly static TokenRole BitwiseAndRole = new TokenRole("&="); public readonly static TokenRole BitwiseAndRole = new TokenRole("&=");
public readonly static TokenRole BitwiseOrRole = new TokenRole("|="); public readonly static TokenRole BitwiseOrRole = new TokenRole("|=");
public readonly static TokenRole ExclusiveOrRole = new TokenRole("^="); public readonly static TokenRole ExclusiveOrRole = new TokenRole("^=");
@ -129,6 +130,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return ShiftLeftRole; return ShiftLeftRole;
case AssignmentOperatorType.ShiftRight: case AssignmentOperatorType.ShiftRight:
return ShiftRightRole; return ShiftRightRole;
case AssignmentOperatorType.UnsignedShiftRight:
return UnsignedShiftRightRole;
case AssignmentOperatorType.BitwiseAnd: case AssignmentOperatorType.BitwiseAnd:
return BitwiseAndRole; return BitwiseAndRole;
case AssignmentOperatorType.BitwiseOr: case AssignmentOperatorType.BitwiseOr:
@ -164,6 +167,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return BinaryOperatorType.ShiftLeft; return BinaryOperatorType.ShiftLeft;
case AssignmentOperatorType.ShiftRight: case AssignmentOperatorType.ShiftRight:
return BinaryOperatorType.ShiftRight; return BinaryOperatorType.ShiftRight;
case AssignmentOperatorType.UnsignedShiftRight:
return BinaryOperatorType.UnsignedShiftRight;
case AssignmentOperatorType.BitwiseAnd: case AssignmentOperatorType.BitwiseAnd:
return BinaryOperatorType.BitwiseAnd; return BinaryOperatorType.BitwiseAnd;
case AssignmentOperatorType.BitwiseOr: case AssignmentOperatorType.BitwiseOr:
@ -195,6 +200,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return ExpressionType.LeftShiftAssign; return ExpressionType.LeftShiftAssign;
case AssignmentOperatorType.ShiftRight: case AssignmentOperatorType.ShiftRight:
return ExpressionType.RightShiftAssign; return ExpressionType.RightShiftAssign;
case AssignmentOperatorType.UnsignedShiftRight:
return ExpressionType.Extension;
case AssignmentOperatorType.BitwiseAnd: case AssignmentOperatorType.BitwiseAnd:
return ExpressionType.AndAssign; return ExpressionType.AndAssign;
case AssignmentOperatorType.BitwiseOr: case AssignmentOperatorType.BitwiseOr:
@ -259,6 +266,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
ShiftLeft, ShiftLeft,
/// <summary>left >>= right</summary> /// <summary>left >>= right</summary>
ShiftRight, ShiftRight,
/// <summary>left >>>= right</summary>
UnsignedShiftRight,
/// <summary>left &amp;= right</summary> /// <summary>left &amp;= right</summary>
BitwiseAnd, BitwiseAnd,

6
ICSharpCode.Decompiler/CSharp/Syntax/Expressions/BinaryOperatorExpression.cs

@ -52,6 +52,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public readonly static TokenRole ModulusRole = new TokenRole("%"); public readonly static TokenRole ModulusRole = new TokenRole("%");
public readonly static TokenRole ShiftLeftRole = new TokenRole("<<"); public readonly static TokenRole ShiftLeftRole = new TokenRole("<<");
public readonly static TokenRole ShiftRightRole = new TokenRole(">>"); public readonly static TokenRole ShiftRightRole = new TokenRole(">>");
public readonly static TokenRole UnsignedShiftRightRole = new TokenRole(">>>");
public readonly static TokenRole NullCoalescingRole = new TokenRole("??"); public readonly static TokenRole NullCoalescingRole = new TokenRole("??");
public readonly static TokenRole RangeRole = new TokenRole(".."); public readonly static TokenRole RangeRole = new TokenRole("..");
public readonly static TokenRole IsKeywordRole = IsExpression.IsKeywordRole; public readonly static TokenRole IsKeywordRole = IsExpression.IsKeywordRole;
@ -151,6 +152,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return ShiftLeftRole; return ShiftLeftRole;
case BinaryOperatorType.ShiftRight: case BinaryOperatorType.ShiftRight:
return ShiftRightRole; return ShiftRightRole;
case BinaryOperatorType.UnsignedShiftRight:
return UnsignedShiftRightRole;
case BinaryOperatorType.NullCoalescing: case BinaryOperatorType.NullCoalescing:
return NullCoalescingRole; return NullCoalescingRole;
case BinaryOperatorType.Range: case BinaryOperatorType.Range:
@ -205,6 +208,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
case BinaryOperatorType.NullCoalescing: case BinaryOperatorType.NullCoalescing:
return ExpressionType.Coalesce; return ExpressionType.Coalesce;
case BinaryOperatorType.Range: case BinaryOperatorType.Range:
case BinaryOperatorType.UnsignedShiftRight:
return ExpressionType.Extension; return ExpressionType.Extension;
default: default:
throw new NotSupportedException("Invalid value for BinaryOperatorType"); throw new NotSupportedException("Invalid value for BinaryOperatorType");
@ -262,6 +266,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
ShiftLeft, ShiftLeft,
/// <summary>left &gt;&gt; right</summary> /// <summary>left &gt;&gt; right</summary>
ShiftRight, ShiftRight,
/// <summary>left &gt;&gt;&gt; right</summary>
UnsignedShiftRight,
/// <summary>left ?? right</summary> /// <summary>left ?? right</summary>
NullCoalescing, NullCoalescing,

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

@ -57,6 +57,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
ExclusiveOr, ExclusiveOr,
LeftShift, LeftShift,
RightShift, RightShift,
UnsignedRightShift,
Equality, Equality,
Inequality, Inequality,
GreaterThan, GreaterThan,
@ -94,6 +95,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static readonly TokenRole ExclusiveOrRole = new TokenRole("^"); public static readonly TokenRole ExclusiveOrRole = new TokenRole("^");
public static readonly TokenRole LeftShiftRole = new TokenRole("<<"); public static readonly TokenRole LeftShiftRole = new TokenRole("<<");
public static readonly TokenRole RightShiftRole = new TokenRole(">>"); public static readonly TokenRole RightShiftRole = new TokenRole(">>");
public static readonly TokenRole UnsignedRightShiftRole = new TokenRole(">>>");
public static readonly TokenRole EqualityRole = new TokenRole("=="); public static readonly TokenRole EqualityRole = new TokenRole("==");
public static readonly TokenRole InequalityRole = new TokenRole("!="); public static readonly TokenRole InequalityRole = new TokenRole("!=");
public static readonly TokenRole GreaterThanRole = new TokenRole(">"); public static readonly TokenRole GreaterThanRole = new TokenRole(">");
@ -127,6 +129,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
names[(int)OperatorType.ExclusiveOr] = new string[] { "^", "op_ExclusiveOr" }; names[(int)OperatorType.ExclusiveOr] = new string[] { "^", "op_ExclusiveOr" };
names[(int)OperatorType.LeftShift] = new string[] { "<<", "op_LeftShift" }; names[(int)OperatorType.LeftShift] = new string[] { "<<", "op_LeftShift" };
names[(int)OperatorType.RightShift] = new string[] { ">>", "op_RightShift" }; names[(int)OperatorType.RightShift] = new string[] { ">>", "op_RightShift" };
names[(int)OperatorType.UnsignedRightShift] = new string[] { ">>>", "op_UnsignedRightShift" };
names[(int)OperatorType.Equality] = new string[] { "==", "op_Equality" }; names[(int)OperatorType.Equality] = new string[] { "==", "op_Equality" };
names[(int)OperatorType.Inequality] = new string[] { "!=", "op_Inequality" }; names[(int)OperatorType.Inequality] = new string[] { "!=", "op_Inequality" };
names[(int)OperatorType.GreaterThan] = new string[] { ">", "op_GreaterThan" }; names[(int)OperatorType.GreaterThan] = new string[] { ">", "op_GreaterThan" };
@ -230,6 +233,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return LeftShiftRole; return LeftShiftRole;
case OperatorType.RightShift: case OperatorType.RightShift:
return RightShiftRole; return RightShiftRole;
case OperatorType.UnsignedRightShift:
return UnsignedRightShiftRole;
case OperatorType.Equality: case OperatorType.Equality:
return EqualityRole; return EqualityRole;
case OperatorType.Inequality: case OperatorType.Inequality:

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

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

2
ICSharpCode.Decompiler/CSharp/Transforms/PrettifyAssignments.cs

@ -98,6 +98,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return AssignmentOperatorType.ShiftLeft; return AssignmentOperatorType.ShiftLeft;
case BinaryOperatorType.ShiftRight: case BinaryOperatorType.ShiftRight:
return AssignmentOperatorType.ShiftRight; return AssignmentOperatorType.ShiftRight;
case BinaryOperatorType.UnsignedShiftRight:
return AssignmentOperatorType.UnsignedShiftRight;
case BinaryOperatorType.BitwiseAnd: case BinaryOperatorType.BitwiseAnd:
return AssignmentOperatorType.BitwiseAnd; return AssignmentOperatorType.BitwiseAnd;
case BinaryOperatorType.BitwiseOr: case BinaryOperatorType.BitwiseOr:

6
ICSharpCode.Decompiler/CSharp/Transforms/ReplaceMethodCallsWithOperators.cs

@ -142,7 +142,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
break; break;
} }
BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(method.Name); BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(method.Name, context.Settings);
if (bop != null && arguments.Length == 2) if (bop != null && arguments.Length == 2)
{ {
invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
@ -350,7 +350,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
} }
} }
static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name) static BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name, DecompilerSettings settings)
{ {
switch (name) switch (name)
{ {
@ -374,6 +374,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return BinaryOperatorType.ShiftLeft; return BinaryOperatorType.ShiftLeft;
case "op_RightShift": case "op_RightShift":
return BinaryOperatorType.ShiftRight; return BinaryOperatorType.ShiftRight;
case "op_UnsignedRightShift" when settings.UnsignedRightShift:
return BinaryOperatorType.UnsignedShiftRight;
case "op_Equality": case "op_Equality":
return BinaryOperatorType.Equality; return BinaryOperatorType.Equality;
case "op_Inequality": case "op_Inequality":

21
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -154,12 +154,13 @@ namespace ICSharpCode.Decompiler
requiredMembers = false; requiredMembers = false;
numericIntPtr = false; numericIntPtr = false;
utf8StringLiterals = false; utf8StringLiterals = false;
unsignedRightShift = false;
} }
} }
public CSharp.LanguageVersion GetMinimumRequiredVersion() public CSharp.LanguageVersion GetMinimumRequiredVersion()
{ {
if (parameterNullCheck || scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals) if (parameterNullCheck || scopedRef || requiredMembers || numericIntPtr || utf8StringLiterals || unsignedRightShift)
return CSharp.LanguageVersion.CSharp11_0; return CSharp.LanguageVersion.CSharp11_0;
if (fileScopedNamespaces || recordStructs) if (fileScopedNamespaces || recordStructs)
return CSharp.LanguageVersion.CSharp10_0; return CSharp.LanguageVersion.CSharp10_0;
@ -1199,6 +1200,24 @@ namespace ICSharpCode.Decompiler
} }
} }
bool unsignedRightShift = true;
/// <summary>
/// Gets/Sets whether to use C# 11.0 unsigned right shift operator.
/// </summary>
[Category("C# 11.0 / VS 2022.4")]
[Description("DecompilerSettings.UnsignedRightShift")]
public bool UnsignedRightShift {
get { return unsignedRightShift; }
set {
if (unsignedRightShift != value)
{
unsignedRightShift = value;
OnPropertyChanged();
}
}
}
bool showXmlDocumentation = true; bool showXmlDocumentation = true;
/// <summary> /// <summary>

10
ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs

@ -215,8 +215,9 @@ namespace ICSharpCode.Decompiler.IL
return false; // operator not supported on pointer types return false; // operator not supported on pointer types
} }
} }
else if (type.IsKnownType(KnownTypeCode.IntPtr) || type.IsKnownType(KnownTypeCode.UIntPtr)) else if ((type.IsKnownType(KnownTypeCode.IntPtr) || type.IsKnownType(KnownTypeCode.UIntPtr)) && type.Kind is not TypeKind.NInt or TypeKind.NUInt)
{ {
// If the LHS is C# 9 IntPtr (but not nint or C# 11 IntPtr):
// "target.intptr *= 2;" is compiler error, but // "target.intptr *= 2;" is compiler error, but
// "target.intptr *= (nint)2;" works // "target.intptr *= (nint)2;" works
if (settings != null && !settings.NativeIntegers) if (settings != null && !settings.NativeIntegers)
@ -234,16 +235,17 @@ namespace ICSharpCode.Decompiler.IL
} }
if (binary.Sign != Sign.None) if (binary.Sign != Sign.None)
{ {
bool signMismatchAllowed = (binary.Sign == Sign.Unsigned && binary.Operator == BinaryNumericOperator.ShiftRight && (settings == null || settings.UnsignedRightShift));
if (type.IsCSharpSmallIntegerType()) if (type.IsCSharpSmallIntegerType())
{ {
// C# will use numeric promotion to int, binary op must be signed // C# will use numeric promotion to int, binary op must be signed
if (binary.Sign != Sign.Signed) if (binary.Sign != Sign.Signed && !signMismatchAllowed)
return false; return false;
} }
else else
{ {
// C# will use sign from type // C# will use sign from type; except for right shift with C# 11 >>> operator.
if (type.GetSign() != binary.Sign) if (type.GetSign() != binary.Sign && !signMismatchAllowed)
return false; return false;
} }
} }

2
ICSharpCode.Decompiler/IL/Transforms/TransformAssignment.cs

@ -386,7 +386,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
ILInstruction rhs; ILInstruction rhs;
if (operatorCall.Arguments.Count == 2) if (operatorCall.Arguments.Count == 2)
{ {
if (CSharp.ExpressionBuilder.GetAssignmentOperatorTypeFromMetadataName(operatorCall.Method.Name) == null) if (CSharp.ExpressionBuilder.GetAssignmentOperatorTypeFromMetadataName(operatorCall.Method.Name, context.Settings) == null)
return false; return false;
rhs = operatorCall.Arguments[1]; rhs = operatorCall.Arguments[1];
} }

4
ICSharpCode.Decompiler/Output/IAmbience.cs

@ -109,6 +109,10 @@ namespace ICSharpCode.Decompiler.Output
/// Support <c>record</c> structs. /// Support <c>record</c> structs.
/// </summary> /// </summary>
SupportRecordStructs = 0x40000, SupportRecordStructs = 0x40000,
/// <summary>
/// Support <c>&gt;&gt;&gt;</c> as unsigned right shift operator.
/// </summary>
SupportUnsignedRightShift = 0x80000,
StandardConversionFlags = ShowParameterNames | StandardConversionFlags = ShowParameterNames |
ShowAccessibility | ShowAccessibility |

4
ILSpy/Languages/CSharpLanguage.cs

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

9
ILSpy/Properties/Resources.Designer.cs generated

@ -1280,6 +1280,15 @@ namespace ICSharpCode.ILSpy.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Unsigned right shift (&gt;&gt;&gt;).
/// </summary>
public static string DecompilerSettings_UnsignedRightShift {
get {
return ResourceManager.GetString("DecompilerSettings.UnsignedRightShift", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Use discards. /// Looks up a localized string similar to Use discards.
/// </summary> /// </summary>

3
ILSpy/Properties/Resources.resx

@ -450,6 +450,9 @@ Are you sure you want to continue?</value>
<data name="DecompilerSettings.SwitchExpressions" xml:space="preserve"> <data name="DecompilerSettings.SwitchExpressions" xml:space="preserve">
<value>Switch expressions</value> <value>Switch expressions</value>
</data> </data>
<data name="DecompilerSettings.UnsignedRightShift" xml:space="preserve">
<value>Unsigned right shift (&gt;&gt;&gt;)</value>
</data>
<data name="DecompilerSettings.UseDiscards" xml:space="preserve"> <data name="DecompilerSettings.UseDiscards" xml:space="preserve">
<value>Use discards</value> <value>Use discards</value>
</data> </data>

Loading…
Cancel
Save