Skip to content
Open
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
15 changes: 15 additions & 0 deletions Rules/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1236,4 +1236,19 @@
<data name="AvoidReservedWordsAsFunctionNamesError" xml:space="preserve">
<value>The reserved word '{0}' was used as a function name. This should be avoided.</value>
</data>
<data name="UseCorrectParametersKindCommonName" xml:space="preserve">
<value>Use correct function parameters definition kind.</value>
</data>
<data name="UseCorrectParametersKindDescription" xml:space="preserve">
<value>Use consistent parameters definition kind to prevent potential unexpected behavior with inline functions parameters or param() block.</value>
</data>
<data name="UseCorrectParametersKindName" xml:space="preserve">
<value>UseCorrectParametersKind</value>
</data>
<data name="UseCorrectParametersKindInlineError" xml:space="preserve">
<value>Use param() block in function body instead of inline parameters.</value>
</data>
<data name="UseCorrectParametersKindParamBlockError" xml:space="preserve">
<value>Use inline parameters definition instead of param() block in function body.</value>
</data>
</root>
171 changes: 171 additions & 0 deletions Rules/UseCorrectParametersKind.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
#if !CORECLR
using System.ComponentModel.Composition;
#endif
using System.Globalization;
using System.Linq;
using System.Management.Automation.Language;
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic;

namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
{
/// <summary>
/// UseCorrectParametersKind: Checks if function parameters definition kind is same as preferred.
/// </summary>
#if !CORECLR
[Export(typeof(IScriptRule))]
#endif
public class UseCorrectParametersKind : ConfigurableRule
{
private enum ParametersDefinitionKind
{
Inline,
ParamBlock
}

private ParametersDefinitionKind parametersKind;

/// <summary>
/// Construct an object of UseCorrectParametersKind type.
/// </summary>
public UseCorrectParametersKind() : base()
{
Enable = false; // Disable rule by default
}

/// <summary>
/// The type of preferred parameters definition for functions.
///
/// Default value is "ParamBlock".
/// </summary>
[ConfigurableRuleProperty(defaultValue: "ParamBlock")]
public string ParametersKind
{
get
{
return parametersKind.ToString();
}
set
{
if (String.IsNullOrWhiteSpace(value) ||
!Enum.TryParse<ParametersDefinitionKind>(value, true, out parametersKind))
{
parametersKind = ParametersDefinitionKind.ParamBlock;
}
}
}

/// <summary>
/// AnalyzeScript: Analyze the script to check if any function is using not preferred parameters kind.
/// </summary>
public override IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
{
if (ast == null) { throw new ArgumentNullException(Strings.NullAstErrorMessage); }

IEnumerable<Ast> functionAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true);
if (parametersKind == ParametersDefinitionKind.ParamBlock)
{
return checkInlineParameters(functionAsts, fileName);
}
else
{
return checkParamBlockParameters(functionAsts, fileName);
}
}

private IEnumerable<DiagnosticRecord> checkInlineParameters(IEnumerable<Ast> functionAsts, string fileName)
{
foreach (FunctionDefinitionAst functionAst in functionAsts)
{
if (functionAst.Parameters != null)
{
yield return new DiagnosticRecord(
string.Format(CultureInfo.CurrentCulture, Strings.UseCorrectParametersKindInlineError, functionAst.Name),
functionAst.Extent,
GetName(),
GetDiagnosticSeverity(),
fileName
);
}
}
}

private IEnumerable<DiagnosticRecord> checkParamBlockParameters(IEnumerable<Ast> functionAsts, string fileName)
{
foreach (FunctionDefinitionAst functionAst in functionAsts)
{
if (functionAst.Body.ParamBlock != null)
{
yield return new DiagnosticRecord(
string.Format(CultureInfo.CurrentCulture, Strings.UseCorrectParametersKindParamBlockError, functionAst.Name),
functionAst.Extent,
GetName(),
GetDiagnosticSeverity(),
fileName
);
}
}
}

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

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

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

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

/// <summary>
/// Gets the severity of the returned diagnostic record: error, warning, or information.
/// </summary>
/// <returns></returns>
public DiagnosticSeverity GetDiagnosticSeverity()
{
return DiagnosticSeverity.Warning;
}

/// <summary>
/// Retrieves the name of the module/assembly the rule is from.
/// </summary>
public override string GetSourceName()
{
return string.Format(CultureInfo.CurrentCulture, Strings.SourceName);
}

/// <summary>
/// Retrieves the type of the rule, Builtin, Managed or Module.
/// </summary>
public override SourceType GetSourceType()
{
return SourceType.Builtin;
}
}
}
2 changes: 1 addition & 1 deletion Tests/Engine/GetScriptAnalyzerRule.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Describe "Test Name parameters" {

It "get Rules with no parameters supplied" {
$defaultRules = Get-ScriptAnalyzerRule
$expectedNumRules = 71
$expectedNumRules = 72
if ($PSVersionTable.PSVersion.Major -le 4)
{
# for PSv3 PSAvoidGlobalAliases is not shipped because
Expand Down
Loading