From 060c90a0248a5392a7784572b08394aca11c48fe Mon Sep 17 00:00:00 2001 From: MartinGC94 Date: Sun, 4 Feb 2024 18:33:40 +0100 Subject: [PATCH] Fall back to type inference when hashtable key value cannot be retrieved from safe expression --- .../engine/parser/TypeInferenceVisitor.cs | 37 ++++++++++--------- .../engine/Api/TypeInference.Tests.ps1 | 12 ++++++ 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs b/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs index 1e1ff771ac5..be864a8a814 100644 --- a/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs +++ b/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs @@ -584,6 +584,20 @@ object ICustomAstVisitor.VisitHashtable(HashtableAst hashtableAst) if (hashtableAst.KeyValuePairs.Count > 0) { var properties = new List(); + void AddInferredTypes(Ast ast, string keyName) + { + bool foundAnyTypes = false; + foreach (PSTypeName item in InferTypes(ast)) + { + foundAnyTypes = true; + properties.Add(new PSMemberNameAndType(keyName, item)); + } + + if (!foundAnyTypes) + { + properties.Add(new PSMemberNameAndType(keyName, new PSTypeName("System.Object"))); + } + } foreach (var kv in hashtableAst.KeyValuePairs) { @@ -615,31 +629,18 @@ object ICustomAstVisitor.VisitHashtable(HashtableAst hashtableAst) _ = SafeExprEvaluator.TrySafeEval(expression, _context.ExecutionContext, out value); } - PSTypeName valueType; if (value is null) { - valueType = new PSTypeName("System.Object"); - } - else - { - valueType = new PSTypeName(value.GetType()); + AddInferredTypes(expression, name); + continue; } + PSTypeName valueType = new(value.GetType()); properties.Add(new PSMemberNameAndType(name, valueType, value)); } else { - bool foundAnyTypes = false; - foreach (var item in InferTypes(kv.Item2)) - { - foundAnyTypes = true; - properties.Add(new PSMemberNameAndType(name, item)); - } - - if (!foundAnyTypes) - { - properties.Add(new PSMemberNameAndType(name, new PSTypeName("System.Object"))); - } + AddInferredTypes(kv.Item2, name); } } } @@ -1639,7 +1640,7 @@ private IEnumerable InferTypesFrom(MemberExpressionAst memberExpress var memberNameList = new List { memberAsStringConst.Value }; foreach (var type in exprType) { - if (type.Type == typeof(PSObject)) + if (type.Type == typeof(PSObject) && type is not PSSyntheticTypeName) { continue; } diff --git a/test/powershell/engine/Api/TypeInference.Tests.ps1 b/test/powershell/engine/Api/TypeInference.Tests.ps1 index 69817f6290d..7676ecf3197 100644 --- a/test/powershell/engine/Api/TypeInference.Tests.ps1 +++ b/test/powershell/engine/Api/TypeInference.Tests.ps1 @@ -1379,12 +1379,24 @@ Describe "Type inference Tests" -tags "CI" { $res.Name -join ' ' | Should -Be "System.IO.FileInfo System.IO.DirectoryInfo" } + It 'Falls back to type inference for hashtable assignments with pure expression with no value' { + $res = [AstTypeInference]::InferTypeOf( {$KeyWithNoValue = Get-ChildItem $HOME; (@{RandomKey = $KeyWithNoValue}).RandomKey }.Ast) + $Res.Count | Should -Be 2 + $res.Name -join ' ' | Should -Be "System.IO.FileInfo System.IO.DirectoryInfo" + } + It 'Infers type of index expression on hashtable with synthetic type' { $res = [AstTypeInference]::InferTypeOf( { (@{RandomKey = Get-ChildItem $HOME})['RandomKey'] }.Ast) $res.Count | Should -Be 2 $res.Name -join ' ' | Should -Be "System.IO.FileInfo System.IO.DirectoryInfo" } + It 'Infers type of member expression on a custom object' { + $res = [AstTypeInference]::InferTypeOf( { ([pscustomobject]@{RandomProp1 = Get-ChildItem $HOME}).RandomProp1 }.Ast) + $res.Count | Should -Be 2 + $res.Name -join ' ' | Should -Be "System.IO.FileInfo System.IO.DirectoryInfo" + } + It 'Infers closest variable type' { $res = [AstTypeInference]::InferTypeOf( { [string]$TestVar = "";[hashtable]$TestVar = @{};$TestVar }.Ast) $res.Name | Select-Object -Last 1 | Should -Be "System.Collections.Hashtable"