1818using Microsoft . PowerShell . DesiredStateConfiguration . Internal ;
1919using System . IO ;
2020using Microsoft . Management . Infrastructure ;
21+ using Microsoft . Windows . PowerShell . ScriptAnalyzer . Extensions ;
2122#if ! CORECLR
2223using 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