1- using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
2- using System ;
1+ using System ;
32using System . Collections . Generic ;
43#if ! CORECLR
54using System . ComponentModel . Composition ;
65#endif
76using System . Globalization ;
87using System . Management . Automation . Language ;
8+ using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
99
1010namespace Microsoft . Windows . PowerShell . ScriptAnalyzer . BuiltinRules
1111{
@@ -17,6 +17,12 @@ class AvoidGlobalAliases : AstVisitor, IScriptRule
1717 private List < DiagnosticRecord > records ;
1818 private string fileName ;
1919
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>
2026 public IEnumerable < DiagnosticRecord > AnalyzeScript ( Ast ast , string fileName )
2127 {
2228 if ( ast == null )
@@ -27,12 +33,20 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
2733 records = new List < DiagnosticRecord > ( ) ;
2834 this . fileName = fileName ;
2935
30- ast . Visit ( this ) ;
36+ if ( IsScriptModule ( ) )
37+ {
38+ ast . Visit ( this ) ;
39+ }
3140
3241 return records ;
3342 }
3443
3544 #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>
3650 public override AstVisitAction VisitCommand ( CommandAst commandAst )
3751 {
3852 if ( ! IsNewAliasCmdlet ( commandAst ) )
@@ -43,11 +57,19 @@ public override AstVisitAction VisitCommand(CommandAst commandAst)
4357 return AstVisitAction . Continue ;
4458 }
4559
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>
4665 public override AstVisitAction VisitCommandParameter ( CommandParameterAst commandParameterAst )
4766 {
4867 if ( IsScopeParameterForNewAliasCmdlet ( commandParameterAst ) )
4968 {
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 )
5173 && ( commandParameterAst . Argument . ToString ( ) . Equals ( "Global" , StringComparison . OrdinalIgnoreCase ) ) )
5274 {
5375 records . Add ( new DiagnosticRecord (
@@ -60,18 +82,22 @@ public override AstVisitAction VisitCommandParameter(CommandParameterAst command
6082 }
6183 else
6284 {
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 ;
6490
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 ) ) )
6793 {
6894 records . Add ( new DiagnosticRecord (
6995 string . Format ( CultureInfo . CurrentCulture , Strings . AvoidGlobalAliasesError ) ,
70- ( ( StringConstantExpressionAst ) nextAst ) . Extent ,
96+ ( nextAst ) . Extent ,
7197 GetName ( ) ,
7298 DiagnosticSeverity . Warning ,
7399 fileName ,
74- ( ( StringConstantExpressionAst ) nextAst ) . Value ) ) ;
100+ ( nextAst ) . Value ) ) ;
75101 }
76102 }
77103 }
@@ -80,6 +106,11 @@ public override AstVisitAction VisitCommandParameter(CommandParameterAst command
80106 }
81107 #endregion
82108
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>
83114 private Ast FindNextAst ( Ast ast )
84115 {
85116 IEnumerable < Ast > matchingLevelAsts = ast . Parent . FindAll ( item => item is Ast , true ) ;
@@ -106,6 +137,12 @@ private Ast FindNextAst(Ast ast)
106137 return currentClosest ;
107138 }
108139
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>
109146 private bool IsAstAfter ( Ast ast1 , Ast ast2 )
110147 {
111148 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)
129166 }
130167 }
131168
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>
132174 private bool IsScopeParameterForNewAliasCmdlet ( CommandParameterAst commandParameterAst )
133175 {
134176 if ( commandParameterAst == null || commandParameterAst . ParameterName == null )
135177 {
136178 return false ;
137179 }
138180
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 ) )
142182 {
143183 return true ;
144184 }
145185
146186 return false ;
147187 }
148188
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>
149194 private bool IsNewAliasCmdlet ( CommandAst commandAst )
150195 {
151196 if ( commandAst == null || commandAst . GetCommandName ( ) == null )
@@ -162,6 +207,15 @@ private bool IsNewAliasCmdlet(CommandAst commandAst)
162207 return false ;
163208 }
164209
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+
165219 public string GetCommonName ( )
166220 {
167221 return string . Format ( CultureInfo . CurrentCulture , Strings . AvoidGlobalAliasesCommonName ) ;
0 commit comments