diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs index 356aba2ba..d8ea97ce4 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs @@ -1,4 +1,6 @@ -namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty +using System; + +namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { public record Base(string A); @@ -37,6 +39,7 @@ public record PairWithPrimaryCtor(A First, B Second); public record PrimaryCtor(int A, string B); + public record PrimaryCtorWithAttribute([RecordTest("param")] [property: RecordTest("property")][field: RecordTest("field")] int a); public record PrimaryCtorWithField(int A, string B) { public double C = 1.0; @@ -63,6 +66,14 @@ } } + [AttributeUsage(AttributeTargets.All)] + public class RecordTestAttribute : Attribute + { + public RecordTestAttribute(string name) + { + } + } + public sealed record Sealed(string A); public sealed record SealedDerived(int B) : Base(B.ToString()); diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 676c924d4..dd0e8ffa4 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -1225,7 +1225,31 @@ namespace ICSharpCode.Decompiler.CSharp if (recordDecompiler?.PrimaryConstructor != null) { foreach (var p in recordDecompiler.PrimaryConstructor.Parameters) - typeDecl.PrimaryConstructorParameters.Add(typeSystemAstBuilder.ConvertParameter(p)); + { + ParameterDeclaration pd = typeSystemAstBuilder.ConvertParameter(p); + (IProperty prop, IField field) = recordDecompiler.GetPropertyInfoByPrimaryConstructorParameter(p); + Syntax.Attribute[] attributes = prop.GetAttributes().Select(attr => typeSystemAstBuilder.ConvertAttribute(attr)).ToArray(); + if (attributes.Length > 0) + { + var section = new AttributeSection { + AttributeTarget = "property" + }; + section.Attributes.AddRange(attributes); + pd.Attributes.Add(section); + } + attributes = field.GetAttributes() + .Where(a => !PatternStatementTransform.attributeTypesToRemoveFromAutoProperties.Contains(a.AttributeType.FullName)) + .Select(attr => typeSystemAstBuilder.ConvertAttribute(attr)).ToArray(); + if (attributes.Length > 0) + { + var section = new AttributeSection { + AttributeTarget = "field" + }; + section.Attributes.AddRange(attributes); + pd.Attributes.Add(section); + } + typeDecl.PrimaryConstructorParameters.Add(pd); + } } foreach (var type in typeDef.NestedTypes) diff --git a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs index 37b02d8eb..5ef219d4a 100644 --- a/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs @@ -168,7 +168,7 @@ namespace ICSharpCode.Decompiler.CSharp if (method.IsStatic || !method.IsConstructor) continue; var m = method.Specialize(subst); - if (IsPrimaryConstructor(m)) + if (IsPrimaryConstructor(m, method)) return method; primaryCtorParameterToAutoProperty.Clear(); autoPropertyToPrimaryCtorParameter.Clear(); @@ -176,7 +176,7 @@ namespace ICSharpCode.Decompiler.CSharp return null; - bool IsPrimaryConstructor(IMethod method) + bool IsPrimaryConstructor(IMethod method, IMethod unspecializedMethod) { Debug.Assert(method.IsConstructor); var body = DecompileBody(method); @@ -202,8 +202,8 @@ namespace ICSharpCode.Decompiler.CSharp return false; if (!backingFieldToAutoProperty.TryGetValue(field, out var property)) return false; - primaryCtorParameterToAutoProperty.Add(method.Parameters[i], property); - autoPropertyToPrimaryCtorParameter.Add(property, method.Parameters[i]); + primaryCtorParameterToAutoProperty.Add(unspecializedMethod.Parameters[i], property); + autoPropertyToPrimaryCtorParameter.Add(property, unspecializedMethod.Parameters[i]); } if (!isStruct) @@ -331,6 +331,12 @@ namespace ICSharpCode.Decompiler.CSharp && autoPropertyToPrimaryCtorParameter.ContainsKey((IProperty)property.Specialize(subst)); } + internal (IProperty prop, IField field) GetPropertyInfoByPrimaryConstructorParameter(IParameter parameter) + { + var prop = primaryCtorParameterToAutoProperty[parameter]; + return (prop, autoPropertyToBackingField[prop]); + } + public bool IsCopyConstructor(IMethod method) { if (method == null) diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index e61bbe41c..aaa032971 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -936,7 +936,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms "System.Runtime.CompilerServices.MethodImplAttribute" }; - static readonly string[] attributeTypesToRemoveFromAutoProperties = new[] { + internal static readonly string[] attributeTypesToRemoveFromAutoProperties = new[] { "System.Runtime.CompilerServices.CompilerGeneratedAttribute", "System.Diagnostics.DebuggerBrowsableAttribute" };