Repository navigation
whitespaceBetweenParameters Removing Redirectors #2000
Description
Activity
SydneyhSmith commented
on May 7, 2024 CollaboratorMore actionsThanks @nixuno we were able to reproduce this
liamjpeters commented
on May 13, 2024 ContributorMore actionsThis is quite an interesting one 😀. A slightly simplified repro in PSScriptAnalyzer is:
Invoke-Formatter -ScriptDefinition 'Invoke-Foo 3>&1 1>&1 2>&1' -Settings @{ Rules = @{ PSUseConsistentWhitespace = @{ Enable = $true CheckParameter = $true } } }
Results in:
Invoke-Foo 1>&1
The
PSUseConsistentWhitespacerule, whenCheckParameteristrue, is looking over all theCommandAstnodes.For each
CommandAst, it's finding all the direct children of the currentCommandAstnode.PSScriptAnalyzer/Rules/UseConsistentWhitespace.cs
Lines 399 to 400 in e1dc126
List<Ast> commandParameterAstElements = commandAst.FindAll( testAst => testAst.Parent == commandAst, searchNestedScriptBlocks: false).ToList(); It's checking then that each one sequentially is separated by a 1 character gap.
PSScriptAnalyzer/Rules/UseConsistentWhitespace.cs
Lines 410 to 411 in e1dc126
var expectedStartColumnNumberOfRightExtent = leftExtent.EndColumnNumber + 1; if (rightExtent.StartColumnNumber > expectedStartColumnNumberOfRightExtent) It seems to be making the implicit assumption that the direct children of the
CommandAstare read from the AST in token order. This is, apparently, not the case.Looking at the AST of our example
Invoke-Foo 3>&1 1>&1 2>&1:ScriptBlockAst [0,25) └ NamedBlockAst [0,25) └ PipelineAst [0,25) └ CommandAst [0,25) ├ StringConstantExpression [0,10) ├ MergingRedirectionAst [16,20) ├ MergingRedirectionAst [21,25) └ MergingRedirectionAst [11,15)We see that evidentially, the
MergingRedirectionAstnodes are read from the tree in stream order (very interesting!).Note the start of each
MergingRedirectionAstnodes extent - the number after[- is not in order down the list. The first node read from the tree is the second token,1>&1, followed by the last token,2>&1, and finally the first of the redirects in our input command,3>&1.I'm not smart enough to confirm this within the main Powershell Repo's parser - but I've tried with various orders of
Invoke-Foo 3>&1 2>&1 1>&1 5>&1 4>&1 6>&1and it appears to be consistent.My unrefined naive solution would be to sort the command parameters (the direct children of the
CommandAst), first by their ExtentsStartLineNumber, then by theStartColumnNumber. Then proceed as planned.This works as expected and our repro code correctly produces:
Invoke-Foo 3>&1 1>&1 2>&1
I would need to look at how badly this sorting impacts performance and for any edge cases.
Prerequisites
Summary
When formatting the following PowerShell while
powershell.codeFormatting.whitespaceBetweenParameters = true, it removes two of the redirectors:Pre-Format
Post-Format
I would expect the redirectors to be left alone.
PowerShell Version
Visual Studio Code Version
Extension Version
ms-vscode.powershell@2024.2.1Steps to Reproduce
test.ps1:Shift + Alt + Fto format the document.Visuals
No response
Logs
No response