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
- using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
18
18
using System . ComponentModel . Composition ;
19
19
using System . Globalization ;
20
+ using Microsoft . Windows . PowerShell . ScriptAnalyzer . Generic ;
20
21
21
22
namespace Microsoft . Windows . PowerShell . ScriptAnalyzer . BuiltinRules
22
23
{
23
24
/// <summary>
24
25
/// ProvideDefaultParameterValue: Check if any uninitialized variable is used.
25
26
/// </summary>
26
27
[ Export ( typeof ( IScriptRule ) ) ]
27
- public class ProvideDefaultParameterValue : IScriptRule
28
+ public class AvoidDefaultValueForMandatoryParameter : IScriptRule
28
29
{
29
30
/// <summary>
30
31
/// AnalyzeScript: Check if any uninitialized variable is used.
@@ -36,48 +37,53 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
36
37
// Finds all functionAst
37
38
IEnumerable < Ast > functionAsts = ast . FindAll ( testAst => testAst is FunctionDefinitionAst , true ) ;
38
39
39
- // Checks whether this is a dsc resource file (we don't raise this rule for get, set and test-target resource
40
- bool isDscResourceFile = ! String . IsNullOrWhiteSpace ( fileName ) && Helper . Instance . IsDscResourceModule ( fileName ) ;
41
-
42
- List < string > targetResourcesFunctions = new List < string > ( new string [ ] { "get-targetresource" , "set-targetresource" , "test-targetresource" } ) ;
43
-
44
-
45
40
foreach ( FunctionDefinitionAst funcAst in functionAsts )
46
41
{
47
- // Finds all ParamAsts.
48
- IEnumerable < Ast > varAsts = funcAst . FindAll ( testAst => testAst is VariableExpressionAst , true ) ;
49
-
50
- // Iterrates all ParamAsts and check if their names are on the list.
51
-
52
- HashSet < string > dscVariables = new HashSet < string > ( ) ;
53
- if ( isDscResourceFile && targetResourcesFunctions . Contains ( funcAst . Name , StringComparer . OrdinalIgnoreCase ) )
42
+ if ( funcAst . Body != null && funcAst . Body . ParamBlock != null
43
+ && funcAst . Body . ParamBlock . Attributes != null && funcAst . Body . ParamBlock . Parameters != null )
54
44
{
55
- // don't raise the rules for variables in the param block.
56
- if ( funcAst . Body != null && funcAst . Body . ParamBlock != null && funcAst . Body . ParamBlock . Parameters != null )
45
+ // only raise this rule for function with cmdletbinding
46
+ if ( ! funcAst . Body . ParamBlock . Attributes . Any ( attr => attr . TypeName . GetReflectionType ( ) == typeof ( CmdletBindingAttribute ) ) )
57
47
{
58
- dscVariables . UnionWith ( funcAst . Body . ParamBlock . Parameters . Select ( paramAst => paramAst . Name . VariablePath . UserPath ) ) ;
48
+ continue ;
59
49
}
60
- }
61
- // only raise the rules for variables in the param block.
62
- if ( funcAst . Body != null && funcAst . Body . ParamBlock != null && funcAst . Body . ParamBlock . Parameters != null )
63
- {
50
+
64
51
foreach ( var paramAst in funcAst . Body . ParamBlock . Parameters )
65
52
{
66
- if ( Helper . Instance . IsUninitialized ( paramAst . Name , funcAst ) && ! dscVariables . Contains ( paramAst . Name . VariablePath . UserPath ) )
53
+ bool mandatory = false ;
54
+
55
+ // check that param is mandatory
56
+ foreach ( var paramAstAttribute in paramAst . Attributes )
67
57
{
68
- yield return new DiagnosticRecord ( string . Format ( CultureInfo . CurrentCulture , Strings . ProvideDefaultParameterValueError , paramAst . Name . VariablePath . UserPath ) ,
69
- paramAst . Name . Extent , GetName ( ) , DiagnosticSeverity . Warning , fileName , paramAst . Name . VariablePath . UserPath ) ;
58
+ if ( paramAstAttribute is AttributeAst )
59
+ {
60
+ var namedArguments = ( paramAstAttribute as AttributeAst ) . NamedArguments ;
61
+ if ( namedArguments != null )
62
+ {
63
+ foreach ( NamedAttributeArgumentAst namedArgument in namedArguments )
64
+ {
65
+ if ( String . Equals ( namedArgument . ArgumentName , "mandatory" , StringComparison . OrdinalIgnoreCase ) )
66
+ {
67
+ // 2 cases: [Parameter(Mandatory)] and [Parameter(Mandatory=$true)]
68
+ if ( namedArgument . ExpressionOmitted || ( ! namedArgument . ExpressionOmitted && String . Equals ( namedArgument . Argument . Extent . Text , "$true" , StringComparison . OrdinalIgnoreCase ) ) )
69
+ {
70
+ mandatory = true ;
71
+ break ;
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
70
77
}
71
- }
72
- }
73
78
74
- if ( funcAst . Parameters != null )
75
- {
76
- foreach ( var paramAst in funcAst . Parameters )
77
- {
78
- if ( Helper . Instance . IsUninitialized ( paramAst . Name , funcAst ) && ! dscVariables . Contains ( paramAst . Name . VariablePath . UserPath ) )
79
+ if ( ! mandatory )
80
+ {
81
+ break ;
82
+ }
83
+
84
+ if ( paramAst . DefaultValue != null )
79
85
{
80
- yield return new DiagnosticRecord ( string . Format ( CultureInfo . CurrentCulture , Strings . ProvideDefaultParameterValueError , paramAst . Name . VariablePath . UserPath ) ,
86
+ yield return new DiagnosticRecord ( string . Format ( CultureInfo . CurrentCulture , Strings . AvoidDefaultValueForMandatoryParameterError , paramAst . Name . VariablePath . UserPath ) ,
81
87
paramAst . Name . Extent , GetName ( ) , DiagnosticSeverity . Warning , fileName , paramAst . Name . VariablePath . UserPath ) ;
82
88
}
83
89
}
@@ -91,7 +97,7 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
91
97
/// <returns>The name of this rule</returns>
92
98
public string GetName ( )
93
99
{
94
- return string . Format ( CultureInfo . CurrentCulture , Strings . NameSpaceFormat , GetSourceName ( ) , Strings . ProvideDefaultParameterValueName ) ;
100
+ return string . Format ( CultureInfo . CurrentCulture , Strings . NameSpaceFormat , GetSourceName ( ) , Strings . AvoidDefaultValueForMandatoryParameterName ) ;
95
101
}
96
102
97
103
/// <summary>
@@ -100,7 +106,7 @@ public string GetName()
100
106
/// <returns>The common name of this rule</returns>
101
107
public string GetCommonName ( )
102
108
{
103
- return string . Format ( CultureInfo . CurrentCulture , Strings . ProvideDefaultParameterValueCommonName ) ;
109
+ return string . Format ( CultureInfo . CurrentCulture , Strings . AvoidDefaultValueForMandatoryParameterCommonName ) ;
104
110
}
105
111
106
112
/// <summary>
@@ -109,7 +115,7 @@ public string GetCommonName()
109
115
/// <returns>The description of this rule</returns>
110
116
public string GetDescription ( )
111
117
{
112
- return string . Format ( CultureInfo . CurrentCulture , Strings . ProvideDefaultParameterValueDescription ) ;
118
+ return string . Format ( CultureInfo . CurrentCulture , Strings . AvoidDefaultValueForMandatoryParameterDescription ) ;
113
119
}
114
120
115
121
/// <summary>
0 commit comments