Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions RuleDocumentation/AvoidNullOrEmptyHelpMessageAttribute.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#AvoidNullOrEmtpyHelpMessageAttribute
**Severity Level: Warning**


##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 BadFuncEmtpyHelpMessageEmpty
{
Param(
[Parameter(HelpMessage='')]
[String] $Param
)

$Param
}

Function BadFuncEmtpyHelpMessageNull
{
Param(
[Parameter(HelpMessage=$null)]
[String] $Param
)

$Param
}

Function BadFuncEmtpyHelpMessageNoAssignment
{
Param(
[Parameter(HelpMessage)]
[String] $Param
)

$Param
}


Correct:

Function GoodFuncEmtpyHelpMessage
{
Param(
[Parameter(HelpMessage='This is helpful')]
[String] $Param
)

$Param
}
157 changes: 157 additions & 0 deletions Rules/AvoidNullOrEmptyHelpMessageAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//
// Copyright (c) Microsoft Corporation.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation.Language;
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic;
using System.ComponentModel.Composition;
using System.Globalization;

namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
{
/// <summary>
/// AvoidUsingNullOrEmptyHelpMessageAttribute: Check if the HelpMessage parameter is set to a non-empty string.
/// </summary>
[Export(typeof(IScriptRule))]
public class AvoidNullOrEmptyHelpMessageAttribute : IScriptRule
{
/// <summary>
/// AvoidUsingNullOrEmptyHelpMessageAttribute: Check if the HelpMessage parameter is set to a non-empty string.
/// </summary>
public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
{
if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);

// Finds all functionAst
IEnumerable<Ast> functionAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true);

foreach (FunctionDefinitionAst funcAst in functionAsts)
{
if (funcAst.Body == null || funcAst.Body.ParamBlock == null
|| funcAst.Body.ParamBlock.Attributes == null || funcAst.Body.ParamBlock.Parameters == null)
{
continue;
}

foreach (var paramAst in funcAst.Body.ParamBlock.Parameters)
{
foreach (var paramAstAttribute in paramAst.Attributes)
{
if (!(paramAstAttribute is AttributeAst))
{
continue;
}

var namedArguments = (paramAstAttribute as AttributeAst).NamedArguments;

if (namedArguments == null)
{
continue;
}

foreach (NamedAttributeArgumentAst namedArgument in namedArguments)
{
if (!(namedArgument.ArgumentName.Equals("HelpMessage", StringComparison.OrdinalIgnoreCase)))
{
continue;
}

string errCondition;
if (namedArgument.ExpressionOmitted
|| namedArgument.Argument.Extent.Text.Equals("\"\"")
|| namedArgument.Argument.Extent.Text.Equals("\'\'"))
{
errCondition = "empty";
}
else if (namedArgument.Argument.Extent.Text.Equals("$null", StringComparison.OrdinalIgnoreCase))
{
errCondition = "null";
}
else
{
errCondition = null;
}

if (!String.IsNullOrEmpty(errCondition))
{
string message = string.Format(CultureInfo.CurrentCulture,
Strings.AvoidNullOrEmptyHelpMessageAttributeError,
paramAst.Name.VariablePath.UserPath);
yield return new DiagnosticRecord(message,
paramAst.Extent,
GetName(),
DiagnosticSeverity.Warning,
fileName,
paramAst.Name.VariablePath.UserPath);
}
}
}
}

}
}

/// <summary>
/// GetName: Retrieves the name of this rule.
/// </summary>
/// <returns>The name of this rule</returns>
public string GetName()
{
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.AvoidNullOrEmptyHelpMessageAttributeName);
}

/// <summary>
/// GetCommonName: Retrieves the common name of this rule.
/// </summary>
/// <returns>The common name of this rule</returns>
public string GetCommonName()
{
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidNullOrEmptyHelpMessageAttributeCommonName);
}

/// <summary>
/// GetDescription: Retrieves the description of this rule.
/// </summary>
/// <returns>The description of this rule</returns>
public string GetDescription()
{
return string.Format(CultureInfo.CurrentCulture, Strings.AvoidNullOrEmptyHelpMessageAttributeDescription);
}

/// <summary>
/// GetSourceType: Retrieves the type of the rule, builtin, managed or module.
/// </summary>
public SourceType GetSourceType()
{
return SourceType.Builtin;
}

/// <summary>
/// GetSeverity: Retrieves the severity of the rule: error, warning of information.
/// </summary>
/// <returns></returns>
public RuleSeverity GetSeverity()
{
return RuleSeverity.Warning;
}

