diff --git a/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs b/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs index 5f59494fc3..0f3f81adcd 100644 --- a/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs +++ b/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs @@ -850,9 +850,20 @@ namespace System.Management.Automation object ICustomAstVisitor.VisitNamedBlock(NamedBlockAst namedBlockAst) { var inferredTypes = new List(); - for (var index = 0; index < namedBlockAst.Statements.Count; index++) + for (int index = 0; index < namedBlockAst.Statements.Count; index++) { - var ast = namedBlockAst.Statements[index]; + StatementAst ast = namedBlockAst.Statements[index]; + if (ast is AssignmentStatementAst + || (ast is PipelineAst pipe && pipe.PipelineElements.Count == 1 && pipe.PipelineElements[0] is CommandExpressionAst cmd + && cmd.Redirections.Count == 0 && cmd.Expression is UnaryExpressionAst unary + && unary.TokenKind is TokenKind.PostfixPlusPlus or TokenKind.PlusPlus or TokenKind.PostfixMinusMinus or TokenKind.MinusMinus)) + { + // Assignments don't output anything to the named block unless they are wrapped in parentheses. + // When they are wrapped in parentheses, they are seen as PipelineAst. + // Increment/decrement operators like $i++ also don't output anything unless there's a redirection, or they are wrapped in parentheses. + continue; + } + inferredTypes.AddRange(InferTypes(ast)); } @@ -921,8 +932,19 @@ namespace System.Management.Automation object ICustomAstVisitor.VisitStatementBlock(StatementBlockAst statementBlockAst) { var inferredTypes = new List(); - foreach (var ast in statementBlockAst.Statements) + foreach (StatementAst ast in statementBlockAst.Statements) { + if (ast is AssignmentStatementAst + || (ast is PipelineAst pipe && pipe.PipelineElements.Count == 1 && pipe.PipelineElements[0] is CommandExpressionAst cmd + && cmd.Redirections.Count == 0 && cmd.Expression is UnaryExpressionAst unary + && unary.TokenKind is TokenKind.PostfixPlusPlus or TokenKind.PlusPlus or TokenKind.PostfixMinusMinus or TokenKind.MinusMinus)) + { + // Assignments don't output anything to the statement block unless they are wrapped in parentheses. + // When they are wrapped in parentheses, they are seen as PipelineAst. + // Increment operators like $i++ also don't output anything unless there's a redirection, or they are wrapped in parentheses. + continue; + } + inferredTypes.AddRange(InferTypes(ast)); } diff --git a/test/powershell/engine/Api/TypeInference.Tests.ps1 b/test/powershell/engine/Api/TypeInference.Tests.ps1 index 7676ecf319..1fe25de530 100644 --- a/test/powershell/engine/Api/TypeInference.Tests.ps1 +++ b/test/powershell/engine/Api/TypeInference.Tests.ps1 @@ -361,10 +361,8 @@ Describe "Type inference Tests" -tags "CI" { It "Infers type from foreach-object of integer" { $res = [AstTypeInference]::InferTypeOf( { [int[]] $i = 1..20; $i | ForEach-Object {$_ * 10} }.Ast) - $res.Count | Should -Be 2 - foreach ($r in $res) { - $r.Name -In 'System.Int32', 'System.Int32[]' | Should -BeTrue - } + $res.Count | Should -Be 1 + $res.Name | Should -Be 'System.Int32' } It "Infers type from generic new" { @@ -386,9 +384,9 @@ Describe "Type inference Tests" -tags "CI" { It "Infers type from foreach-object with begin/end" { $res = [AstTypeInference]::InferTypeOf( { [int[]] $i = 1..20; $i | ForEach-Object -Begin {"Hi"} {$_ * 10} -End {[int]} }.Ast) - $res.Count | Should -Be 4 + $res.Count | Should -Be 3 foreach ($r in $res) { - $r.Name -In 'System.Int32', 'System.Int32[]', 'System.String', 'System.Type' | Should -BeTrue + $r.Name -In 'System.Int32', 'System.String', 'System.Type' | Should -BeTrue } } @@ -618,16 +616,6 @@ Describe "Type inference Tests" -tags "CI" { $res.Name | Should -Be 'System.Int32' } - It 'Infers type from attributed expession' { - $res = [AstTypeInference]::InferTypeOf( { - [ValidateRange(1, 2)] - [int]$i = 1 - }.Ast) - - $res.Count | Should -Be 1 - $res.Name | Should -Be System.Int32 - } - It 'Infers type from if statement' { $res = [AstTypeInference]::InferTypeOf( { if ($true) { return 1} @@ -1399,7 +1387,8 @@ Describe "Type inference Tests" -tags "CI" { 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" + $res.Count | Should -Be 1 + $res.Name | Should -Be "System.Collections.Hashtable" } It 'Infers closest variable type and ignores unrelated param blocks' { @@ -1444,6 +1433,66 @@ Describe "Type inference Tests" -tags "CI" { ) $null = [AstTypeInference]::InferTypeOf($FoundAst) } + + It 'Should only consider assignments wrapped in parentheses to be a part of the output in a Named block' { + $res = [AstTypeInference]::InferTypeOf( { [string]$Assignment1 = "Hello"; ([int]$Assignment2 = 42) }.Ast) + $res.Count | Should -Be 1 + $res.Name | Should -Be 'System.Int32' + } + + It 'Should only consider assignments wrapped in parentheses to be a part of the output in a Statement block' { + $res = [AstTypeInference]::InferTypeOf( { if ($true){ [string]$Assignment1 = "Hello"; ([int]$Assignment2 = 42) }}.Ast) + $res.Count | Should -Be 1 + $res.Name | Should -Be 'System.Int32' + } + + It 'Should only consider increments/decrements wrapped in parentheses to be a part of the output in a Named block' { + $res = [AstTypeInference]::InferTypeOf( { + [Int16]$Int16 = 1; [Int32]$Int32 = 1; [Int64]$Int64 = 1; [System.Int128]$Int128 = 1; + + $Int16++; $Int32--; ++$Int64; --$Int128}.Ast) + $res.Count | Should -Be 0 + + $res = [AstTypeInference]::InferTypeOf( { + [UInt16]$Uint16 = 1; [UInt32]$Uint32 = 1; [UInt64]$Uint64 = 1; [System.UInt128]$Uint128 = 1 + + ($Uint16++); ($Uint32--); (++$Uint64); (--$Uint128) }.Ast) + $res.Count | Should -Be 4 + $res.Name -join ',' | Should -Be ('System.UInt16', 'System.UInt32', 'System.UInt64', 'System.UInt128' -join ',') + } + + It 'Should only consider increments/decrements wrapped in parentheses to be a part of the output in a Statement block' { + $res = [AstTypeInference]::InferTypeOf( {if ($true){ + [Int16]$Int16 = 1; [Int32]$Int32 = 1; [Int64]$Int64 = 1; [System.Int128]$Int128 = 1; + + $Int16++; $Int32--; ++$Int64; --$Int128}}.Ast) + $res.Count | Should -Be 0 + + $res = [AstTypeInference]::InferTypeOf( {if ($true){ + [UInt16]$Uint16 = 1; [UInt32]$Uint32 = 1; [UInt64]$Uint64 = 1; [System.UInt128]$Uint128 = 1 + + ($Uint16++); ($Uint32--); (++$Uint64); (--$Uint128) }}.Ast) + $res.Count | Should -Be 4 + $res.Name -join ',' | Should -Be ('System.UInt16', 'System.UInt32', 'System.UInt64', 'System.UInt128' -join ',') + } + + It 'Redirected increments/decrements should be considered part of the output in a Named block' { + $res = [AstTypeInference]::InferTypeOf( { + [Int16]$Int16 = 1; [Int32]$Int32 = 1; [Int64]$Int64 = 1; [System.Int128]$Int128 = 1; + + $Int16++ *>&1; $Int32-- *>&1; ++$Int64 *>&1; --$Int128 *>&1}.Ast) + $res.Count | Should -Be 4 + $res.Name -join ',' | Should -Be ('System.Int16', 'System.Int32', 'System.Int64', 'System.Int128' -join ',') + } + + It 'Redirected increments/decrements should be considered part of the output in a Statement block' { + $res = [AstTypeInference]::InferTypeOf( {if ($true){ + [Int16]$Int16 = 1; [Int32]$Int32 = 1; [Int64]$Int64 = 1; [System.Int128]$Int128 = 1; + + $Int16++ *>&1; $Int32-- *>&1; ++$Int64 *>&1; --$Int128 *>&1}}.Ast) + $res.Count | Should -Be 4 + $res.Name -join ',' | Should -Be ('System.Int16', 'System.Int32', 'System.Int64', 'System.Int128' -join ',') + } } Describe "AstTypeInference tests" -Tags CI {