diff --git a/src/System.Management.Automation/engine/LanguagePrimitives.cs b/src/System.Management.Automation/engine/LanguagePrimitives.cs index 30b7dd26660..eb44031531c 100644 --- a/src/System.Management.Automation/engine/LanguagePrimitives.cs +++ b/src/System.Management.Automation/engine/LanguagePrimitives.cs @@ -3581,20 +3581,20 @@ internal object Convert(object valueToConvert, TypeTable backupTable) { IList resultAsList = null; - int listSize = 0; Array array = null; try { - // typeof(Array).IsAssignableFrom(typeof(object[])) == true - if ((IsScalar) || (!(valueToConvert is Array))) + int listSize = 0; + if (IsScalar) { listSize = 1; } else { - array = (Array)valueToConvert; - listSize = array.Length; + // typeof(Array).IsAssignableFrom(typeof(object[])) == true + array = valueToConvert as Array; + listSize = array != null ? array.Length : 1; } resultAsList = ListCtorLambda(listSize); @@ -3610,7 +3610,7 @@ internal object Convert(object valueToConvert, { resultAsList.Add(valueToConvert); } - else if (listSize == 1) + else if (array == null) { object convertedValue = LanguagePrimitives.ConvertTo(valueToConvert, ElementType, formatProvider); resultAsList.Add(convertedValue); diff --git a/test/powershell/Language/Parser/Conversions.Tests.ps1 b/test/powershell/Language/Parser/Conversions.Tests.ps1 index 37efea9cfad..6aca007f90a 100644 --- a/test/powershell/Language/Parser/Conversions.Tests.ps1 +++ b/test/powershell/Language/Parser/Conversions.Tests.ps1 @@ -20,4 +20,56 @@ Describe 'conversion syntax' -Tags "CI" { # This test relies on the fact that there are overloads (at least 2) for ToString method. ([System.Management.Automation.ActionPreference]"Stop").ToString() | Should Be "Stop" } + + Context "Cast object[] to more narrow generic collection" { + BeforeAll { + $testCases1 = @( + ## It's intentional to have 'Command' to be `{$result = ...}` and run it with `. $Command`. + ## This is because `$result = & {[List[int]]@(1,2)}` will cause the resulted List to be unraveled, + ## and in that case `$result` would be just an object array. + ## To prevent unraveling, Command needs to be `{, [List[int]]@(1,2)}`, but then the test case title + ## would become `, [List[int]]@(1,2)`, which is more confusing than `$result = [List[int]]@(1,2)`. + ## This is why the current form of `$result = [List[int]]@(1,2)` is used intentionally here. + + @{ Command = {$result = [Collections.Generic.List[int]]@(1)}; CollectionType = 'List`1'; ElementType = "Int32"; Elements = @(1) } + @{ Command = {$result = [Collections.Generic.List[int]]@(1,2)}; CollectionType = 'List`1'; ElementType = "Int32"; Elements = @(1,2) } + @{ Command = {$result = [Collections.Generic.List[int]]"4"}; CollectionType = 'List`1'; ElementType = "Int32"; Elements = @(4) } + @{ Command = {$result = [Collections.Generic.List[int]]@("4","5")}; CollectionType = 'List`1'; ElementType = "Int32"; Elements = @(4,5) } + + @{ Command = {$result = [Collections.Generic.List[string]]@(1)}; CollectionType = 'List`1'; ElementType = "String"; Elements = @("1") } + @{ Command = {$result = [Collections.Generic.List[string]]@(1,2)}; CollectionType = 'List`1'; ElementType = "String"; Elements = @("1","2") } + @{ Command = {$result = [Collections.Generic.List[string]]1}; CollectionType = 'List`1'; ElementType = "String"; Elements = @("1") } + @{ Command = {$result = [Collections.Generic.List[string]]@("4")}; CollectionType = 'List`1'; ElementType = "String"; Elements = @("4") } + + @{ Command = {$result = [System.Collections.ObjectModel.Collection[int]]@(1)}; CollectionType = 'Collection`1'; ElementType = "Int32"; Elements = @(1) } + @{ Command = {$result = [System.Collections.ObjectModel.Collection[int]]@(1,2)}; CollectionType = 'Collection`1'; ElementType = "Int32"; Elements = @(1,2) } + @{ Command = {$result = [System.Collections.ObjectModel.Collection[int]]"4"}; CollectionType = 'Collection`1'; ElementType = "Int32"; Elements = @(4) } + @{ Command = {$result = [System.Collections.ObjectModel.Collection[int]]@("4","5")}; CollectionType = 'Collection`1'; ElementType = "Int32"; Elements = @(4,5) } + + @{ Command = {$result = [Collections.Generic.List[System.IO.FileInfo]]@('TestFile')}; + CollectionType = 'List`1'; ElementType = "FileInfo"; Elements = @('TestFile') } + @{ Command = {$result = [Collections.Generic.List[System.IO.FileInfo]]@('TestFile1', 'TestFile2')}; + CollectionType = 'List`1'; ElementType = "FileInfo"; Elements = @('TestFile1', 'TestFile2') } + @{ Command = {$result = [Collections.Generic.List[System.IO.FileInfo]]'TestFile'}; + CollectionType = 'List`1'; ElementType = "FileInfo"; Elements = @('TestFile') } + ) + } + + It "" -TestCases $testCases1 { + param($Command, $CollectionType, $ElementType, $Elements) + + $result = $null + . $Command + + $result | Should Not BeNullOrEmpty + $result.GetType().Name | Should Be $CollectionType + + $genericArgs = $result.GetType().GetGenericArguments() + $genericArgs.Length | Should Be 1 + $genericArgs[0].Name | Should Be $ElementType + + $result.Count | Should Be $Elements.Length + $result -join ";" | Should Be ($Elements -join ";") + } + } }