diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index 8d403b8e2f..de36b9d724 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -7175,6 +7175,13 @@ namespace System.Management.Automation.Language { argExprs[i] = Expression.Default(parameterType); } + else if (!parameters[i].HasDefaultValue && parameterType != typeof(object) && argValue == Type.Missing) + { + // If the method contains just [Optional] without a default value set then we cannot use + // Type.Missing as a placeholder. Instead we use the default value for that type. Only + // exception to this rule is when the parameter type is object. + argExprs[i] = Expression.Default(parameterType); + } else { // We don't specify the parameter type in the constant expression. Normally the default diff --git a/test/powershell/engine/Basic/CLRBinding.Tests.ps1 b/test/powershell/engine/Basic/CLRBinding.Tests.ps1 new file mode 100644 index 0000000000..98a05d8518 --- /dev/null +++ b/test/powershell/engine/Basic/CLRBinding.Tests.ps1 @@ -0,0 +1,151 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +Describe ".NET Method Binding Tests" -tags CI { + BeforeAll { + Add-Type -TypeDefinition @' +using System; +using System.Runtime.InteropServices; + +namespace CLRBindingTests; + +public class TestClass +{ + public int Prop { get; } + + public TestClass(int value = 1) + { + Prop = value; + } + + public static string StaticWithDefaultExpected() => StaticWithDefault(); + public static string StaticWithDefault(string value = "foo") => value; + + public static string StaticWithOptionalAndValueExpected() => StaticWithOptionalAndValue(); + public static string StaticWithOptionalAndValue([Optional, DefaultParameterValue("bar")] string value) => value; + + public static string StaticWithOptionalExpected() => StaticWithOptional(); + public static string StaticWithOptional([Optional] string value) => value; + + public object InstanceWithDefaultExpected() => InstanceWithDefault(); + public object InstanceWithDefault(object value = null) => value; + + public object InstanceWithOptionalAndValueExpected() => InstanceWithOptionalAndValue(); + public object InstanceWithOptionalAndValue([Optional, DefaultParameterValue("foo")] object value) => value; + + public object InstanceWithOptionalExpected() => InstanceWithOptional(); + public object InstanceWithOptional([Optional] object value) => value; + + public string MultipleArgsWithDefaultExpected(string prefix) => MultipleArgsWithDefault(prefix); + public string MultipleArgsWithDefault(string prefix, string extra = "abc") => $"{prefix}{extra}"; + + public string MultipleArgsWithOptionalAndValueExpected(string prefix) => MultipleArgsWithOptionalAndValue(prefix); + public string MultipleArgsWithOptionalAndValue(string prefix, [Optional, DefaultParameterValue("def")] string extra) => $"{prefix}{extra}"; + + public string MultipleArgsWithOptionalExpected(string prefix) => MultipleArgsWithOptional(prefix); + public string MultipleArgsWithOptional(string prefix, [Optional] string extra) => $"{prefix}{extra}"; +} + +public class TestClassCstorWithOptionalAndValue +{ + public int Prop { get; } + + public TestClassCstorWithOptionalAndValue([Optional, DefaultParameterValue(2)] int value) + { + Prop = value; + } +} + +public class TestClassCstorWithOptional +{ + public int Prop { get; } + + public TestClassCstorWithOptional([Optional] int value) + { + Prop = value; + } +} +'@ + } + + It "Binds to constructor with default argument" { + $c = [CLRBindingTests.TestClass]::new() + $c.Prop | Should -Be 1 + } + + It "Binds to constructor with Optional with DefaultValue argument" { + $c = [CLRBindingTests.TestClassCstorWithOptionalAndValue]::new() + $c.Prop | Should -Be 2 + } + + It "Binds to constructor with Optional argument" { + $c = [CLRBindingTests.TestClassCstorWithOptional]::new() + $c.Prop | Should -Be 0 + } + + It "Binds to static method with default argument" { + $expected = [CLRBindingTests.TestClass]::StaticWithDefaultExpected() + $actual = [CLRBindingTests.TestClass]::StaticWithDefault() + $actual | Should -Be $expected + } + + It "Binds to static method with Optional with DefaultValue argument" { + $expected = [CLRBindingTests.TestClass]::StaticWithOptionalAndValueExpected() + $actual = [CLRBindingTests.TestClass]::StaticWithOptionalAndValue() + $actual | Should -Be $expected + } + + It "Binds to static method with Optional argument" { + $expected = [CLRBindingTests.TestClass]::StaticWithOptionalExpected() + $actual = [CLRBindingTests.TestClass]::StaticWithOptional() + $actual | Should -Be $expected + } + + It "Binds to instance method with default argument" { + $c = [CLRBindingTests.TestClass]::new() + + $expected = $c.InstanceWithDefaultExpected() + $actual = $c.InstanceWithDefault() + $actual | Should -Be $expected + } + + It "Binds to instance method with Optional with DefaultValue argument" { + $c = [CLRBindingTests.TestClass]::new() + + $expected = $c.InstanceWithOptionalAndValueExpected() + $actual = $c.InstanceWithOptionalAndValue() + $actual | Should -Be $expected + } + + It "Binds to instance method with Optional argument" { + $c = [CLRBindingTests.TestClass]::new() + + $expected = $c.InstanceWithOptionalExpected() + $actual = $c.InstanceWithOptional() + $actual | Should -Be $expected + } + + It "Binds to instance method with normal arg and default argument" { + $c = [CLRBindingTests.TestClass]::new() + + $expected = $c.MultipleArgsWithDefaultExpected("prefix") + $actual = $c.MultipleArgsWithDefault("prefix") + $actual | Should -Be $expected + } + + It "Binds to instance method with Optional with normal arg and DefaultValue argument" { + $c = [CLRBindingTests.TestClass]::new() + + $expected = $c.MultipleArgsWithOptionalAndValueExpected("prefix") + $actual = $c.MultipleArgsWithOptionalAndValue("prefix") + $actual | Should -Be $expected + } + + It "Binds to instance method with normal arg and Optional argument" { + $c = [CLRBindingTests.TestClass]::new() + + $expected = $c.MultipleArgsWithOptionalExpected("prefix") + $actual = $c.MultipleArgsWithOptional("prefix") + $actual | Should -Be $expected + } +}