1
- //
2
- // Copyright (c) Microsoft Corporation.
1
+ // Copyright (c) Microsoft Corporation.
3
2
//
4
3
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5
4
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
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
- using System . Linq ;
13
+ using System . ComponentModel . Composition ;
16
14
using System . Management . Automation ;
17
15
using System . Management . Automation . Language ;
18
- using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
19
- using System . ComponentModel . Composition ;
20
16
using System . Globalization ;
17
+ using System . Linq ;
18
+ using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
21
19
22
20
namespace Microsoft . Windows . PowerShell . ScriptAnalyzer . BuiltinRules
23
- { /// <summary>
21
+ {
22
+ /// <summary>
24
23
/// UseShouldProcessForStateChangingFunctions: Analyzes the ast to check if ShouldProcess is included in Advanced functions if the Verb of the function could change system state.
25
24
/// </summary>
26
25
[ Export ( typeof ( IScriptRule ) ) ]
27
26
public class UseShouldProcessForStateChangingFunctions : IScriptRule
28
27
{
28
+ /// <summary>
29
+ /// Array of verbs that can potentially change the state of a system
30
+ /// </summary>
31
+ private string [ ] stateChangingVerbs =
32
+ {
33
+ "Restart-" ,
34
+ "Stop-" ,
35
+ "New-" ,
36
+ "Set-" ,
37
+ "Update-" ,
38
+ "Reset-" ,
39
+ "Remove-"
40
+ } ;
41
+
29
42
/// <summary>
30
43
/// AnalyzeScript: Analyzes the ast to check if ShouldProcess is included in Advanced functions if the Verb of the function could change system state.
31
44
/// </summary>
@@ -34,39 +47,45 @@ public class UseShouldProcessForStateChangingFunctions : IScriptRule
34
47
/// <returns>A List of diagnostic results of this rule</returns>
35
48
public IEnumerable < DiagnosticRecord > AnalyzeScript ( Ast ast , string fileName )
36
49
{
37
- if ( ast == null ) throw new ArgumentNullException ( Strings . NullAstErrorMessage ) ;
38
-
39
- IEnumerable < Ast > funcDefAsts = ast . FindAll ( testAst => testAst is FunctionDefinitionAst , true ) ;
40
- string supportsShouldProcess = "SupportsShouldProcess" ;
41
- string trueString = "$ true" ;
50
+ if ( ast == null )
51
+ {
52
+ throw new ArgumentNullException ( Strings . NullAstErrorMessage ) ;
53
+ }
54
+ IEnumerable < Ast > funcDefAsts = ast . FindAll ( testAst => testAst is FunctionDefinitionAst , true ) ;
42
55
foreach ( FunctionDefinitionAst funcDefAst in funcDefAsts )
43
56
{
44
57
string funcName = funcDefAst . Name ;
45
58
bool hasShouldProcessAttribute = false ;
46
-
47
- if ( funcName . StartsWith ( "Restart-" , StringComparison . OrdinalIgnoreCase ) ||
48
- funcName . StartsWith ( "Stop-" , StringComparison . OrdinalIgnoreCase ) ||
49
- funcName . StartsWith ( "New-" , StringComparison . OrdinalIgnoreCase ) ||
50
- funcName . StartsWith ( "Set-" , StringComparison . OrdinalIgnoreCase ) ||
51
- funcName . StartsWith ( "Update-" , StringComparison . OrdinalIgnoreCase ) ||
52
- funcName . StartsWith ( "Reset-" , StringComparison . OrdinalIgnoreCase ) )
59
+ var targetFuncName = this . stateChangingVerbs . Where (
60
+ elem => funcName . StartsWith (
61
+ elem ,
62
+ StringComparison . OrdinalIgnoreCase ) ) . FirstOrDefault ( ) ;
63
+ if ( targetFuncName != null )
53
64
{
54
65
IEnumerable < Ast > attributeAsts = funcDefAst . FindAll ( testAst => testAst is NamedAttributeArgumentAst , true ) ;
55
66
if ( funcDefAst . Body != null && funcDefAst . Body . ParamBlock != null
56
67
&& funcDefAst . Body . ParamBlock . Attributes != null &&
57
68
funcDefAst . Body . ParamBlock . Parameters != null )
58
69
{
59
- if ( ! funcDefAst . Body . ParamBlock . Attributes . Any ( attr => attr . TypeName . GetReflectionType ( ) == typeof ( CmdletBindingAttribute ) ) )
70
+ if ( ! funcDefAst . Body . ParamBlock . Attributes . Any ( attr => attr . TypeName . GetReflectionType ( ) == typeof ( CmdletBindingAttribute ) ) )
60
71
{
61
72
continue ;
62
73
}
63
74
64
75
foreach ( NamedAttributeArgumentAst attributeAst in attributeAsts )
65
76
{
66
- if ( attributeAst . ArgumentName . Equals ( supportsShouldProcess , StringComparison . OrdinalIgnoreCase ) )
77
+ if ( attributeAst . ArgumentName . Equals ( "SupportsShouldProcess" , StringComparison . OrdinalIgnoreCase ) )
67
78
{
68
- if ( ( attributeAst . Argument . Extent . Text . Equals ( trueString , StringComparison . OrdinalIgnoreCase ) ) && ! attributeAst . ExpressionOmitted ||
69
- attributeAst . ExpressionOmitted )
79
+ if ( ! attributeAst . ExpressionOmitted )
80
+ {
81
+ var varExpAst = attributeAst . Argument as VariableExpressionAst ;
82
+ if ( varExpAst != null
83
+ && varExpAst . VariablePath . UserPath . Equals ( "true" , StringComparison . OrdinalIgnoreCase ) )
84
+ {
85
+ hasShouldProcessAttribute = true ;
86
+ }
87
+ }
88
+ else
70
89
{
71
90
hasShouldProcessAttribute = true ;
72
91
}
@@ -76,7 +95,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
76
95
if ( ! hasShouldProcessAttribute )
77
96
{
78
97
yield return
79
- new DiagnosticRecord ( string . Format ( CultureInfo . CurrentCulture , Strings . UseShouldProcessForStateChangingFunctionsError , funcName ) , funcDefAst . Extent , GetName ( ) , DiagnosticSeverity . Warning , fileName ) ;
98
+ new DiagnosticRecord ( string . Format ( CultureInfo . CurrentCulture , Strings . UseShouldProcessForStateChangingFunctionsError , funcName ) , funcDefAst . Extent , this . GetName ( ) , DiagnosticSeverity . Warning , fileName ) ;
80
99
}
81
100
}
82
101
}
@@ -89,7 +108,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
89
108
/// <returns>The name of this rule</returns>
90
109
public string GetName ( )
91
110
{
92
- return string . Format ( CultureInfo . CurrentCulture , Strings . NameSpaceFormat , GetSourceName ( ) , Strings . UseShouldProcessForStateChangingFunctionsName ) ;
111
+ return string . Format ( CultureInfo . CurrentCulture , Strings . NameSpaceFormat , this . GetSourceName ( ) , Strings . UseShouldProcessForStateChangingFunctionsName ) ;
93
112
}
94
113
95
114
/// <summary>
@@ -111,8 +130,9 @@ public string GetDescription()
111
130
}
112
131
113
132
/// <summary>
114
- /// GetSourceType: Retrieves the type of the rule: builtin , managed or module.
133
+ /// GetSourceType: Retrieves the type of the rule: built-in , managed or module.
115
134
/// </summary>
135
+ /// <returns>Source type {PS, PSDSC}</returns>
116
136
public SourceType GetSourceType ( )
117
137
{
118
138
return SourceType . Builtin ;
@@ -121,20 +141,19 @@ public SourceType GetSourceType()
121
141
/// <summary>
122
142
/// GetSeverity: Retrieves the severity of the rule: error, warning of information.
123
143
/// </summary>
124
- /// <returns></returns>
144
+ /// <returns>Rule severity {Information, Warning, Error} </returns>
125
145
public RuleSeverity GetSeverity ( )
126
146
{
127
147
return RuleSeverity . Warning ;
128
148
}
129
149
130
-
131
150
/// <summary>
132
151
/// GetSourceName: Retrieves the module/assembly name the rule is from.
133
152
/// </summary>
153
+ /// <returns>Source name</returns>
134
154
public string GetSourceName ( )
135
155
{
136
156
return string . Format ( CultureInfo . CurrentCulture , Strings . SourceName ) ;
137
157
}
138
158
}
139
-
140
159
}
0 commit comments