@@ -51,55 +51,113 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
5151 {
5252 throw new ArgumentNullException ( Strings . NullAstErrorMessage ) ;
5353 }
54- IEnumerable < Ast > funcDefAsts = ast . FindAll ( testAst => testAst is FunctionDefinitionAst , true ) ;
55- foreach ( FunctionDefinitionAst funcDefAst in funcDefAsts )
54+ IEnumerable < Ast > funcDefWithNoShouldProcessAttrAsts = ast . FindAll ( IsStateChangingFunctionWithNoShouldProcessAttribute , true ) ;
55+ foreach ( FunctionDefinitionAst funcDefAst in funcDefWithNoShouldProcessAttrAsts )
5656 {
57- string funcName = funcDefAst . Name ;
58- bool hasShouldProcessAttribute = false ;
59- var targetFuncName = this . stateChangingVerbs . Where (
60- elem => funcName . StartsWith (
61- elem ,
62- StringComparison . OrdinalIgnoreCase ) ) . FirstOrDefault ( ) ;
63- if ( targetFuncName != null )
57+ yield return new DiagnosticRecord (
58+ string . Format ( CultureInfo . CurrentCulture , Strings . UseShouldProcessForStateChangingFunctionsError , funcDefAst . Name ) ,
59+ funcDefAst . Extent ,
60+ this . GetName ( ) ,
61+ DiagnosticSeverity . Warning ,
62+ fileName ) ;
63+ }
64+
65+ }
66+ /// <summary>
67+ /// Checks if the ast defines a state changing function
68+ /// </summary>
69+ /// <param name="ast"></param>
70+ /// <returns>Returns true or false</returns>
71+ private bool IsStateChangingFunctionWithNoShouldProcessAttribute ( Ast ast )
72+ {
73+ var funcDefAst = ast as FunctionDefinitionAst ;
74+ if ( funcDefAst == null )
75+ {
76+ return false ;
77+ }
78+ string funcName = funcDefAst . Name ;
79+ var targetFuncName = this . stateChangingVerbs . Where (
80+ elem => funcName . StartsWith (
81+ elem ,
82+ StringComparison . OrdinalIgnoreCase ) ) . FirstOrDefault ( ) ;
83+ if ( targetFuncName == null
84+ || funcDefAst . Body == null
85+ || funcDefAst . Body . ParamBlock == null
86+ || funcDefAst . Body . ParamBlock . Attributes == null
87+ || ! funcDefAst . Body . ParamBlock . Attributes . Any (
88+ attr => attr . TypeName . GetReflectionType ( ) == typeof ( CmdletBindingAttribute ) ) )
89+ {
90+ return false ;
91+ }
92+ return ! HasShouldProcessTrue ( funcDefAst . Body . ParamBlock . Attributes ) ;
93+ }
94+
95+ /// <summary>
96+ /// Checks if an attribute has SupportShouldProcess set to $true
97+ /// </summary>
98+ /// <param name="attributeAsts"></param>
99+ /// <returns>Returns true or false</returns>
100+ private bool HasShouldProcessTrue ( IEnumerable < AttributeAst > attributeAsts )
101+ {
102+ if ( attributeAsts == null )
103+ {
104+ return false ;
105+ }
106+ foreach ( var attributeAst in attributeAsts )
107+ {
108+ if ( attributeAst == null || attributeAst . NamedArguments == null )
109+ {
110+ continue ;
111+ }
112+ foreach ( var namedAttributeAst in attributeAst . NamedArguments )
64113 {
65- IEnumerable < Ast > attributeAsts = funcDefAst . FindAll ( testAst => testAst is NamedAttributeArgumentAst , true ) ;
66- if ( funcDefAst . Body != null && funcDefAst . Body . ParamBlock != null
67- && funcDefAst . Body . ParamBlock . Attributes != null &&
68- funcDefAst . Body . ParamBlock . Parameters != null )
114+ if ( namedAttributeAst == null )
69115 {
70- if ( ! funcDefAst . Body . ParamBlock . Attributes . Any ( attr => attr . TypeName . GetReflectionType ( ) == typeof ( CmdletBindingAttribute ) ) )
71- {
72- continue ;
73- }
116+ continue ;
117+ }
118+ if ( ! IsShouldProcessAttribute ( namedAttributeAst ) )
119+ {
120+ continue ;
121+ }
122+ return IsShouldProcessTrue ( namedAttributeAst ) ;
123+ }
124+ }
125+ // Cannot find any SupportShouldProcess attribute
126+ return false ;
127+ }
74128
75- foreach ( NamedAttributeArgumentAst attributeAst in attributeAsts )
76- {
77- if ( attributeAst . ArgumentName . Equals ( "SupportsShouldProcess" , StringComparison . OrdinalIgnoreCase ) )
78- {
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
89- {
90- hasShouldProcessAttribute = true ;
91- }
92- }
93- }
129+ /// <summary>
130+ /// Checks if the named attribute is SupportShouldProcess
131+ /// </summary>
132+ /// <param name="namedAttributeArgumentAst"></param>
133+ /// <returns>Returns true or false</returns>
134+ private bool IsShouldProcessAttribute ( NamedAttributeArgumentAst namedAttributeArgumentAst )
135+ {
136+ return namedAttributeArgumentAst != null
137+ && namedAttributeArgumentAst . ArgumentName . Equals ( "SupportsShouldProcess" , StringComparison . OrdinalIgnoreCase ) ;
138+ }
94139
95- if ( ! hasShouldProcessAttribute )
96- {
97- yield return
98- new DiagnosticRecord ( string . Format ( CultureInfo . CurrentCulture , Strings . UseShouldProcessForStateChangingFunctionsError , funcName ) , funcDefAst . Extent , this . GetName ( ) , DiagnosticSeverity . Warning , fileName ) ;
99- }
100- }
140+ /// <summary>
141+ /// Checks if the SupportShouldProcess attribute is true
142+ /// </summary>
143+ /// <param name="namedAttributeArgumentAst"></param>
144+ /// <returns>Returns true or false</returns>
145+ private bool IsShouldProcessTrue ( NamedAttributeArgumentAst namedAttributeArgumentAst )
146+ {
147+ if ( namedAttributeArgumentAst . ExpressionOmitted )
148+ {
149+ return true ;
150+ }
151+ else
152+ {
153+ var varExpAst = namedAttributeArgumentAst . Argument as VariableExpressionAst ;
154+ if ( varExpAst != null
155+ && varExpAst . VariablePath . UserPath . Equals ( "true" , StringComparison . OrdinalIgnoreCase ) )
156+ {
157+ return true ;
101158 }
102159 }
160+ return false ;
103161 }
104162
105163 /// <summary>
0 commit comments