@@ -21,8 +21,60 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
21
21
#endif
22
22
public class ReviewUnusedParameter : IScriptRule
23
23
{
24
+ private readonly string TraverseArgName = "CommandsToTraverse" ;
25
+ public List < string > TraverseCommands { get ; private set ; }
26
+
27
+ /// <summary>
28
+ /// Configure the rule.
29
+ ///
30
+ /// Sets the list of commands to traverse of this rule
31
+ /// </summary>
32
+ private void SetProperties ( )
33
+ {
34
+ TraverseCommands = new List < string > ( ) { "Where-Object" , "ForEach-Object" } ;
35
+
36
+ Dictionary < string , object > ruleArgs = Helper . Instance . GetRuleArguments ( GetName ( ) ) ;
37
+ if ( ruleArgs == null )
38
+ {
39
+ return ;
40
+ }
41
+
42
+ if ( ! ruleArgs . TryGetValue ( TraverseArgName , out object obj ) )
43
+ {
44
+ return ;
45
+ }
46
+ IEnumerable < string > commands = obj as IEnumerable < string > ;
47
+ if ( commands == null )
48
+ {
49
+ // try with enumerable objects
50
+ var enumerableObjs = obj as IEnumerable < object > ;
51
+ if ( enumerableObjs == null )
52
+ {
53
+ return ;
54
+ }
55
+ foreach ( var x in enumerableObjs )
56
+ {
57
+ var y = x as string ;
58
+ if ( y == null )
59
+ {
60
+ return ;
61
+ }
62
+ else
63
+ {
64
+ TraverseCommands . Add ( y ) ;
65
+ }
66
+ }
67
+ }
68
+ else
69
+ {
70
+ TraverseCommands . AddRange ( commands ) ;
71
+ }
72
+ }
73
+
24
74
public IEnumerable < DiagnosticRecord > AnalyzeScript ( Ast ast , string fileName )
25
75
{
76
+ SetProperties ( ) ;
77
+
26
78
if ( ast == null )
27
79
{
28
80
throw new ArgumentNullException ( Strings . NullAstErrorMessage ) ;
@@ -46,10 +98,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
46
98
IEnumerable < Ast > parameterAsts = scriptBlockAst . FindAll ( oneAst => oneAst is ParameterAst , false ) ;
47
99
48
100
// list all variables
49
- IDictionary < string , int > variableCount = scriptBlockAst . FindAll ( oneAst => oneAst is VariableExpressionAst , false )
50
- . Select ( variableExpressionAst => ( ( VariableExpressionAst ) variableExpressionAst ) . VariablePath . UserPath )
51
- . GroupBy ( variableName => variableName , StringComparer . OrdinalIgnoreCase )
52
- . ToDictionary ( variableName => variableName . Key , variableName => variableName . Count ( ) , StringComparer . OrdinalIgnoreCase ) ;
101
+ IDictionary < string , int > variableCount = GetVariableCount ( scriptBlockAst ) ;
53
102
54
103
foreach ( ParameterAst parameterAst in parameterAsts )
55
104
{
@@ -164,5 +213,39 @@ public string GetSourceName()
164
213
{
165
214
return string . Format ( CultureInfo . CurrentCulture , Strings . SourceName ) ;
166
215
}
216
+
217
+ /// <summary>
218
+ /// Returns a dictionary including all variables in the scriptblock and their count
219
+ /// </summary>
220
+ /// <param name="ast">The scriptblock ast to scan</param>
221
+ /// <param name="data">Previously generated data. New findings are added to any existing dictionary if present</param>
222
+ /// <returns>a dictionary including all variables in the scriptblock and their count</returns>
223
+ IDictionary < string , int > GetVariableCount ( ScriptBlockAst ast , Dictionary < string , int > data = null )
224
+ {
225
+ Dictionary < string , int > content = data ;
226
+ if ( null == data )
227
+ content = new Dictionary < string , int > ( StringComparer . OrdinalIgnoreCase ) ;
228
+ IDictionary < string , int > result = ast . FindAll ( oneAst => oneAst is VariableExpressionAst , false )
229
+ . Select ( variableExpressionAst => ( ( VariableExpressionAst ) variableExpressionAst ) . VariablePath . UserPath )
230
+ . GroupBy ( variableName => variableName , StringComparer . OrdinalIgnoreCase )
231
+ . ToDictionary ( variableName => variableName . Key , variableName => variableName . Count ( ) , StringComparer . OrdinalIgnoreCase ) ;
232
+
233
+ foreach ( string key in result . Keys )
234
+ {
235
+ if ( content . ContainsKey ( key ) )
236
+ content [ key ] = content [ key ] + result [ key ] ;
237
+ else
238
+ content [ key ] = result [ key ] ;
239
+ }
240
+
241
+ IEnumerable < Ast > foundScriptBlocks = ast . FindAll ( oneAst => oneAst is ScriptBlockExpressionAst , false )
242
+ . Where ( oneAst => oneAst ? . Parent is CommandAst && ( ( CommandAst ) oneAst . Parent ) . CommandElements [ 0 ] is StringConstantExpressionAst && TraverseCommands . Contains ( ( ( StringConstantExpressionAst ) ( ( CommandAst ) oneAst . Parent ) . CommandElements [ 0 ] ) . Value , StringComparer . OrdinalIgnoreCase ) )
243
+ . Select ( oneAst => ( ( ScriptBlockExpressionAst ) oneAst ) . ScriptBlock ) ;
244
+ foreach ( Ast astItem in foundScriptBlocks )
245
+ if ( astItem != ast )
246
+ GetVariableCount ( ( ScriptBlockAst ) astItem , content ) ;
247
+
248
+ return content ;
249
+ }
167
250
}
168
251
}
0 commit comments