1313using System ;
1414using System . Linq ;
1515using System . Collections . Generic ;
16+ using System . Management . Automation ;
1617using System . Management . Automation . Language ;
1718using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
1819#if ! CORECLR
@@ -34,9 +35,7 @@ public class UseShouldProcessCorrectly : IScriptRule
3435 private Ast ast ;
3536 private string fileName ;
3637 private FunctionReferenceDigraph funcDigraph ;
37-
3838 private List < DiagnosticRecord > diagnosticRecords ;
39-
4039 private readonly Vertex shouldProcessVertex ;
4140
4241 public UseShouldProcessCorrectly ( )
@@ -53,13 +52,17 @@ public UseShouldProcessCorrectly()
5352 /// <returns>A List of diagnostic results of this rule</returns>
5453 public IEnumerable < DiagnosticRecord > AnalyzeScript ( Ast ast , string fileName )
5554 {
56- if ( ast == null ) throw new ArgumentNullException ( Strings . NullAstErrorMessage ) ;
55+ if ( ast == null )
56+ {
57+ throw new ArgumentNullException ( Strings . NullAstErrorMessage ) ;
58+ }
5759
5860 diagnosticRecords . Clear ( ) ;
5961 this . ast = ast ;
6062 this . fileName = fileName ;
6163 funcDigraph = new FunctionReferenceDigraph ( ) ;
6264 ast . Visit ( funcDigraph ) ;
65+ CheckForSupportShouldProcess ( ) ;
6366 FindViolations ( ) ;
6467 foreach ( var dr in diagnosticRecords )
6568 {
@@ -226,90 +229,117 @@ private bool UpstreamDeclaresShouldProcess(Vertex v)
226229 /// <returns>True if the given function declares SupportShouldProcess, otherwise null</returns>
227230 private bool DeclaresSupportsShouldProcess ( FunctionDefinitionAst ast )
228231 {
229- if ( ast . Body . ParamBlock == null )
232+ if ( ast . Body . ParamBlock == null
233+ || ast . Body . ParamBlock . Attributes == null )
230234 {
231235 return false ;
232236 }
233237
234- foreach ( var attr in ast . Body . ParamBlock . Attributes )
238+ var shouldProcessAttribute = Helper . Instance . GetShouldProcessAttributeAst ( ast . Body . ParamBlock . Attributes ) ;
239+ if ( shouldProcessAttribute == null )
235240 {
236- if ( attr . NamedArguments == null )
237- {
238- continue ;
239- }
241+ return false ;
242+ }
243+
244+ return Helper . Instance . GetNamedArgumentAttributeValue ( shouldProcessAttribute ) ;
245+ }
240246
241- foreach ( var namedArg in attr . NamedArguments )
247+ private CommandInfo GetCommandInfo ( string cmdName )
248+ {
249+ try
250+ {
251+ using ( var ps = System . Management . Automation . PowerShell . Create ( ) )
242252 {
243- if ( namedArg . ArgumentName . Equals (
244- "SupportsShouldProcess" ,
245- StringComparison . OrdinalIgnoreCase ) )
246- {
247- var argAst = namedArg . Argument as VariableExpressionAst ;
248- if ( argAst != null )
249- {
250- if ( argAst . VariablePath . UserPath . Equals ( "true" , StringComparison . OrdinalIgnoreCase ) )
251- {
252- return true ;
253- }
254- }
255- else
256- {
257- return namedArg . ExpressionOmitted ;
258- }
259- }
253+ var cmdInfo = ps . AddCommand ( "Get-Command" )
254+ . AddArgument ( cmdName )
255+ . Invoke < CommandInfo > ( )
256+ . FirstOrDefault ( ) ;
257+ return cmdInfo ;
260258 }
261259 }
262-
263- return false ;
260+ catch ( System . Management . Automation . CommandNotFoundException )
261+ {
262+ return null ;
263+ }
264264 }
265265
266- private bool DeclaresSupportsShouldProcess ( string cmdName )
266+ private bool SupportsShouldProcess ( string cmdName )
267267 {
268268 if ( String . IsNullOrWhiteSpace ( cmdName ) )
269269 {
270270 return false ;
271271 }
272272
273- try
273+ var cmdInfo = GetCommandInfo ( cmdName ) ;
274+ if ( cmdInfo == null )
274275 {
275- using ( var ps = System . Management . Automation . PowerShell . Create ( ) )
276- {
277- ps . AddCommand ( "Get-command" ) . AddArgument ( "cmdName" ) ;
278- var cmdInfo = ps . Invoke < System . Management . Automation . CmdletInfo > ( ) . FirstOrDefault ( ) ;
279- if ( cmdInfo == null )
280- {
281- return false ;
282- }
283- var attributes = cmdInfo . ImplementingType . GetTypeInfo ( ) . GetCustomAttributes (
284- typeof ( System . Management . Automation . CmdletCommonMetadataAttribute ) ,
285- true ) ;
276+ return false ;
277+ }
286278
287- foreach ( var attr in attributes )
279+ var cmdletInfo = cmdInfo as CmdletInfo ;
280+ if ( cmdletInfo == null )
281+ {
282+ // check if it is of functioninfo type
283+ var funcInfo = cmdInfo as FunctionInfo ;
284+ if ( funcInfo != null
285+ && funcInfo . CmdletBinding
286+ && funcInfo . ScriptBlock != null
287+ && funcInfo . ScriptBlock . Attributes != null )
288+ {
289+ foreach ( var attr in funcInfo . ScriptBlock . Attributes )
288290 {
289- var cmdletAttribute = attr as System . Management . Automation . CmdletAttribute ;
290- if ( cmdletAttribute == null )
291- {
292- continue ;
293- }
294-
295- if ( cmdletAttribute . SupportsShouldProcess )
291+ var cmdletBindingAttr = attr as CmdletBindingAttribute ;
292+ if ( cmdletBindingAttr != null )
296293 {
297- return true ;
294+ return cmdletBindingAttr . SupportsShouldProcess ;
298295 }
299296 }
300- if ( attributes . Count ( ) == 0 )
301- {
302- return false ;
303- }
304297 }
298+
299+ return false ;
305300 }
306- catch ( System . Management . Automation . CommandNotFoundException e )
301+
302+ var attributes = cmdletInfo . ImplementingType . GetTypeInfo ( ) . GetCustomAttributes (
303+ typeof ( System . Management . Automation . CmdletCommonMetadataAttribute ) ,
304+ true ) ;
305+
306+ foreach ( var attr in attributes )
307307 {
308- return false ;
308+ var cmdletAttribute = attr as System . Management . Automation . CmdletAttribute ;
309+ if ( cmdletAttribute != null )
310+ {
311+ return cmdletAttribute . SupportsShouldProcess ;
312+ }
309313 }
310314
311315 return false ;
312316 }
317+
318+ private void CheckForSupportShouldProcess ( )
319+ {
320+ var commandsWithSupportShouldProcess = new List < Vertex > ( ) ;
321+
322+ // for all the vertices without any neighbors check if they support shouldprocess
323+ foreach ( var v in funcDigraph . GetVertices ( ) )
324+ {
325+ if ( funcDigraph . GetOutDegree ( v ) == 0 )
326+ {
327+ if ( SupportsShouldProcess ( v . Name ) )
328+ {
329+ commandsWithSupportShouldProcess . Add ( v ) ;
330+ }
331+ }
332+ }
333+
334+ if ( commandsWithSupportShouldProcess . Count > 0 )
335+ {
336+ funcDigraph . AddVertex ( shouldProcessVertex ) ;
337+ foreach ( var v in commandsWithSupportShouldProcess )
338+ {
339+ funcDigraph . AddEdge ( v , shouldProcessVertex ) ;
340+ }
341+ }
342+ }
313343 }
314344
315345 /// <summary>
@@ -370,7 +400,7 @@ public override bool Equals(Object other)
370400 /// </summary>
371401 public override int GetHashCode ( )
372402 {
373- return name . GetHashCode ( ) ;
403+ return name . ToLower ( ) . GetHashCode ( ) ;
374404 }
375405 }
376406
@@ -484,7 +514,6 @@ public override AstVisitAction VisitCommand(CommandAst ast)
484514 // if (IsPartOfBinaryModule(cmdName, out cmdInfo))
485515 // if (HasSupportShouldProcessAttribute(cmdInfo))
486516 // AddEdge(cmdName, shouldProcessVertex)
487-
488517 var vertex = new Vertex ( cmdName , ast ) ;
489518 AddVertex ( vertex ) ;
490519 if ( IsWithinFunctionDefinition ( ) )
@@ -550,7 +579,7 @@ public IEnumerable<Vertex> GetVertices()
550579 /// <param name="vertex">Origin vertxx</param>
551580 /// <param name="shouldVertex">Destination vertex</param>
552581 /// <returns></returns>
553- internal bool IsConnected ( Vertex vertex , Vertex shouldVertex )
582+ public bool IsConnected ( Vertex vertex , Vertex shouldVertex )
554583 {
555584 if ( digraph . ContainsVertex ( vertex )
556585 && digraph . ContainsVertex ( shouldVertex ) )
@@ -559,5 +588,10 @@ internal bool IsConnected(Vertex vertex, Vertex shouldVertex)
559588 }
560589 return false ;
561590 }
591+
592+ public int GetOutDegree ( Vertex v )
593+ {
594+ return digraph . GetOutDegree ( v ) ;
595+ }
562596 }
563597}
0 commit comments