diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 8a010379110..e25586fa55d 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -5043,14 +5043,24 @@ private static void EscapeCharIfNeeded( break; default: - // Handle all the different quote types - if (useSingleQuoteEscapeRules && path[index].IsSingleQuote()) + if (useSingleQuoteEscapeRules) { - _ = sb.Append('\''); - quotesAreNeeded = true; + // Bareword or singlequoted input string. + if (path[index].IsSingleQuote()) + { + // SingleQuotes are escaped with more single quotes. quotesAreNeeded is set so bareword strings can quoted. + _ = sb.Append('\''); + quotesAreNeeded = true; + } + else if (!quotesAreNeeded && stringType == StringConstantType.BareWord && path[index].IsDoubleQuote()) + { + // Bareword string with double quote inside. Make sure to quote it so we don't need to escape it. + quotesAreNeeded = true; + } } - else if (!useSingleQuoteEscapeRules && path[index].IsDoubleQuote()) + else if (path[index].IsDoubleQuote()) { + // Double quoted or bareword with variables input string. Need to escape double quotes. _ = sb.Append('`'); quotesAreNeeded = true; } diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index e088d368011..93bdfe89882 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -2257,6 +2257,42 @@ param ($Param1) Remove-Item -LiteralPath $LiteralPath } + + It "Should add single quotes if there are double quotes in bare word file path" { + $BadQuote = [char]8220 + $TestFile1 = Join-Path -Path $TestDrive -ChildPath "Test1${BadQuote}File" + $null = New-Item -Path $TestFile1 -Force + $res = TabExpansion2 -inputScript "Get-ChildItem -Path $TestDrive\" + ($res.CompletionMatches | Where-Object ListItemText -Like "Test1?File").CompletionText | Should -Be "'$TestFile1'" + Remove-Item -LiteralPath $TestFile1 -Force + } + + It "Should escape double quote if the input string uses double quotes" { + $BadQuote = [char]8220 + $TestFile1 = Join-Path -Path $TestDrive -ChildPath "Test1${BadQuote}File" + $null = New-Item -Path $TestFile1 -Force + $res = TabExpansion2 -inputScript "Get-ChildItem -Path `"$TestDrive\" + $Expected = "`"$($TestFile1.Insert($TestFile1.LastIndexOf($BadQuote), '`'))`"" + ($res.CompletionMatches | Where-Object ListItemText -Like "Test1?File").CompletionText | Should -Be $Expected + Remove-Item -LiteralPath $TestFile1 -Force + } + + It "Should escape single quotes in file paths" { + $SingleQuote = "'" + $TestFile1 = Join-Path -Path $TestDrive -ChildPath "Test1${SingleQuote}File" + $null = New-Item -Path $TestFile1 -Force + # Regardless if the input string was singlequoted or not, we expect to add surrounding single quotes and + # escape the single quote in the file path with another singlequote. + $Expected = "'$($TestFile1.Insert($TestFile1.LastIndexOf($SingleQuote), "'"))'" + + $res = TabExpansion2 -inputScript "Get-ChildItem -Path '$TestDrive\" + ($res.CompletionMatches | Where-Object ListItemText -Like "Test1?File").CompletionText | Should -Be $Expected + + $res = TabExpansion2 -inputScript "Get-ChildItem -Path $TestDrive\" + ($res.CompletionMatches | Where-Object ListItemText -Like "Test1?File").CompletionText | Should -Be $Expected + + Remove-Item -LiteralPath $TestFile1 -Force + } } It 'Should correct slashes in UNC path completion' -Skip:(!$IsWindows) {