Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

UseCorrectCasing: Do not use CommandInfoCache when CommandInfoParameters property throws due to runspace affinity problem of PowerShell engine #1523

Merged
Merged
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
7 changes: 6 additions & 1 deletion Engine/CommandInfoCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,20 @@ protected virtual void Dispose(bool disposing)
/// </summary>
/// <param name="commandName">Name of the command to get a commandinfo object for.</param>
/// <param name="commandTypes">What types of command are needed. If omitted, all types are retrieved.</param>
/// <param name="bypassCache">When needed due to runspace affinity problems of some PowerShell objects.</param>
/// <returns></returns>
public CommandInfo GetCommandInfo(string commandName, CommandTypes? commandTypes = null)
public CommandInfo GetCommandInfo(string commandName, CommandTypes? commandTypes = null, bool bypassCache = false)
{
if (string.IsNullOrWhiteSpace(commandName))
{
return null;
}

var key = new CommandLookupKey(commandName, commandTypes);
if (bypassCache)
{
return GetCommandInfoInternal(commandName, commandTypes);
}
// Atomically either use PowerShell to query a command info object, or fetch it from the cache
return _commandInfoCache.GetOrAdd(key, new Lazy<CommandInfo>(() => GetCommandInfoInternal(commandName, commandTypes))).Value;
}
Expand Down
5 changes: 3 additions & 2 deletions Engine/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -673,10 +673,11 @@ public bool PositionalParameterUsed(CommandAst cmdAst, bool moreThanTwoPositiona
/// </summary>
/// <param name="name"></param>
/// <param name="commandType"></param>
/// <param name="bypassCache"></param>
/// <returns></returns>
public CommandInfo GetCommandInfo(string name, CommandTypes? commandType = null)
public CommandInfo GetCommandInfo(string name, CommandTypes? commandType = null, bool bypassCache = false)
{
return CommandInfoCache.GetCommandInfo(name, commandTypes: commandType);
return CommandInfoCache.GetCommandInfo(name, commandTypes: commandType, bypassCache: bypassCache);
}

/// <summary>
Expand Down
14 changes: 13 additions & 1 deletion Rules/UseCorrectCasing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,19 @@ public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string file

var commandParameterAsts = commandAst.FindAll(
testAst => testAst is CommandParameterAst, true).Cast<CommandParameterAst>();
var availableParameters = commandInfo.Parameters;
Dictionary<string, ParameterMetadata> availableParameters;
try
{
availableParameters = commandInfo.Parameters;
}
// It's a known issue that objects from PowerShell can have a runspace affinity,
// therefore if that happens, we query a fresh object instead of using the cache.
// https://github.com/PowerShell/PowerShell/issues/4003
catch (InvalidOperationException)
{
commandInfo = Helper.Instance.GetCommandInfo(commandName, bypassCache: true);
availableParameters = commandInfo.Parameters;
}
foreach (var commandParameterAst in commandParameterAsts)
{
var parameterName = commandParameterAst.ParameterName;
Expand Down
9 changes: 9 additions & 0 deletions Tests/Rules/UseCorrectCasing.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,13 @@ Describe "UseCorrectCasing" {
Invoke-Formatter 'Get-Process -NonExistingParameterName' -ErrorAction Stop
}

It "Does not throw when correcting certain cmdlets (issue 1516)" {
$scriptDefinition = 'Get-Content;Test-Path;Get-ChildItem;Get-Content;Test-Path;Get-ChildItem'
$settings = @{ 'Rules' = @{ 'PSUseCorrectCasing' = @{ 'Enable' = $true } } }
{
1..100 |
ForEach-Object { $null = Invoke-ScriptAnalyzer -ScriptDefinition $scriptDefinition -Settings $settings -ErrorAction Stop }
} |
Should -Not -Throw
}
}