Skip to content

Commit e8bfe71

Browse files
author
Kapil Borle
committed
Adds a new rule to check if HelpMessage parameter attribute is non-null and non-empty.
1 parent ff8fdb1 commit e8bfe71

8 files changed

+327
-0
lines changed
1.22 KB

#AvoidNullOrEmtpyHelpMessageAttribute Severity Level: Error

##Description

Setting the HelpMessage attribute to an empty string or null value causes PowerShell interpreter to throw an error while executing the corresponding function.

##How to Fix

To fix a violation of this rule, please set its value to a non-empty string.

##Example

Wrong:

Function BadFuncEmtpyHelpMessage { Param( [Parameter(HelpMessage="")] [String] $Param )

$Param

}

Correct:

Function GoodFuncEmtpyHelpMessage { Param( [Parameter(HelpMessage="This is help.")] [String] $Param )

$Param

}

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
//
2+
// Copyright (c) Microsoft Corporation.
3+
//
4+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10+
// THE SOFTWARE.
11+
//
12+
13+
using System;
14+
using System.Collections.Generic;
15+
using System.Linq;
16+
using System.Management.Automation.Language;
17+
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic;
18+
using System.ComponentModel.Composition;
19+
using System.Globalization;
20+
21+
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
22+
{
23+
/// <summary>
24+
/// AvoidUsingNullOrEmptyHelpMessageParameter: Check if the HelpMessage parameter is set to a non-empty string.
25+
/// </summary>
26+
[Export(typeof(IScriptRule))]
27+
public class AvoidNullOrEmptyHelpMessageAttribute : IScriptRule
28+
{
29+
/// <summary>
30+
/// AvoidUsingNullOrEmptyHelpMessageParameter: Check if the HelpMessage parameter is set to a non-empty string.
31+
/// </summary>
32+
public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
33+
{
34+
if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);
35+
36+
// Finds all functionAst
37+
IEnumerable<Ast> functionAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true);
38+
39+
foreach (FunctionDefinitionAst funcAst in functionAsts)
40+
{
41+
if (funcAst.Body == null || funcAst.Body.ParamBlock == null
42+
|| funcAst.Body.ParamBlock.Attributes == null || funcAst.Body.ParamBlock.Parameters == null)
43+
continue;
44+
45+
foreach (var paramAst in funcAst.Body.ParamBlock.Parameters)
46+
{
47+
foreach (var paramAstAttribute in paramAst.Attributes)
48+
{
49+
if (!(paramAstAttribute is AttributeAst))
50+
continue;
51+
52+
var namedArguments = (paramAstAttribute as AttributeAst).NamedArguments;
53+
54+
if (namedArguments == null)
55+
continue;
56+
57+
foreach (NamedAttributeArgumentAst namedArgument in namedArguments)
58+
{
59+
if (!(String.Equals(namedArgument.ArgumentName, "HelpMessage", StringComparison.OrdinalIgnoreCase))
60+
|| namedArgument.ExpressionOmitted)
61+
continue;
62+
63+
string errCondition;
64+
if (namedArgument.Argument.Extent.Text.Equals("\"\""))
65+
{
66+
errCondition = "empty";
67+
}
68+
else if (namedArgument.Argument.Extent.Text.Equals("$null", StringComparison.OrdinalIgnoreCase))
69+
{
70+
errCondition = "null";
71+
}
72+
else
73+
{
74+
errCondition = null;
75+
}
76+
77+
if (!String.IsNullOrEmpty(errCondition))
78+
{
79+
string message = string.Format(CultureInfo.CurrentCulture,
80+
Strings.AvoidNullOrEmptyHelpMessageAttributeError,
81+
paramAst.Name.VariablePath.UserPath);
82+
yield return new DiagnosticRecord(message,
83+
paramAst.Extent,
84+
GetName(),
85+
DiagnosticSeverity.Error,
86+
fileName,
87+
paramAst.Name.VariablePath.UserPath);
88+
}
89+
}
90+
}
91+
}
92+
93+
}
94+
}
95+
96+
/// <summary>
97+
/// GetName: Retrieves the name of this rule.
98+
/// </summary>
99+
/// <returns>The name of this rule</returns>
100+
public string GetName()
101+
{
102+
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.AvoidNullOrEmptyHelpMessageAttributeName);
103+
}
104+
105+
/// <summary>
106+
/// GetCommonName: Retrieves the common name of this rule.
107+
/// </summary>
108+
/// <returns>The common name of this rule</returns>
109+
public string GetCommonName()
110+
{
111+
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidNullOrEmptyHelpMessageAttributeCommonName);
112+
}
113+
114+
/// <summary>
115+
/// GetDescription: Retrieves the description of this rule.
116+
/// </summary>
117+
/// <returns>The description of this rule</returns>
118+
public string GetDescription()
119+
{
120+
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidNullOrEmptyHelpMessageAttributeDescription);
121+
}
122+
123+
/// <summary>
124+
/// GetSourceType: Retrieves the type of the rule, builtin, managed or module.
125+
/// </summary>
126+
public SourceType GetSourceType()
127+
{
128+
return SourceType.Builtin;
129+
}
130+
131+
/// <summary>
132+
/// GetSeverity: Retrieves the severity of the rule: error, warning of information.
133+
/// </summary>
134+
/// <returns></returns>
135+
public RuleSeverity GetSeverity()
136+
{
137+
return RuleSeverity.Error;
138+
}
139+
140+
/// <summary>
141+
/// GetSourceName: Retrieves the module/assembly name the rule is from.
142+
/// </summary>
143+
public string GetSourceName()
144+
{
145+
return string.Format(CultureInfo.CurrentCulture, Strings.SourceName);
146+
}
147+
}
148+
}

