Add Type Inference for $_ / $PSItem in catch{ } blocks (#8020)
This commit is contained in:
parent
64c1ca8926
commit
33f2f0faaf
|
@ -1670,6 +1670,19 @@ namespace System.Management.Automation
|
|||
|
||||
} // class ErrorRecord
|
||||
|
||||
/// <summary>
|
||||
/// Dummy generic class for type inference purposes on typed catch blocks.
|
||||
/// </summary>
|
||||
/// <typeparam name="TException">Anything that inherits Exception.</typeparam>
|
||||
internal class ErrorRecord<TException> : ErrorRecord where TException : Exception
|
||||
{
|
||||
public new TException Exception { get; }
|
||||
|
||||
public ErrorRecord(Exception exception, string errorId, ErrorCategory errorCategory, object targetObject) : base(exception, errorId, errorCategory, targetObject)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implemented by exception classes which contain additional
|
||||
/// <see cref="System.Management.Automation.ErrorRecord"/>
|
||||
|
|
|
@ -1803,7 +1803,7 @@ namespace System.Management.Automation
|
|||
// $_ is special, see if we're used in a script block in some pipeline.
|
||||
while (parent != null)
|
||||
{
|
||||
if (parent is ScriptBlockExpressionAst)
|
||||
if (parent is ScriptBlockExpressionAst || parent is CatchClauseAst)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -1832,6 +1832,27 @@ namespace System.Management.Automation
|
|||
parent = parent.Parent;
|
||||
}
|
||||
|
||||
if (parent is CatchClauseAst catchBlock)
|
||||
{
|
||||
if (catchBlock.CatchTypes.Count > 0)
|
||||
{
|
||||
foreach (TypeConstraintAst catchType in catchBlock.CatchTypes)
|
||||
{
|
||||
Type exceptionType = catchType.TypeName.GetReflectionType();
|
||||
if (exceptionType != null && typeof(Exception).IsAssignableFrom(exceptionType))
|
||||
{
|
||||
inferredTypes.Add(new PSTypeName(typeof(ErrorRecord<>).MakeGenericType(exceptionType)));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inferredTypes.Add(new PSTypeName(typeof(ErrorRecord)));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (parent.Parent is CommandAst commandAst)
|
||||
{
|
||||
// We found a command, see if there is a previous command in the pipeline.
|
||||
|
|
|
@ -926,6 +926,90 @@ Describe "Type inference Tests" -tags "CI" {
|
|||
$res.Name | Should -Be System.Int32
|
||||
}
|
||||
|
||||
It 'Infers type of variable $_ in catch block' {
|
||||
$variableAst = { try {} catch { $_ } }.Ast.Find({ param($a) $a -is [System.Management.Automation.Language.VariableExpressionAst] }, $true)
|
||||
$res = [AstTypeInference]::InferTypeOf($variableAst)
|
||||
|
||||
$res | Should -HaveCount 1
|
||||
$res.Name | Should -Be System.Management.Automation.ErrorRecord
|
||||
}
|
||||
|
||||
It 'Infers type of untyped $_.Exception in catch block' {
|
||||
$memberAst = { try {} catch { $_.Exception } }.Ast.Find({ param($a) $a -is [System.Management.Automation.Language.MemberExpressionAst] }, $true)
|
||||
$res = [AstTypeInference]::InferTypeOf($memberAst)
|
||||
|
||||
$res | Should -HaveCount 1
|
||||
$res.Name | Should -Be System.Exception
|
||||
}
|
||||
|
||||
$catchClauseTypes = @(
|
||||
@{ Type = 'System.ArgumentException' }
|
||||
@{ Type = 'System.ArgumentNullException' }
|
||||
@{ Type = 'System.ArgumentOutOfRangeException' }
|
||||
@{ Type = 'System.Collections.Generic.KeyNotFoundException' }
|
||||
@{ Type = 'System.DivideByZeroException' }
|
||||
@{ Type = 'System.FormatException' }
|
||||
@{ Type = 'System.IndexOutOfRangeException' }
|
||||
@{ Type = 'System.InvalidOperationException' }
|
||||
@{ Type = 'System.IO.DirectoryNotFoundException' }
|
||||
@{ Type = 'System.IO.DriveNotFoundException' }
|
||||
@{ Type = 'System.IO.FileNotFoundException' }
|
||||
@{ Type = 'System.IO.PathTooLongException' }
|
||||
@{ Type = 'System.Management.Automation.CommandNotFoundException' }
|
||||
@{ Type = 'System.Management.Automation.JobFailedException' }
|
||||
@{ Type = 'System.Management.Automation.RuntimeException' }
|
||||
@{ Type = 'System.Management.Automation.ValidationMetadataException' }
|
||||
@{ Type = 'System.NotImplementedException' }
|
||||
@{ Type = 'System.NotSupportedException' }
|
||||
@{ Type = 'System.ObjectDisposedException' }
|
||||
@{ Type = 'System.OverflowException' }
|
||||
@{ Type = 'System.PlatformNotSupportedException' }
|
||||
@{ Type = 'System.RankException' }
|
||||
@{ Type = 'System.TimeoutException' }
|
||||
@{ Type = 'System.UriFormatException' }
|
||||
)
|
||||
|
||||
It 'Infers type of $_.Exception in [<Type>] typed catch block' -TestCases $catchClauseTypes {
|
||||
param($Type)
|
||||
|
||||
$memberAst = [scriptblock]::Create("try {} catch [$Type] { `$_.Exception }").Ast.Find(
|
||||
{ param($a) $a -is [System.Management.Automation.Language.MemberExpressionAst] },
|
||||
$true
|
||||
)
|
||||
$res = [AstTypeInference]::InferTypeOf($memberAst)
|
||||
|
||||
$res | Should -HaveCount 1
|
||||
$res.Name | Should -Be $Type
|
||||
}
|
||||
|
||||
It 'Infers possible types of $_.Exception in multi-typed catch block' {
|
||||
$memberAst = { try {} catch [System.ArgumentException], [System.NotImplementedException] { $_.Exception } }.Ast.Find(
|
||||
{ param($a) $a -is [System.Management.Automation.Language.MemberExpressionAst] },
|
||||
$true
|
||||
)
|
||||
$res = [AstTypeInference]::InferTypeOf($memberAst)
|
||||
|
||||
$res | Should -HaveCount 2
|
||||
$res[0].Name | Should -Be System.ArgumentException
|
||||
$res[1].Name | Should -Be System.NotImplementedException
|
||||
}
|
||||
|
||||
It 'Infers type of $_.Exception in each successive catch block' {
|
||||
$memberAst = {
|
||||
try {}
|
||||
catch [System.ArgumentException] { $_.Exception }
|
||||
catch { $_.Exception }
|
||||
}.Ast.FindAll(
|
||||
{ param($a) $a -is [System.Management.Automation.Language.MemberExpressionAst] },
|
||||
$true
|
||||
)
|
||||
$res = foreach ($item in $memberAst) { [AstTypeInference]::InferTypeOf($item) }
|
||||
|
||||
$res | Should -HaveCount 2
|
||||
$res[0].Name | Should -Be System.ArgumentException
|
||||
$res[1].Name | Should -Be System.Exception
|
||||
}
|
||||
|
||||
It 'Infers type of function member' {
|
||||
$res = [AstTypeInference]::InferTypeOf( {
|
||||
class X {
|
||||
|
|
Loading…
Reference in a new issue