Browse Source

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 <siegfried@pammer.io>

---------

Co-authored-by: Siegfried Pammer <siegfried@pammer.io>
pull/3481/head
Jeremy Pritts 2 months ago committed by GitHub
parent
commit
c1ecf1ae9c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 12
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/OptionalArguments.cs
  2. 3
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  3. 6
      ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs
  4. 39
      ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

12
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 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -307,8 +308,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
} }
#endif #endif
public static void Issue3469a([Optional][DefaultParameterValue(0)] int i, [Optional] DateTime d)
{
}
#if CS120 #if CS120
public static Action<int, DateTime> Issue3469b()
{
#pragma warning disable CS9099 // Parameter 1 has default value 'default(int)' in lambda but '<missing>' in the target delegate type
return ([Optional][DefaultParameterValue(0)] int i, [Optional] DateTime d) => {
};
#pragma warning restore CS9099
}
public static D LambdaWithOptionalParameter() public static D LambdaWithOptionalParameter()
{ {
return (int x = 10) => x; return (int x = 10) => x;

3
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -1735,8 +1735,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
{ {
decl.Name = parameter.Name; decl.Name = parameter.Name;
} }
if (parameter.IsOptional && decl.ParameterModifier is ReferenceKind.None or ReferenceKind.In or ReferenceKind.RefReadOnly if (parameter.IsDefaultValueAssignmentAllowed() && this.ShowConstantValues)
&& parameter.HasConstantValueInSignature && this.ShowConstantValues)
{ {
try try
{ {

6
ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataParameter.cs

@ -63,14 +63,14 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation
var metadata = module.metadata; var metadata = module.metadata;
var parameter = metadata.GetParameter(handle); 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); b.Add(KnownAttribute.Optional);
} }
if (!(IsDecimalConstant || !HasConstantValueInSignature) && (!defaultValueAssignmentAllowed || !IsOptional)) if (!IsDecimalConstant && HasConstantValueInSignature && !defaultValueAssignmentAllowed)
{ {
b.Add(KnownAttribute.DefaultParameterValue, KnownTypeCode.Object, GetConstantValue(throwOnInvalidMetadata: false)); b.Add(KnownAttribute.DefaultParameterValue, KnownTypeCode.Object, GetConstantValue(throwOnInvalidMetadata: false));
} }

39
ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

@ -612,6 +612,45 @@ namespace ICSharpCode.Decompiler.TypeSystem
} }
#endregion #endregion
#region IParameter.IsDefaultValueAssignmentAllowed
/// <summary>
/// Checks if the parameter is allowed to be assigned a default value.
/// </summary>
/// <remarks>
/// This checks <see cref="IParameter.IsOptional"/>, <see cref="IParameter.HasConstantValueInSignature"/>, <see cref="IParameter.ReferenceKind"/>,
/// and <see cref="IParameter.IsParams"/> on this parameter and all subsequent parameters.
/// If the parameter has no <see cref="IParameter.Owner"/>, it does not check subsequent parameters.
/// </remarks>
/// <param name="parameter">The parameter</param>
/// <returns>True if the <paramref name="parameter"/> has a default value and is allowed to be assigned a default value.</returns>
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) #region IAssembly.GetTypeDefinition(string,string,int)
/// <summary> /// <summary>
/// Gets the type definition for a top-level type. /// Gets the type definition for a top-level type.

Loading…
Cancel
Save