Rules/ScriptAnalyzerBuiltinRules.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
<Compile Include="AvoidEmptyCatchBlock.cs" />
7373
<Compile Include="AvoidGlobalVars.cs" />
7474
<Compile Include="AvoidInvokingEmptyMembers.cs" />
75+
<Compile Include="AvoidNullOrEmptyHelpMessageAttribute.cs" />
7576
<Compile Include="AvoidPositionalParameters.cs" />
7677
<Compile Include="AvoidReservedCharInCmdlet.cs" />
7778
<Compile Include="AvoidReservedParams.cs" />

Rules/Strings.Designer.cs

Lines changed: 36 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Rules/Strings.resx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,4 +786,16 @@
786786
<data name="MisleadingBacktickError" xml:space="preserve">
787787
<value>This line has a backtick at the end trailed by a whitespace character. Did you mean for this to be a line continuation?</value>
788788
</data>
789+
<data name="AvoidNullOrEmptyHelpMessageAttributeCommonName" xml:space="preserve">
790+
<value>Avoid using null or empty HelpMessage parameter attribute.</value>
791+
</data>
792+
<data name="AvoidNullOrEmptyHelpMessageAttributeDescription" xml:space="preserve">
793+
<value>Setting the HelpMessage attribute to an empty string or null value causes PowerShell interpreter to throw an error while executing the corresponding function.</value>
794+
</data>
795+
<data name="AvoidNullOrEmptyHelpMessageAttributeError" xml:space="preserve">
796+
<value>HelpMessage parameter attribute should not be null or empty. To fix a violation of this rule, please set its value to a non-empty string.</value>
797+
</data>
798+
<data name="AvoidNullOrEmptyHelpMessageAttributeName" xml:space="preserve">
799+
<value>AvoidNullOrEmptyHelpMessageAttribute</value>
800+
</data>
789801
</root>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
function BadFuncNullHelpMessage
2+
{
3+
[CmdletBinding()]
4+
[OutputType([String])]
5+
param(
6+
# this one null value
7+
[Parameter(HelpMessage=$null)]
8+
[string] $Param1="String",
9+
10+
# this parameter has no help message
11+
[Parameter(HelpMessage="This is helpful.")]
12+
[string] $Param2
13+
)
14+
$Param1
15+
$Param2 = "test"
16+
}
17+
18+
function BadFuncEmptyHelpMessage
19+
{
20+
[CmdletBinding()]
21+
[OutputType([String])]
22+
param(
23+
# this has an empty string
24+
[Parameter(HelpMessage="")]
25+
[string] $Param1="String",
26+
27+
# this parameter has no default value
28+
[Parameter(HelpMessage="This is helpful.")]
29+
[string] $Param2
30+
)
31+
$Param1
32+
$Param2 = "test"
33+
}
34+
35+
function GoodFunc1($Param1)
36+
{
37+
$Param1
38+
}
39+
40+
# same as BadFunc but this one has no cmdletbinding
41+
function BadFuncNullHelpMessageNoCmdletBinding
42+
{
43+
param(
44+
# this one null value
45+
[Parameter(HelpMessage=$null)]
46+
[string] $Param1="String",
47+
48+
# this parameter has no help message
49+
[Parameter(HelpMessage="This is helpful.")]
50+
[string] $Param2
51+
)
52+
$Param1
53+
$Param2 = "test"
54+
}
55+
56+
# same as BadFunc but this one has no cmdletbinding
57+
function BadFuncEmptyHelpMessageNoCmdletBinding
58+
{
59+
param(
60+
# this has an empty string
61+
[Parameter(HelpMessage="")]
62+
[string] $Param1="String",
63+
64+
# this parameter has no default value
65+
[Parameter(HelpMessage="This is helpful.")]
66+
[string] $Param2
67+
)
68+
$Param1
69+
$Param2 = "test"
70+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Import-Module PSScriptAnalyzer
2+
$violationName = "PSAvoidNullOrEmptyHelpMessageAttribute"
3+
$violationMessage = "HelpMessage parameter attribute should not be null or empty. To fix a violation of this rule, please set its value to a non-empty string."
4+
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
5+
$violations = Invoke-ScriptAnalyzer "$directory\AvoidNullOrEmptyHelpMessageAttribute.ps1" -IncludeRule PSAvoidNullOrEmptyHelpMessageAttribute
6+
$noViolations = Invoke-ScriptAnalyzer "$directory\AvoidNullOrEmptyHelpMessageAttributeNoViolations.ps1" -IncludeRule PSAvoidNullOrEmptyHelpMessageAttribute
7+
8+
Describe "AvoidNullOrEmptyHelpMessageAttribute" {
9+
Context "When there are violations" {
10+
It "has 1 provide default value for mandatory parameter violation" {
11+
$violations.Count | Should Be 4
12+
}
13+
14+
It "has the correct description message" {
15+
$violations[0].Message | Should Match $violationMessage
16+
}
17+
}
18+
19+
Context "When there are no violations" {
20+
It "returns no violations" {
21+
$noViolations.Count | Should Be 0
22+
}
23+
}
24+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
function GoodFuncCmdletBinding
2+
{
3+
[CmdletBinding()]
4+
param(
5+
# this one null value
6+
[Parameter(HelpMessage="This is helpful.")]
7+
[string] $Param1="String",
8+
9+
# this parameter has no help message
10+
[Parameter(HelpMessage="This is helpful too.")]
11+
[string] $Param2
12+
)
13+
$Param1
14+
$Param2 = "test"
15+
}
16+
17+
function GoodFunc1($Param1)
18+
{
19+
$Param1
20+
}
21+
22+
# same as BadFunc but this one has no cmdletbinding
23+
function GoodFuncNoCmdletBinding
24+
{
25+
param(
26+
# this one null value
27+
[Parameter(HelpMessage="This is helpful.")]
28+
[string] $Param1="String",
29+
30+
# this parameter has no help message
31+
[Parameter(HelpMessage="This is helpful too.")]
32+
[string] $Param2
33+
)
34+
$Param1
35+
$Param2 = "test"
36+
}

0 commit comments

Comments
 (0)