diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs index d69c8f35a..a65d7223b 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs @@ -16,6 +16,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -307,8 +308,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty { } #endif - + public static void Issue3469a([Optional][DefaultParameterValue(0)] int i, [Optional] DateTime d) + { + } #if CS120 + public static Action Issue3469b() + { +#pragma warning disable CS9099 // Parameter 1 has default value 'default(int)' in lambda but '' in the target delegate type + return ([Optional][DefaultParameterValue(0)] int i, [Optional] DateTime d) => { + }; +#pragma warning restore CS9099 + } public static D LambdaWithOptionalParameter() { return (int x = 10) => x; diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index 3583c6a44..119185bc5 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1735,8 +1735,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax { decl.Name = parameter.Name; } - if (parameter.IsOptional && decl.ParameterModifier is ReferenceKind.None or ReferenceKind.In or ReferenceKind.RefReadOnly - && parameter.HasConstantValueInSignature && this.ShowConstantValues) + if (parameter.IsDefaultValueAssignmentAllowed() && this.ShowConstantValues) { try { diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs index 14bee08dc..8089b1eae 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs @@ -63,14 +63,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation var metadata = module.metadata; var parameter = metadata.GetParameter(handle); - bool defaultValueAssignmentAllowed = ReferenceKind is ReferenceKind.None or ReferenceKind.In or ReferenceKind.RefReadOnly; + bool defaultValueAssignmentAllowed = this.IsDefaultValueAssignmentAllowed(); - if (IsOptional && (!defaultValueAssignmentAllowed || !HasConstantValueInSignature)) + if (IsOptional && !defaultValueAssignmentAllowed) { b.Add(KnownAttribute.Optional); } - if (!(IsDecimalConstant || !HasConstantValueInSignature) && (!defaultValueAssignmentAllowed || !IsOptional)) + if (!IsDecimalConstant && HasConstantValueInSignature && !defaultValueAssignmentAllowed) { b.Add(KnownAttribute.DefaultParameterValue, KnownTypeCode.Object, GetConstantValue(throwOnInvalidMetadata: false)); } diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs index e26781cfb..d4009bb31 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs @@ -612,6 +612,45 @@ namespace ICSharpCode.Decompiler.TypeSystem } #endregion + #region IParameter.IsDefaultValueAssignmentAllowed + /// + /// Checks if the parameter is allowed to be assigned a default value. + /// + /// + /// This checks , , , + /// and on this parameter and all subsequent parameters. + /// If the parameter has no , it does not check subsequent parameters. + /// + /// The parameter + /// True if the has a default value and is allowed to be assigned a default value. + public static bool IsDefaultValueAssignmentAllowed(this IParameter parameter) + { + if (!DefaultValueAssignmentAllowedIndividual(parameter)) + return false; + + if (parameter.Owner == null) + return true; // Shouldn't happen, but we need to check for it. + + for (int i = parameter.Owner.Parameters.Count - 1; i >= 0; i--) + { + IParameter otherParameter = parameter.Owner.Parameters[i]; + if (otherParameter == parameter) + break; + + if (DefaultValueAssignmentAllowedIndividual(otherParameter) || otherParameter.IsParams) + continue; + + return false; + } + return true; + + static bool DefaultValueAssignmentAllowedIndividual(IParameter parameter) + { + return parameter.IsOptional && parameter.HasConstantValueInSignature && parameter.ReferenceKind is ReferenceKind.None or ReferenceKind.In or ReferenceKind.RefReadOnly; + } + } + #endregion + #region IAssembly.GetTypeDefinition(string,string,int) /// /// Gets the type definition for a top-level type.