|
1 |
| -// Copyright (c) Microsoft Corporation. All rights reserved. |
| 1 | +// Copyright (c) Microsoft Corporation. All rights reserved. |
2 | 2 | // Licensed under the MIT License.
|
3 | 3 |
|
4 | 4 | using System;
|
@@ -111,61 +111,53 @@ public string GetSourceName()
|
111 | 111 | /// <returns>An enumerable containing diagnostic records</returns>
|
112 | 112 | private IEnumerable<DiagnosticRecord> AnalyzeScriptBlockAst(ScriptBlockAst scriptBlockAst, string fileName)
|
113 | 113 | {
|
| 114 | + // TODO: add other Cmdlets like invoke-command later? |
114 | 115 | var foreachObjectCmdletNamesAndAliases = Helper.Instance.CmdletNameAndAliases("Foreach-Object");
|
115 | 116 |
|
116 |
| - // Find all commandAst objects for `Foreach-Object -Parallel`. As for parametername matching, there are three |
| 117 | + // Find all commandAst objects for `Foreach-Object -Parallel`. As for parameter name matching, there are three |
117 | 118 | // parameters starting with a 'p': Parallel, PipelineVariable and Process, so we use startsWith 'pa' as the shortest unambiguous form.
|
118 | 119 | // Because we are already going trough all ScriptBlockAst objects, we do not need to look for nested script blocks here.
|
119 |
| - if (!(scriptBlockAst.FindAll( |
| 120 | + var foreachObjectParallelCommandAsts = scriptBlockAst.FindAll( |
120 | 121 | predicate: c => c is CommandAst commandAst &&
|
121 |
| - foreachObjectCmdletNamesAndAliases.Contains(commandAst.GetCommandName(), StringComparer.OrdinalIgnoreCase) && |
122 |
| - commandAst.CommandElements.Any( |
123 |
| - e => e is CommandParameterAst parameterAst && |
124 |
| - parameterAst.ParameterName.StartsWith("pa", StringComparison.OrdinalIgnoreCase)), |
125 |
| - searchNestedScriptBlocks: true) is IEnumerable<Ast> foreachObjectParallelAsts)) |
| 122 | + foreachObjectCmdletNamesAndAliases.Contains( |
| 123 | + commandAst.GetCommandName(), StringComparer.OrdinalIgnoreCase) && |
| 124 | + commandAst.CommandElements.Any( |
| 125 | + e => e is CommandParameterAst parameterAst && |
| 126 | + parameterAst.ParameterName.StartsWith("pa", StringComparison.OrdinalIgnoreCase)), |
| 127 | + searchNestedScriptBlocks: false).Select(a=>a as CommandAst); |
| 128 | + |
| 129 | + foreach (var commandAst in foreachObjectParallelCommandAsts) |
126 | 130 | {
|
127 |
| - yield break; |
128 |
| - } |
129 |
| - |
130 |
| - foreach (var ast in foreachObjectParallelAsts) |
131 |
| - { |
132 |
| - var commandAst = ast as CommandAst; |
133 |
| - |
134 |
| - if (commandAst == null) |
135 |
| - { |
136 |
| - continue; |
137 |
| - } |
138 |
| - |
| 131 | + if (commandAst == null) |
| 132 | + yield break; |
| 133 | + |
| 134 | + // Find all variables that are assigned within this ScriptBlock |
139 | 135 | var varsInAssignments = commandAst.FindAll(
|
140 |
| - predicate: a => a is AssignmentStatementAst assignment && |
141 |
| - assignment.Left.FindAll( |
142 |
| - predicate: aa => aa is VariableExpressionAst, |
143 |
| - searchNestedScriptBlocks: true) != null, |
144 |
| - searchNestedScriptBlocks: true); |
145 |
| - |
146 |
| - var commandElements = commandAst.CommandElements; |
147 |
| - var nonAssignedNonUsingVars = new List<Ast>() { }; |
148 |
| - foreach (var cmdEl in commandElements) |
149 |
| - { |
150 |
| - nonAssignedNonUsingVars.AddRange( |
151 |
| - cmdEl.FindAll( |
152 |
| - predicate: aa => aa is VariableExpressionAst varAst && |
153 |
| - !(varAst.Parent is UsingExpressionAst) && |
154 |
| - !varsInAssignments.Contains(varAst), true)); |
155 |
| - } |
| 136 | + predicate: a => a is VariableExpressionAst varExpr && |
| 137 | + varExpr.Parent is AssignmentStatementAst assignment && |
| 138 | + assignment.Left.Equals(varExpr), |
| 139 | + searchNestedScriptBlocks: true). |
| 140 | + Select(a => a as VariableExpressionAst); |
| 141 | + |
| 142 | + // Find all variables that are not locally assigned, and don't have $using: directive |
| 143 | + var nonAssignedNonUsingVars = commandAst.CommandElements. |
| 144 | + SelectMany(a => a.FindAll( |
| 145 | + predicate: aa => aa is VariableExpressionAst varAst && |
| 146 | + !(varAst.Parent is UsingExpressionAst) && |
| 147 | + !varsInAssignments.Contains(varAst), |
| 148 | + searchNestedScriptBlocks: true). |
| 149 | + Select(aaa => aaa as VariableExpressionAst)); |
156 | 150 |
|
157 | 151 | foreach (var variableExpression in nonAssignedNonUsingVars)
|
158 | 152 | {
|
159 |
| - var _temp = variableExpression as VariableExpressionAst; |
160 |
| - |
161 | 153 | yield return new DiagnosticRecord(
|
162 | 154 | message: string.Format(CultureInfo.CurrentCulture,
|
163 | 155 | Strings.AvoidUnInitializedVarsInNewRunspacesError, variableExpression.ToString()),
|
164 | 156 | extent: variableExpression.Extent,
|
165 | 157 | ruleName: GetName(),
|
166 | 158 | severity: DiagnosticSeverity.Warning,
|
167 | 159 | scriptPath: fileName,
|
168 |
| - ruleId: _temp?.ToString()); |
| 160 | + ruleId: variableExpression.ToString()); |
169 | 161 | }
|
170 | 162 | }
|
171 | 163 | }
|
|
0 commit comments