Skip to content

Commit 1dc2ebe

Browse files
Fix error formatting for pipeline enumeration exceptions (#20211)
1 parent 6f0c9a5 commit 1dc2ebe

2 files changed

Lines changed: 58 additions & 34 deletions

File tree

src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs

Lines changed: 49 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,7 +1034,8 @@ private static IEnumerable<FormatViewDefinition> ViewsOf_System_Management_Autom
10341034
yield return new FormatViewDefinition("ErrorInstance",
10351035
CustomControl.Create(outOfBand: true)
10361036
.StartEntry()
1037-
.AddScriptBlockExpressionBinding(@"
1037+
.AddScriptBlockExpressionBinding(
1038+
"""
10381039
$errorColor = ''
10391040
$commandPrefix = ''
10401041
if (@('NativeCommandErrorMessage','NativeCommandError') -notcontains $_.FullyQualifiedErrorId -and @('CategoryView','ConciseView','DetailedView') -notcontains $ErrorView)
@@ -1091,8 +1092,9 @@ private static IEnumerable<FormatViewDefinition> ViewsOf_System_Management_Autom
10911092
}
10921093
10931094
$errorColor + $commandPrefix
1094-
")
1095-
.AddScriptBlockExpressionBinding(@"
1095+
""")
1096+
.AddScriptBlockExpressionBinding(
1097+
"""
10961098
Set-StrictMode -Off
10971099
$ErrorActionPreference = 'Stop'
10981100
trap { 'Error found in error view definition: ' + $_.Exception.Message }
@@ -1126,34 +1128,47 @@ function Get-ConciseViewPositionMessage {
11261128
$message = ''
11271129
$prefix = ''
11281130
1131+
# Handle case where there is a TargetObject from a Pester `Should` assertion failure and we can show the error at the target rather than the script source
1132+
# Note that in some versions, this is a Dictionary<,> and in others it's a hashtable. So we explicitly cast to a shared interface in the method invocation
1133+
# to force using `IDictionary.Contains`. Hashtable does have it's own `ContainKeys` as well, but if they ever opt to use a custom `IDictionary`, that may not.
1134+
$useTargetObject = $null -ne $err.TargetObject -and
1135+
$err.TargetObject -is [System.Collections.IDictionary] -and
1136+
([System.Collections.IDictionary]$err.TargetObject).Contains('Line') -and
1137+
([System.Collections.IDictionary]$err.TargetObject).Contains('LineText')
1138+
11291139
# The checks here determine if we show line detailed error information:
11301140
# - check if `ParserError` and comes from PowerShell which eventually results in a ParseException, but during this execution it's an ErrorRecord
1141+
$isParseError = $err.CategoryInfo.Category -eq 'ParserError' -and
1142+
$err.Exception -is [System.Management.Automation.ParentContainsErrorRecordException]
1143+
11311144
# - check if invocation is a script or multiple lines in the console
1145+
$isMultiLineOrExternal = $myinv.ScriptName -or $myinv.ScriptLineNumber -gt 1
1146+
11321147
# - check that it's not a script module as expectation is that users don't want to see the line of error within a module
1133-
if ((($err.CategoryInfo.Category -eq 'ParserError' -and $err.Exception -is 'System.Management.Automation.ParentContainsErrorRecordException') -or $myinv.ScriptName -or $myinv.ScriptLineNumber -gt 1) -and $myinv.ScriptName -notmatch '\.psm1$') {
1134-
$useTargetObject = $false
1148+
$shouldShowLineDetail = ($isParseError -or $isMultiLineOrExternal) -and
1149+
$myinv.ScriptName -notmatch '\.psm1$'
1150+
1151+
if ($useTargetObject -or $shouldShowLineDetail) {
11351152
1136-
# Handle case where there is a TargetObject and we can show the error at the target rather than the script source
1137-
if ($_.TargetObject.Line -and $_.TargetObject.LineText) {
1138-
$posmsg = ""${resetcolor}$($_.TargetObject.File)${newline}""
1139-
$useTargetObject = $true
1153+
if ($useTargetObject) {
1154+
$posmsg = "${resetcolor}$($err.TargetObject.File)${newline}"
11401155
}
11411156
elseif ($myinv.ScriptName) {
11421157
if ($env:TERM_PROGRAM -eq 'vscode') {
11431158
# If we are running in vscode, we know the file:line:col links are clickable so we use this format
1144-
$posmsg = ""${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber):$($myinv.OffsetInLine)${newline}""
1159+
$posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber):$($myinv.OffsetInLine)${newline}"
11451160
}
11461161
else {
1147-
$posmsg = ""${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber)${newline}""
1162+
$posmsg = "${resetcolor}$($myinv.ScriptName):$($myinv.ScriptLineNumber)${newline}"
11481163
}
11491164
}
11501165
else {
1151-
$posmsg = ""${newline}""
1166+
$posmsg = "${newline}"
11521167
}
11531168
11541169
if ($useTargetObject) {
1155-
$scriptLineNumber = $_.TargetObject.Line
1156-
$scriptLineNumberLength = $_.TargetObject.Line.ToString().Length
1170+
$scriptLineNumber = $err.TargetObject.Line
1171+
$scriptLineNumberLength = $err.TargetObject.Line.ToString().Length
11571172
}
11581173
else {
11591174
$scriptLineNumber = $myinv.ScriptLineNumber
@@ -1170,7 +1185,7 @@ function Get-ConciseViewPositionMessage {
11701185
}
11711186
11721187
$verticalBar = '|'
1173-
$posmsg += ""${accentColor}${headerWhitespace}Line ${verticalBar}${newline}""
1188+
$posmsg += "${accentColor}${headerWhitespace}Line ${verticalBar}${newline}"
11741189
11751190
$highlightLine = ''
11761191
if ($useTargetObject) {
@@ -1195,19 +1210,19 @@ function Get-ConciseViewPositionMessage {
11951210
$line = $line.Insert($offsetInLine + $offsetLength, $resetColor).Insert($offsetInLine, $accentColor)
11961211
}
11971212
1198-
$posmsg += ""${accentColor}${lineWhitespace}${ScriptLineNumber} ${verticalBar} ${resetcolor}${line}""
1213+
$posmsg += "${accentColor}${lineWhitespace}${ScriptLineNumber} ${verticalBar} ${resetcolor}${line}"
11991214
$offsetWhitespace = ' ' * $offsetInLine
1200-
$prefix = ""${accentColor}${headerWhitespace} ${verticalBar} ${errorColor}""
1215+
$prefix = "${accentColor}${headerWhitespace} ${verticalBar} ${errorColor}"
12011216
if ($highlightLine -ne '') {
1202-
$posMsg += ""${prefix}${highlightLine}${newline}""
1217+
$posMsg += "${prefix}${highlightLine}${newline}"
12031218
}
1204-
$message = ""${prefix}""
1219+
$message = "${prefix}"
12051220
}
12061221
12071222
if (! $err.ErrorDetails -or ! $err.ErrorDetails.Message) {
1208-
if ($err.CategoryInfo.Category -eq 'ParserError' -and $err.Exception.Message.Contains(""~$newline"")) {
1223+
if ($err.CategoryInfo.Category -eq 'ParserError' -and $err.Exception.Message.Contains("~$newline")) {
12091224
# need to parse out the relevant part of the pre-rendered positionmessage
1210-
$message += $err.Exception.Message.split(""~$newline"")[1].split(""${newline}${newline}"")[0]
1225+
$message += $err.Exception.Message.split("~$newline")[1].split("${newline}${newline}")[0]
12111226
}
12121227
elseif ($err.Exception) {
12131228
$message += $err.Exception.Message
@@ -1229,7 +1244,7 @@ function Get-ConciseViewPositionMessage {
12291244
$prefixVtLength = $prefix.Length - $prefixLength
12301245
12311246
# replace newlines in message so it lines up correct
1232-
$message = $message.Replace($newline, ' ').Replace(""`n"", ' ').Replace(""`t"", ' ')
1247+
$message = $message.Replace($newline, ' ').Replace("`n", ' ').Replace("`t", ' ')
12331248
12341249
$windowWidth = 120
12351250
if ($Host.UI.RawUI -ne $null) {
@@ -1264,7 +1279,7 @@ function Get-ConciseViewPositionMessage {
12641279
$message += $newline
12651280
}
12661281
1267-
$posmsg += ""${errorColor}"" + $message
1282+
$posmsg += "${errorColor}" + $message
12681283
12691284
$reason = 'Error'
12701285
if ($err.Exception -and $err.Exception.WasThrownFromThrowStatement) {
@@ -1276,8 +1291,8 @@ function Get-ConciseViewPositionMessage {
12761291
$reason = $myinv.MyCommand
12771292
}
12781293
# If it's a scriptblock, better to show the command in the scriptblock that had the error
1279-
elseif ($_.CategoryInfo.Activity) {
1280-
$reason = $_.CategoryInfo.Activity
1294+
elseif ($err.CategoryInfo.Activity) {
1295+
$reason = $err.CategoryInfo.Activity
12811296
}
12821297
elseif ($myinv.MyCommand) {
12831298
$reason = $myinv.MyCommand
@@ -1294,7 +1309,7 @@ function Get-ConciseViewPositionMessage {
12941309
12951310
$errorMsg = 'Error'
12961311
1297-
""${errorColor}${reason}: ${posmsg}${resetcolor}""
1312+
"${errorColor}${reason}: ${posmsg}${resetcolor}"
12981313
}
12991314
13001315
$myinv = $_.InvocationInfo
@@ -1305,17 +1320,17 @@ function Get-ConciseViewPositionMessage {
13051320
}
13061321
13071322
if ($err.FullyQualifiedErrorId -eq 'NativeCommandErrorMessage' -or $err.FullyQualifiedErrorId -eq 'NativeCommandError') {
1308-
return ""${errorColor}$($err.Exception.Message)${resetcolor}""
1323+
return "${errorColor}$($err.Exception.Message)${resetcolor}"
13091324
}
13101325
13111326
if ($ErrorView -eq 'DetailedView') {
13121327
$message = Get-Error | Out-String
1313-
return ""${errorColor}${message}${resetcolor}""
1328+
return "${errorColor}${message}${resetcolor}"
13141329
}
13151330
13161331
if ($ErrorView -eq 'CategoryView') {
13171332
$message = $err.CategoryInfo.GetMessage()
1318-
return ""${errorColor}${message}${resetcolor}""
1333+
return "${errorColor}${message}${resetcolor}"
13191334
}
13201335
13211336
$posmsg = ''
@@ -1335,7 +1350,7 @@ function Get-ConciseViewPositionMessage {
13351350
13361351
if ($ErrorView -eq 'ConciseView') {
13371352
if ($err.PSMessageDetails) {
1338-
$posmsg = ""${errorColor}${posmsg}""
1353+
$posmsg = "${errorColor}${posmsg}"
13391354
}
13401355
return $posmsg
13411356
}
@@ -1355,14 +1370,14 @@ function Get-ConciseViewPositionMessage {
13551370
13561371
$posmsg += $newline + $indentString
13571372
1358-
$indentString = ""+ FullyQualifiedErrorId : "" + $err.FullyQualifiedErrorId
1373+
$indentString = "+ FullyQualifiedErrorId : " + $err.FullyQualifiedErrorId
13591374
$posmsg += $newline + $indentString
13601375
13611376
$originInfo = $err.OriginInfo
13621377
13631378
if (($null -ne $originInfo) -and ($null -ne $originInfo.PSComputerName))
13641379
{
1365-
$indentString = ""+ PSComputerName : "" + $originInfo.PSComputerName
1380+
$indentString = "+ PSComputerName : " + $originInfo.PSComputerName
13661381
$posmsg += $newline + $indentString
13671382
}
13681383
@@ -1372,8 +1387,8 @@ function Get-ConciseViewPositionMessage {
13721387
$err.Exception.Message + $posmsg
13731388
}
13741389
1375-
""${errorColor}${finalMsg}${resetcolor}""
1376-
")
1390+
"${errorColor}${finalMsg}${resetcolor}"
1391+
""")
13771392
.EndEntry()
13781393
.EndControl());
13791394
}

test/powershell/engine/Formatting/ErrorView.Tests.ps1

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,15 @@ Describe 'Tests for $ErrorView' -Tag CI {
166166
start-job { [cmdletbinding()]param() $e = [System.Management.Automation.ErrorRecord]::new([System.Exception]::new('hello'), 1, 'ParserError', $null); $pscmdlet.ThrowTerminatingError($e) } | Wait-Job | Receive-Job -ErrorVariable e -ErrorAction SilentlyContinue
167167
$e | Out-String | Should -BeLike '*ParserError*'
168168
}
169+
170+
It 'Exception thrown from Enumerator.MoveNext in a pipeline shows information' {
171+
$e = {
172+
$l = [System.Collections.Generic.List[string]] @('one', 'two')
173+
$l | ForEach-Object { $null = $l.Remove($_) }
174+
} | Should -Throw -ErrorId 'BadEnumeration' -PassThru | Out-String
175+
176+
$e | Should -BeLike 'InvalidOperation:*'
177+
}
169178
}
170179

171180
Context 'NormalView tests' {

0 commit comments

Comments
 (0)