diff --git a/Engine/Helper.cs b/Engine/Helper.cs index 4573d4cd4..28e4d49a0 100644 --- a/Engine/Helper.cs +++ b/Engine/Helper.cs @@ -38,6 +38,7 @@ public class Helper private readonly static Version minSupportedPSVersion = new Version(3, 0); private Dictionary> ruleArguments; private PSVersionTable psVersionTable; + private Dictionary commandInfoCache; #endregion @@ -146,6 +147,7 @@ public void Initialize() KeywordBlockDictionary = new Dictionary>>(StringComparer.OrdinalIgnoreCase); VariableAnalysisDictionary = new Dictionary(); ruleArguments = new Dictionary>(StringComparer.OrdinalIgnoreCase); + commandInfoCache = new Dictionary(StringComparer.OrdinalIgnoreCase); IEnumerable aliases = this.invokeCommand.GetCommands("*", CommandTypes.Alias, true); @@ -594,12 +596,13 @@ public string VariableNameWithoutScope(VariablePath variablePath) /// public bool HasSplattedVariable(CommandAst cmdAst) { - if (cmdAst == null || cmdAst.CommandElements == null) - { - return false; - } - - return cmdAst.CommandElements.Any(cmdElem => cmdElem is VariableExpressionAst && (cmdElem as VariableExpressionAst).Splatted); + return cmdAst != null + && cmdAst.CommandElements != null + && cmdAst.CommandElements.Any(cmdElem => + { + var varExprAst = cmdElem as VariableExpressionAst; + return varExprAst != null && varExprAst.Splatted; + }); } /// @@ -610,12 +613,16 @@ public bool HasSplattedVariable(CommandAst cmdAst) /// public bool PositionalParameterUsed(CommandAst cmdAst, bool moreThanThreePositional = false) { - if (cmdAst == null || cmdAst.GetCommandName() == null) + if (cmdAst == null) { return false; } - CommandInfo commandInfo = GetCommandInfo(GetCmdletNameFromAlias(cmdAst.GetCommandName())) ?? GetCommandInfo(cmdAst.GetCommandName()); + var commandInfo = GetCommandInfo(cmdAst.GetCommandName()); + if (commandInfo == null || (commandInfo.CommandType != System.Management.Automation.CommandTypes.Cmdlet)) + { + return false; + } IEnumerable switchParams = null; @@ -642,29 +649,31 @@ public bool PositionalParameterUsed(CommandAst cmdAst, bool moreThanThreePositio foreach (CommandElementAst ceAst in cmdAst.CommandElements) { - if (ceAst is CommandParameterAst) + var cmdParamAst = ceAst as CommandParameterAst; + if (cmdParamAst != null) + { + // Skip if it's a switch parameter + if (switchParams != null && + switchParams.Any( + pm => String.Equals( + pm.Name, + cmdParamAst.ParameterName, StringComparison.OrdinalIgnoreCase))) { - // Skip if it's a switch parameter - if (switchParams != null && - switchParams.Any(pm => String.Equals(pm.Name, (ceAst as CommandParameterAst).ParameterName, StringComparison.OrdinalIgnoreCase))) - { - continue; - } - + continue; + } - parameters += 1; + parameters += 1; - if ((ceAst as CommandParameterAst).Argument != null) - { - arguments += 1; - } - - } - else + if (cmdParamAst.Argument != null) { arguments += 1; } + } + else + { + arguments += 1; + } } // if not the first element in a pipeline, increase the number of arguments by 1 @@ -685,6 +694,23 @@ public bool PositionalParameterUsed(CommandAst cmdAst, bool moreThanThreePositio } + /// + /// Get a CommandInfo object of the given command name + /// + /// Returns null if command does not exists + private CommandInfo GetCommandInfoInternal(string cmdName, CommandTypes? commandType = null) + { + using (var ps = System.Management.Automation.PowerShell.Create()) + { + var cmdInfo = ps.AddCommand("Get-Command") + .AddArgument(cmdName) + .AddParameter("ErrorAction", "SilentlyContinue") + .Invoke() + .FirstOrDefault(); + return cmdInfo; + } + } + /// /// Given a command's name, checks whether it exists /// @@ -693,19 +719,28 @@ public bool PositionalParameterUsed(CommandAst cmdAst, bool moreThanThreePositio /// public CommandInfo GetCommandInfo(string name, CommandTypes? commandType = null) { - CommandTypes defaultCmdType = CommandTypes.Alias - | CommandTypes.Cmdlet - | CommandTypes.ExternalScript - | CommandTypes.Filter - | CommandTypes.Function - | CommandTypes.Script - | CommandTypes.Workflow; -#if !PSV3 - defaultCmdType |= CommandTypes.Configuration; -#endif + if (string.IsNullOrWhiteSpace(name)) + { + return null; + } + + // check if it is an alias + string cmdletName = Helper.Instance.GetCmdletNameFromAlias(name); + if (string.IsNullOrWhiteSpace(cmdletName)) + { + cmdletName = name; + } + lock (getCommandLock) { - return this.invokeCommand.GetCommand(name, commandType ?? defaultCmdType); + if (commandInfoCache.ContainsKey(cmdletName)) + { + return commandInfoCache[cmdletName]; + } + + var commandInfo = GetCommandInfoInternal(cmdletName, commandType); + commandInfoCache.Add(cmdletName, commandInfo); + return commandInfo; } } diff --git a/Rules/UseCmdletCorrectly.cs b/Rules/UseCmdletCorrectly.cs index 5090de28e..6146711dc 100644 --- a/Rules/UseCmdletCorrectly.cs +++ b/Rules/UseCmdletCorrectly.cs @@ -55,7 +55,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) if (cmdAst.GetCommandName() == null) continue; // Checks mandatory parameters. - if (!IsMandatoryParameterExisted(cmdAst)) + if (!MandatoryParameterExists(cmdAst)) { yield return new DiagnosticRecord(String.Format(CultureInfo.CurrentCulture, Strings.UseCmdletCorrectlyError, cmdAst.GetCommandName()), cmdAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName); @@ -68,7 +68,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) /// /// /// - private bool IsMandatoryParameterExisted(CommandAst cmdAst) + private bool MandatoryParameterExists(CommandAst cmdAst) { CommandInfo cmdInfo = null; List mandParams = new List(); @@ -88,9 +88,7 @@ private bool IsMandatoryParameterExisted(CommandAst cmdAst) #region Compares parameter list and mandatory parameter list. - cmdInfo = Helper.Instance.GetCommandInfo(Helper.Instance.GetCmdletNameFromAlias(cmdAst.GetCommandName())) - ?? Helper.Instance.GetCommandInfo(cmdAst.GetCommandName()); - + cmdInfo = Helper.Instance.GetCommandInfo(cmdAst.GetCommandName()); if (cmdInfo == null || (cmdInfo.CommandType != System.Management.Automation.CommandTypes.Cmdlet)) { return true; @@ -109,7 +107,7 @@ private bool IsMandatoryParameterExisted(CommandAst cmdAst) // If cannot find any mandatory parameter, it's not necessary to do a further check for current cmdlet. try { - int noOfParamSets = cmdInfo.ParameterSets.Count; + int noOfParamSets = cmdInfo.ParameterSets.Count; foreach (ParameterMetadata pm in cmdInfo.Parameters.Values) { int count = 0; @@ -163,7 +161,7 @@ private bool IsMandatoryParameterExisted(CommandAst cmdAst) return returnValue; } - + /// /// GetName: Retrieves the name of this rule. /// diff --git a/Rules/UseShouldProcessCorrectly.cs b/Rules/UseShouldProcessCorrectly.cs index e56cd49f4..c7cb64296 100644 --- a/Rules/UseShouldProcessCorrectly.cs +++ b/Rules/UseShouldProcessCorrectly.cs @@ -297,29 +297,6 @@ private bool DeclaresSupportsShouldProcess(FunctionDefinitionAst ast) return Helper.Instance.GetNamedArgumentAttributeValue(shouldProcessAttribute); } - /// - /// Get a CommandInfo object of the given command name - /// - /// Returns null if command does not exists - private CommandInfo GetCommandInfo(string cmdName) - { - try - { - using (var ps = System.Management.Automation.PowerShell.Create()) - { - var cmdInfo = ps.AddCommand("Get-Command") - .AddArgument(cmdName) - .Invoke() - .FirstOrDefault(); - return cmdInfo; - } - } - catch (System.Management.Automation.CommandNotFoundException) - { - return null; - } - } - /// /// Checks if the given command supports ShouldProcess /// @@ -331,7 +308,7 @@ private bool SupportsShouldProcess(string cmdName) return false; } - var cmdInfo = GetCommandInfo(cmdName); + var cmdInfo = Helper.Instance.GetCommandInfo(cmdName); if (cmdInfo == null) { return false;