diff --git a/License.txt b/LICENSE similarity index 100% rename from License.txt rename to LICENSE diff --git a/README.md b/README.md index 25d4aea1a..efe28c450 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ +|Master |BugFixes |Development | +|:------:|:------:|:-------:|:-------:| +[![Build status](https://ci.appveyor.com/api/projects/status/h5mot3vqtvxw5d7l/branch/master?svg=true)](https://ci.appveyor.com/project/PowerShell/psscriptanalyzer/branch/master)|[![Build status](https://ci.appveyor.com/api/projects/status/h5mot3vqtvxw5d7l/branch/bugfixes?svg=true)](https://ci.appveyor.com/project/PowerShell/psscriptanalyzer/branch/bugfixes)|[![Build status](https://ci.appveyor.com/api/projects/status/h5mot3vqtvxw5d7l/branch/development?svg=true)](https://ci.appveyor.com/project/PowerShell/psscriptanalyzer/branch/development) | -Introduction + + +Introduction ============ PSScriptAnalyzer is a static code checker for Windows PowerShell modules and scripts. PSScriptAnalyzer checks the quality of Windows PowerShell code by running a set of rules. The rules are based on PowerShell best practices identified by PowerShell Team and the community. It generates DiagnosticResults (errors and warnings) to inform users about potential code defects and suggests possible solutions for improvements. @@ -42,14 +47,6 @@ Use Visual Studio to build "PSScriptAnalyzer.sln". Use ~/PSScriptAnalyzer/ folde **Note: If there are any build errors, please refer to Requirements section and make sure all dependencies are properly installed** -Build Status -============== - -| |Master Branch | -|---------|:------:|:------:|:-------:|:-------:| -|**Debug Version**|[![Build status](https://ci.appveyor.com/api/projects/status/h5mot3vqtvxw5d7l/branch/master?svg=true)](https://ci.appveyor.com/project/PowerShell/psscriptanalyzer/branch/master) | - - Running Tests ============= diff --git a/RuleDocumentation/AvoidUsingDeprecatedManifestFields.md b/RuleDocumentation/AvoidUsingDeprecatedManifestFields.md new file mode 100644 index 000000000..8f7fa0015 --- /dev/null +++ b/RuleDocumentation/AvoidUsingDeprecatedManifestFields.md @@ -0,0 +1,27 @@ +#AvoidUsingDeprecatedManifestFields +**Severity Level: Warning** + + +##Description + +PowerShell V5.0 introduced some new fields and replaced some old fields with in module manifest files (.psd1). Therefore, fields such as "ModuleToProcess" is replaced with "RootModule". Using the deprecated manifest fields will result in PSScriptAnalyzer warnings. + +##How to Fix + +To fix a violation of this, please replace "ModuleToProcess" with "RootModule". + +##Example + +Wrong: +``` +ModuleToProcess ='psscriptanalyzer' + +ModuleVersion = '1.0' +``` + +Correct: +``` +RootModule ='psscriptanalyzer' + +ModuleVersion = '1.0' +``` diff --git a/RuleDocumentation/ProvideDefaultParameterValue.md b/RuleDocumentation/ProvideDefaultParameterValue.md new file mode 100644 index 000000000..5569e266e --- /dev/null +++ b/RuleDocumentation/ProvideDefaultParameterValue.md @@ -0,0 +1,30 @@ +#ProvideDefaultParameterValue +**Severity Level: Warning** + + +##Description +Parameters must have a default value as uninitialized parameters will lead to potential bugs in the scripts. + +##How to Fix + +To fix a violation of this rule, please specify a default value for all parameters. + +##Example + +Wrong: + +``` +function Test($Param1) +{ + $Param1 +} +``` + +Correct: + +``` +function Test($Param1 = $null) +{ + $Param1 +} +``` diff --git a/Rules/AvoidUninitializedVariable.cs b/Rules/AvoidUninitializedVariable.cs index 6c13950ca..accbbcc3d 100644 --- a/Rules/AvoidUninitializedVariable.cs +++ b/Rules/AvoidUninitializedVariable.cs @@ -52,12 +52,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) IEnumerable funcAsts = ast.FindAll(item => item is FunctionDefinitionAst, true); IEnumerable funcMemberAsts = ast.FindAll(item => item is FunctionMemberAst, true); - - // Checks whether this is a dsc resource file (we don't raise this rule for get, set and test-target resource - bool isDscResourceFile = Helper.Instance.IsDscResourceModule(fileName); - - List targetResourcesFunctions = new List( new string[] { "get-targetresource", "set-targetresource", "test-targetresource" }); - + foreach (FunctionDefinitionAst funcAst in funcAsts) { // Finds all VariableExpressionAst. @@ -65,13 +60,16 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) HashSet paramVariables = new HashSet(); - if (isDscResourceFile && targetResourcesFunctions.Contains(funcAst.Name, StringComparer.OrdinalIgnoreCase)) + // don't raise the rules for variables in the param block. + if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Parameters != null) { - // don't raise the rules for variables in the param block. - if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Parameters != null) - { - paramVariables.UnionWith(funcAst.Body.ParamBlock.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath)); - } + paramVariables.UnionWith(funcAst.Body.ParamBlock.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath)); + } + + //don't raise the rules for parameters outside the param block + if(funcAst.Parameters != null) + { + paramVariables.UnionWith(funcAst.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath)); } // Iterates all VariableExpressionAst and check the command name. diff --git a/Rules/AvoidUsingDeprecatedManifestFields.cs b/Rules/AvoidUsingDeprecatedManifestFields.cs new file mode 100644 index 000000000..964522490 --- /dev/null +++ b/Rules/AvoidUsingDeprecatedManifestFields.cs @@ -0,0 +1,132 @@ +// +// 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.Management.Automation.Language; +using System.Management.Automation; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; +using System.ComponentModel.Composition; +using System.Globalization; + +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules +{ + /// + /// AvoidUsingDeprecatedManifestFields: Run Test Module Manifest to check that no deprecated fields are being used. + /// + [Export(typeof(IScriptRule))] + public class AvoidUsingDeprecatedManifestFields : IScriptRule + { + /// + /// AnalyzeScript: Run Test Module Manifest to check that no deprecated fields are being used. + /// + /// The script's ast + /// The script's file name + /// A List of diagnostic results of this rule + public IEnumerable AnalyzeScript(Ast ast, string fileName) + { + if (ast == null) + { + throw new ArgumentNullException(Strings.NullAstErrorMessage); + } + + if (String.Equals(System.IO.Path.GetExtension(fileName), ".psd1", StringComparison.OrdinalIgnoreCase)) + { + var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace); + IEnumerable result = null; + try + { + ps.AddCommand("Test-ModuleManifest"); + ps.AddParameter("Path", fileName); + + // Suppress warnings emitted during the execution of Test-ModuleManifest + // ModuleManifest rule must catch any violations (warnings/errors) and generate DiagnosticRecord(s) + ps.AddParameter("WarningAction", ActionPreference.SilentlyContinue); + ps.AddParameter("WarningVariable", "Message"); + ps.AddScript("$Message"); + result = ps.Invoke(); + + } + catch + {} + + if (result != null) + { + foreach (var warning in result) + { + if (warning.BaseObject != null) + { + yield return + new DiagnosticRecord( + String.Format(CultureInfo.CurrentCulture, warning.BaseObject.ToString()), ast.Extent, + GetName(), DiagnosticSeverity.Warning, fileName); + } + } + } + + } + + } + + /// + /// GetName: Retrieves the name of this rule. + /// + /// The name of this rule + public string GetName() + { + return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.AvoidUsingDeprecatedManifestFieldsName); + } + + /// + /// GetCommonName: Retrieves the common name of this rule. + /// + /// The common name of this rule + public string GetCommonName() + { + return String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingDeprecatedManifestFieldsCommonName); + } + + /// + /// GetDescription: Retrieves the description of this rule. + /// + /// The description of this rule + public string GetDescription() + { + return String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingDeprecatedManifestFieldsDescription); + } + + /// + /// Method: Retrieves the type of the rule: builtin, managed or module. + /// + public SourceType GetSourceType() + { + return SourceType.Builtin; + } + + /// + /// GetSeverity: Retrieves the severity of the rule: error, warning of information. + /// + /// + public RuleSeverity GetSeverity() + { + return RuleSeverity.Warning; + } + + /// + /// Method: Retrieves the module/assembly name the rule is from. + /// + public string GetSourceName() + { + return string.Format(CultureInfo.CurrentCulture, Strings.SourceName); + } + } +} diff --git a/Rules/ProvideDefaultParameterValue.cs b/Rules/ProvideDefaultParameterValue.cs new file mode 100644 index 000000000..3d5c24023 --- /dev/null +++ b/Rules/ProvideDefaultParameterValue.cs @@ -0,0 +1,140 @@ +// +// 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.Linq; +using System.Collections.Generic; +using System.Management.Automation.Language; +using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic; +using System.ComponentModel.Composition; +using System.Globalization; + +namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules +{ + /// + /// ProvideDefaultParameterValue: Check if any uninitialized variable is used. + /// + [Export(typeof(IScriptRule))] + public class ProvideDefaultParameterValue : IScriptRule + { + /// + /// AnalyzeScript: Check if any uninitialized variable is used. + /// + public IEnumerable AnalyzeScript(Ast ast, string fileName) + { + if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage); + + // Finds all functionAst + IEnumerable functionAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true); + + // Checks whether this is a dsc resource file (we don't raise this rule for get, set and test-target resource + bool isDscResourceFile = Helper.Instance.IsDscResourceModule(fileName); + + List targetResourcesFunctions = new List(new string[] { "get-targetresource", "set-targetresource", "test-targetresource" }); + + + foreach (FunctionDefinitionAst funcAst in functionAsts) + { + // Finds all ParamAsts. + IEnumerable varAsts = funcAst.FindAll(testAst => testAst is VariableExpressionAst, true); + + // Iterrates all ParamAsts and check if their names are on the list. + + HashSet dscVariables = new HashSet(); + if (isDscResourceFile && targetResourcesFunctions.Contains(funcAst.Name, StringComparer.OrdinalIgnoreCase)) + { + // don't raise the rules for variables in the param block. + if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Parameters != null) + { + dscVariables.UnionWith(funcAst.Body.ParamBlock.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath)); + } + } + // only raise the rules for variables in the param block. + if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Parameters != null) + { + foreach (var paramAst in funcAst.Body.ParamBlock.Parameters) + { + if (Helper.Instance.IsUninitialized(paramAst.Name, funcAst) && !dscVariables.Contains(paramAst.Name.VariablePath.UserPath)) + { + yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueError, paramAst.Name.VariablePath.UserPath), + paramAst.Name.Extent, GetName(), DiagnosticSeverity.Warning, fileName, paramAst.Name.VariablePath.UserPath); + } + } + } + + if (funcAst.Parameters != null) + { + foreach (var paramAst in funcAst.Parameters) + { + if (Helper.Instance.IsUninitialized(paramAst.Name, funcAst) && !dscVariables.Contains(paramAst.Name.VariablePath.UserPath)) + { + yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueError, paramAst.Name.VariablePath.UserPath), + paramAst.Name.Extent, GetName(), DiagnosticSeverity.Warning, fileName, paramAst.Name.VariablePath.UserPath); + } + } + } + } + } + + /// + /// GetName: Retrieves the name of this rule. + /// + /// The name of this rule + public string GetName() + { + return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.ProvideDefaultParameterValueName); + } + + /// + /// GetCommonName: Retrieves the common name of this rule. + /// + /// The common name of this rule + public string GetCommonName() + { + return string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueCommonName); + } + + /// + /// GetDescription: Retrieves the description of this rule. + /// + /// The description of this rule + public string GetDescription() + { + return string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueDescription); + } + + /// + /// Method: Retrieves the type of the rule: builtin, managed or module. + /// + public SourceType GetSourceType() + { + return SourceType.Builtin; + } + + /// + /// GetSeverity: Retrieves the severity of the rule: error, warning of information. + /// + /// + public RuleSeverity GetSeverity() + { + return RuleSeverity.Warning; + } + + /// + /// Method: Retrieves the module/assembly name the rule is from. + /// + public string GetSourceName() + { + return string.Format(CultureInfo.CurrentCulture, Strings.SourceName); + } + } +} diff --git a/Rules/ScriptAnalyzerBuiltinRules.csproj b/Rules/ScriptAnalyzerBuiltinRules.csproj index 5f76f89c5..e9b8a56f6 100644 --- a/Rules/ScriptAnalyzerBuiltinRules.csproj +++ b/Rules/ScriptAnalyzerBuiltinRules.csproj @@ -58,6 +58,8 @@ + + diff --git a/Rules/Strings.Designer.cs b/Rules/Strings.Designer.cs index a748b432d..54175ef88 100644 --- a/Rules/Strings.Designer.cs +++ b/Rules/Strings.Designer.cs @@ -510,6 +510,33 @@ internal static string AvoidUsingConvertToSecureStringWithPlainTextName { } } + /// + /// Looks up a localized string similar to Avoid Using Deprecated Manifest Fields. + /// + internal static string AvoidUsingDeprecatedManifestFieldsCommonName { + get { + return ResourceManager.GetString("AvoidUsingDeprecatedManifestFieldsCommonName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to "ModuleToProcess" is obsolete in the latest PowerShell version. Please update with the latest field "RootModule" in manifest files to avoid PowerShell version inconsistency.. + /// + internal static string AvoidUsingDeprecatedManifestFieldsDescription { + get { + return ResourceManager.GetString("AvoidUsingDeprecatedManifestFieldsDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to AvoidUsingDeprecatedManifestFields. + /// + internal static string AvoidUsingDeprecatedManifestFieldsName { + get { + return ResourceManager.GetString("AvoidUsingDeprecatedManifestFieldsName", resourceCulture); + } + } + /// /// Looks up a localized string similar to Avoid Using Empty Catch Block. /// @@ -1104,6 +1131,42 @@ internal static string ProvideCommentHelpName { } } + /// + /// Looks up a localized string similar to Default Parameter Values. + /// + internal static string ProvideDefaultParameterValueCommonName { + get { + return ResourceManager.GetString("ProvideDefaultParameterValueCommonName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Parameters must have a default value. To fix a violation of this rule, please specify a default value for all parameters. + /// + internal static string ProvideDefaultParameterValueDescription { + get { + return ResourceManager.GetString("ProvideDefaultParameterValueDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Parameter '{0}' is not initialized. Parameters must have a default value. To fix a violation of this rule, please specify a default value for all parameters. + /// + internal static string ProvideDefaultParameterValueError { + get { + return ResourceManager.GetString("ProvideDefaultParameterValueError", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ProvideDefaultParameterValue. + /// + internal static string ProvideDefaultParameterValueName { + get { + return ResourceManager.GetString("ProvideDefaultParameterValueName", resourceCulture); + } + } + /// /// Looks up a localized string similar to Verbose. /// diff --git a/Rules/Strings.resx b/Rules/Strings.resx index 833279fcc..c16fa35e3 100644 --- a/Rules/Strings.resx +++ b/Rules/Strings.resx @@ -714,4 +714,25 @@ No examples found for resource '{0}' + + Default Parameter Values + + + Parameters must have a default value. To fix a violation of this rule, please specify a default value for all parameters + + + Parameter '{0}' is not initialized. Parameters must have a default value. To fix a violation of this rule, please specify a default value for all parameters + + + ProvideDefaultParameterValue + + + Avoid Using Deprecated Manifest Fields + + + "ModuleToProcess" is obsolete in the latest PowerShell version. Please update with the latest field "RootModule" in manifest files to avoid PowerShell version inconsistency. + + + AvoidUsingDeprecatedManifestFields + \ No newline at end of file diff --git a/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 b/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 index f5303a2f4..ad4c411b2 100644 --- a/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 +++ b/Tests/Engine/InvokeScriptAnalyzer.tests.ps1 @@ -136,12 +136,12 @@ Describe "Test IncludeRule" { Context "IncludeRule supports wild card" { It "includes 1 wildcard rule"{ $includeWildcard = Invoke-ScriptAnalyzer $directory\..\Rules\BadCmdlet.ps1 -IncludeRule $avoidRules - $includeWildcard.Count | Should be 5 + $includeWildcard.Count | Should be 3 } it "includes 2 wildcardrules" { $includeWildcard = Invoke-ScriptAnalyzer $directory\..\Rules\BadCmdlet.ps1 -IncludeRule $avoidRules, $useRules - $includeWildcard.Count | Should be 9 + $includeWildcard.Count | Should be 7 } } } diff --git a/Tests/Engine/RuleSuppression.ps1 b/Tests/Engine/RuleSuppression.ps1 index d2fc8e485..d5cd4e055 100644 --- a/Tests/Engine/RuleSuppression.ps1 +++ b/Tests/Engine/RuleSuppression.ps1 @@ -6,7 +6,7 @@ Param( function SuppressMe () { [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSProvideVerboseMessage")] - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUninitializedVariable", "unused1")] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSProvideDefaultParameterValue", "unused1")] Param([string]$unUsed1, [int] $unUsed2) { Write-Host "I do nothing" @@ -16,8 +16,8 @@ function SuppressMe () function SuppressTwoVariables() { - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUninitializedVariable", "b")] - [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUninitializedVariable", "a")] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSProvideDefaultParameterValue", "b")] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSProvideDefaultParameterValue", "a")] Param([string]$a, [int]$b) { } diff --git a/Tests/Engine/RuleSuppression.tests.ps1 b/Tests/Engine/RuleSuppression.tests.ps1 index 15a026025..bb6ff4a77 100644 --- a/Tests/Engine/RuleSuppression.tests.ps1 +++ b/Tests/Engine/RuleSuppression.tests.ps1 @@ -33,7 +33,7 @@ Describe "RuleSuppressionWithoutScope" { Context "RuleSuppressionID" { It "Only suppress violations for that ID" { - $suppression = $violations | Where-Object {$_.RuleName -eq "PSAvoidUninitializedVariable" } + $suppression = $violations | Where-Object {$_.RuleName -eq "PSProvideDefaultParameterValue" } $suppression.Count | Should Be 1 } } diff --git a/Tests/Rules/AvoidUsingDeprecatedManifestFields.tests.ps1 b/Tests/Rules/AvoidUsingDeprecatedManifestFields.tests.ps1 new file mode 100644 index 000000000..f5729024b --- /dev/null +++ b/Tests/Rules/AvoidUsingDeprecatedManifestFields.tests.ps1 @@ -0,0 +1,19 @@ +Import-Module PSScriptAnalyzer +$violationName = "PSAvoidUsingDeprecatedManifestFields" +$directory = Split-Path -Parent $MyInvocation.MyCommand.Path +$violations = Invoke-ScriptAnalyzer $directory\TestBadModule\TestDeprecatedManifestFields.psd1 | Where-Object {$_.RuleName -eq $violationName} +$noViolations = Invoke-ScriptAnalyzer $directory\TestGoodModule\TestGoodModule.psd1 | Where-Object {$_.RuleName -eq $violationName} + +Describe "AvoidUsingDeprecatedManifestFields" { + Context "When there are violations" { + It "has 1 violations" { + $violations.Count | Should Be 1 + } + } + + Context "When there are no violations" { + It "returns no violations" { + $noViolations.Count | Should Be 0 + } + } +} \ No newline at end of file diff --git a/Tests/Rules/ProvideDefaultParameterValue.ps1 b/Tests/Rules/ProvideDefaultParameterValue.ps1 new file mode 100644 index 000000000..706ba9e6c --- /dev/null +++ b/Tests/Rules/ProvideDefaultParameterValue.ps1 @@ -0,0 +1,20 @@ +function BadFunc +{ + param( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Param1, + [Parameter(Mandatory=$false)] + [ValidateNotNullOrEmpty()] + [string] + $Param2 + ) + $Param1 + $Param1 = "test" +} + +function BadFunc2($Param1) +{ + $Param1 +} \ No newline at end of file diff --git a/Tests/Rules/ProvideDefaultParameterValue.tests.ps1 b/Tests/Rules/ProvideDefaultParameterValue.tests.ps1 new file mode 100644 index 000000000..c731d7974 --- /dev/null +++ b/Tests/Rules/ProvideDefaultParameterValue.tests.ps1 @@ -0,0 +1,24 @@ +Import-Module PSScriptAnalyzer +$violationName = "PSProvideDefaultParameterValue" +$violationMessage = "Parameter 'Param2' is not initialized. Parameters must have a default value. To fix a violation of this rule, please specify a default value for all parameters" +$directory = Split-Path -Parent $MyInvocation.MyCommand.Path +$violations = Invoke-ScriptAnalyzer $directory\ProvideDefaultParameterValue.ps1 | Where-Object {$_.RuleName -match $violationName} +$noViolations = Invoke-ScriptAnalyzer $directory\ProvideDefaultParameterValueNoViolations.ps1 + +Describe "ProvideDefaultParameters" { + Context "When there are violations" { + It "has 2 provide default parameter value violation" { + $violations.Count | Should Be 2 + } + + It "has the correct description message" { + $violations[0].Message | Should Match $violationMessage + } + } + + Context "When there are no violations" { + It "returns no violations" { + $noViolations.Count | Should Be 0 + } + } +} \ No newline at end of file diff --git a/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 b/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 new file mode 100644 index 000000000..a42931454 --- /dev/null +++ b/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 @@ -0,0 +1,19 @@ +function GoodFunc +{ + param( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Param1, + [Parameter(Mandatory=$false)] + [ValidateNotNullOrEmpty()] + [string] + $Param2=$null + ) + $Param1 +} + +function GoodFunc2($Param1 = $null) +{ + $Param1 +} \ No newline at end of file diff --git a/Tests/Rules/TestBadModule/TestBadModule.psd1 b/Tests/Rules/TestBadModule/TestBadModule.psd1 index 44d52f161..b8d7bea6c 100644 Binary files a/Tests/Rules/TestBadModule/TestBadModule.psd1 and b/Tests/Rules/TestBadModule/TestBadModule.psd1 differ diff --git a/Tests/Rules/TestBadModule/TestDeprecatedManifestFields.psd1 b/Tests/Rules/TestBadModule/TestDeprecatedManifestFields.psd1 new file mode 100644 index 000000000..768d5454e --- /dev/null +++ b/Tests/Rules/TestBadModule/TestDeprecatedManifestFields.psd1 @@ -0,0 +1,120 @@ +# +# Module manifest for module 'Deprecated Module manifest fields" +# +# Generated by: Microsoft PowerShell Team +# +# Generated on: 5/18/2015 +# + +@{ + +# Script module or binary module file associated with this manifest. +ModuleToProcess ='psscriptanalyzer' + +# Version number of this module. +ModuleVersion = '1.0' + +# ID used to uniquely identify this module +GUID = 'a9f79c02-4503-4300-a022-5e8c01f3449f' + +# Author of this module +Author = '' + +# Company or vendor of this module +CompanyName = '' + +# Copyright statement for this module +Copyright = '(c) 2015 Microsoft. All rights reserved.' + +# Description of the functionality provided by this module +# Description = '' + +# Minimum version of the Windows PowerShell engine required by this module +# PowerShellVersion = '' + +# Name of the Windows PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the Windows PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module +# DotNetFrameworkVersion = '' + +# Minimum version of the common language runtime (CLR) required by this module +# CLRVersion = '' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +# RequiredModules = @() + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# Functions to export from this module +FunctionsToExport = '*' + +# Cmdlets to export from this module +CmdletsToExport = '*' + +# Variables to export from this module +VariablesToExport = '*' + +# Aliases to export from this module +AliasesToExport = '*' + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable +#with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + # Tags = @() + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + # ProjectUri = '' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} \ No newline at end of file