diff --git a/Engine/CommandInfoCache.cs b/Engine/CommandInfoCache.cs index 02e926b53..dbcb41eda 100644 --- a/Engine/CommandInfoCache.cs +++ b/Engine/CommandInfoCache.cs @@ -57,8 +57,9 @@ protected virtual void Dispose(bool disposing) /// /// Name of the command to get a commandinfo object for. /// What types of command are needed. If omitted, all types are retrieved. + /// When needed due to runspace affinity problems of some PowerShell objects. /// - public CommandInfo GetCommandInfo(string commandName, CommandTypes? commandTypes = null) + public CommandInfo GetCommandInfo(string commandName, CommandTypes? commandTypes = null, bool bypassCache = false) { if (string.IsNullOrWhiteSpace(commandName)) { @@ -66,6 +67,10 @@ public CommandInfo GetCommandInfo(string commandName, CommandTypes? commandTypes } 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(() => GetCommandInfoInternal(commandName, commandTypes))).Value; } diff --git a/Engine/Helper.cs b/Engine/Helper.cs index cbd8d0cd3..743cb4d68 100644 --- a/Engine/Helper.cs +++ b/Engine/Helper.cs @@ -673,10 +673,11 @@ public bool PositionalParameterUsed(CommandAst cmdAst, bool moreThanTwoPositiona /// /// /// + /// /// - 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); } /// diff --git a/Rules/UseCorrectCasing.cs b/Rules/UseCorrectCasing.cs index d1f579731..9569b1904 100644 --- a/Rules/UseCorrectCasing.cs +++ b/Rules/UseCorrectCasing.cs @@ -69,7 +69,19 @@ public override IEnumerable AnalyzeScript(Ast ast, string file var commandParameterAsts = commandAst.FindAll( testAst => testAst is CommandParameterAst, true).Cast(); - var availableParameters = commandInfo.Parameters; + Dictionary 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; diff --git a/Tests/Rules/UseCorrectCasing.tests.ps1 b/Tests/Rules/UseCorrectCasing.tests.ps1 index 7343677ff..a5280a6e3 100644 --- a/Tests/Rules/UseCorrectCasing.tests.ps1 +++ b/Tests/Rules/UseCorrectCasing.tests.ps1 @@ -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 + } }