18
18
using Microsoft . PowerShell . DesiredStateConfiguration . Internal ;
19
19
using System . IO ;
20
20
using Microsoft . Management . Infrastructure ;
21
+ using Microsoft . Windows . PowerShell . ScriptAnalyzer . Extensions ;
21
22
#if ! CORECLR
22
23
using System . ComponentModel . Composition ;
23
24
#endif
@@ -29,8 +30,8 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
29
30
/// UseIdenticalMandatoryParametersDSC: Check that the Get/Test/Set TargetResource
30
31
/// have identical mandatory parameters.
31
32
/// </summary>
32
- #if ! CORECLR
33
- [ Export ( typeof ( IDSCResourceRule ) ) ]
33
+ #if ! CORECLR
34
+ [ Export ( typeof ( IDSCResourceRule ) ) ]
34
35
#endif
35
36
public class UseIdenticalMandatoryParametersDSC : IDSCResourceRule
36
37
{
@@ -44,106 +45,75 @@ public IEnumerable<DiagnosticRecord> AnalyzeDSCResource(Ast ast, string fileName
44
45
{
45
46
if ( ast == null ) throw new ArgumentNullException ( Strings . NullAstErrorMessage ) ;
46
47
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
53
48
// todo write tests for same
54
49
// 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 ) ;
62
51
63
52
// Loop through Set/Test/Get TargetResource DSC cmdlets
64
- foreach ( FunctionDefinitionAst functionDefinitionAst in functionDefinitionAsts )
53
+ foreach ( FunctionDefinitionAst functionDefinitionAst in Helper . Instance . DscResourceFunctions ( ast ) )
65
54
{
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 )
76
60
{
77
- // Loop through the attributes for each of those cmdlets
78
- foreach ( var paramAstAttributes in paramAst . Attributes )
61
+ if ( ! manParams . Contains ( key ) )
79
62
{
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 ) ;
109
73
}
110
74
}
111
75
}
76
+ }
112
77
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
+ }
118
83
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" ) ;
133
96
}
134
97
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 )
136
105
{
137
106
var moduleInfo = GetModuleInfo ( fileName ) ;
107
+ var emptyArray = new string [ 0 ] ;
138
108
if ( moduleInfo == null )
139
109
{
140
- return null ;
110
+ return emptyArray ;
141
111
}
142
112
143
113
var mofFilepath = GetMofFilepath ( fileName ) ;
144
114
if ( mofFilepath == null )
145
115
{
146
- return null ;
116
+ return emptyArray ;
147
117
}
148
118
149
119
var errors = new System . Collections . ObjectModel . Collection < Exception > ( ) ;
@@ -164,7 +134,8 @@ private string[] GetKeys(string fileName)
164
134
. CimClassProperties ?
165
135
. Where ( p => p . Flags . HasFlag ( CimFlags . Key ) )
166
136
. Select ( p => p . Name )
167
- . ToArray ( ) ;
137
+ . ToArray ( ) ??
138
+ emptyArray ;
168
139
}
169
140
170
141
private string GetMofFilepath ( string filePath )
0 commit comments