Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,16 @@ public async Task<VariableDetailsBase> GetVariableFromExpression(string variable
await debugInfoHandle.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
variableList = variables;
// Search the scope containers in order of narrowest to broadest scope (local,
// then script, then global) so that a variable defined in a more local scope
// correctly shadows one of the same name in a parent scope, matching
// PowerShell's own variable resolution. The flattened list of every variable is
// appended as a fallback so that names not present in those scopes (such as
// frame-specific variables) still resolve. See issue #1882.
variableList = new[] { localScopeVariables, scriptScopeVariables, globalScopeVariables }
.Where(container => container is not null)
.SelectMany(container => container.Children.Values)
.Concat(variables);
}
finally
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
$scopeTestVariable = "from parent scope"
& {
$scopeTestVariable = "from local scope"
Write-Output $scopeTestVariable
}
21 changes: 21 additions & 0 deletions test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,27 @@ await debugService.SetLineBreakpointsAsync(
Assert.False(var.IsExpandable);
}

// Regression test for #1882: when a variable of the same name exists in both a parent
// scope and the current (more local) scope, evaluating it via a watch/evaluate request
// must return the most-local value, matching what's shown in the Variables explorer and
// PowerShell's own variable resolution.
[Fact]
public async Task DebuggerResolvesVariableFromMostLocalScope()
{
ScriptFile scopeScriptFile = GetDebugScript("VariableScopeTest.ps1");
await debugService.SetLineBreakpointsAsync(
scopeScriptFile.FilePath,
new[] { BreakpointDetails.Create(scopeScriptFile.FilePath, 4) });

Task _ = ExecuteScriptFileAsync(scopeScriptFile.FilePath);
await AssertDebuggerStopped(scopeScriptFile.FilePath, 4);

VariableDetailsBase resolved = await debugService.GetVariableFromExpression(
"$scopeTestVariable", CancellationToken.None);
Assert.NotNull(resolved);
Assert.Equal("\"from local scope\"", resolved.ValueString);
}

[Fact]
public async Task DebuggerGetsVariables()
{
Expand Down
Loading