Skip to content

Commit 5908771

Browse files
committed
Merge pull request #236 from PowerShell/master
Integrate Master with BugFixes
2 parents 11507fc + 76200c9 commit 5908771

19 files changed

+639
-27
lines changed

License.txt renamed to LICENSE

File renamed without changes.

README.md

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
|Master |BugFixes |Development |
2+
|:------:|:------:|:-------:|:-------:|
3+
[![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) |
14

2-
Introduction
5+
6+
7+
Introduction
38
============
49

510
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
4247
**Note: If there are any build errors, please refer to Requirements section and make sure all dependencies are properly installed**
4348

4449

45-
Build Status
46-
==============
47-
48-
| |Master Branch |
49-
|---------|:------:|:------:|:-------:|:-------:|
50-
|**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) |
51-
52-
5350
Running Tests
5451
=============
5552

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#AvoidUsingDeprecatedManifestFields
2+
**Severity Level: Warning**
3+
4+
5+
##Description
6+
7+
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.
8+
9+
##How to Fix
10+
11+
To fix a violation of this, please replace "ModuleToProcess" with "RootModule".
12+
13+
##Example
14+
15+
Wrong:
16+
```
17+
ModuleToProcess ='psscriptanalyzer'
18+
19+
ModuleVersion = '1.0'
20+
```
21+
22+
Correct:
23+
```
24+
RootModule ='psscriptanalyzer'
25+
26+
ModuleVersion = '1.0'
27+
```
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#ProvideDefaultParameterValue
2+
**Severity Level: Warning**
3+
4+
5+
##Description
6+
Parameters must have a default value as uninitialized parameters will lead to potential bugs in the scripts.
7+
8+
##How to Fix
9+
10+
To fix a violation of this rule, please specify a default value for all parameters.
11+
12+
##Example
13+
14+
Wrong:
15+
16+
```
17+
function Test($Param1)
18+
{
19+
$Param1
20+
}
21+
```
22+
23+
Correct:
24+
25+
```
26+
function Test($Param1 = $null)
27+
{
28+
$Param1
29+
}
30+
```

Rules/AvoidUninitializedVariable.cs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,26 +52,24 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
5252

5353
IEnumerable<Ast> funcAsts = ast.FindAll(item => item is FunctionDefinitionAst, true);
5454
IEnumerable<Ast> funcMemberAsts = ast.FindAll(item => item is FunctionMemberAst, true);
55-
56-
// Checks whether this is a dsc resource file (we don't raise this rule for get, set and test-target resource
57-
bool isDscResourceFile = Helper.Instance.IsDscResourceModule(fileName);
58-
59-
List<string> targetResourcesFunctions = new List<string>( new string[] { "get-targetresource", "set-targetresource", "test-targetresource" });
60-
55+
6156
foreach (FunctionDefinitionAst funcAst in funcAsts)
6257
{
6358
// Finds all VariableExpressionAst.
6459
IEnumerable<Ast> varAsts = funcAst.FindAll(testAst => testAst is VariableExpressionAst, true);
6560

6661
HashSet<string> paramVariables = new HashSet<string>();
6762

68-
if (isDscResourceFile && targetResourcesFunctions.Contains(funcAst.Name, StringComparer.OrdinalIgnoreCase))
63+
// don't raise the rules for variables in the param block.
64+
if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Parameters != null)
6965
{
70-
// don't raise the rules for variables in the param block.
71-
if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Parameters != null)
72-
{
73-
paramVariables.UnionWith(funcAst.Body.ParamBlock.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath));
74-
}
66+
paramVariables.UnionWith(funcAst.Body.ParamBlock.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath));
67+
}
68+
69+
//don't raise the rules for parameters outside the param block
70+
if(funcAst.Parameters != null)
71+
{
72+
paramVariables.UnionWith(funcAst.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath));
7573
}
7674

