Browse Source

Add support for C# 11 required members.

pull/2833/head
Daniel Grunwald 3 years ago
parent
commit
f3013010cd
  1. 6
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs
  2. 26
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Structs.cs
  3. 12
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  4. 51
      ICSharpCode.Decompiler/CSharp/Syntax/CSharpModifierToken.cs
  5. 1
      ICSharpCode.Decompiler/CSharp/Syntax/Modifiers.cs
  6. 21
      ICSharpCode.Decompiler/DecompilerSettings.cs
  7. 7
      ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
  8. 9
      ILSpy/Properties/Resources.Designer.cs
  9. 3
      ILSpy/Properties/Resources.resx

6
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs

@ -129,7 +129,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
#if ROSLYN4
#if CS100
internal class RecordStructs internal class RecordStructs
{ {
public record struct Base(string A); public record struct Base(string A);
@ -230,6 +231,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
} }
#endif
#if CS110
#endif #endif
} }
namespace System.Runtime.CompilerServices namespace System.Runtime.CompilerServices

26
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Structs.cs

@ -51,4 +51,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
} }
} }
#endif #endif
}
#if CS110
public struct StructWithRequiredMembers
{
public required string FirstName;
public required string LastName { get; set; }
}
#endif
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
internal sealed class CompilerFeatureRequiredAttribute : Attribute
{
public CompilerFeatureRequiredAttribute(string featureName)
{
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
internal sealed class RequiredMemberAttribute : Attribute
{
}
}

12
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -1380,6 +1380,10 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
} }
if (settings.RequiredMembers)
{
RemoveAttribute(typeDecl, KnownAttribute.RequiredAttribute);
}
if (typeDecl.ClassType == ClassType.Enum) if (typeDecl.ClassType == ClassType.Enum)
{ {
switch (decompileRun.EnumValueDisplayMode) switch (decompileRun.EnumValueDisplayMode)
@ -1871,6 +1875,10 @@ namespace ICSharpCode.Decompiler.CSharp
typeSystemAstBuilder.UseSpecialConstants = !(field.DeclaringType.Equals(field.ReturnType) || isMathPIOrE); typeSystemAstBuilder.UseSpecialConstants = !(field.DeclaringType.Equals(field.ReturnType) || isMathPIOrE);
var fieldDecl = typeSystemAstBuilder.ConvertEntity(field); var fieldDecl = typeSystemAstBuilder.ConvertEntity(field);
SetNewModifier(fieldDecl); SetNewModifier(fieldDecl);
if (settings.RequiredMembers && RemoveAttribute(fieldDecl, KnownAttribute.RequiredAttribute))
{
fieldDecl.Modifiers |= Modifiers.Required;
}
if (settings.FixedBuffers && IsFixedField(field, out var elementType, out var elementCount)) if (settings.FixedBuffers && IsFixedField(field, out var elementType, out var elementCount))
{ {
var fixedFieldDecl = new FixedFieldDeclaration(); var fixedFieldDecl = new FixedFieldDeclaration();
@ -1983,6 +1991,10 @@ namespace ICSharpCode.Decompiler.CSharp
propertyDecl.Modifiers &= ~(Modifiers.New | Modifiers.Virtual); propertyDecl.Modifiers &= ~(Modifiers.New | Modifiers.Virtual);
propertyDecl.Modifiers |= Modifiers.Override; propertyDecl.Modifiers |= Modifiers.Override;
} }
if (settings.RequiredMembers && RemoveAttribute(propertyDecl, KnownAttribute.RequiredAttribute))
{
propertyDecl.Modifiers |= Modifiers.Required;
}
return propertyDecl; return propertyDecl;
} }
catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException))

51
ICSharpCode.Decompiler/CSharp/Syntax/CSharpModifierToken.cs

@ -67,7 +67,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
Modifiers.New, Modifiers.New,
Modifiers.Unsafe, Modifiers.Unsafe,
Modifiers.Abstract, Modifiers.Virtual, Modifiers.Sealed, Modifiers.Static, Modifiers.Override, Modifiers.Abstract, Modifiers.Virtual, Modifiers.Sealed, Modifiers.Static, Modifiers.Override,
Modifiers.Readonly, Modifiers.Volatile, Modifiers.Required, Modifiers.Readonly, Modifiers.Volatile,
Modifiers.Ref, Modifiers.Ref,
Modifiers.Extern, Modifiers.Partial, Modifiers.Const, Modifiers.Extern, Modifiers.Partial, Modifiers.Const,
Modifiers.Async, Modifiers.Async,
@ -119,6 +119,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return "async"; return "async";
case Modifiers.Ref: case Modifiers.Ref:
return "ref"; return "ref";
case Modifiers.Required:
return "required";
case Modifiers.Any: case Modifiers.Any:
// even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST // even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST
return "any"; return "any";
@ -129,50 +131,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static int GetModifierLength(Modifiers modifier) public static int GetModifierLength(Modifiers modifier)
{ {
switch (modifier) return GetModifierName(modifier).Length;
{
case Modifiers.Private:
return "private".Length;
case Modifiers.Internal:
return "internal".Length;
case Modifiers.Protected:
return "protected".Length;
case Modifiers.Public:
return "public".Length;
case Modifiers.Abstract:
return "abstract".Length;
case Modifiers.Virtual:
return "virtual".Length;
case Modifiers.Sealed:
return "sealed".Length;
case Modifiers.Static:
return "static".Length;
case Modifiers.Override:
return "override".Length;
case Modifiers.Readonly:
return "readonly".Length;
case Modifiers.Const:
return "const".Length;
case Modifiers.New:
return "new".Length;
case Modifiers.Partial:
return "partial".Length;
case Modifiers.Extern:
return "extern".Length;
case Modifiers.Volatile:
return "volatile".Length;
case Modifiers.Unsafe:
return "unsafe".Length;
case Modifiers.Async:
return "async".Length;
case Modifiers.Ref:
return "ref".Length;
case Modifiers.Any:
// even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST
return "any".Length;
default:
throw new NotSupportedException("Invalid value for Modifiers");
}
} }
public static Modifiers GetModifierValue(string modifier) public static Modifiers GetModifierValue(string modifier)
@ -215,6 +174,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return Modifiers.Async; return Modifiers.Async;
case "ref": case "ref":
return Modifiers.Ref; return Modifiers.Ref;
case "required":
return Modifiers.Required;
case "any": case "any":
// even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST // even though it's used for pattern matching only, 'any' needs to be in this list to be usable in the AST
return Modifiers.Any; return Modifiers.Any;

