1
- //
2
1
// Copyright (c) Microsoft Corporation.
3
2
//
4
3
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
8
7
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9
8
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10
9
// THE SOFTWARE.
11
- //
12
10
13
11
using System ;
14
12
using System . Collections . Generic ;
15
13
using System . Management . Automation . Language ;
16
- using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
17
14
#if ! CORECLR
18
15
using System . ComponentModel . Composition ;
19
16
#endif
20
17
using System . Globalization ;
18
+ using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
21
19
22
20
namespace Microsoft . Windows . PowerShell . ScriptAnalyzer . BuiltinRules
23
21
{
@@ -37,77 +35,24 @@ public class UseDeclaredVarsMoreThanAssignments : IScriptRule
37
35
/// <returns>A List of results from this rule</returns>
38
36
public IEnumerable < DiagnosticRecord > AnalyzeScript ( Ast ast , string fileName )
39
37
{
40
- if ( ast == null ) throw new ArgumentNullException ( Strings . NullAstErrorMessage ) ;
41
-
42
- IEnumerable < Ast > assignmentAsts = ast . FindAll ( testAst => testAst is AssignmentStatementAst , true ) ;
43
- IEnumerable < Ast > varAsts = ast . FindAll ( testAst => testAst is VariableExpressionAst , true ) ;
44
- IEnumerable < Ast > varsInAssignment ;
45
-
46
- Dictionary < string , AssignmentStatementAst > assignments = new Dictionary < string , AssignmentStatementAst > ( StringComparer . OrdinalIgnoreCase ) ;
47
-
48
- string varKey ;
49
- bool inAssignment ;
50
-
51
- if ( assignmentAsts != null )
38
+ if ( ast == null )
52
39
{
53
- foreach ( AssignmentStatementAst assignmentAst in assignmentAsts )
54
- {
55
- // Only checks for the case where lhs is a variable. Ignore things like $foo.property
56
- VariableExpressionAst assignmentVarAst = assignmentAst . Left as VariableExpressionAst ;
57
-
58
- if ( assignmentVarAst != null )
59
- {
60
- //Ignore if variable is global or environment variable or scope is function
61
- if ( ! Helper . Instance . IsVariableGlobalOrEnvironment ( assignmentVarAst , ast )
62
- && ! assignmentVarAst . VariablePath . IsScript
63
- && ! string . Equals ( assignmentVarAst . VariablePath . DriveName , "function" , StringComparison . OrdinalIgnoreCase ) )
64
- {
65
-
66
- string variableName = Helper . Instance . VariableNameWithoutScope ( assignmentVarAst . VariablePath ) ;
67
-
68
- if ( ! assignments . ContainsKey ( variableName ) )
69
- {
70
- assignments . Add ( variableName , assignmentAst ) ;
71
- }
72
- }
73
- }
74
- }
40
+ throw new ArgumentNullException ( Strings . NullAstErrorMessage ) ;
75
41
}
76
42
77
- if ( varAsts != null )
43
+ var scriptBlockAsts = ast . FindAll ( x => x is ScriptBlockAst , true ) ;
44
+ if ( scriptBlockAsts == null )
78
45
{
79
- foreach ( VariableExpressionAst varAst in varAsts )
80
- {
81
- varKey = Helper . Instance . VariableNameWithoutScope ( varAst . VariablePath ) ;
82
- inAssignment = false ;
83
-
84
- if ( assignments . ContainsKey ( varKey ) )
85
- {
86
- varsInAssignment = assignments [ varKey ] . Left . FindAll ( testAst => testAst is VariableExpressionAst , true ) ; ;
87
-
88
- //Checks if this variableAst is part of the logged assignment
89
- foreach ( VariableExpressionAst varInAssignment in varsInAssignment )
90
- {
91
- inAssignment |= varInAssignment . Equals ( varAst ) ;
92
- }
93
-
94
- if ( ! inAssignment )
95
- {
96
- assignments . Remove ( varKey ) ;
97
- }
98
- //Check if variable belongs to PowerShell built-in variables
99
- if ( Helper . Instance . HasSpecialVars ( varKey ) )
100
- {
101
- assignments . Remove ( varKey ) ;
102
- }
103
- }
104
- }
46
+ yield break ;
105
47
}
106
48
107
- foreach ( string key in assignments . Keys )
49
+ foreach ( var scriptBlockAst in scriptBlockAsts )
108
50
{
109
- yield return new DiagnosticRecord ( string . Format ( CultureInfo . CurrentCulture , Strings . UseDeclaredVarsMoreThanAssignmentsError , key ) ,
110
- assignments [ key ] . Left . Extent , GetName ( ) , DiagnosticSeverity . Warning , fileName , key ) ;
51
+ var sbAst = scriptBlockAst as ScriptBlockAst ;
52
+ foreach ( var diagnosticRecord in AnalyzeScriptBlockAst ( sbAst , fileName ) )
53
+ {
54
+ yield return diagnosticRecord ;
55
+ }
111
56
}
112
57
}
113
58
@@ -162,10 +107,92 @@ public string GetSourceName()
162
107
{
163
108
return string . Format ( CultureInfo . CurrentCulture , Strings . SourceName ) ;
164
109
}
165
- }
166
110
167
- }
111
+ /// <summary>
112
+ /// Checks if a variable is initialized and referenced in either its assignment or children scopes
113
+ /// </summary>
114
+ /// <param name="scriptBlockAst">Ast of type ScriptBlock</param>
115
+ /// <param name="fileName">Name of file containing the ast</param>
116
+ /// <returns>An enumerable containing diagnostic records</returns>
117
+ private IEnumerable < DiagnosticRecord > AnalyzeScriptBlockAst ( ScriptBlockAst scriptBlockAst , string fileName )
118
+ {
119
+ IEnumerable < Ast > assignmentAsts = scriptBlockAst . FindAll ( testAst => testAst is AssignmentStatementAst , false ) ;
120
+ IEnumerable < Ast > varAsts = scriptBlockAst . FindAll ( testAst => testAst is VariableExpressionAst , true ) ;
121
+ IEnumerable < Ast > varsInAssignment ;
122
+
123
+ Dictionary < string , AssignmentStatementAst > assignments = new Dictionary < string , AssignmentStatementAst > ( StringComparer . OrdinalIgnoreCase ) ;
168
124
125
+ string varKey ;
126
+ bool inAssignment ;
127
+
128
+ if ( assignmentAsts == null )
129
+ {
130
+ yield break ;
131
+ }
132
+
133
+ foreach ( AssignmentStatementAst assignmentAst in assignmentAsts )
134
+ {
135
+ // Only checks for the case where lhs is a variable. Ignore things like $foo.property
136
+ VariableExpressionAst assignmentVarAst = assignmentAst . Left as VariableExpressionAst ;
137
+
138
+ if ( assignmentVarAst != null )
139
+ {
140
+ // Ignore if variable is global or environment variable or scope is function
141
+ if ( ! Helper . Instance . IsVariableGlobalOrEnvironment ( assignmentVarAst , scriptBlockAst )
142
+ && ! assignmentVarAst . VariablePath . IsScript
143
+ && ! string . Equals ( assignmentVarAst . VariablePath . DriveName , "function" , StringComparison . OrdinalIgnoreCase ) )
144
+ {
145
+ string variableName = Helper . Instance . VariableNameWithoutScope ( assignmentVarAst . VariablePath ) ;
146
+
147
+ if ( ! assignments . ContainsKey ( variableName ) )
148
+ {
149
+ assignments . Add ( variableName , assignmentAst ) ;
150
+ }
151
+ }
152
+ }
153
+ }
154
+
155
+ if ( varAsts != null )
156
+ {
157
+ foreach ( VariableExpressionAst varAst in varAsts )
158
+ {
159
+ varKey = Helper . Instance . VariableNameWithoutScope ( varAst . VariablePath ) ;
160
+ inAssignment = false ;
169
161
162
+ if ( assignments . ContainsKey ( varKey ) )
163
+ {
164
+ varsInAssignment = assignments [ varKey ] . Left . FindAll ( testAst => testAst is VariableExpressionAst , true ) ;
170
165
166
+ // Checks if this variableAst is part of the logged assignment
167
+ foreach ( VariableExpressionAst varInAssignment in varsInAssignment )
168
+ {
169
+ inAssignment |= varInAssignment . Equals ( varAst ) ;
170
+ }
171
171
172
+ if ( ! inAssignment )
173
+ {
174
+ assignments . Remove ( varKey ) ;
175
+ }
176
+
177
+ // Check if variable belongs to PowerShell built-in variables
178
+ if ( Helper . Instance . HasSpecialVars ( varKey ) )
179
+ {
180
+ assignments . Remove ( varKey ) ;
181
+ }
182
+ }
183
+ }
184
+ }
185
+
186
+ foreach ( string key in assignments . Keys )
187
+ {
188
+ yield return new DiagnosticRecord (
189
+ string . Format ( CultureInfo . CurrentCulture , Strings . UseDeclaredVarsMoreThanAssignmentsError , key ) ,
190
+ assignments [ key ] . Left . Extent ,
191
+ GetName ( ) ,
192
+ DiagnosticSeverity . Warning ,
193
+ fileName ,
194
+ key ) ;
195
+ }
196
+ }
197
+ }
198
+ }
0 commit comments