Skip to content

Commit 7fdd87d

Browse files
author
Kapil Borle
committed
Update rule logic
1 parent 4e8d2a9 commit 7fdd87d

File tree

1 file changed

+51
-80
lines changed

1 file changed

+51
-80
lines changed

Rules/UseIdenticalMandatoryParametersDSC.cs

Lines changed: 51 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
using Microsoft.PowerShell.DesiredStateConfiguration.Internal;
1919
using System.IO;
2020
using Microsoft.Management.Infrastructure;
21+
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Extensions;
2122
#if !CORECLR
2223
using System.ComponentModel.Composition;
2324
#endif
@@ -29,8 +30,8 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
2930
/// UseIdenticalMandatoryParametersDSC: Check that the Get/Test/Set TargetResource
3031
/// have identical mandatory parameters.
3132
/// </summary>
32-
#if !CORECLR
33-
[Export(typeof(IDSCResourceRule))]
33+
#if !CORECLR
34+
[Export(typeof(IDSCResourceRule))]
3435
#endif
3536
public class UseIdenticalMandatoryParametersDSC : IDSCResourceRule
3637
{
@@ -44,106 +45,75 @@ public IEnumerable<DiagnosticRecord> AnalyzeDSCResource(Ast ast, string fileName
4445
{
4546
if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);
4647

47-
// Expected TargetResource functions in the DSC Resource module
48-
List<string> expectedTargetResourceFunctionNames = new List<string>(new string[] { "Set-TargetResource", "Test-TargetResource", "Get-TargetResource" });
49-
50-
var functionDefinitionAsts = Helper.Instance.DscResourceFunctions(ast).Cast<FunctionDefinitionAst>();
51-
52-
// todo update logic to take keys into consideration
5348
// todo write tests for same
5449
// todo update documentation
55-
var tempKeys = GetKeys(fileName);
56-
var keys = tempKeys == null ?
57-
new HashSet<String>() :
58-
new HashSet<string>(tempKeys, StringComparer.OrdinalIgnoreCase);
59-
60-
// Dictionary to keep track of Mandatory parameters and their presence in Get/Test/Set TargetResource cmdlets
61-
var mandatoryParameters = new Dictionary<string, List<FunctionDefinitionAst>>(StringComparer.OrdinalIgnoreCase);
50+
var keys = GetKeys(fileName);
6251

6352
// Loop through Set/Test/Get TargetResource DSC cmdlets
64-
foreach (FunctionDefinitionAst functionDefinitionAst in functionDefinitionAsts)
53+
foreach (FunctionDefinitionAst functionDefinitionAst in Helper.Instance.DscResourceFunctions(ast))
6554
{
66-
IEnumerable<Ast> funcParamAsts = functionDefinitionAst.FindAll(item =>
67-
{
68-
var paramAst = item as ParameterAst;
69-
return paramAst != null &&
70-
keys.Contains(paramAst.Name.VariablePath.UserPath);
71-
},
72-
true);
73-
74-
// Loop through the parameters for each cmdlet
75-
foreach (ParameterAst paramAst in funcParamAsts)
55+
var manParams = new HashSet<string>(
56+
GetMandatoryParameters(functionDefinitionAst).Select(p => p.Name.VariablePath.UserPath),
57+
StringComparer.OrdinalIgnoreCase);
58+
59+
foreach (var key in keys)
7660
{
77-
// Loop through the attributes for each of those cmdlets
78-
foreach (var paramAstAttributes in paramAst.Attributes)
61+
if (!manParams.Contains(key))
7962
{
80-
if (paramAstAttributes is AttributeAst)
81-
{
82-
var namedArguments = (paramAstAttributes as AttributeAst).NamedArguments;
83-
if (namedArguments != null)
84-
{
85-
// Loop through the named attribute arguments for each parameter
86-
foreach (NamedAttributeArgumentAst namedArgument in namedArguments)
87-
{
88-
// Look for Mandatory parameters
89-
if (String.Equals(namedArgument.ArgumentName, "mandatory", StringComparison.OrdinalIgnoreCase))
90-
{
91-
// Covers Case - [Parameter(Mandatory)] and [Parameter(Mandatory)=$true]
92-
if (namedArgument.ExpressionOmitted || (!namedArgument.ExpressionOmitted && String.Equals(namedArgument.Argument.Extent.Text, "$true", StringComparison.OrdinalIgnoreCase)))
93-
{
94-
if (mandatoryParameters.ContainsKey(paramAst.Name.VariablePath.UserPath))
95-
{
96-
mandatoryParameters[paramAst.Name.VariablePath.UserPath].Add(functionDefinitionAst);
97-
}
98-
else
99-
{
100-
var functionNames = new List<FunctionDefinitionAst>();
101-
functionNames.Add(functionDefinitionAst);
102-
mandatoryParameters.Add(paramAst.Name.VariablePath.UserPath, functionNames);
103-
}
104-
}
105-
}
106-
}
107-
}
108-
}
63+
yield return new DiagnosticRecord(
64+
string.Format(
65+
CultureInfo.InvariantCulture,
66+
Strings.UseIdenticalMandatoryParametersDSCError,
67+
key,
68+
functionDefinitionAst.Name),
69+
Helper.Instance.GetScriptExtentForFunctionName(functionDefinitionAst),
70+
GetName(),
71+
DiagnosticSeverity.Error,
72+
fileName);
10973
}
11074
}
11175
}
76+
}
11277

113-
// Get the mandatory parameter names that do not appear in all the DSC Resource cmdlets
114-
IEnumerable<string> paramNames = mandatoryParameters.Where(x => x.Value.Count < expectedTargetResourceFunctionNames.Count).Select(x => x.Key);
115-
foreach (string paramName in paramNames)
116-
{
117-
var functionsNotContainingParam = functionDefinitionAsts.Except(mandatoryParameters[paramName]);
78+
private IEnumerable<ParameterAst> GetMandatoryParameters(FunctionDefinitionAst functionDefinitionAst)
79+
{
80+
return functionDefinitionAst.GetParameterAsts()?.Where(IsParameterMandatory) ??
81+
Enumerable.Empty<ParameterAst>();
82+
}
11883

119-
foreach (var funcDefnAst in functionsNotContainingParam)
120-
{
121-
yield return new DiagnosticRecord(
122-
string.Format(
123-
CultureInfo.InvariantCulture,
124-
Strings.UseIdenticalMandatoryParametersDSCError,
125-
paramName,
126-
funcDefnAst.Name),
127-
Helper.Instance.GetScriptExtentForFunctionName(funcDefnAst),
128-
GetName(),
129-
DiagnosticSeverity.Error,
130-
fileName);
131-
}
132-
}
84+
private bool IsParameterMandatory(ParameterAst paramAst)
85+
{
86+
var attrAsts = from attr in paramAst.Attributes
87+
where IsParameterAttribute(attr) && attr is AttributeAst
88+
select (AttributeAst)attr;
89+
90+
return attrAsts.Any(a => a.NamedArguments.Any(IsNamedAttributeArgumentMandatory));
91+
}
92+
93+
private bool IsParameterAttribute(AttributeBaseAst attributeBaseAst)
94+
{
95+
return attributeBaseAst.TypeName.GetReflectionType().Name.Equals("ParameterAttribute");
13396
}
13497

135-
private string[] GetKeys(string fileName)
98+
private bool IsNamedAttributeArgumentMandatory(NamedAttributeArgumentAst namedAttrArgAst)
99+
{
100+
return namedAttrArgAst.ArgumentName.Equals("mandatory", StringComparison.OrdinalIgnoreCase) &&
101+
namedAttrArgAst.GetValue();
102+
}
103+
104+
private IEnumerable<string> GetKeys(string fileName)
136105
{
137106
var moduleInfo = GetModuleInfo(fileName);
107+
var emptyArray = new string[0];
138108
if (moduleInfo == null)
139109
{
140-
return null;
110+
return emptyArray;
141111
}
142112

143113
var mofFilepath = GetMofFilepath(fileName);
144114
if (mofFilepath == null)
145115
{
146-
return null;
116+
return emptyArray;
147117
}
148118

149119
var errors = new System.Collections.ObjectModel.Collection<Exception>();
@@ -164,7 +134,8 @@ private string[] GetKeys(string fileName)
164134
.CimClassProperties?
165135
.Where(p => p.Flags.HasFlag(CimFlags.Key))
166136
.Select(p => p.Name)
167-
.ToArray();
137+
.ToArray() ??
138+
emptyArray;
168139
}
169140

170141
private string GetMofFilepath(string filePath)

0 commit comments

Comments
 (0)