From 239c891d122d78f32d3e60f346cece75bebebce5 Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Sat, 6 Apr 2019 19:45:18 +0100 Subject: [PATCH 1/5] UseCorrectCasing: Fix special case of ? for command lookup and do not correct application names to end in .exe on Windows --- Engine/CommandInfoCache.cs | 8 ++++++++ Rules/UseCorrectCasing.cs | 7 +++++++ Tests/Rules/UseCorrectCasing.tests.ps1 | 9 +++++++++ 3 files changed, 24 insertions(+) diff --git a/Engine/CommandInfoCache.cs b/Engine/CommandInfoCache.cs index 20fd211aa..58e467092 100644 --- a/Engine/CommandInfoCache.cs +++ b/Engine/CommandInfoCache.cs @@ -69,6 +69,14 @@ public CommandInfo GetCommandInfoLegacy(string commandOrAliasName, CommandTypes? /// Returns null if command does not exists private static CommandInfo GetCommandInfoInternal(string cmdName, CommandTypes? commandType) { + if (cmdName == "?") + { + // 'Get-Command ?' would return % due to PowerShell interpreting is a single-character-wildcard search and not just the ? alias. + // For more details see https://github.com/PowerShell/PowerShell/issues/9308 + // Using the escape character makes PowerShell interpret ? as a literal string. + cmdName = "`?"; + } + using (var ps = System.Management.Automation.PowerShell.Create()) { ps.AddCommand("Get-Command") diff --git a/Rules/UseCorrectCasing.cs b/Rules/UseCorrectCasing.cs index 490008b1d..6033789e6 100644 --- a/Rules/UseCorrectCasing.cs +++ b/Rules/UseCorrectCasing.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Management.Automation.Language; using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; +using System.Management.Automation; #if !CORECLR using System.ComponentModel.Composition; #endif @@ -52,6 +53,12 @@ public override IEnumerable AnalyzeScript(Ast ast, string file var fullyqualifiedName = $"{commandInfo.ModuleName}\\{shortName}"; var isFullyQualified = commandName.Equals(fullyqualifiedName, StringComparison.OrdinalIgnoreCase); var correctlyCasedCommandName = isFullyQualified ? fullyqualifiedName : shortName; + // For binaries that could exist on both Windows and Linux like e.g. git we do not want to expand + // git to git.exe to keep the script cross-platform compliant + if (commandInfo.CommandType == CommandTypes.Application && correctlyCasedCommandName.EndsWith(".exe")) + { + correctlyCasedCommandName = correctlyCasedCommandName.Substring(0, correctlyCasedCommandName.Length - 4); + } if (!commandName.Equals(correctlyCasedCommandName, StringComparison.Ordinal)) { diff --git a/Tests/Rules/UseCorrectCasing.tests.ps1 b/Tests/Rules/UseCorrectCasing.tests.ps1 index 396413b38..6c53015a0 100644 --- a/Tests/Rules/UseCorrectCasing.tests.ps1 +++ b/Tests/Rules/UseCorrectCasing.tests.ps1 @@ -11,6 +11,15 @@ Describe "UseCorrectCasing" { Invoke-Formatter '"$(get-childitem)"' | Should -Be '"$(get-childitem)"' } + It "Corrects alias correctly" { + Invoke-Formatter 'Gci' | Should -Be 'gci' + Invoke-Formatter '?' | Should -Be '?' + } + + It "Corrects applications on Windows to not end in .exe" -Skip:($IsLinux -or $IsMacOS) { + Invoke-Formatter 'Cmd' | Should -Be 'cmd' + } + It "corrects case of script function" { function Invoke-DummyFunction { From 2de5dcf4365154b4d61b020a817631410c160986 Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Sun, 7 Apr 2019 17:41:37 +0100 Subject: [PATCH 2/5] Use more generic WildcardPattern.Escape method --- Engine/CommandInfoCache.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Engine/CommandInfoCache.cs b/Engine/CommandInfoCache.cs index 58e467092..d41c02dbb 100644 --- a/Engine/CommandInfoCache.cs +++ b/Engine/CommandInfoCache.cs @@ -69,13 +69,9 @@ public CommandInfo GetCommandInfoLegacy(string commandOrAliasName, CommandTypes? /// Returns null if command does not exists private static CommandInfo GetCommandInfoInternal(string cmdName, CommandTypes? commandType) { - if (cmdName == "?") - { - // 'Get-Command ?' would return % due to PowerShell interpreting is a single-character-wildcard search and not just the ? alias. - // For more details see https://github.com/PowerShell/PowerShell/issues/9308 - // Using the escape character makes PowerShell interpret ? as a literal string. - cmdName = "`?"; - } + // 'Get-Command ?' would return % for example due to PowerShell interpreting is a single-character-wildcard search and not just the ? alias. + // For more details see https://github.com/PowerShell/PowerShell/issues/9308 + cmdName = WildcardPattern.Escape(cmdName); using (var ps = System.Management.Automation.PowerShell.Create()) { From 4dcd37306ad429dc5590f5e500b92cc999ab42da Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Tue, 9 Apr 2019 22:21:20 +0100 Subject: [PATCH 3/5] Remove application extension more generically and on Windows only --- Rules/UseCorrectCasing.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Rules/UseCorrectCasing.cs b/Rules/UseCorrectCasing.cs index 6033789e6..b0ddba5f9 100644 --- a/Rules/UseCorrectCasing.cs +++ b/Rules/UseCorrectCasing.cs @@ -6,6 +6,8 @@ using System.Management.Automation.Language; using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; using System.Management.Automation; +using System.IO; +using System.Runtime.InteropServices; #if !CORECLR using System.ComponentModel.Composition; #endif @@ -53,11 +55,15 @@ public override IEnumerable AnalyzeScript(Ast ast, string file var fullyqualifiedName = $"{commandInfo.ModuleName}\\{shortName}"; var isFullyQualified = commandName.Equals(fullyqualifiedName, StringComparison.OrdinalIgnoreCase); var correctlyCasedCommandName = isFullyQualified ? fullyqualifiedName : shortName; - // For binaries that could exist on both Windows and Linux like e.g. git we do not want to expand - // git to git.exe to keep the script cross-platform compliant - if (commandInfo.CommandType == CommandTypes.Application && correctlyCasedCommandName.EndsWith(".exe")) + var isWindows = true; +#if CORECLR + isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); +#endif + if (commandInfo.CommandType == CommandTypes.Application && isWindows) { - correctlyCasedCommandName = correctlyCasedCommandName.Substring(0, correctlyCasedCommandName.Length - 4); + // For binaries that could exist on both Windows and Linux like e.g. git we do not want to expand + // git to git.exe to keep the script cross-platform compliant + correctlyCasedCommandName = Path.GetFileNameWithoutExtension(correctlyCasedCommandName); } if (!commandName.Equals(correctlyCasedCommandName, StringComparison.Ordinal)) From b77f863ccbf3da148e79f81e23e0b2828b9ed578 Mon Sep 17 00:00:00 2001 From: Christoph Bergmeister Date: Wed, 10 Apr 2019 21:32:41 +0100 Subject: [PATCH 4/5] Make applications with an extension preserver the extension and re-use isWindows variable better --- Rules/UseCorrectCasing.cs | 11 ++++++----- Tests/Rules/UseCorrectCasing.tests.ps1 | 4 ++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Rules/UseCorrectCasing.cs b/Rules/UseCorrectCasing.cs index b0ddba5f9..ecf3038a5 100644 --- a/Rules/UseCorrectCasing.cs +++ b/Rules/UseCorrectCasing.cs @@ -32,6 +32,11 @@ public override IEnumerable AnalyzeScript(Ast ast, string file IEnumerable commandAsts = ast.FindAll(testAst => testAst is CommandAst, true); + bool isWindows = true; +#if CORECLR + isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); +#endif + // Iterates all CommandAsts and check the command name. foreach (CommandAst commandAst in commandAsts) { @@ -55,11 +60,7 @@ public override IEnumerable AnalyzeScript(Ast ast, string file var fullyqualifiedName = $"{commandInfo.ModuleName}\\{shortName}"; var isFullyQualified = commandName.Equals(fullyqualifiedName, StringComparison.OrdinalIgnoreCase); var correctlyCasedCommandName = isFullyQualified ? fullyqualifiedName : shortName; - var isWindows = true; -#if CORECLR - isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); -#endif - if (commandInfo.CommandType == CommandTypes.Application && isWindows) + if (commandInfo.CommandType == CommandTypes.Application && isWindows && !Path.HasExtension(commandName)) { // For binaries that could exist on both Windows and Linux like e.g. git we do not want to expand // git to git.exe to keep the script cross-platform compliant diff --git a/Tests/Rules/UseCorrectCasing.tests.ps1 b/Tests/Rules/UseCorrectCasing.tests.ps1 index 6c53015a0..7a14cca9a 100644 --- a/Tests/Rules/UseCorrectCasing.tests.ps1 +++ b/Tests/Rules/UseCorrectCasing.tests.ps1 @@ -20,6 +20,10 @@ Describe "UseCorrectCasing" { Invoke-Formatter 'Cmd' | Should -Be 'cmd' } + It "Preserves extension of applications on Windows" -Skip:($IsLinux -or $IsMacOS) { + Invoke-Formatter 'Cmd.exe' | Should -Be 'cmd.exe' + } + It "corrects case of script function" { function Invoke-DummyFunction { From f9d2b6bd00356f88a18412e2c58a7afce2ead6a4 Mon Sep 17 00:00:00 2001 From: Robert Holt Date: Mon, 15 Apr 2019 21:48:36 +0100 Subject: [PATCH 5/5] Apply suggestions from code review Co-Authored-By: bergmeister --- Rules/UseCorrectCasing.cs | 4 ++-- Tests/Rules/UseCorrectCasing.tests.ps1 | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Rules/UseCorrectCasing.cs b/Rules/UseCorrectCasing.cs index ecf3038a5..2bfe322cf 100644 --- a/Rules/UseCorrectCasing.cs +++ b/Rules/UseCorrectCasing.cs @@ -60,7 +60,7 @@ public override IEnumerable AnalyzeScript(Ast ast, string file var fullyqualifiedName = $"{commandInfo.ModuleName}\\{shortName}"; var isFullyQualified = commandName.Equals(fullyqualifiedName, StringComparison.OrdinalIgnoreCase); var correctlyCasedCommandName = isFullyQualified ? fullyqualifiedName : shortName; - if (commandInfo.CommandType == CommandTypes.Application && isWindows && !Path.HasExtension(commandName)) + if (isWindows && commandInfo.CommandType == CommandTypes.Application && !Path.HasExtension(commandName)) { // For binaries that could exist on both Windows and Linux like e.g. git we do not want to expand // git to git.exe to keep the script cross-platform compliant @@ -172,4 +172,4 @@ public override string GetSourceName() return string.Format(CultureInfo.CurrentCulture, Strings.SourceName); } } -} \ No newline at end of file +} diff --git a/Tests/Rules/UseCorrectCasing.tests.ps1 b/Tests/Rules/UseCorrectCasing.tests.ps1 index 7a14cca9a..e4ec35e9a 100644 --- a/Tests/Rules/UseCorrectCasing.tests.ps1 +++ b/Tests/Rules/UseCorrectCasing.tests.ps1 @@ -18,10 +18,17 @@ Describe "UseCorrectCasing" { It "Corrects applications on Windows to not end in .exe" -Skip:($IsLinux -or $IsMacOS) { Invoke-Formatter 'Cmd' | Should -Be 'cmd' + Invoke-Formatter 'Cmd' | Should -Be 'cmd' + Invoke-Formatter 'MORE' | Should -Be 'more' + Invoke-Formatter 'WinRM' | Should -Be 'winrm' + Invoke-Formatter 'CertMgr' | Should -Be 'certmgr' } It "Preserves extension of applications on Windows" -Skip:($IsLinux -or $IsMacOS) { Invoke-Formatter 'Cmd.exe' | Should -Be 'cmd.exe' + Invoke-Formatter 'MORE.com' | Should -Be 'more.com' + Invoke-Formatter 'WinRM.cmd' | Should -Be 'winrm.cmd' + Invoke-Formatter 'CertMgr.MSC' | Should -Be 'certmgr.msc' } It "corrects case of script function" { @@ -31,4 +38,4 @@ Describe "UseCorrectCasing" { } Invoke-Formatter 'invoke-dummyFunction' | Should -Be 'Invoke-DummyFunction' } -} \ No newline at end of file +}