1
- using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
2
- using System ;
1
+ using System ;
3
2
using System . Collections . Generic ;
4
3
#if ! CORECLR
5
4
using System . ComponentModel . Composition ;
6
5
#endif
7
6
using System . Globalization ;
8
7
using System . Management . Automation . Language ;
8
+ using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
9
9
10
10
namespace Microsoft . Windows . PowerShell . ScriptAnalyzer . BuiltinRules
11
11
{
@@ -17,6 +17,12 @@ class AvoidGlobalAliases : AstVisitor, IScriptRule
17
17
private List < DiagnosticRecord > records ;
18
18
private string fileName ;
19
19
20
+ /// <summary>
21
+ /// Analyzes the ast to check that global aliases are not used.
22
+ /// </summary>
23
+ /// <param name="ast">The script's ast</param>
24
+ /// <param name="fileName">The script's file name</param>
25
+ /// <returns>A List of diagnostic results of this rule</returns>
20
26
public IEnumerable < DiagnosticRecord > AnalyzeScript ( Ast ast , string fileName )
21
27
{
22
28
if ( ast == null )
@@ -27,12 +33,20 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
27
33
records = new List < DiagnosticRecord > ( ) ;
28
34
this . fileName = fileName ;
29
35
30
- ast . Visit ( this ) ;
36
+ if ( IsScriptModule ( ) )
37
+ {
38
+ ast . Visit ( this ) ;
39
+ }
31
40
32
41
return records ;
33
42
}
34
43
35
44
#region VisitCommand functions
45
+ /// <summary>
46
+ /// Analyzes a CommandAst, if it is a New-Alias command, the AST is further analyzed.
47
+ /// </summary>
48
+ /// <param name="commandAst">The CommandAst to be analyzed</param>
49
+ /// <returns>AstVisitAction to continue to analyze the ast's children</returns>
36
50
public override AstVisitAction VisitCommand ( CommandAst commandAst )
37
51
{
38
52
if ( ! IsNewAliasCmdlet ( commandAst ) )
@@ -43,11 +57,19 @@ public override AstVisitAction VisitCommand(CommandAst commandAst)
43
57
return AstVisitAction . Continue ;
44
58
}
45
59
60
+ /// <summary>
61
+ /// Analyzes a CommandParameterAst for the global scope.
62
+ /// </summary>
63
+ /// <param name="commandParameterAst">The CommandParameterAst to be analyzed</param>
64
+ /// <returns>AstVisitAction to skip child ast processing after creating any diagnostic records</returns>
46
65
public override AstVisitAction VisitCommandParameter ( CommandParameterAst commandParameterAst )
47
66
{
48
67
if ( IsScopeParameterForNewAliasCmdlet ( commandParameterAst ) )
49
68
{
50
- if ( ( commandParameterAst . Argument != null ) // if the cmdlet looks like -Scope:Global check Parameter.Argument
69
+ // Check the commandParameterAst Argument property if it exist. This covers the case
70
+ // of the cmdlet looking like "New-Alias -Scope:Global"
71
+
72
+ if ( ( commandParameterAst . Argument != null )
51
73
&& ( commandParameterAst . Argument . ToString ( ) . Equals ( "Global" , StringComparison . OrdinalIgnoreCase ) ) )
52
74
{
53
75
records . Add ( new DiagnosticRecord (
@@ -60,18 +82,22 @@ public override AstVisitAction VisitCommandParameter(CommandParameterAst command
60
82
}
61
83
else
62
84
{
63
- var nextAst = FindNextAst ( commandParameterAst ) ;
85
+ // If the commandParameterAst Argument property is null the next ast in the tree
86
+ // can still be a string const. This covers the case of the cmdlet looking like
87
+ // "New-Alias -Scope Global"
88
+
89
+ var nextAst = FindNextAst ( commandParameterAst ) as StringConstantExpressionAst ;
64
90
65
- if ( ( nextAst is StringConstantExpressionAst ) // if the cmdlet looks like -Scope Global
66
- && ( ( ( StringConstantExpressionAst ) nextAst ) . Value . ToString ( ) . Equals ( "Global" , StringComparison . OrdinalIgnoreCase ) ) )
91
+ if ( ( nextAst != null )
92
+ && ( ( nextAst ) . Value . ToString ( ) . Equals ( "Global" , StringComparison . OrdinalIgnoreCase ) ) )
67
93
{
68
94
records . Add ( new DiagnosticRecord (
69
95
string . Format ( CultureInfo . CurrentCulture , Strings . AvoidGlobalAliasesError ) ,
70
- ( ( StringConstantExpressionAst ) nextAst ) . Extent ,
96
+ ( nextAst ) . Extent ,
71
97
GetName ( ) ,
72
98
DiagnosticSeverity . Warning ,
73
99
fileName ,
74
- ( ( StringConstantExpressionAst ) nextAst ) . Value ) ) ;
100
+ ( nextAst ) . Value ) ) ;
75
101
}
76
102
}
77
103
}
@@ -80,6 +106,11 @@ public override AstVisitAction VisitCommandParameter(CommandParameterAst command
80
106
}
81
107
#endregion
82
108
109
+ /// <summary>
110
+ /// Returns the next ast of the same level in the ast tree.
111
+ /// </summary>
112
+ /// <param name="ast">Ast used as a base</param>
113
+ /// <returns>Next ast of the same level in the ast tree</returns>
83
114
private Ast FindNextAst ( Ast ast )
84
115
{
85
116
IEnumerable < Ast > matchingLevelAsts = ast . Parent . FindAll ( item => item is Ast , true ) ;
@@ -106,6 +137,12 @@ private Ast FindNextAst(Ast ast)
106
137
return currentClosest ;
107
138
}
108
139
140
+ /// <summary>
141
+ /// Determines if ast1 is after ast2 in the ast tree.
142
+ /// </summary>
143
+ /// <param name="ast1">First ast</param>
144
+ /// <param name="ast2">Second ast</param>
145
+ /// <returns>True if ast2 is after ast1 in the ast tree</returns>
109
146
private bool IsAstAfter ( Ast ast1 , Ast ast2 )
110
147
{
111
148
if ( ast1 . Extent . EndLineNumber > ast2 . Extent . StartLineNumber ) // ast1 ends on a line after ast2 starts
@@ -129,23 +166,31 @@ private bool IsAstAfter(Ast ast1, Ast ast2)
129
166
}
130
167
}
131
168
169
+ /// <summary>
170
+ /// Determines if CommandParameterAst is for the "Scope" parameter.
171
+ /// </summary>
172
+ /// <param name="commandParameterAst">CommandParameterAst to validate</param>
173
+ /// <returns>True if the CommandParameterAst is for the Scope parameter</returns>
132
174
private bool IsScopeParameterForNewAliasCmdlet ( CommandParameterAst commandParameterAst )
133
175
{
134
176
if ( commandParameterAst == null || commandParameterAst . ParameterName == null )
135
177
{
136
178
return false ;
137
179
}
138
180
139
- if ( commandParameterAst . ParameterName . Equals ( "Scope" , StringComparison . OrdinalIgnoreCase )
140
- && ( commandParameterAst . Parent is CommandAst )
141
- && IsNewAliasCmdlet ( ( CommandAst ) commandParameterAst . Parent ) )
181
+ if ( commandParameterAst . ParameterName . Equals ( "Scope" , StringComparison . OrdinalIgnoreCase ) )
142
182
{
143
183
return true ;
144
184
}
145
185
146
186
return false ;
147
187
}
148
188
189
+ /// <summary>
190
+ /// Determines if CommandAst is for the "New-Alias" command, checking aliases.
191
+ /// </summary>
192
+ /// <param name="commandAst">CommandAst to validate</param>
193
+ /// <returns>True if the CommandAst is for the "New-Alias" command</returns>
149
194
private bool IsNewAliasCmdlet ( CommandAst commandAst )
150
195
{
151
196
if ( commandAst == null || commandAst . GetCommandName ( ) == null )
@@ -162,6 +207,15 @@ private bool IsNewAliasCmdlet(CommandAst commandAst)
162
207
return false ;
163
208
}
164
209
210
+ /// <summary>
211
+ /// Determines if analyzing a script module.
212
+ /// </summary>
213
+ /// <returns>True is file name ends with ".psm1"</returns>
214
+ private bool IsScriptModule ( )
215
+ {
216
+ return fileName . EndsWith ( ".psm1" ) ;
217
+ }
218
+
165
219
public string GetCommonName ( )
166
220
{
167
221
return string . Format ( CultureInfo . CurrentCulture , Strings . AvoidGlobalAliasesCommonName ) ;
0 commit comments