7775
// Iterates all VariableExpressionAst and check the command name.
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
//
2+
// Copyright (c) Microsoft Corporation.
3+
//
4+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10+
// THE SOFTWARE.
11+
//
12+
13+
using System;
14+
using System.Collections.Generic;
15+
using System.Management.Automation.Language;
16+
using System.Management.Automation;
17+
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic;
18+
using System.ComponentModel.Composition;
19+
using System.Globalization;
20+
21+
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
22+
{
23+
/// <summary>
24+
/// AvoidUsingDeprecatedManifestFields: Run Test Module Manifest to check that no deprecated fields are being used.
25+
/// </summary>
26+
[Export(typeof(IScriptRule))]
27+
public class AvoidUsingDeprecatedManifestFields : IScriptRule
28+
{
29+
/// <summary>
30+
/// AnalyzeScript: Run Test Module Manifest to check that no deprecated fields are being used.
31+
/// </summary>
32+
/// <param name="ast">The script's ast</param>
33+
/// <param name="fileName">The script's file name</param>
34+
/// <returns>A List of diagnostic results of this rule</returns>
35+
public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
36+
{
37+
if (ast == null)
38+
{
39+
throw new ArgumentNullException(Strings.NullAstErrorMessage);
40+
}
41+
42+
if (String.Equals(System.IO.Path.GetExtension(fileName), ".psd1", StringComparison.OrdinalIgnoreCase))
43+
{
44+
var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace);
45+
IEnumerable<PSObject> result = null;
46+
try
47+
{
48+
ps.AddCommand("Test-ModuleManifest");
49+
ps.AddParameter("Path", fileName);
50+
51+
// Suppress warnings emitted during the execution of Test-ModuleManifest
52+
// ModuleManifest rule must catch any violations (warnings/errors) and generate DiagnosticRecord(s)
53+
ps.AddParameter("WarningAction", ActionPreference.SilentlyContinue);
54+
ps.AddParameter("WarningVariable", "Message");
55+
ps.AddScript("$Message");
56+
result = ps.Invoke();
57+
58+
}
59+
catch
60+
{}
61+
62+
if (result != null)
63+
{
64+
foreach (var warning in result)
65+
{
66+
if (warning.BaseObject != null)
67+
{
68+
yield return
69+
new DiagnosticRecord(
70+
String.Format(CultureInfo.CurrentCulture, warning.BaseObject.ToString()), ast.Extent,
71+
GetName(), DiagnosticSeverity.Warning, fileName);
72+
}
73+
}
74+
}
75+
76+
}
77+
78+
}
79+
80+
/// <summary>
81+
/// GetName: Retrieves the name of this rule.
82+
/// </summary>
83+
/// <returns>The name of this rule</returns>
84+
public string GetName()
85+
{
86+
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.AvoidUsingDeprecatedManifestFieldsName);
87+
}
88+
89+
/// <summary>
90+
/// GetCommonName: Retrieves the common name of this rule.
91+
/// </summary>
92+
/// <returns>The common name of this rule</returns>
93+
public string GetCommonName()
94+
{
95+
return String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingDeprecatedManifestFieldsCommonName);
96+
}
97+
98+
/// <summary>
99+
/// GetDescription: Retrieves the description of this rule.
100+
/// </summary>
101+
/// <returns>The description of this rule</returns>
102+
public string GetDescription()
103+
{
104+
return String.Format(CultureInfo.CurrentCulture, Strings.AvoidUsingDeprecatedManifestFieldsDescription);
105+
}
106+
107+
/// <summary>
108+
/// Method: Retrieves the type of the rule: builtin, managed or module.
109+
/// </summary>
110+
public SourceType GetSourceType()
111+
{
112+
return SourceType.Builtin;
113+
}
114+
115+
/// <summary>
116+
/// GetSeverity: Retrieves the severity of the rule: error, warning of information.
117+
/// </summary>
118+
/// <returns></returns>
119+
public RuleSeverity GetSeverity()
120+
{
121+
return RuleSeverity.Warning;
122+
}
123+
124+
/// <summary>
125+
/// Method: Retrieves the module/assembly name the rule is from.
126+
/// </summary>
127+
public string GetSourceName()
128+
{
129+
return string.Format(CultureInfo.CurrentCulture, Strings.SourceName);
130+
}
131+
}
132+
}

