1
+ // Copyright (c) Microsoft Corporation. All rights reserved.
2
+ // Licensed under the MIT License.
3
+
4
+ using System ;
5
+ using System . Collections . Generic ;
6
+ using System . Management . Automation . Language ;
7
+ using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
8
+ #if ! CORECLR
9
+ using System . ComponentModel . Composition ;
10
+ #endif
11
+ using System . Globalization ;
12
+
13
+ namespace Microsoft . Windows . PowerShell . ScriptAnalyzer . BuiltinRules
14
+ {
15
+ /// <summary>
16
+ /// UseCorrectCasing: Check if cmdlet is cased correctly.
17
+ /// </summary>
18
+ #if ! CORECLR
19
+ [ Export ( typeof ( IScriptRule ) ) ]
20
+ #endif
21
+ public class UseCorrectCasing : ConfigurableRule
22
+ {
23
+ /// <summary>
24
+ /// AnalyzeScript: Analyze the script to check if cmdlet alias is used.
25
+ /// </summary>
26
+ public override IEnumerable < DiagnosticRecord > AnalyzeScript ( Ast ast , string fileName )
27
+ {
28
+ if ( ast == null ) throw new ArgumentNullException ( Strings . NullAstErrorMessage ) ;
29
+
30
+ IEnumerable < Ast > commandAsts = ast . FindAll ( testAst => testAst is CommandAst , true ) ;
31
+
32
+ // Iterates all CommandAsts and check the command name.
33
+ foreach ( CommandAst commandAst in commandAsts )
34
+ {
35
+ string commandName = commandAst . GetCommandName ( ) ;
36
+
37
+ // Handles the exception caused by commands like, {& $PLINK $args 2> $TempErrorFile}.
38
+ // You can also review the remark section in following document,
39
+ // MSDN: CommandAst.GetCommandName Method
40
+ if ( commandName == null )
41
+ {
42
+ continue ;
43
+ }
44
+
45
+ var commandInfo = Helper . Instance . GetCommandInfo ( commandName ) ;
46
+ if ( commandInfo == null )
47
+ {
48
+ continue ;
49
+ }
50
+
51
+ var shortName = commandInfo . Name ;
52
+ var fullyqualifiedName = $ "{ commandInfo . ModuleName } \\ { shortName } ";
53
+ var isFullyQualified = commandName . Equals ( fullyqualifiedName , StringComparison . OrdinalIgnoreCase ) ;
54
+ var correctlyCasedCommandName = isFullyQualified ? fullyqualifiedName : shortName ;
55
+
56
+ if ( ! commandName . Equals ( correctlyCasedCommandName , StringComparison . Ordinal ) )
57
+ {
58
+ yield return new DiagnosticRecord (
59
+ string . Format ( CultureInfo . CurrentCulture , Strings . UseCorrectCasingError , commandName , shortName ) ,
60
+ GetCommandExtent ( commandAst ) ,
61
+ GetName ( ) ,
62
+ DiagnosticSeverity . Warning ,
63
+ fileName ,
64
+ commandName ,
65
+ suggestedCorrections : GetCorrectionExtent ( commandAst , correctlyCasedCommandName ) ) ;
66
+ }
67
+ }
68
+ }
69
+
70
+ /// <summary>
71
+ /// For a command like "gci -path c:", returns the extent of "gci" in the command
72
+ /// </summary>
73
+ private IScriptExtent GetCommandExtent ( CommandAst commandAst )
74
+ {
75
+ var cmdName = commandAst . GetCommandName ( ) ;
76
+ foreach ( var cmdElement in commandAst . CommandElements )
77
+ {
78
+ var stringConstExpressinAst = cmdElement as StringConstantExpressionAst ;
79
+ if ( stringConstExpressinAst != null )
80
+ {
81
+ if ( stringConstExpressinAst . Value . Equals ( cmdName ) )
82
+ {
83
+ return stringConstExpressinAst . Extent ;
84
+ }
85
+ }
86
+ }
87
+ return commandAst . Extent ;
88
+ }
89
+
90
+ private IEnumerable < CorrectionExtent > GetCorrectionExtent ( CommandAst commandAst , string correctlyCaseName )
91
+ {
92
+ var description = string . Format (
93
+ CultureInfo . CurrentCulture ,
94
+ Strings . UseCorrectCasingDescription ,
95
+ correctlyCaseName ,
96
+ correctlyCaseName ) ;
97
+ var cmdExtent = GetCommandExtent ( commandAst ) ;
98
+ var correction = new CorrectionExtent (
99
+ cmdExtent . StartLineNumber ,
100
+ cmdExtent . EndLineNumber ,
101
+ cmdExtent . StartColumnNumber ,
102
+ cmdExtent . EndColumnNumber ,
103
+ correctlyCaseName ,
104
+ commandAst . Extent . File ,
105
+ description ) ;
106
+ yield return correction ;
107
+ }
108
+
109
+ /// <summary>
110
+ /// GetName: Retrieves the name of this rule.
111
+ /// </summary>
112
+ /// <returns>The name of this rule</returns>
113
+ public override string GetName ( )
114
+ {
115
+ return string . Format ( CultureInfo . CurrentCulture , Strings . NameSpaceFormat , GetSourceName ( ) , Strings . UseCorrectCasingName ) ;
116
+ }
117
+
118
+ /// <summary>
119
+ /// GetCommonName: Retrieves the common name of this rule.
120
+ /// </summary>
121
+ /// <returns>The common name of this rule</returns>
122
+ public override string GetCommonName ( )
123
+ {
124
+ return string . Format ( CultureInfo . CurrentCulture , Strings . UseCorrectCasingCommonName ) ;
125
+ }
126
+
127
+ /// <summary>
128
+ /// GetDescription: Retrieves the description of this rule.
129
+ /// </summary>
130
+ /// <returns>The description of this rule</returns>
131
+ public override string GetDescription ( )
132
+ {
133
+ return string . Format ( CultureInfo . CurrentCulture , Strings . UseCorrectCasingDescription ) ;
134
+ }
135
+
136
+ /// <summary>
137
+ /// GetSourceType: Retrieves the type of the rule, Builtin, Managed or Module.
138
+ /// </summary>
139
+ public override SourceType GetSourceType ( )
140
+ {
141
+ return SourceType . Builtin ;
142
+ }
143
+
144
+ /// <summary>
145
+ /// GetSeverity: Retrieves the severity of the rule: error, warning of information.
146
+ /// </summary>
147
+ /// <returns></returns>
148
+ public override RuleSeverity GetSeverity ( )
149
+ {
150
+ return RuleSeverity . Information ;
151
+ }
152
+
153
+ /// <summary>
154
+ /// GetSourceName: Retrieves the name of the module/assembly the rule is from.
155
+ /// </summary>
156
+ public override string GetSourceName ( )
157
+ {
158
+ return string . Format ( CultureInfo . CurrentCulture , Strings . SourceName ) ;
159
+ }
160
+ }
161
+ }
0 commit comments