diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs
index 934d4a6fe..62486e6e0 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs
@@ -129,7 +129,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
-#if ROSLYN4
+
+#if CS100
internal class RecordStructs
{
public record struct Base(string A);
@@ -230,6 +231,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
}
+#endif
+#if CS110
+
#endif
}
namespace System.Runtime.CompilerServices
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Structs.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Structs.cs
index fd5dedfda..c796990eb 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Structs.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Structs.cs
@@ -51,4 +51,28 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
}
#endif
-}
\ No newline at end of file
+
+#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
+ {
+ }
+}
diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
index 3371ce180..087b509c0 100644
--- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
+++ b/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)
{
switch (decompileRun.EnumValueDisplayMode)
@@ -1871,6 +1875,10 @@ namespace ICSharpCode.Decompiler.CSharp
typeSystemAstBuilder.UseSpecialConstants = !(field.DeclaringType.Equals(field.ReturnType) || isMathPIOrE);
var fieldDecl = typeSystemAstBuilder.ConvertEntity(field);
SetNewModifier(fieldDecl);
+ if (settings.RequiredMembers && RemoveAttribute(fieldDecl, KnownAttribute.RequiredAttribute))
+ {
+ fieldDecl.Modifiers |= Modifiers.Required;
+ }
if (settings.FixedBuffers && IsFixedField(field, out var elementType, out var elementCount))
{
var fixedFieldDecl = new FixedFieldDeclaration();
@@ -1983,6 +1991,10 @@ namespace ICSharpCode.Decompiler.CSharp
propertyDecl.Modifiers &= ~(Modifiers.New | Modifiers.Virtual);
propertyDecl.Modifiers |= Modifiers.Override;
}
+ if (settings.RequiredMembers && RemoveAttribute(propertyDecl, KnownAttribute.RequiredAttribute))
+ {
+ propertyDecl.Modifiers |= Modifiers.Required;
+ }
return propertyDecl;
}
catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException))
diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/CSharpModifierToken.cs b/ICSharpCode.Decompiler/CSharp/Syntax/CSharpModifierToken.cs
index b81a7d76b..bb0f98198 100644
--- a/ICSharpCode.Decompiler/CSharp/Syntax/CSharpModifierToken.cs
+++ b/ICSharpCode.Decompiler/CSharp/Syntax/CSharpModifierToken.cs
@@ -67,7 +67,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
Modifiers.New,
Modifiers.Unsafe,
Modifiers.Abstract, Modifiers.Virtual, Modifiers.Sealed, Modifiers.Static, Modifiers.Override,
- Modifiers.Readonly, Modifiers.Volatile,
+ Modifiers.Required, Modifiers.Readonly, Modifiers.Volatile,
Modifiers.Ref,
Modifiers.Extern, Modifiers.Partial, Modifiers.Const,
Modifiers.Async,
@@ -119,6 +119,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return "async";
case Modifiers.Ref:
return "ref";
+ case Modifiers.Required:
+ return "required";
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";
@@ -129,50 +131,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
public static int GetModifierLength(Modifiers modifier)
{
- switch (modifier)
- {
- 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");
- }
+ return GetModifierName(modifier).Length;
}
public static Modifiers GetModifierValue(string modifier)
@@ -215,6 +174,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return Modifiers.Async;
case "ref":
return Modifiers.Ref;
+ case "required":
+ return Modifiers.Required;
case "any":
// 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;
diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Modifiers.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Modifiers.cs
index 98464b09c..43804b1cb 100644
--- a/ICSharpCode.Decompiler/CSharp/Syntax/Modifiers.cs
+++ b/ICSharpCode.Decompiler/CSharp/Syntax/Modifiers.cs
@@ -55,6 +55,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
Unsafe = 0x8000,
Async = 0x10000,
Ref = 0x20000,
+ Required = 0x40000,
VisibilityMask = Private | Internal | Protected | Public,
diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs
index d00f198f0..4ce0c44c0 100644
--- a/ICSharpCode.Decompiler/DecompilerSettings.cs
+++ b/ICSharpCode.Decompiler/DecompilerSettings.cs
@@ -151,12 +151,13 @@ namespace ICSharpCode.Decompiler
{
parameterNullCheck = false;
lifetimeAnnotations = false;
+ requiredMembers = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
- if (parameterNullCheck || lifetimeAnnotations)
+ if (parameterNullCheck || lifetimeAnnotations || requiredMembers)
return CSharp.LanguageVersion.CSharp11_0;
if (fileScopedNamespaces || recordStructs)
return CSharp.LanguageVersion.CSharp10_0;
@@ -356,6 +357,24 @@ namespace ICSharpCode.Decompiler
}
}
+ bool requiredMembers = true;
+
+ ///
+ /// Use C# 11 required modifier.
+ ///
+ [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;
///
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
index f0263c370..aa289b498 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
@@ -106,11 +106,14 @@ namespace ICSharpCode.Decompiler.TypeSystem
// C# 9 attributes:
NativeInteger,
PreserveBaseOverrides,
+
+ // C# 11 attributes:
+ RequiredAttribute,
}
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]{
default,
@@ -175,6 +178,8 @@ namespace ICSharpCode.Decompiler.TypeSystem
// C# 9 attributes:
new TopLevelTypeName("System.Runtime.CompilerServices", "NativeIntegerAttribute"),
new TopLevelTypeName("System.Runtime.CompilerServices", "PreserveBaseOverridesAttribute"),
+ // C# 11 attributes:
+ new TopLevelTypeName("System.Runtime.CompilerServices", "RequiredMemberAttribute"),
};
public static ref readonly TopLevelTypeName GetTypeName(this KnownAttribute attr)
diff --git a/ILSpy/Properties/Resources.Designer.cs b/ILSpy/Properties/Resources.Designer.cs
index a90649709..60114829a 100644
--- a/ILSpy/Properties/Resources.Designer.cs
+++ b/ILSpy/Properties/Resources.Designer.cs
@@ -1226,6 +1226,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
+ ///
+ /// Looks up a localized string similar to Required members.
+ ///
+ public static string DecompilerSettings_RequiredMembers {
+ get {
+ return ResourceManager.GetString("DecompilerSettings.RequiredMembers", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Separate local variable declarations and initializers (int x = 5; -> int x; x = 5;), if possible.
///
diff --git a/ILSpy/Properties/Resources.resx b/ILSpy/Properties/Resources.resx
index a3b47c1e2..d29a1c274 100644
--- a/ILSpy/Properties/Resources.resx
+++ b/ILSpy/Properties/Resources.resx
@@ -1054,4 +1054,7 @@ Do you want to continue?
_Window
+
+ Required members
+
\ No newline at end of file