Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/System.Management.Automation/engine/LanguagePrimitives.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
52 changes: 52 additions & 0 deletions test/powershell/Language/Parser/Conversions.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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 "<Command>" -TestCases $testCases1 {
param($Command, $CollectionType, $ElementType, $Elements)

$result = $null
. $Command
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be less surprising to me if Command was just a script block and here you wrote:

$result = . $Command

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The resulted collection will be unraveled if I use $result = . {[Collections.Generic.List[int]]@(1)}.

In order to prevent unraveling, a comma needs to be prefixed like "{,[Collections.Generic.List[int]]@(1)}".
But then the test case title becomes ",[Collections.Generic.List[int]]@(1)" which I thought was more confusing than "$result = [Collections.Generic.List[int]]@(1)" and that's why I choose the current form.
Do you want me to change back to {, [Collections.Generic.List[int]]@(1)}?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see - neither is great - I'm fine either way then.
Because you tried what I suggested first - maybe it's worth a comment to save someone else the trouble trying to change it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Will add a comment.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment added.


$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 ";")
}
}
}