Rules/ProvideDefaultParameterValue.cs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
//
2+
// Copyright (c) Microsoft Corporation.
3+
//
4+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10+
// THE SOFTWARE.
11+
//
12+
13+
using System;
14+
using System.Linq;
15+
using System.Collections.Generic;
16+
using System.Management.Automation.Language;
17+
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic;
18+
using System.ComponentModel.Composition;
19+
using System.Globalization;
20+
21+
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
22+
{
23+
/// <summary>
24+
/// ProvideDefaultParameterValue: Check if any uninitialized variable is used.
25+
/// </summary>
26+
[Export(typeof(IScriptRule))]
27+
public class ProvideDefaultParameterValue : IScriptRule
28+
{
29+
/// <summary>
30+
/// AnalyzeScript: Check if any uninitialized variable is used.
31+
/// </summary>
32+
public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
33+
{
34+
if (ast == null) throw new ArgumentNullException(Strings.NullAstErrorMessage);
35+
36+
// Finds all functionAst
37+
IEnumerable<Ast> functionAsts = ast.FindAll(testAst => testAst is FunctionDefinitionAst, true);
38+
39+
// Checks whether this is a dsc resource file (we don't raise this rule for get, set and test-target resource
40+
bool isDscResourceFile = Helper.Instance.IsDscResourceModule(fileName);
41+
42+
List<string> targetResourcesFunctions = new List<string>(new string[] { "get-targetresource", "set-targetresource", "test-targetresource" });
43+
44+
45+
foreach (FunctionDefinitionAst funcAst in functionAsts)
46+
{
47+
// Finds all ParamAsts.
48+
IEnumerable<Ast> varAsts = funcAst.FindAll(testAst => testAst is VariableExpressionAst, true);
49+
50+
// Iterrates all ParamAsts and check if their names are on the list.
51+
52+
HashSet<string> dscVariables = new HashSet<string>();
53+
if (isDscResourceFile && targetResourcesFunctions.Contains(funcAst.Name, StringComparer.OrdinalIgnoreCase))
54+
{
55+
// don't raise the rules for variables in the param block.
56+
if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Parameters != null)
57+
{
58+
dscVariables.UnionWith(funcAst.Body.ParamBlock.Parameters.Select(paramAst => paramAst.Name.VariablePath.UserPath));
59+
}
60+
}
61+
// only raise the rules for variables in the param block.
62+
if (funcAst.Body != null && funcAst.Body.ParamBlock != null && funcAst.Body.ParamBlock.Parameters != null)
63+
{
64+
foreach (var paramAst in funcAst.Body.ParamBlock.Parameters)
65+
{
66+
if (Helper.Instance.IsUninitialized(paramAst.Name, funcAst) && !dscVariables.Contains(paramAst.Name.VariablePath.UserPath))
67+
{
68+
yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueError, paramAst.Name.VariablePath.UserPath),
69+
paramAst.Name.Extent, GetName(), DiagnosticSeverity.Warning, fileName, paramAst.Name.VariablePath.UserPath);
70+
}
71+
}
72+
}
73+
74+
if (funcAst.Parameters != null)
75+
{
76+
foreach (var paramAst in funcAst.Parameters)
77+
{
78+
if (Helper.Instance.IsUninitialized(paramAst.Name, funcAst) && !dscVariables.Contains(paramAst.Name.VariablePath.UserPath))
79+
{
80+
yield return new DiagnosticRecord(string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueError, paramAst.Name.VariablePath.UserPath),
81+
paramAst.Name.Extent, GetName(), DiagnosticSeverity.Warning, fileName, paramAst.Name.VariablePath.UserPath);
82+
}
83+
}
84+
}
85+
}
86+
}
87+
88+
/// <summary>
89+
/// GetName: Retrieves the name of this rule.
90+
/// </summary>
91+
/// <returns>The name of this rule</returns>
92+
public string GetName()
93+
{
94+
return string.Format(CultureInfo.CurrentCulture, Strings.NameSpaceFormat, GetSourceName(), Strings.ProvideDefaultParameterValueName);
95+
}
96+
97+
/// <summary>
98+
/// GetCommonName: Retrieves the common name of this rule.
99+
/// </summary>
100+
/// <returns>The common name of this rule</returns>
101+
public string GetCommonName()
102+
{
103+
return string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueCommonName);
104+
}
105+
106+
/// <summary>
107+
/// GetDescription: Retrieves the description of this rule.
108+
/// </summary>
109+
/// <returns>The description of this rule</returns>
110+
public string GetDescription()
111+
{
112+
return string.Format(CultureInfo.CurrentCulture, Strings.ProvideDefaultParameterValueDescription);
113+
}
114+
115+
/// <summary>
116+
/// Method: Retrieves the type of the rule: builtin, managed or module.
117+
/// </summary>
118+
public SourceType GetSourceType()
119+
{
120+
return SourceType.Builtin;
121+
}
122+
123+
/// <summary>
124+
/// GetSeverity: Retrieves the severity of the rule: error, warning of information.
125+
/// </summary>
126+
/// <returns></returns>
127+
public RuleSeverity GetSeverity()
128+
{
129+
return RuleSeverity.Warning;
130+
}
131+
132+
/// <summary>
133+
/// Method: Retrieves the module/assembly name the rule is from.
134+
/// </summary>
135+
public string GetSourceName()
136+
{
137+
return string.Format(CultureInfo.CurrentCulture, Strings.SourceName);
138+
}
139+
}
140+
}

0 commit comments

Comments
 (0)