/// <summary>
/// GetSourceName: Retrieves the module/assembly name the rule is from.
/// </summary>
public string GetSourceName()
{
return string.Format(CultureInfo.CurrentCulture, Strings.SourceName);
}
}
}
1 change: 1 addition & 0 deletions Rules/ScriptAnalyzerBuiltinRules.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
<Compile Include="AvoidEmptyCatchBlock.cs" />
<Compile Include="AvoidGlobalVars.cs" />
<Compile Include="AvoidInvokingEmptyMembers.cs" />
<Compile Include="AvoidNullOrEmptyHelpMessageAttribute.cs" />
<Compile Include="AvoidPositionalParameters.cs" />
<Compile Include="AvoidReservedCharInCmdlet.cs" />
<Compile Include="AvoidReservedParams.cs" />
Expand Down
36 changes: 36 additions & 0 deletions Rules/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions Rules/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -786,4 +786,16 @@
<data name="MisleadingBacktickError" xml:space="preserve">
<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>
</data>
<data name="AvoidNullOrEmptyHelpMessageAttributeCommonName" xml:space="preserve">
<value>Avoid using null or empty HelpMessage parameter attribute.</value>
</data>
<data name="AvoidNullOrEmptyHelpMessageAttributeDescription" xml:space="preserve">
<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>
</data>
<data name="AvoidNullOrEmptyHelpMessageAttributeError" xml:space="preserve">
<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>
</data>
<data name="AvoidNullOrEmptyHelpMessageAttributeName" xml:space="preserve">
<value>AvoidNullOrEmptyHelpMessageAttribute</value>
</data>
</root>
2 changes: 1 addition & 1 deletion Tests/Engine/GetScriptAnalyzerRule.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Describe "Test Name parameters" {

It "Get Rules with no parameters supplied" {
$defaultRules = Get-ScriptAnalyzerRule
$defaultRules.Count | Should be 39
$defaultRules.Count | Should be 40
}
}

Expand Down
103 changes: 103 additions & 0 deletions Tests/Rules/AvoidNullOrEmptyHelpMessageAttribute.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
function BadFuncNullHelpMessage
{
[CmdletBinding()]
[OutputType([String])]
param(
# this one null value
[Parameter(HelpMessage=$null)]
[string] $Param1="String",

# this parameter has no help message
[Parameter(HelpMessage="This is helpful.")]
[string] $Param2
)
$Param1
$Param2 = "test"
}

function BadFuncEmptyHelpMessage
{
[CmdletBinding()]
[OutputType([String])]
param(
# this has an empty string
[Parameter(HelpMessage="")]
[string] $Param1="String",

# this parameter has no default value
[Parameter(HelpMessage="This is helpful.")]
[string] $Param2
)
$Param1
$Param2 = "test"
}

function GoodFunc1($Param1)
{
$Param1
}

# same as BadFunc but this one has no cmdletbinding
function BadFuncNullHelpMessageNoCmdletBinding
{
param(
# this one null value
[Parameter(HelpMessage=$null)]
[string] $Param1="String",

# this parameter has no help message
[Parameter(HelpMessage="This is helpful.")]
[string] $Param2
)
$Param1
$Param2 = "test"
}

# same as BadFunc but this one has no cmdletbinding
function BadFuncEmptyHelpMessageNoCmdletBinding
{
param(
# this has an empty string
[Parameter(HelpMessage="")]
[string] $Param1="String",

# this parameter has no default value
[Parameter(HelpMessage="This is helpful.")]
[string] $Param2
)
$Param1
$Param2 = "test"
}


# same as BadFunc but this one has no cmdletbinding
function BadFuncEmptyHelpMessageNoCmdletBindingSingleQoutes
{
param(
# this has an empty string
[Parameter(HelpMessage='')]
[string] $Param1="String",

# this parameter has no default value
[Parameter(HelpMessage="This is helpful.")]
[string] $Param2
)
$Param1
$Param2 = "test"
}

# same as BadFunc but this one has no cmdletbinding
function BadFuncEmptyHelpMessageNoCmdletBindingNoAssignment
{
param(
# this has an empty string
[Parameter(HelpMessage)]
[string] $Param1="String",

# this parameter has no default value
[Parameter(HelpMessage="This is helpful.")]
[string] $Param2
)
$Param1
$Param2 = "test"
}
Loading