Skip to content

Commit 564b28d

Browse files
author
Kapil Borle
committed
Check if a builtin cmdlet/function has SupportsShouldProcess
1 parent 3c9a49c commit 564b28d

File tree

1 file changed

+94
-60
lines changed

1 file changed

+94
-60
lines changed

Rules/UseShouldProcessCorrectly.cs

Lines changed: 94 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using System;
1414
using System.Linq;
1515
using System.Collections.Generic;
16+
using System.Management.Automation;
1617
using System.Management.Automation.Language;
1718
using 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

Comments
 (0)