From 9f2564cbcd8fe2819d1ea3d6efb6dac7c4284e8b Mon Sep 17 00:00:00 2001 From: "Raghu Shantha [MSFT]" Date: Fri, 15 May 2015 14:35:54 -0700 Subject: [PATCH 01/32] Added Build status for BugFixes and Development branches --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 25d4aea1a..1be9e6146 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,13 @@ 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 | +| |Master |BugFixes |Development | |---------|:------:|:------:|:-------:|:-------:| -|**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) | +|**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)|[![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) | Running Tests From 506da93c56936ce727a3bab1553f9588a920f2c8 Mon Sep 17 00:00:00 2001 From: "Raghu Shantha [MSFT]" Date: Fri, 15 May 2015 14:48:38 -0700 Subject: [PATCH 02/32] Update README.md --- README.md | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1be9e6146..bff7837b6 100644 --- a/README.md +++ b/README.md @@ -1,4 +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 ============ @@ -42,15 +48,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 |BugFixes |Development | -|---------|:------:|:------:|:-------:|:-------:| -|**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)|[![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) | - - Running Tests ============= From f1b8484582941f0baaba69758344391b072d3654 Mon Sep 17 00:00:00 2001 From: "Raghu Shantha [MSFT]" Date: Fri, 15 May 2015 14:54:26 -0700 Subject: [PATCH 03/32] Update README.md --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index bff7837b6..efe28c450 100644 --- a/README.md +++ b/README.md @@ -1,11 +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. From 44574b5f708d87b6078600848e62be8a9707312d Mon Sep 17 00:00:00 2001 From: GoodOlClint Date: Fri, 15 May 2015 23:11:03 -0500 Subject: [PATCH 04/32] Add new ProvideDefaultParameterValue rule. Per @rkeithhill suggestion in #133 --- Rules/ProvideDefaultParameterValue.cs | 117 ++++++++++++++++++++++++ Rules/ScriptAnalyzerBuiltinRules.csproj | 1 + Rules/Strings.Designer.cs | 38 +++++++- Rules/Strings.resx | 12 +++ 4 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 Rules/ProvideDefaultParameterValue.cs diff --git a/Rules/ProvideDefaultParameterValue.cs b/Rules/ProvideDefaultParameterValue.cs new file mode 100644 index 000000000..05e32d3d4 --- /dev/null +++ b/Rules/ProvideDefaultParameterValue.cs @@ -0,0 +1,117 @@ +// +// 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); + + 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 paramVariables = new HashSet(); + // only 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)); + } + + // Iterates all VariableExpressionAst and check the command name. + foreach (VariableExpressionAst varAst in varAsts) + { + if (Helper.Instance.IsUninitialized(varAst, funcAst) && paramVariables.Contains(varAst.VariablePath.UserPath)) + { + yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueError, varAst.VariablePath.UserPath), + varAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName, varAst.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 7401ac269..9f553cb06 100644 --- a/Rules/ScriptAnalyzerBuiltinRules.csproj +++ b/Rules/ScriptAnalyzerBuiltinRules.csproj @@ -59,6 +59,7 @@ + diff --git a/Rules/Strings.Designer.cs b/Rules/Strings.Designer.cs index 641440213..7261f6308 100644 --- a/Rules/Strings.Designer.cs +++ b/Rules/Strings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.34014 +// Runtime Version:4.0.30319.34209 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -1104,6 +1104,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 initialize non-global variables.. + /// + internal static string ProvideDefaultParameterValueDescription { + get { + return ResourceManager.GetString("ProvideDefaultParameterValueDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Parameter '{0}' is not initialized. Non-global variables must be initialized. To fix a violation of this rule, please initialize non-global variables.. + /// + 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 b26e4d6fa..021884dbb 100644 --- a/Rules/Strings.resx +++ b/Rules/Strings.resx @@ -714,4 +714,16 @@ No examples found for resource '{0}' + + Default Parameter Values + + + Parameters must have a default value. To fix a violation of this rule, please initialize non-global variables. + + + Parameter '{0}' is not initialized. Non-global variables must be initialized. To fix a violation of this rule, please initialize non-global variables. + + + ProvideDefaultParameterValue + \ No newline at end of file From 513b04c3501ee67fcb1a8872e9371365de719298 Mon Sep 17 00:00:00 2001 From: GoodOlClint Date: Fri, 15 May 2015 23:12:14 -0500 Subject: [PATCH 05/32] Tests for ProvideDefaultParameterValue rule Getting "Object reference not set to an instance of an object error" when running test, but seems to complete successfully. Need to investigate error. --- Tests/Rules/ProvideDefaultParameterValue.ps1 | 9 +++++++ .../ProvideDefaultParameterValue.tests.ps1 | 24 +++++++++++++++++++ ...ovideDefaultParameterValueNoViolations.ps1 | 8 +++++++ 3 files changed, 41 insertions(+) create mode 100644 Tests/Rules/ProvideDefaultParameterValue.ps1 create mode 100644 Tests/Rules/ProvideDefaultParameterValue.tests.ps1 create mode 100644 Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 diff --git a/Tests/Rules/ProvideDefaultParameterValue.ps1 b/Tests/Rules/ProvideDefaultParameterValue.ps1 new file mode 100644 index 000000000..19c2c1d68 --- /dev/null +++ b/Tests/Rules/ProvideDefaultParameterValue.ps1 @@ -0,0 +1,9 @@ +function BadFunc +{ + param( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string] + $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..83fdbbbbf --- /dev/null +++ b/Tests/Rules/ProvideDefaultParameterValue.tests.ps1 @@ -0,0 +1,24 @@ +Import-Module PSScriptAnalyzer +$violationName = "PSProvideDefaultParameterValue" +$violationMessage = "Parameter 'Param1' is not initialized. Non-global variables must be initialized. To fix a violation of this rule, please initialize non-global variables." +$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 1 provide default parameter value violation" { + $violations.Count | Should Be 1 + } + + 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..4480b71e5 --- /dev/null +++ b/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 @@ -0,0 +1,8 @@ +function GoodFunc +{ + param( + [parmaeter(mandatory=$false)] + [string]$Param1=$null + ) + $Param1 +} \ No newline at end of file From 503d18e224d69f9fff6b6c6d88aab467273cb70c Mon Sep 17 00:00:00 2001 From: GoodOlClint Date: Fri, 15 May 2015 23:17:38 -0500 Subject: [PATCH 06/32] Don't trigger PSAvoidUninitializedVariable on function parameters --- Rules/AvoidUninitializedVariable.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Rules/AvoidUninitializedVariable.cs b/Rules/AvoidUninitializedVariable.cs index 2872f0e56..1c3abfbfd 100644 --- a/Rules/AvoidUninitializedVariable.cs +++ b/Rules/AvoidUninitializedVariable.cs @@ -56,7 +56,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) // 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" }); + List targetResourcesFunctions = new List(new string[] { "get-targetresource", "set-targetresource", "test-targetresource" }); foreach (FunctionDefinitionAst funcAst in funcAsts) { @@ -65,13 +65,10 @@ 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)); } // Iterates all VariableExpressionAst and check the command name. From 2f5480df844ed289f1deb6f78d0d151333653f2b Mon Sep 17 00:00:00 2001 From: GoodOlClint Date: Fri, 15 May 2015 23:48:17 -0500 Subject: [PATCH 07/32] Correct PSProvideDefaultParameterValue NoViolations test script --- Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 b/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 index 4480b71e5..9ed67845f 100644 --- a/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 +++ b/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 @@ -1,8 +1,10 @@ function GoodFunc { param( - [parmaeter(mandatory=$false)] - [string]$Param1=$null + [Parameter(Mandatory=$false)] + [ValidateNotNullOrEmpty()] + [string] + $Param1=$null ) $Param1 } \ No newline at end of file From 4e9732c795dec7b3efa144aaaf2b26ace8ee0fc2 Mon Sep 17 00:00:00 2001 From: GoodOlClint Date: Fri, 15 May 2015 23:57:31 -0500 Subject: [PATCH 08/32] Updated strings for PSProvideDefaultParameterValue --- Rules/Strings.Designer.cs | 4 ++-- Rules/Strings.resx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Rules/Strings.Designer.cs b/Rules/Strings.Designer.cs index 7261f6308..3f8180b6c 100644 --- a/Rules/Strings.Designer.cs +++ b/Rules/Strings.Designer.cs @@ -1114,7 +1114,7 @@ internal static string ProvideDefaultParameterValueCommonName { } /// - /// Looks up a localized string similar to Parameters must have a default value. To fix a violation of this rule, please initialize non-global variables.. + /// 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 parameters. /// internal static string ProvideDefaultParameterValueDescription { get { @@ -1123,7 +1123,7 @@ internal static string ProvideDefaultParameterValueDescription { } /// - /// Looks up a localized string similar to Parameter '{0}' is not initialized. Non-global variables must be initialized. To fix a violation of this rule, please initialize non-global variables.. + /// 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 parameters. /// internal static string ProvideDefaultParameterValueError { get { diff --git a/Rules/Strings.resx b/Rules/Strings.resx index 021884dbb..da0010853 100644 --- a/Rules/Strings.resx +++ b/Rules/Strings.resx @@ -718,10 +718,10 @@ Default Parameter Values - Parameters must have a default value. To fix a violation of this rule, please initialize non-global variables. + 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. Non-global variables must be initialized. To fix a violation of this rule, please initialize non-global variables. + 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 From 55919be8030020d07a424b8d83f5e02c5cbc38ab Mon Sep 17 00:00:00 2001 From: GoodOlClint Date: Fri, 15 May 2015 23:57:49 -0500 Subject: [PATCH 09/32] Updated Tests for PSProvideDefaultParameterValue --- Tests/Rules/ProvideDefaultParameterValue.ps1 | 4 ++++ Tests/Rules/ProvideDefaultParameterValue.tests.ps1 | 6 +++--- Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 | 4 ++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Tests/Rules/ProvideDefaultParameterValue.ps1 b/Tests/Rules/ProvideDefaultParameterValue.ps1 index 19c2c1d68..694be2e74 100644 --- a/Tests/Rules/ProvideDefaultParameterValue.ps1 +++ b/Tests/Rules/ProvideDefaultParameterValue.ps1 @@ -6,4 +6,8 @@ [string] $Param1 ) +} + +function BadFunc2($Param1) +{ } \ No newline at end of file diff --git a/Tests/Rules/ProvideDefaultParameterValue.tests.ps1 b/Tests/Rules/ProvideDefaultParameterValue.tests.ps1 index 83fdbbbbf..5289fdc72 100644 --- a/Tests/Rules/ProvideDefaultParameterValue.tests.ps1 +++ b/Tests/Rules/ProvideDefaultParameterValue.tests.ps1 @@ -1,14 +1,14 @@ Import-Module PSScriptAnalyzer $violationName = "PSProvideDefaultParameterValue" -$violationMessage = "Parameter 'Param1' is not initialized. Non-global variables must be initialized. To fix a violation of this rule, please initialize non-global variables." +$violationMessage = "Parameter 'Param1' 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 1 provide default parameter value violation" { - $violations.Count | Should Be 1 + It "has 2 provide default parameter value violation" { + $violations.Count | Should Be 2 } It "has the correct description message" { diff --git a/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 b/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 index 9ed67845f..b8ce7ec66 100644 --- a/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 +++ b/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 @@ -7,4 +7,8 @@ $Param1=$null ) $Param1 +} + +function GoodFunc2($Param1 = $null) +{ } \ No newline at end of file From 94dcf514edd01dc2e6c1f0478cc995f5135550fb Mon Sep 17 00:00:00 2001 From: GoodOlClint Date: Sat, 16 May 2015 00:06:53 -0500 Subject: [PATCH 10/32] Raise PSProvideDefaultParameterValues for parameters outside a param block --- Rules/ProvideDefaultParameterValue.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Rules/ProvideDefaultParameterValue.cs b/Rules/ProvideDefaultParameterValue.cs index 05e32d3d4..b9add5d18 100644 --- a/Rules/ProvideDefaultParameterValue.cs +++ b/Rules/ProvideDefaultParameterValue.cs @@ -50,6 +50,11 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) paramVariables.UnionWith(funcAst.Body.ParamBlock.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath)); } + if (funcAst.Parameters != null) + { + paramVariables.UnionWith(funcAst.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath)); + } + // Iterates all VariableExpressionAst and check the command name. foreach (VariableExpressionAst varAst in varAsts) { From fdd11985bef3e070268f003263a19caa55d9ddff Mon Sep 17 00:00:00 2001 From: GoodOlClint Date: Sat, 16 May 2015 00:07:16 -0500 Subject: [PATCH 11/32] Don't raise PSAvoidUninitalizedVariables for parameters outside a param block --- Rules/AvoidUninitializedVariable.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Rules/AvoidUninitializedVariable.cs b/Rules/AvoidUninitializedVariable.cs index 1c3abfbfd..1812cf360 100644 --- a/Rules/AvoidUninitializedVariable.cs +++ b/Rules/AvoidUninitializedVariable.cs @@ -70,6 +70,12 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { 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. foreach (VariableExpressionAst varAst in varAsts) From 67dbc2c538e0a6660fb27b865ac2f06b57aa1fab Mon Sep 17 00:00:00 2001 From: GoodOlClint Date: Sat, 16 May 2015 00:19:58 -0500 Subject: [PATCH 12/32] Updated PSProvideDefaultParameterValue Rules --- Tests/Rules/ProvideDefaultParameterValue.ps1 | 3 +++ Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 | 1 + 2 files changed, 4 insertions(+) diff --git a/Tests/Rules/ProvideDefaultParameterValue.ps1 b/Tests/Rules/ProvideDefaultParameterValue.ps1 index 694be2e74..17508fe67 100644 --- a/Tests/Rules/ProvideDefaultParameterValue.ps1 +++ b/Tests/Rules/ProvideDefaultParameterValue.ps1 @@ -6,8 +6,11 @@ [string] $Param1 ) + $Param1 + $Param1 = "test" } function BadFunc2($Param1) { + $Param1 } \ No newline at end of file diff --git a/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 b/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 index b8ce7ec66..cd0a1219b 100644 --- a/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 +++ b/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 @@ -11,4 +11,5 @@ function GoodFunc2($Param1 = $null) { + $Param1 } \ No newline at end of file From be83037c96ed202f1d6b1ad2223a671a66ffcc79 Mon Sep 17 00:00:00 2001 From: GoodOlClint Date: Sat, 16 May 2015 00:20:41 -0500 Subject: [PATCH 13/32] Do not trigger PSProvideDefaultParameterValue when parameters are used inside function --- Rules/ProvideDefaultParameterValue.cs | 28 +++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Rules/ProvideDefaultParameterValue.cs b/Rules/ProvideDefaultParameterValue.cs index b9add5d18..2a266ee36 100644 --- a/Rules/ProvideDefaultParameterValue.cs +++ b/Rules/ProvideDefaultParameterValue.cs @@ -31,7 +31,7 @@ public class ProvideDefaultParameterValue : IScriptRule /// public IEnumerable AnalyzeScript(Ast ast, string fileName) { - if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage); + if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage); // Finds all functionAst IEnumerable functionAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true); @@ -40,28 +40,32 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { // 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 paramVariables = new HashSet(); // only 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)); + foreach (var paramAst in funcAst.Body.ParamBlock.Parameters) + { + if (Helper.Instance.IsUninitialized(paramAst.Name, funcAst)) + { + 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) { - paramVariables.UnionWith(funcAst.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath)); - } - - // Iterates all VariableExpressionAst and check the command name. - foreach (VariableExpressionAst varAst in varAsts) - { - if (Helper.Instance.IsUninitialized(varAst, funcAst) && paramVariables.Contains(varAst.VariablePath.UserPath)) + foreach (var paramAst in funcAst.Parameters) { - yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueError, varAst.VariablePath.UserPath), - varAst.Extent, GetName(), DiagnosticSeverity.Warning, fileName, varAst.VariablePath.UserPath); + if (Helper.Instance.IsUninitialized(paramAst.Name, funcAst)) + { + 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); + } } } } From ead2e6f746c2aacc5769f2988bc1c9b2038667a7 Mon Sep 17 00:00:00 2001 From: GoodOlClint Date: Sat, 16 May 2015 00:28:06 -0500 Subject: [PATCH 14/32] More robust PSProvideDefaultParameterValue tests --- Tests/Rules/ProvideDefaultParameterValue.ps1 | 8 ++++++-- Tests/Rules/ProvideDefaultParameterValue.tests.ps1 | 2 +- Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 | 6 +++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Tests/Rules/ProvideDefaultParameterValue.ps1 b/Tests/Rules/ProvideDefaultParameterValue.ps1 index 17508fe67..706ba9e6c 100644 --- a/Tests/Rules/ProvideDefaultParameterValue.ps1 +++ b/Tests/Rules/ProvideDefaultParameterValue.ps1 @@ -1,10 +1,14 @@ function BadFunc { param( - [Parameter()] + [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] - $Param1 + $Param1, + [Parameter(Mandatory=$false)] + [ValidateNotNullOrEmpty()] + [string] + $Param2 ) $Param1 $Param1 = "test" diff --git a/Tests/Rules/ProvideDefaultParameterValue.tests.ps1 b/Tests/Rules/ProvideDefaultParameterValue.tests.ps1 index 5289fdc72..c731d7974 100644 --- a/Tests/Rules/ProvideDefaultParameterValue.tests.ps1 +++ b/Tests/Rules/ProvideDefaultParameterValue.tests.ps1 @@ -1,6 +1,6 @@ Import-Module PSScriptAnalyzer $violationName = "PSProvideDefaultParameterValue" -$violationMessage = "Parameter 'Param1' is not initialized. Parameters must have a default value. To fix a violation of this rule, please specify a default value for all parameters" +$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 diff --git a/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 b/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 index cd0a1219b..a42931454 100644 --- a/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 +++ b/Tests/Rules/ProvideDefaultParameterValueNoViolations.ps1 @@ -1,10 +1,14 @@ function GoodFunc { param( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string] + $Param1, [Parameter(Mandatory=$false)] [ValidateNotNullOrEmpty()] [string] - $Param1=$null + $Param2=$null ) $Param1 } From 18678a37f292c2d9fbb1ffa8c22beb1cb21b5d31 Mon Sep 17 00:00:00 2001 From: GoodOlClint Date: Sun, 17 May 2015 17:49:13 -0500 Subject: [PATCH 15/32] Correct Engine tests for PSProvideDefaultParameterValue --- Tests/Engine/InvokeScriptAnalyzer.tests.ps1 | 4 ++-- Tests/Engine/RuleSuppression.ps1 | 2 +- Tests/Engine/RuleSuppression.tests.ps1 | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) 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 45ae97bd3..44fb3e867 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" 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 } } From 2528ca68b121ed22f807165be19787b1db34b95f Mon Sep 17 00:00:00 2001 From: GoodOlClint Date: Sun, 17 May 2015 18:05:44 -0500 Subject: [PATCH 16/32] Remove isDscResourceFile Check from PSAvoidUninitalizedVariables Rule --- Rules/AvoidUninitializedVariable.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Rules/AvoidUninitializedVariable.cs b/Rules/AvoidUninitializedVariable.cs index 1812cf360..7a0031740 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. From 276f464c92abdb120d62c55964446589ad1b4fde Mon Sep 17 00:00:00 2001 From: GoodOlClint Date: Sun, 17 May 2015 18:27:59 -0500 Subject: [PATCH 17/32] Add isDscResourceFile Check to PSProvideDefaultParameterValue --- Rules/ProvideDefaultParameterValue.cs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Rules/ProvideDefaultParameterValue.cs b/Rules/ProvideDefaultParameterValue.cs index 2a266ee36..b10c2a70b 100644 --- a/Rules/ProvideDefaultParameterValue.cs +++ b/Rules/ProvideDefaultParameterValue.cs @@ -36,6 +36,12 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) // 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. @@ -43,13 +49,21 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) // Iterrates all ParamAsts and check if their names are on the list. - HashSet paramVariables = new HashSet(); + 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)) + 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); @@ -61,7 +75,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { foreach (var paramAst in funcAst.Parameters) { - if (Helper.Instance.IsUninitialized(paramAst.Name, funcAst)) + 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); From d6bde9dcabb1c06bed8154736f81162889416b03 Mon Sep 17 00:00:00 2001 From: Yuting Chen Date: Mon, 18 May 2015 10:02:57 -0700 Subject: [PATCH 18/32] Add rule documentation for ProvideDefaultParameter --- .../ProvideDefaultParameterValue.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 RuleDocumentation/ProvideDefaultParameterValue.md diff --git a/RuleDocumentation/ProvideDefaultParameterValue.md b/RuleDocumentation/ProvideDefaultParameterValue.md new file mode 100644 index 000000000..d88db18cb --- /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: + +``` +ffunction Test($Param1 = $null) +{ + $Param1 +} +``` From 8e1b187de271cb9cc79eb6b371d189dca5135e9e Mon Sep 17 00:00:00 2001 From: "Yuting Chen[MSFT]" Date: Mon, 18 May 2015 10:05:32 -0700 Subject: [PATCH 19/32] Update ProvideDefaultParameterValue.md --- RuleDocumentation/ProvideDefaultParameterValue.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RuleDocumentation/ProvideDefaultParameterValue.md b/RuleDocumentation/ProvideDefaultParameterValue.md index d88db18cb..5569e266e 100644 --- a/RuleDocumentation/ProvideDefaultParameterValue.md +++ b/RuleDocumentation/ProvideDefaultParameterValue.md @@ -7,7 +7,7 @@ Parameters must have a default value as uninitialized parameters will lead to po ##How to Fix -To fix a violation of this rule, please specify a default value for all parameters +To fix a violation of this rule, please specify a default value for all parameters. ##Example @@ -23,7 +23,7 @@ function Test($Param1) Correct: ``` -ffunction Test($Param1 = $null) +function Test($Param1 = $null) { $Param1 } From a22fe8f7aeef6e4666b92328ce79e173cb84fe55 Mon Sep 17 00:00:00 2001 From: Yuting Chen Date: Mon, 18 May 2015 15:14:40 -0700 Subject: [PATCH 20/32] Add new rule AvoidUsingDeprecatedManifestFileds --- Rules/AvoidUsingDeprecatedManifestFields.cs | 123 ++++++++++++++++++ Rules/ScriptAnalyzerBuiltinRules.csproj | 1 + Rules/Strings.Designer.cs | 33 ++++- Rules/Strings.resx | 9 ++ ...oidUsingDeprecatedManifestFields.tests.ps1 | 19 +++ Tests/Rules/TestBadModule/TestBadModule.psd1 | Bin 6564 -> 6598 bytes .../TestDeprecatedManifestFields.psd1 | 120 +++++++++++++++++ 7 files changed, 302 insertions(+), 3 deletions(-) create mode 100644 Rules/AvoidUsingDeprecatedManifestFields.cs create mode 100644 Tests/Rules/AvoidUsingDeprecatedManifestFields.tests.ps1 create mode 100644 Tests/Rules/TestBadModule/TestDeprecatedManifestFields.psd1 diff --git a/Rules/AvoidUsingDeprecatedManifestFields.cs b/Rules/AvoidUsingDeprecatedManifestFields.cs new file mode 100644 index 000000000..76aa39cb2 --- /dev/null +++ b/Rules/AvoidUsingDeprecatedManifestFields.cs @@ -0,0 +1,123 @@ +// +// 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 +{ + /// + /// MissingModuleManifestField: 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) + { + yield return new DiagnosticRecord(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/ScriptAnalyzerBuiltinRules.csproj b/Rules/ScriptAnalyzerBuiltinRules.csproj index 9f553cb06..087930117 100644 --- a/Rules/ScriptAnalyzerBuiltinRules.csproj +++ b/Rules/ScriptAnalyzerBuiltinRules.csproj @@ -59,6 +59,7 @@ + diff --git a/Rules/Strings.Designer.cs b/Rules/Strings.Designer.cs index 3f8180b6c..108a90fce 100644 --- a/Rules/Strings.Designer.cs +++ b/Rules/Strings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.34209 +// Runtime Version:4.0.30319.35317 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -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 Some manifest fields are obsolte in the latest PowerShell version. Please update with the latest manifest fields in modules 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. /// @@ -1114,7 +1141,7 @@ internal static string ProvideDefaultParameterValueCommonName { } /// - /// 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 parameters. + /// 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 { @@ -1123,7 +1150,7 @@ internal static string ProvideDefaultParameterValueDescription { } /// - /// 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 parameters. + /// 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 { diff --git a/Rules/Strings.resx b/Rules/Strings.resx index da0010853..edd55e8f4 100644 --- a/Rules/Strings.resx +++ b/Rules/Strings.resx @@ -726,4 +726,13 @@ ProvideDefaultParameterValue + + Avoid Using Deprecated Manifest Fields + + + Some manifest fields are obsolte in the latest PowerShell version. Please update with the latest manifest fields in modules to avoid PowerShell version inconsistency. + + + AvoidUsingDeprecatedManifestFields + \ No newline at end of file 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/TestBadModule/TestBadModule.psd1 b/Tests/Rules/TestBadModule/TestBadModule.psd1 index 44d52f161732f285a76ab619ba67ef98e355ab5b..1a49df3cc4ed031576f1c7983db059a5df666f08 100644 GIT binary patch delta 52 zcmZ2te9U-53FG8%LUPIq47Lmk4C)NU49N^d44DiC3?&SS40%AD!%)dk#gNKSw0Sb4 GpacMOH4NbZ delta 26 icmX?Ryu^4z2_vKO Date: Mon, 18 May 2015 15:17:32 -0700 Subject: [PATCH 21/32] Edit some fields --- .../Rules/TestBadModule/TestDeprecatedManifestFields.psd1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/Rules/TestBadModule/TestDeprecatedManifestFields.psd1 b/Tests/Rules/TestBadModule/TestDeprecatedManifestFields.psd1 index fd03fbb90..61456462a 100644 --- a/Tests/Rules/TestBadModule/TestDeprecatedManifestFields.psd1 +++ b/Tests/Rules/TestBadModule/TestDeprecatedManifestFields.psd1 @@ -1,7 +1,7 @@ # # Module manifest for module 'Deprecated Module manifest fields" # -# Generated by: yutch +# Generated by: Microsoft PowerShell Team # # Generated on: 5/18/2015 # @@ -18,13 +18,13 @@ ModuleVersion = '1.0' GUID = 'a9f79c02-4503-4300-a022-5e8c01f3449f' # Author of this module -Author = 'quoct' +Author = '' # Company or vendor of this module -CompanyName = 'Unknown' +CompanyName = '' # Copyright statement for this module -Copyright = '(c) 2015 quoct. All rights reserved.' +Copyright = '(c) 2015 Microsoft. All rights reserved.' # Description of the functionality provided by this module # Description = '' From eae794c0f0f152d2ce78867ad18d5ecd40141ef1 Mon Sep 17 00:00:00 2001 From: Yuting Chen Date: Mon, 18 May 2015 15:19:25 -0700 Subject: [PATCH 22/32] Update some string format --- Rules/AvoidUsingDeprecatedManifestFields.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Rules/AvoidUsingDeprecatedManifestFields.cs b/Rules/AvoidUsingDeprecatedManifestFields.cs index 76aa39cb2..d8fa6a2dd 100644 --- a/Rules/AvoidUsingDeprecatedManifestFields.cs +++ b/Rules/AvoidUsingDeprecatedManifestFields.cs @@ -21,7 +21,7 @@ namespace Microsoft.Windows.Powershell.ScriptAnalyzer.BuiltinRules { /// - /// MissingModuleManifestField: Run Test Module Manifest to check that no deprecated fields are being used. + /// AvoidUsingDeprecatedManifestFields: Run Test Module Manifest to check that no deprecated fields are being used. /// [Export(typeof(IScriptRule))] public class AvoidUsingDeprecatedManifestFields : IScriptRule @@ -60,7 +60,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { foreach (var warning in result) { - yield return new DiagnosticRecord(warning.BaseObject.ToString(), ast.Extent, GetName(), DiagnosticSeverity.Warning, fileName); + yield return new DiagnosticRecord(String.Format((CultureInfo.CurrentCulture, warning.BaseObject.ToString()), ast.Extent, GetName(), DiagnosticSeverity.Warning, fileName); } } From f9498856315005581cb70af70c9ad81811d5834b Mon Sep 17 00:00:00 2001 From: Yuting Chen Date: Mon, 18 May 2015 15:38:30 -0700 Subject: [PATCH 23/32] Change the string format --- Rules/AvoidUsingDeprecatedManifestFields.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rules/AvoidUsingDeprecatedManifestFields.cs b/Rules/AvoidUsingDeprecatedManifestFields.cs index d8fa6a2dd..2d4263858 100644 --- a/Rules/AvoidUsingDeprecatedManifestFields.cs +++ b/Rules/AvoidUsingDeprecatedManifestFields.cs @@ -60,7 +60,7 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { foreach (var warning in result) { - yield return new DiagnosticRecord(String.Format((CultureInfo.CurrentCulture, warning.BaseObject.ToString()), ast.Extent, GetName(), DiagnosticSeverity.Warning, fileName); + yield return new DiagnosticRecord(String.Format(CultureInfo.CurrentCulture, warning.BaseObject.ToString()), ast.Extent, GetName(), DiagnosticSeverity.Warning, fileName); } } From 3a626937dd3baf634b1ce97768499938bd3ff2ff Mon Sep 17 00:00:00 2001 From: Yuting Chen Date: Mon, 18 May 2015 16:00:21 -0700 Subject: [PATCH 24/32] Add braces for if condition --- Rules/AvoidUsingDeprecatedManifestFields.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Rules/AvoidUsingDeprecatedManifestFields.cs b/Rules/AvoidUsingDeprecatedManifestFields.cs index 2d4263858..7f69f874e 100644 --- a/Rules/AvoidUsingDeprecatedManifestFields.cs +++ b/Rules/AvoidUsingDeprecatedManifestFields.cs @@ -34,7 +34,10 @@ public class AvoidUsingDeprecatedManifestFields : IScriptRule /// A List of diagnostic results of this rule public IEnumerable AnalyzeScript(Ast ast, string fileName) { - if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage); + if (ast == null) + { + throw new ArgumentNullException(Strings.NullAstErrorMessage); + } if (String.Equals(System.IO.Path.GetExtension(fileName), ".psd1", StringComparison.OrdinalIgnoreCase)) { From 67fc9170be74611a652505a268a5e4c33fce25fa Mon Sep 17 00:00:00 2001 From: Yuting Chen Date: Mon, 18 May 2015 16:03:48 -0700 Subject: [PATCH 25/32] Modify error strings and test file fields --- Rules/Strings.Designer.cs | 2 +- Rules/Strings.resx | 2 +- Tests/Rules/TestBadModule/TestBadModule.psd1 | Bin 6598 -> 6602 bytes .../TestDeprecatedManifestFields.psd1 | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Rules/Strings.Designer.cs b/Rules/Strings.Designer.cs index 108a90fce..9a80ba177 100644 --- a/Rules/Strings.Designer.cs +++ b/Rules/Strings.Designer.cs @@ -520,7 +520,7 @@ internal static string AvoidUsingDeprecatedManifestFieldsCommonName { } /// - /// Looks up a localized string similar to Some manifest fields are obsolte in the latest PowerShell version. Please update with the latest manifest fields in modules to avoid PowerShell version inconsistency.. + /// Looks up a localized string similar to Some manifest fields are obsolete in the latest PowerShell version. Please update with the latest manifest fields in modules to avoid PowerShell version inconsistency.. /// internal static string AvoidUsingDeprecatedManifestFieldsDescription { get { diff --git a/Rules/Strings.resx b/Rules/Strings.resx index edd55e8f4..b6ed8f8c6 100644 --- a/Rules/Strings.resx +++ b/Rules/Strings.resx @@ -730,7 +730,7 @@ Avoid Using Deprecated Manifest Fields - Some manifest fields are obsolte in the latest PowerShell version. Please update with the latest manifest fields in modules to avoid PowerShell version inconsistency. + Some manifest fields are obsolete in the latest PowerShell version. Please update with the latest manifest fields in modules to avoid PowerShell version inconsistency. AvoidUsingDeprecatedManifestFields diff --git a/Tests/Rules/TestBadModule/TestBadModule.psd1 b/Tests/Rules/TestBadModule/TestBadModule.psd1 index 1a49df3cc4ed031576f1c7983db059a5df666f08..b8d7bea6c0af30f4efab05b25b4cefc2b0d6fa26 100644 GIT binary patch delta 16 XcmX?Re9CykEJo%6hT_dL8FeK9IGqK$ delta 12 TcmX?Qe9U;mEXK|A7 Date: Mon, 18 May 2015 16:10:51 -0700 Subject: [PATCH 26/32] Added null check for BaseObject --- Rules/AvoidUsingDeprecatedManifestFields.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Rules/AvoidUsingDeprecatedManifestFields.cs b/Rules/AvoidUsingDeprecatedManifestFields.cs index 7f69f874e..8e20179cd 100644 --- a/Rules/AvoidUsingDeprecatedManifestFields.cs +++ b/Rules/AvoidUsingDeprecatedManifestFields.cs @@ -63,7 +63,13 @@ public IEnumerable AnalyzeScript(Ast ast, string fileName) { foreach (var warning in result) { - yield return new DiagnosticRecord(String.Format(CultureInfo.CurrentCulture, warning.BaseObject.ToString()), ast.Extent, GetName(), DiagnosticSeverity.Warning, fileName); + if (warning.BaseObject != null) + { + yield return + new DiagnosticRecord( + String.Format(CultureInfo.CurrentCulture, warning.BaseObject.ToString()), ast.Extent, + GetName(), DiagnosticSeverity.Warning, fileName); + } } } From a3ec05475bde9ae1034670c753ef13fa3c8b0248 Mon Sep 17 00:00:00 2001 From: Yuting Chen Date: Mon, 18 May 2015 16:29:00 -0700 Subject: [PATCH 27/32] Update the error description string --- Rules/Strings.Designer.cs | 2 +- Rules/Strings.resx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Rules/Strings.Designer.cs b/Rules/Strings.Designer.cs index 9a80ba177..00f24aa6f 100644 --- a/Rules/Strings.Designer.cs +++ b/Rules/Strings.Designer.cs @@ -520,7 +520,7 @@ internal static string AvoidUsingDeprecatedManifestFieldsCommonName { } /// - /// Looks up a localized string similar to Some manifest fields are obsolete in the latest PowerShell version. Please update with the latest manifest fields in modules to avoid PowerShell version inconsistency.. + /// 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 { diff --git a/Rules/Strings.resx b/Rules/Strings.resx index b6ed8f8c6..7686518a9 100644 --- a/Rules/Strings.resx +++ b/Rules/Strings.resx @@ -730,7 +730,7 @@ Avoid Using Deprecated Manifest Fields - Some manifest fields are obsolete in the latest PowerShell version. Please update with the latest manifest fields in modules to avoid PowerShell version inconsistency. + "ModuleToProcess" is obsolete in the latest PowerShell version. Please update with the latest field "RootModule" in manifest files to avoid PowerShell version inconsistency. AvoidUsingDeprecatedManifestFields From 9704c4752a803ad350e28d16156c4b22aefbfaa3 Mon Sep 17 00:00:00 2001 From: Yuting Chen Date: Tue, 19 May 2015 14:57:04 -0700 Subject: [PATCH 28/32] Add rule documentation for new rule --- .../AvoidUsingDeprecatedManifestFields.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 RuleDocumentation/AvoidUsingDeprecatedManifestFields.md diff --git a/RuleDocumentation/AvoidUsingDeprecatedManifestFields.md b/RuleDocumentation/AvoidUsingDeprecatedManifestFields.md new file mode 100644 index 000000000..a8f84539e --- /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' +''' From 0607a75c702a2114f6c52e54f2e56ec136d85733 Mon Sep 17 00:00:00 2001 From: Yuting Chen Date: Tue, 19 May 2015 14:57:56 -0700 Subject: [PATCH 29/32] Change the formats --- RuleDocumentation/AvoidUsingDeprecatedManifestFields.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RuleDocumentation/AvoidUsingDeprecatedManifestFields.md b/RuleDocumentation/AvoidUsingDeprecatedManifestFields.md index a8f84539e..8f7fa0015 100644 --- a/RuleDocumentation/AvoidUsingDeprecatedManifestFields.md +++ b/RuleDocumentation/AvoidUsingDeprecatedManifestFields.md @@ -20,8 +20,8 @@ ModuleVersion = '1.0' ``` Correct: -''' +``` RootModule ='psscriptanalyzer' ModuleVersion = '1.0' -''' +``` From 2bc754d7a8050ba5491e7f6e50bad45f9ba18642 Mon Sep 17 00:00:00 2001 From: "Raghu Shantha [MSFT]" Date: Wed, 20 May 2015 12:32:55 -0700 Subject: [PATCH 30/32] Rename License.txt to LICENSE.md --- License.txt => LICENSE.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename License.txt => LICENSE.md (100%) diff --git a/License.txt b/LICENSE.md similarity index 100% rename from License.txt rename to LICENSE.md From 5f677e951797afaf5994c3075fa25873b7915270 Mon Sep 17 00:00:00 2001 From: "Raghu Shantha [MSFT]" Date: Wed, 20 May 2015 12:33:31 -0700 Subject: [PATCH 31/32] Rename LICENSE.md to LICENSE --- LICENSE.md => LICENSE | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename LICENSE.md => LICENSE (100%) diff --git a/LICENSE.md b/LICENSE similarity index 100% rename from LICENSE.md rename to LICENSE From 76200c9dcb119f8c886ee230ed34c261a86b047b Mon Sep 17 00:00:00 2001 From: "Raghu Shantha [MSFT]" Date: Tue, 2 Jun 2015 10:58:28 -0700 Subject: [PATCH 32/32] Fix for failing RuleSuppressionID tests --- Tests/Engine/RuleSuppression.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Engine/RuleSuppression.ps1 b/Tests/Engine/RuleSuppression.ps1 index 23b095793..d5cd4e055 100644 --- a/Tests/Engine/RuleSuppression.ps1 +++ b/Tests/Engine/RuleSuppression.ps1 @@ -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) { }