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 ) ) ]
@@ -34,53 +33,53 @@ public class UseShouldProcessForStateChangingFunctions : IScriptRule
34
33
/// <returns>A List of diagnostic results of this rule</returns>
35
34
public IEnumerable < DiagnosticRecord > AnalyzeScript ( Ast ast , string fileName )
36
35
{
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" ;
42
- foreach ( FunctionDefinitionAst funcDefAst in funcDefAsts )
36
+ if ( ast == null )
43
37
{
44
- string funcName = funcDefAst . Name ;
45
- 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 ) )
53
- {
54
- IEnumerable < Ast > attributeAsts = funcDefAst . FindAll ( testAst => testAst is NamedAttributeArgumentAst , true ) ;
55
- if ( funcDefAst . Body != null && funcDefAst . Body . ParamBlock != null
56
- && funcDefAst . Body . ParamBlock . Attributes != null &&
57
- funcDefAst . Body . ParamBlock . Parameters != null )
58
- {
59
- if ( ! funcDefAst . Body . ParamBlock . Attributes . Any ( attr => attr . TypeName . GetReflectionType ( ) == typeof ( CmdletBindingAttribute ) ) )
60
- {
61
- continue ;
62
- }
63
-
64
- foreach ( NamedAttributeArgumentAst attributeAst in attributeAsts )
65
- {
66
- if ( attributeAst . ArgumentName . Equals ( supportsShouldProcess , StringComparison . OrdinalIgnoreCase ) )
67
- {
68
- if ( ( attributeAst . Argument . Extent . Text . Equals ( trueString , StringComparison . OrdinalIgnoreCase ) ) && ! attributeAst . ExpressionOmitted ||
69
- attributeAst . ExpressionOmitted )
70
- {
71
- hasShouldProcessAttribute = true ;
72
- }
73
- }
74
- }
38
+ throw new ArgumentNullException ( Strings . NullAstErrorMessage ) ;
39
+ }
40
+ IEnumerable < Ast > funcDefWithNoShouldProcessAttrAsts = ast . FindAll ( IsStateChangingFunctionWithNoShouldProcessAttribute , true ) ;
41
+ foreach ( FunctionDefinitionAst funcDefAst in funcDefWithNoShouldProcessAttrAsts )
42
+ {
43
+ yield return new DiagnosticRecord (
44
+ string . Format ( CultureInfo . CurrentCulture , Strings . UseShouldProcessForStateChangingFunctionsError , funcDefAst . Name ) ,
45
+ Helper . Instance . GetScriptExtentForFunctionName ( funcDefAst ) ,
46
+ this . GetName ( ) ,
47
+ DiagnosticSeverity . Warning ,
48
+ fileName ) ;
49
+ }
50
+
51
+ }
52
+ /// <summary>
53
+ /// Checks if the ast defines a state changing function
54
+ /// </summary>
55
+ /// <param name="ast"></param>
56
+ /// <returns>Returns true or false</returns>
57
+ private bool IsStateChangingFunctionWithNoShouldProcessAttribute ( Ast ast )
58
+ {
59
+ var funcDefAst = ast as FunctionDefinitionAst ;
60
+ if ( funcDefAst == null )
61
+ {
62
+ return false ;
63
+ }
64
+ return Helper . Instance . IsStateChangingFunctionName ( funcDefAst . Name )
65
+ && ( funcDefAst . Body . ParamBlock == null
66
+ || funcDefAst . Body . ParamBlock . Attributes == null
67
+ || ! HasShouldProcessTrue ( funcDefAst . Body . ParamBlock . Attributes ) ) ;
68
+ }
75
69
76
- if ( ! hasShouldProcessAttribute )
77
- {
78
- yield return
79
- new DiagnosticRecord ( string . Format ( CultureInfo . CurrentCulture , Strings . UseShouldProcessForStateChangingFunctionsError , funcName ) , funcDefAst . Extent , GetName ( ) , DiagnosticSeverity . Warning , fileName ) ;
80
- }
81
- }
82
- }
70
+ /// <summary>
71
+ /// Checks if an attribute has SupportShouldProcess set to $true
72
+ /// </summary>
73
+ /// <param name="attributeAsts"></param>
74
+ /// <returns>Returns true or false</returns>
75
+ private bool HasShouldProcessTrue ( IEnumerable < AttributeAst > attributeAsts )
76
+ {
77
+ var shouldProcessAttributeAst = Helper . Instance . GetShouldProcessAttributeAst ( attributeAsts ) ;
78
+ if ( shouldProcessAttributeAst == null )
79
+ {
80
+ return false ;
83
81
}
82
+ return Helper . Instance . GetNamedArgumentAttributeValue ( shouldProcessAttributeAst ) ;
84
83
}
85
84
86
85
/// <summary>
@@ -89,7 +88,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
89
88
/// <returns>The name of this rule</returns>
90
89
public string GetName ( )
91
90
{
92
- return string . Format ( CultureInfo . CurrentCulture , Strings . NameSpaceFormat , GetSourceName ( ) , Strings . UseShouldProcessForStateChangingFunctionsName ) ;
91
+ return string . Format ( CultureInfo . CurrentCulture , Strings . NameSpaceFormat , this . GetSourceName ( ) , Strings . UseShouldProcessForStateChangingFunctionsName ) ;
93
92
}
94
93
95
94
/// <summary>
@@ -111,8 +110,9 @@ public string GetDescription()
111
110
}
112
111
113
112
/// <summary>
114
- /// GetSourceType: Retrieves the type of the rule: builtin , managed or module.
113
+ /// GetSourceType: Retrieves the type of the rule: built-in , managed or module.
115
114
/// </summary>
115
+ /// <returns>Source type {PS, PSDSC}</returns>
116
116
public SourceType GetSourceType ( )
117
117
{
118
118
return SourceType . Builtin ;
@@ -121,20 +121,19 @@ public SourceType GetSourceType()
121
121
/// <summary>
122
122
/// GetSeverity: Retrieves the severity of the rule: error, warning of information.
123
123
/// </summary>
124
- /// <returns></returns>
124
+ /// <returns>Rule severity {Information, Warning, Error} </returns>
125
125
public RuleSeverity GetSeverity ( )
126
126
{
127
127
return RuleSeverity . Warning ;
128
128
}
129
129
130
-
131
130
/// <summary>
132
131
/// GetSourceName: Retrieves the module/assembly name the rule is from.
133
132
/// </summary>
133
+ /// <returns>Source name</returns>
134
134
public string GetSourceName ( )
135
135
{
136
136
return string . Format ( CultureInfo . CurrentCulture , Strings . SourceName ) ;
137
137
}
138
138
}
139
-
140
139
}
0 commit comments