diff --git a/Rules/ReviewUnusedParameter.cs b/Rules/ReviewUnusedParameter.cs index 5a06500d8..a1336aed7 100644 --- a/Rules/ReviewUnusedParameter.cs +++ b/Rules/ReviewUnusedParameter.cs @@ -36,6 +36,12 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) foreach (ScriptBlockAst scriptBlockAst in scriptBlockAsts) { + // bail out if PS bound parameter used. + if (scriptBlockAst.Find(IsBoundParametersReference, false) != null) + { + continue; + } + // find all declared parameters IEnumerable parameterAsts = scriptBlockAst.FindAll(oneAst => oneAst is ParameterAst, false); @@ -45,12 +51,6 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) .GroupBy(variableName => variableName, StringComparer.OrdinalIgnoreCase) .ToDictionary(variableName => variableName.Key, variableName => variableName.Count(), StringComparer.OrdinalIgnoreCase); - // all bets are off if the script uses PSBoundParameters - if (variableCount.ContainsKey("PSBoundParameters")) - { - continue; - } - foreach (ParameterAst parameterAst in parameterAsts) { // there should be at least two usages of the variable since the parameter declaration counts as one @@ -72,6 +72,47 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) } } + /// + /// Checks for PS bound parameter reference. + /// + /// AST to be analyzed. This should be non-null + /// Boolean true indicating that given AST has PS bound parameter reference, otherwise false + private static bool IsBoundParametersReference(Ast ast) + { + // $PSBoundParameters + if (ast is VariableExpressionAst variableAst + && variableAst.VariablePath.UserPath.Equals("PSBoundParameters", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + if (ast is MemberExpressionAst memberAst + && memberAst.Member is StringConstantExpressionAst memberStringAst + && memberStringAst.Value.Equals("BoundParameters", StringComparison.OrdinalIgnoreCase)) + { + // $MyInvocation.BoundParameters + if (memberAst.Expression is VariableExpressionAst veAst + && veAst.VariablePath.UserPath.Equals("MyInvocation", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + // $PSCmdlet.MyInvocation.BoundParameters + if (memberAst.Expression is MemberExpressionAst meAstNested) + { + if (meAstNested.Expression is VariableExpressionAst veAstNested + && veAstNested.VariablePath.UserPath.Equals("PSCmdlet", StringComparison.OrdinalIgnoreCase) + && meAstNested.Member is StringConstantExpressionAst sceAstNested + && sceAstNested.Value.Equals("MyInvocation", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + } + } + + return false; + } + /// /// GetName: Retrieves the name of this rule. ///