Skip to content

Commit c1ecf1a

Browse files
Handle explicit optional parameter after default parameter (#3470)
* Handle explicit optional parameter after default parameter * Rename and add documentation * Move extension method * Add unit test for lamba function * Apply suggestions from code review Co-authored-by: Siegfried Pammer <[email protected]> --------- Co-authored-by: Siegfried Pammer <[email protected]>
1 parent 0261ca3 commit c1ecf1a

File tree

4 files changed

+54
-6
lines changed

4 files changed

+54
-6
lines changed

ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1717
// DEALINGS IN THE SOFTWARE.
1818

19+
using System;
1920
using System.Collections.Generic;
2021
using System.Runtime.CompilerServices;
2122
using System.Runtime.InteropServices;
@@ -307,8 +308,17 @@ public static void Issue2920p(in int x = 3)
307308
{
308309
}
309310
#endif
310-
311+
public static void Issue3469a([Optional][DefaultParameterValue(0)] int i, [Optional] DateTime d)
312+
{
313+
}
311314
#if CS120
315+
public static Action<int, DateTime> Issue3469b()
316+
{
317+
#pragma warning disable CS9099 // Parameter 1 has default value 'default(int)' in lambda but '<missing>' in the target delegate type
318+
return ([Optional][DefaultParameterValue(0)] int i, [Optional] DateTime d) => {
319+
};
320+
#pragma warning restore CS9099
321+
}
312322
public static D LambdaWithOptionalParameter()
313323
{
314324
return (int x = 10) => x;

ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,8 +1735,7 @@ public ParameterDeclaration ConvertParameter(IParameter parameter)
17351735
{
17361736
decl.Name = parameter.Name;
17371737
}
1738-
if (parameter.IsOptional && decl.ParameterModifier is ReferenceKind.None or ReferenceKind.In or ReferenceKind.RefReadOnly
1739-
&& parameter.HasConstantValueInSignature && this.ShowConstantValues)
1738+
if (parameter.IsDefaultValueAssignmentAllowed() && this.ShowConstantValues)
17401739
{
17411740
try
17421741
{

ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,14 @@ public IEnumerable<IAttribute> GetAttributes()
6363
var metadata = module.metadata;
6464
var parameter = metadata.GetParameter(handle);
6565

66-
bool defaultValueAssignmentAllowed = ReferenceKind is ReferenceKind.None or ReferenceKind.In or ReferenceKind.RefReadOnly;
66+
bool defaultValueAssignmentAllowed = this.IsDefaultValueAssignmentAllowed();
6767

68-
if (IsOptional && (!defaultValueAssignmentAllowed || !HasConstantValueInSignature))
68+
if (IsOptional && !defaultValueAssignmentAllowed)
6969
{
7070
b.Add(KnownAttribute.Optional);
7171
}
7272

73-
if (!(IsDecimalConstant || !HasConstantValueInSignature) && (!defaultValueAssignmentAllowed || !IsOptional))
73+
if (!IsDecimalConstant && HasConstantValueInSignature && !defaultValueAssignmentAllowed)
7474
{
7575
b.Add(KnownAttribute.DefaultParameterValue, KnownTypeCode.Object, GetConstantValue(throwOnInvalidMetadata: false));
7676
}

ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,45 @@ public static IAttribute GetAttribute(this IParameter parameter, KnownAttribute
612612
}
613613
#endregion
614614

615+
#region IParameter.IsDefaultValueAssignmentAllowed
616+
/// <summary>
617+
/// Checks if the parameter is allowed to be assigned a default value.
618+
/// </summary>
619+
/// <remarks>
620+
/// This checks <see cref="IParameter.IsOptional"/>, <see cref="IParameter.HasConstantValueInSignature"/>, <see cref="IParameter.ReferenceKind"/>,
621+
/// and <see cref="IParameter.IsParams"/> on this parameter and all subsequent parameters.
622+
/// If the parameter has no <see cref="IParameter.Owner"/>, it does not check subsequent parameters.
623+
/// </remarks>
624+
/// <param name="parameter">The parameter</param>
625+
/// <returns>True if the <paramref name="parameter"/> has a default value and is allowed to be assigned a default value.</returns>
626+
public static bool IsDefaultValueAssignmentAllowed(this IParameter parameter)
627+
{
628+
if (!DefaultValueAssignmentAllowedIndividual(parameter))
629+
return false;
630+
631+
if (parameter.Owner == null)
632+
return true; // Shouldn't happen, but we need to check for it.
633+
634+
for (int i = parameter.Owner.Parameters.Count - 1; i >= 0; i--)
635+
{
636+
IParameter otherParameter = parameter.Owner.Parameters[i];
637+
if (otherParameter == parameter)
638+
break;
639+
640+
if (DefaultValueAssignmentAllowedIndividual(otherParameter) || otherParameter.IsParams)
641+
continue;
642+
643+
return false;
644+
}
645+
return true;
646+
647+
static bool DefaultValueAssignmentAllowedIndividual(IParameter parameter)
648+
{
649+
return parameter.IsOptional && parameter.HasConstantValueInSignature && parameter.ReferenceKind is ReferenceKind.None or ReferenceKind.In or ReferenceKind.RefReadOnly;
650+
}
651+
}
652+
#endregion
653+
615654
#region IAssembly.GetTypeDefinition(string,string,int)
616655
/// <summary>
617656
/// Gets the type definition for a top-level type.

0 commit comments

Comments
 (0)