From 0c96975d1ef3d27fa1e64b8c4b4676be7e5614fb Mon Sep 17 00:00:00 2001 From: MartinGC94 <42123497+MartinGC94@users.noreply.github.com> Date: Wed, 26 Mar 2025 19:00:46 +0100 Subject: [PATCH] Exclude outvariable assignments within the same CommandAst when inferring variables (#25224) --- .../engine/parser/TypeInferenceVisitor.cs | 25 +++++++++++-------- .../engine/Api/TypeInference.Tests.ps1 | 6 +++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs b/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs index 9e26e4eff3..a7744ac641 100644 --- a/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs +++ b/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs @@ -2942,13 +2942,16 @@ namespace System.Management.Automation } } - private void SetLastAssignmentType(PSTypeName typeName, int assignmentOffset) + private void SetLastAssignmentType(PSTypeName typeName, IScriptExtent assignmentExtent) { - if (LastAssignmentOffset < assignmentOffset) + if (LastAssignmentOffset < assignmentExtent.StartOffset && !VariableTarget.Extent.IsWithin(assignmentExtent)) { + // If the variable we are inferring the value of is inside this assignment then the assignment is invalid + // For example: $x = 1..10; Get-Random 2>variable:x -InputObject ($x.) here the variable should be inferred based on the initial 1..10 assignment + // and not the error redirected variable. ClearAssignmentData(); LastAssignmentType = typeName; - LastAssignmentOffset = assignmentOffset; + LastAssignmentOffset = assignmentExtent.StartOffset; } } @@ -3109,17 +3112,17 @@ namespace System.Management.Automation { case "ErrorVariable": case "ev": - SetLastAssignmentType(new PSTypeName(typeof(List)), commandAst.Extent.StartOffset); + SetLastAssignmentType(new PSTypeName(typeof(List)), commandAst.Extent); break; case "WarningVariable": case "wv": - SetLastAssignmentType(new PSTypeName(typeof(List)), commandAst.Extent.StartOffset); + SetLastAssignmentType(new PSTypeName(typeof(List)), commandAst.Extent); break; case "InformationVariable": case "iv": - SetLastAssignmentType(new PSTypeName(typeof(List)), commandAst.Extent.StartOffset); + SetLastAssignmentType(new PSTypeName(typeof(List)), commandAst.Extent); break; case "OutVariable": @@ -3166,23 +3169,23 @@ namespace System.Management.Automation switch (fileRedirection.FromStream) { case RedirectionStream.Error: - SetLastAssignmentType(new PSTypeName(typeof(ErrorRecord)), commandAst.Extent.StartOffset); + SetLastAssignmentType(new PSTypeName(typeof(ErrorRecord)), commandAst.Extent); break; case RedirectionStream.Warning: - SetLastAssignmentType(new PSTypeName(typeof(WarningRecord)), commandAst.Extent.StartOffset); + SetLastAssignmentType(new PSTypeName(typeof(WarningRecord)), commandAst.Extent); break; case RedirectionStream.Verbose: - SetLastAssignmentType(new PSTypeName(typeof(VerboseRecord)), commandAst.Extent.StartOffset); + SetLastAssignmentType(new PSTypeName(typeof(VerboseRecord)), commandAst.Extent); break; case RedirectionStream.Debug: - SetLastAssignmentType(new PSTypeName(typeof(DebugRecord)), commandAst.Extent.StartOffset); + SetLastAssignmentType(new PSTypeName(typeof(DebugRecord)), commandAst.Extent); break; case RedirectionStream.Information: - SetLastAssignmentType(new PSTypeName(typeof(InformationRecord)), commandAst.Extent.StartOffset); + SetLastAssignmentType(new PSTypeName(typeof(InformationRecord)), commandAst.Extent); break; default: diff --git a/test/powershell/engine/Api/TypeInference.Tests.ps1 b/test/powershell/engine/Api/TypeInference.Tests.ps1 index 5719c5c09a..fc6bdae74b 100644 --- a/test/powershell/engine/Api/TypeInference.Tests.ps1 +++ b/test/powershell/engine/Api/TypeInference.Tests.ps1 @@ -1197,6 +1197,12 @@ Describe "Type inference Tests" -tags "CI" { $res.Name | Should -Be System.String } + It 'Ignores assignment when a variable is declared and used within the same commandAst' { + $variableAst = { Get-Random 2>variable:RandomError1 -InputObject ($RandomError1) }.Ast.FindAll({ param($a) $a -is [Language.VariableExpressionAst] }, $true) | select -Last 1 + $res = [AstTypeInference]::InferTypeOf($variableAst) + $res.Count | Should -Be 0 + } + It 'Ignores the last assignment when a variable is reused' { $variableAst = { $x = New-Guid; $x = $x.Where{$_} }.Ast.FindAll({ param($a) $a -is [Language.VariableExpressionAst] }, $true) | select -Last 1 $res = [AstTypeInference]::InferTypeOf($variableAst)