1
ICSharpCode.Decompiler/CSharp/Syntax/Modifiers.cs

@ -55,6 +55,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
Unsafe = 0x8000, Unsafe = 0x8000,
Async = 0x10000, Async = 0x10000,
Ref = 0x20000, Ref = 0x20000,
Required = 0x40000,
VisibilityMask = Private | Internal | Protected | Public, VisibilityMask = Private | Internal | Protected | Public,

21
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -151,12 +151,13 @@ namespace ICSharpCode.Decompiler
{ {
parameterNullCheck = false; parameterNullCheck = false;
lifetimeAnnotations = false; lifetimeAnnotations = false;
requiredMembers = false;
} }
} }
public CSharp.LanguageVersion GetMinimumRequiredVersion() public CSharp.LanguageVersion GetMinimumRequiredVersion()
{ {
if (parameterNullCheck || lifetimeAnnotations) if (parameterNullCheck || lifetimeAnnotations || requiredMembers)
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;
@ -356,6 +357,24 @@ namespace ICSharpCode.Decompiler
} }
} }
bool requiredMembers = true;
/// <summary>
/// Use C# 11 <c>required</c> modifier.
/// </summary>
[Category("C# 11.0 / VS 2022.4")]
[Description("DecompilerSettings.RequiredMembers")]
public bool RequiredMembers {
get { return requiredMembers; }
set {
if (requiredMembers != value)
{
requiredMembers = value;
OnPropertyChanged();
}
}
}
bool switchExpressions = true; bool switchExpressions = true;
/// <summary> /// <summary>

7
ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs

@ -106,11 +106,14 @@ namespace ICSharpCode.Decompiler.TypeSystem
// C# 9 attributes: // C# 9 attributes:
NativeInteger, NativeInteger,
PreserveBaseOverrides, PreserveBaseOverrides,
// C# 11 attributes:
RequiredAttribute,
} }
public static class KnownAttributes public static class KnownAttributes
{ {
internal const int Count = (int)KnownAttribute.PreserveBaseOverrides + 1; internal const int Count = (int)KnownAttribute.RequiredAttribute + 1;
static readonly TopLevelTypeName[] typeNames = new TopLevelTypeName[Count]{ static readonly TopLevelTypeName[] typeNames = new TopLevelTypeName[Count]{
default, default,
@ -175,6 +178,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
// C# 9 attributes: // C# 9 attributes:
new TopLevelTypeName("System.Runtime.CompilerServices", "NativeIntegerAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", "NativeIntegerAttribute"),
new TopLevelTypeName("System.Runtime.CompilerServices", "PreserveBaseOverridesAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", "PreserveBaseOverridesAttribute"),
// C# 11 attributes:
new TopLevelTypeName("System.Runtime.CompilerServices", "RequiredMemberAttribute"),
}; };
public static ref readonly TopLevelTypeName GetTypeName(this KnownAttribute attr) public static ref readonly TopLevelTypeName GetTypeName(this KnownAttribute attr)

9
ILSpy/Properties/Resources.Designer.cs generated

@ -1226,6 +1226,15 @@ namespace ICSharpCode.ILSpy.Properties {
} }
} }
/// <summary>
/// Looks up a localized string similar to Required members.
/// </summary>
public static string DecompilerSettings_RequiredMembers {
get {
return ResourceManager.GetString("DecompilerSettings.RequiredMembers", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Separate local variable declarations and initializers (int x = 5; -&gt; int x; x = 5;), if possible. /// Looks up a localized string similar to Separate local variable declarations and initializers (int x = 5; -&gt; int x; x = 5;), if possible.
/// </summary> /// </summary>

3
ILSpy/Properties/Resources.resx

@ -1054,4 +1054,7 @@ Do you want to continue?</value>
<data name="_Window" xml:space="preserve"> <data name="_Window" xml:space="preserve">
<value>_Window</value> <value>_Window</value>
</data> </data>
<data name="DecompilerSettings.RequiredMembers" xml:space="preserve">
<value>Required members</value>
</data>
</root> </root>
Loading…
Cancel
Save