Skip to content

UseCorrectCasing: Fix wildcard lookups for command lookup and do not append .exe for applications on Windows #1210

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
4 changes: 4 additions & 0 deletions Engine/CommandInfoCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ public CommandInfo GetCommandInfoLegacy(string commandOrAliasName, CommandTypes?
/// <returns>Returns null if command does not exists</returns>
private static CommandInfo GetCommandInfoInternal(string cmdName, CommandTypes? commandType)
{
// '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())
{
ps.AddCommand("Get-Command")
Expand Down
14 changes: 14 additions & 0 deletions Rules/UseCorrectCasing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
using System.Collections.Generic;
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
Expand All @@ -29,6 +32,11 @@ public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string file

IEnumerable<Ast> 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)
{
Expand All @@ -52,6 +60,12 @@ public override IEnumerable<DiagnosticRecord> 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))
{
// 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))
{
Expand Down
13 changes: 13 additions & 0 deletions Tests/Rules/UseCorrectCasing.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,19 @@ 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 "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
{
Expand Down