Skip to content

Commit 0337dfe

Browse files
committed
Add UseCorrectParametersKind rule
* Consistent function parameters definition * Rule is disabled by default * Possible types of preferred function parameters are: "Inline", "ParamBlock"
1 parent e280479 commit 0337dfe

File tree

6 files changed

+644
-1
lines changed

6 files changed

+644
-1
lines changed

Rules/Strings.resx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,4 +1236,19 @@
12361236
<data name="AvoidReservedWordsAsFunctionNamesError" xml:space="preserve">
12371237
<value>The reserved word '{0}' was used as a function name. This should be avoided.</value>
12381238
</data>
1239+
<data name="UseCorrectParametersKindCommonName" xml:space="preserve">
1240+
<value>Use correct function parameters definition kind.</value>
1241+
</data>
1242+
<data name="UseCorrectParametersKindDescription" xml:space="preserve">
1243+
<value>Use consistent parameters definition kind to prevent potential unexpected behavior with inline functions parameters or param() block.</value>
1244+
</data>
1245+
<data name="UseCorrectParametersKindName" xml:space="preserve">
1246+
<value>UseCorrectParametersKind</value>
1247+
</data>
1248+
<data name="UseCorrectParametersKindInlineError" xml:space="preserve">
1249+
<value>Use param() block in function body instead of inline parameters.</value>
1250+
</data>
1251+
<data name="UseCorrectParametersKindParamBlockError" xml:space="preserve">
1252+
<value>Use inline parameters definition instead of param() block in function body.</value>
1253+
</data>
12391254
</root>

Rules/UseCorrectParametersKind.cs

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
#if !CORECLR
7+
using System.ComponentModel.Composition;
8+
#endif
9+
using System.Globalization;
10+
using System.Linq;
11+
using System.Management.Automation.Language;
12+
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic;
13+
14+
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
15+
{
16+
/// <summary>
17+
/// UseCorrectParametersKind: Checks if function parameters definition kind is same as preferred.
18+
/// </summary>
19+
#if !CORECLR
20+
[Export(typeof(IScriptRule))]
21+
#endif
22+
public class UseCorrectParametersKind : ConfigurableRule
23+
{
24+
private enum ParametersDefinitionKind
25+
{
26+
Inline,
27+
ParamBlock
28+
}
29+
30+
private ParametersDefinitionKind parametersKind;
31+
32+
/// <summary>
33+
/// Construct an object of UseCorrectParametersKind type.
34+
/// </summary>
35+
public UseCorrectParametersKind() : base()
36+
{
37+
Enable = false; // Disable rule by default
38+
}
39+
40+
/// <summary>
41+
/// The type of preferred parameters definition for functions.
42+
///
43+
/// Default value is "ParamBlock".
44+
/// </summary>
45+
[ConfigurableRuleProperty(defaultValue: "ParamBlock")]
46+
public string ParametersKind
47+
{
48+
get
49+
{
50+
return parametersKind.ToString();
51+
}
52+
set
53+
{
54+
if (String.IsNullOrWhiteSpace(value) ||
55+
!Enum.TryParse<ParametersDefinitionKind>(value, true, out parametersKind))
56+
{
57+
parametersKind = ParametersDefinitionKind.ParamBlock;
58+
}
59+
}
60+
}
61+
62+
/// <summary>
63+
/// AnalyzeScript: Analyze the script to check if any function is using not preferred parameters kind.
64+
/// </summary>
65+
public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
66+
{
67+
if (ast == null) { throw new ArgumentNullException(Strings.NullAstErrorMessage); }
68+
69+
IEnumerable<Ast> functionAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true);
70+
if (parametersKind == ParametersDefinitionKind.ParamBlock)
71+
{
72+
return checkInlineParameters(functionAsts, fileName);
73+
}
74+
else
75+
{
76+
return checkParamBlockParameters(functionAsts, fileName);
77+
}
78+
}
79+
80+
private IEnumerable<DiagnosticRecord> checkInlineParameters(IEnumerable<Ast> functionAsts, string fileName)
81+
{
82+
foreach (FunctionDefinitionAst functionAst in functionAsts)
83+
{
84+
if (functionAst.Parameters != null)
85+
{
86+
yield return new DiagnosticRecord(
87+
string.Format(CultureInfo.CurrentCulture, Strings.UseCorrectParametersKindInlineError, functionAst.Name),
88+
functionAst.Extent,
89+
GetName(),
90+
GetDiagnosticSeverity(),
91+
fileName
92+
);
93+
}
94+
}
95+
}
96+
97+
private IEnumerable<DiagnosticRecord> checkParamBlockParameters(IEnumerable<Ast> functionAsts, string fileName)
98+
{
99+
foreach (FunctionDefinitionAst functionAst in functionAsts)
100+
{
101+
if (functionAst.Body.ParamBlock != null)
102+
{
103+
yield return new DiagnosticRecord(
104+
string.Format(CultureInfo.CurrentCulture, Strings.UseCorrectParametersKindParamBlockError, functionAst.Name),
105+
functionAst.Extent,
106+
GetName(),
107+
GetDiagnosticSeverity(),
108+
fileName
109+
);
110+
}
111+
}
112+
}
113+
114+
/// <summary>
115+
/// Retrieves the common name of this rule.
116+
/// </summary>
117+
public override string GetCommonName()
118+
{
119+
return string.Format(CultureInfo.CurrentCulture, Strings.UseCorrectParametersKindCommonName);
120+
}
121+
122+
/// <summary>
123+
/// Retrieves the description of this rule.
124+
/// </summary>
125+
public override string GetDescription()
126+
{
127+
return string.Format(CultureInfo.CurrentCulture, Strings.UseCorrectParametersKindDescription);
128+
}
129+
130+
/// <summary>
131+
/// Retrieves the name of this rule.
132+
/// </summary>
133+
public override string GetName()
134+
{
135+
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.UseCorrectParametersKindName);
136+
}
137+
138+
/// <summary>
139+
/// Retrieves the severity of the rule: error, warning or information.
140+
/// </summary>
141+
public override RuleSeverity GetSeverity()
142+
{
143+
return RuleSeverity.Warning;
144+
}
145+
146+
/// <summary>
147+
/// Gets the severity of the returned diagnostic record: error, warning, or information.
148+
/// </summary>
149+
/// <returns></returns>
150+
public DiagnosticSeverity GetDiagnosticSeverity()
151+
{
152+
return DiagnosticSeverity.Warning;
153+
}
154+
155+
/// <summary>
156+
/// Retrieves the name of the module/assembly the rule is from.
157+
/// </summary>
158+
public override string GetSourceName()
159+
{
160+
return string.Format(CultureInfo.CurrentCulture, Strings.SourceName);
161+
}
162+
163+
/// <summary>
164+
/// Retrieves the type of the rule, Builtin, Managed or Module.
165+
/// </summary>
166+
public override SourceType GetSourceType()
167+
{
168+
return SourceType.Builtin;
169+
}
170+
}
171+
}

Tests/Engine/GetScriptAnalyzerRule.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ Describe "Test Name parameters" {
6363

6464
It "get Rules with no parameters supplied" {
6565
$defaultRules = Get-ScriptAnalyzerRule
66-
$expectedNumRules = 71
66+
$expectedNumRules = 72
6767
if ($PSVersionTable.PSVersion.Major -le 4)
6868
{
6969
# for PSv3 PSAvoidGlobalAliases is not shipped because

0 commit comments

Comments
 (0)