13
13
using System ;
14
14
using System . Linq ;
15
15
using System . Collections . Generic ;
16
+ using System . Management . Automation ;
16
17
using System . Management . Automation . Language ;
17
18
using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
18
19
#if ! CORECLR
@@ -34,9 +35,7 @@ public class UseShouldProcessCorrectly : IScriptRule
34
35
private Ast ast ;
35
36
private string fileName ;
36
37
private FunctionReferenceDigraph funcDigraph ;
37
-
38
38
private List < DiagnosticRecord > diagnosticRecords ;
39
-
40
39
private readonly Vertex shouldProcessVertex ;
41
40
42
41
public UseShouldProcessCorrectly ( )
@@ -53,13 +52,17 @@ public UseShouldProcessCorrectly()
53
52
/// <returns>A List of diagnostic results of this rule</returns>
54
53
public IEnumerable < DiagnosticRecord > AnalyzeScript ( Ast ast , string fileName )
55
54
{
56
- if ( ast == null ) throw new ArgumentNullException ( Strings . NullAstErrorMessage ) ;
55
+ if ( ast == null )
56
+ {
57
+ throw new ArgumentNullException ( Strings . NullAstErrorMessage ) ;
58
+ }
57
59
58
60
diagnosticRecords . Clear ( ) ;
59
61
this . ast = ast ;
60
62
this . fileName = fileName ;
61
63
funcDigraph = new FunctionReferenceDigraph ( ) ;
62
64
ast . Visit ( funcDigraph ) ;
65
+ CheckForSupportShouldProcess ( ) ;
63
66
FindViolations ( ) ;
64
67
foreach ( var dr in diagnosticRecords )
65
68
{
@@ -226,90 +229,117 @@ private bool UpstreamDeclaresShouldProcess(Vertex v)
226
229
/// <returns>True if the given function declares SupportShouldProcess, otherwise null</returns>
227
230
private bool DeclaresSupportsShouldProcess ( FunctionDefinitionAst ast )
228
231
{
229
- if ( ast . Body . ParamBlock == null )
232
+ if ( ast . Body . ParamBlock == null
233
+ || ast . Body . ParamBlock . Attributes == null )
230
234
{
231
235
return false ;
232
236
}
233
237
234
- foreach ( var attr in ast . Body . ParamBlock . Attributes )
238
+ var shouldProcessAttribute = Helper . Instance . GetShouldProcessAttributeAst ( ast . Body . ParamBlock . Attributes ) ;
239
+ if ( shouldProcessAttribute == null )
235
240
{
236
- if ( attr . NamedArguments == null )
237
- {
238
- continue ;
239
- }
241
+ return false ;
242
+ }
243
+
244
+ return Helper . Instance . GetNamedArgumentAttributeValue ( shouldProcessAttribute ) ;
245
+ }
240
246
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 ( ) )
242
252
{
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 ;
260
258
}
261
259
}
262
-
263
- return false ;
260
+ catch ( System . Management . Automation . CommandNotFoundException )
261
+ {
262
+ return null ;
263
+ }
264
264
}
265
265
266
- private bool DeclaresSupportsShouldProcess ( string cmdName )
266
+ private bool SupportsShouldProcess ( string cmdName )
267
267
{
268
268
if ( String . IsNullOrWhiteSpace ( cmdName ) )
269
269
{
270
270
return false ;
271
271
}
272
272
273
- try
273
+ var cmdInfo = GetCommandInfo ( cmdName ) ;
274
+ if ( cmdInfo == null )
274
275
{
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
+ }
286
278
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 )
288
290
{
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 )
296
293
{
297
- return true ;
294
+ return cmdletBindingAttr . SupportsShouldProcess ;
298
295
}
299
296
}
300
- if ( attributes . Count ( ) == 0 )
301
- {
302
- return false ;
303
- }
304
297
}
298
+
299
+ return false ;
305
300
}
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 )
307
307
{
308
- return false ;
308
+ var cmdletAttribute = attr as System . Management . Automation . CmdletAttribute ;
309
+ if ( cmdletAttribute != null )
310
+ {
311
+ return cmdletAttribute . SupportsShouldProcess ;
312
+ }
309
313
}
310
314
311
315
return false ;
312
316
}
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
+ }
313
343
}
314
344
315
345
/// <summary>
@@ -370,7 +400,7 @@ public override bool Equals(Object other)
370
400
/// </summary>
371
401
public override int GetHashCode ( )
372
402
{
373
- return name . GetHashCode ( ) ;
403
+ return name . ToLower ( ) . GetHashCode ( ) ;
374
404
}
375
405
}
376
406
@@ -484,7 +514,6 @@ public override AstVisitAction VisitCommand(CommandAst ast)
484
514
// if (IsPartOfBinaryModule(cmdName, out cmdInfo))
485
515
// if (HasSupportShouldProcessAttribute(cmdInfo))
486
516
// AddEdge(cmdName, shouldProcessVertex)
487
-
488
517
var vertex = new Vertex ( cmdName , ast ) ;
489
518
AddVertex ( vertex ) ;
490
519
if ( IsWithinFunctionDefinition ( ) )
@@ -550,7 +579,7 @@ public IEnumerable<Vertex> GetVertices()
550
579
/// <param name="vertex">Origin vertxx</param>
551
580
/// <param name="shouldVertex">Destination vertex</param>
552
581
/// <returns></returns>
553
- internal bool IsConnected ( Vertex vertex , Vertex shouldVertex )
582
+ public bool IsConnected ( Vertex vertex , Vertex shouldVertex )
554
583
{
555
584
if ( digraph . ContainsVertex ( vertex )
556
585
&& digraph . ContainsVertex ( shouldVertex ) )
@@ -559,5 +588,10 @@ internal bool IsConnected(Vertex vertex, Vertex shouldVertex)
559
588
}
560
589
return false ;
561
590
}
591
+
592
+ public int GetOutDegree ( Vertex v )
593
+ {
594
+ return digraph . GetOutDegree ( v ) ;
595
+ }
562
596
}
563
597
}
0 commit comments