1+ // Copyright (c) Microsoft Corporation. All rights reserved.
2+ // Licensed under the MIT License.
3+
4+ using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
5+ using System ;
6+ using System . Collections . Generic ;
7+ using System . Globalization ;
8+ using System . Management . Automation . Language ;
9+ using System . Linq ;
10+
11+ #if ! CORECLR
12+ using System . ComponentModel . Composition ;
13+ #endif
14+
15+ namespace Microsoft . Windows . PowerShell . ScriptAnalyzer . BuiltinRules
16+ {
17+ #if ! CORECLR
18+ [ Export ( typeof ( IScriptRule ) ) ]
19+ #endif
20+
21+ /// <summary>
22+ /// Rule that warns when reserved words are used as function names
23+ /// </summary>
24+ public class AvoidDynamicVariableNames : IScriptRule
25+ {
26+ /// <summary>
27+ /// Analyzes the PowerShell AST for uses of reserved words as function names.
28+ /// </summary>
29+ /// <param name="ast">The PowerShell Abstract Syntax Tree to analyze.</param>
30+ /// <param name="fileName">The name of the file being analyzed (for diagnostic reporting).</param>
31+ /// <returns>A collection of diagnostic records for each violation.</returns>
32+ public IEnumerable < DiagnosticRecord > AnalyzeScript ( Ast ast , string fileName )
33+ {
34+ if ( ast == null ) throw new ArgumentNullException ( Strings . NullAstErrorMessage ) ;
35+
36+ // Find all FunctionDefinitionAst in the Ast
37+ var newVariableAsts = ast . FindAll ( testAst =>
38+ testAst is CommandAst cmdAst &&
39+ (
40+ String . Equals ( cmdAst . GetCommandName ( ) , "New-Variable" , StringComparison . OrdinalIgnoreCase ) ||
41+ String . Equals ( cmdAst . GetCommandName ( ) , "Set-Variable" , StringComparison . OrdinalIgnoreCase )
42+ ) ,
43+ true
44+ ) ;
45+
46+ foreach ( CommandAst newVariableAst in newVariableAsts )
47+ {
48+ // Use StaticParameterBinder to reliably get parameter values
49+ var bindingResult = StaticParameterBinder . BindCommand ( newVariableAst , true ) ;
50+
51+ // Dynamic parameters return null for the ConstantValue property
52+ if (
53+ bindingResult . BoundParameters . ContainsKey ( "Name" ) &&
54+ bindingResult . BoundParameters [ "Name" ] == null
55+ )
56+ {
57+ yield return new DiagnosticRecord (
58+ string . Format (
59+ CultureInfo . CurrentCulture ,
60+ Strings . AvoidDynamicVariableNamesError ,
61+ newVariableAst . Parent . Extent . Text ) ,
62+ newVariableAst . Parent . Extent ,
63+ GetName ( ) ,
64+ DiagnosticSeverity . Warning ,
65+ fileName
66+ ) ;
67+ }
68+ }
69+ }
70+
71+ public string GetCommonName ( ) => Strings . AvoidDynamicVariableNamesCommonName ;
72+
73+ public string GetDescription ( ) => Strings . AvoidDynamicVariableNamesDescription ;
74+
75+ public string GetName ( ) => string . Format (
76+ CultureInfo . CurrentCulture ,
77+ Strings . NameSpaceFormat ,
78+ GetSourceName ( ) ,
79+ Strings . AvoidDynamicVariableNamesName ) ;
80+
81+ public RuleSeverity GetSeverity ( ) => RuleSeverity . Warning ;
82+
83+ public string GetSourceName ( ) => Strings . SourceName ;
84+
85+ public SourceType GetSourceType ( ) => SourceType . Builtin ;
86+ }
87+ }
0 commit comments