Skip to content

Commit afccf35

Browse files
author
Kapil Borle
committed
Add correction extent to UseToExportFieldsInManifest rule
Suggest corrections to FunctionsToExport and AliasesToExport when their values are set to '*' or '$null', e.g., FunctionsToExport = '*'. This does not support wildcard in an array, eg @('Set-Foo', 'Get-*').
1 parent cc68a2e commit afccf35

9 files changed

Lines changed: 139 additions & 17 deletions

Rules/UseToExportFieldsInManifest.cs

Lines changed: 82 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using System.ComponentModel.Composition;
2020
using System.Globalization;
2121
using System.Text.RegularExpressions;
22+
using System.Diagnostics;
2223

2324
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
2425
{
@@ -29,6 +30,12 @@ namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
2930
[Export(typeof(IScriptRule))]
3031
public class UseToExportFieldsInManifest : IScriptRule
3132
{
33+
34+
private const string functionsToExport = "FunctionsToExport";
35+
private const string cmdletsToExport = "CmdletsToExport";
36+
private const string variablesToExport = "VariablesToExport";
37+
private const string aliasesToExport = "AliasesToExport";
38+
3239
/// <summary>
3340
/// AnalyzeScript: Analyzes the AST to check if AliasToExport, CmdletsToExport, FunctionsToExport
3441
/// and VariablesToExport fields do not use wildcards and $null in their entries.
@@ -48,43 +55,101 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
4855
yield break;
4956
}
5057

51-
if (!IsValidManifest(ast, fileName))
52-
{
58+
// check if valid module manifest
59+
IEnumerable<ErrorRecord> errorRecord = null;
60+
PSModuleInfo psModuleInfo = Helper.Instance.GetModuleManifest(fileName, out errorRecord);
61+
if ((errorRecord != null && errorRecord.Count() > 0) || psModuleInfo == null)
62+
{
5363
yield break;
5464
}
55-
56-
String[] manifestFields = {"FunctionsToExport", "CmdletsToExport", "VariablesToExport", "AliasesToExport"};
65+
5766
var hashtableAst = ast.Find(x => x is HashtableAst, false) as HashtableAst;
5867

5968
if (hashtableAst == null)
6069
{
6170
yield break;
6271
}
6372

64-
foreach(String field in manifestFields)
73+
string[] manifestFields = { functionsToExport, cmdletsToExport, variablesToExport, aliasesToExport };
74+
75+
foreach(string field in manifestFields)
6576
{
6677
IScriptExtent extent;
6778
if (!HasAcceptableExportField(field, hashtableAst, ast.Extent.Text, out extent) && extent != null)
6879
{
69-
yield return new DiagnosticRecord(GetError(field), extent, GetName(), DiagnosticSeverity.Warning, fileName);
80+
yield return new DiagnosticRecord(
81+
GetError(field),
82+
extent,
83+
GetName(),
84+
DiagnosticSeverity.Warning,
85+
fileName,
86+
suggestedCorrections: GetCorrectionExtent(field, extent, psModuleInfo));
7087
}
7188
}
7289

7390
}
74-
75-
/// <summary>
76-
/// Checks if the manifest file is valid.
77-
/// </summary>
78-
/// <param name="ast"></param>
79-
/// <param name="fileName"></param>
80-
/// <returns>A boolean value indicating the validity of the manifest file.</returns>
81-
private bool IsValidManifest(Ast ast, string fileName)
91+
92+
private string GetListLiteral<T>(Dictionary<string, T> exportedItems)
8293
{
83-
var missingManifestRule = new MissingModuleManifestField();
84-
return !missingManifestRule.AnalyzeScript(ast, fileName).GetEnumerator().MoveNext();
85-
94+
if (exportedItems == null || exportedItems.Keys == null)
95+
{
96+
return null;
97+
}
98+
var sbuilder = new System.Text.StringBuilder();
99+
sbuilder.Append("@(");
100+
sbuilder.Append(string.Join(", ", exportedItems.Keys.Select(key => string.Format("'{0}'", key))));
101+
sbuilder.Append(")");
102+
return sbuilder.ToString();
86103
}
87104

105+
106+
private List<CorrectionExtent> GetCorrectionExtent(string field, IScriptExtent extent, PSModuleInfo psModuleInfo)
107+
{
108+
Debug.Assert(field != null);
109+
Debug.Assert(psModuleInfo != null);
110+
Debug.Assert(extent != null);
111+
var corrections = new List<CorrectionExtent>();
112+
string correctionText = null;
113+
switch (field)
114+
{
115+
case functionsToExport:
116+
correctionText = GetListLiteral(psModuleInfo.ExportedFunctions);
117+
break;
118+
case cmdletsToExport:
119+
correctionText = GetListLiteral(psModuleInfo.ExportedCmdlets);
120+
break;
121+
case variablesToExport:
122+
correctionText = GetListLiteral(psModuleInfo.ExportedVariables);
123+
break;
124+
case aliasesToExport:
125+
correctionText = GetListLiteral(psModuleInfo.ExportedAliases);
126+
break;
127+
default:
128+
throw new NotImplementedException(string.Format("{0} not implemented", field));
129+
}
130+
corrections.Add(new CorrectionExtent(
131+
extent.StartLineNumber,
132+
extent.EndLineNumber,
133+
extent.StartColumnNumber,
134+
extent.EndColumnNumber,
135+
correctionText,
136+
extent.File));
137+
return corrections;
138+
}
139+
140+
///// <summary>
141+
///// Checks if the manifest file is valid.
142+
///// </summary>
143+
///// <param name="ast"></param>
144+
///// <param name="fileName"></param>
145+
///// <returns>A boolean value indicating the validity of the manifest file.</returns>
146+
//private bool IsValidManifest(Ast ast, string fileName)
147+
//{
148+
// var missingManifestRule = new MissingModuleManifestField();
149+
// return !missingManifestRule.AnalyzeScript(ast, fileName).GetEnumerator().MoveNext();
150+
151+
//}
152+
88153
/// <summary>
89154
/// Checks if the ast contains wildcard character.
90155
/// </summary>
58 Bytes
Binary file not shown.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Function Get-Foo
2+
{
3+
4+
}
5+
6+
Function Get-Bar
7+
{
8+
9+
}
10+
11+
Export-ModuleMember -Function Get-Foo -Alias gfoo
12+
Export-ModuleMember -Function Get-Bar -Alias gbar
54 Bytes
Binary file not shown.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Function Get-Foo
2+
{
3+
4+
}
5+
6+
Function Get-Bar
7+
{
8+
9+
}
10+
11+
Export-ModuleMember -Function Get-Foo
12+
Export-ModuleMember -Function Get-Bar
62 Bytes
Binary file not shown.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Function Get-Foo
2+
{
3+
4+
}
5+
6+
Function Get-Bar
7+
{
8+
9+
}
10+
11+
Export-ModuleMember -Function Get-Foo
12+
Export-ModuleMember -Function Get-Bar
66 Bytes
Binary file not shown.

Tests/Rules/UseToExportFieldsInManifest.tests.ps1

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,26 @@ Describe "UseManifestExportFields" {
3939
$results[0].Extent.Text | Should be "'*'"
4040
}
4141

42+
It "suggests corrections for FunctionsToExport with wildcard" {
43+
Import-Module .\PSScriptAnalyzerTestHelper.psm1
44+
$violations = Run-PSScriptAnalyzerRule $testManifestBadFunctionsWildcardPath
45+
$violationFilepath = Join-path $testManifestPath $testManifestBadFunctionsWildcardPath
46+
Test-CorrectionExtent $violationFilepath $violations[0] 1 "'*'" "@('Get-Foo', 'Get-Bar')"
47+
}
48+
4249
It "detects FunctionsToExport with null" {
4350
$results = Run-PSScriptAnalyzerRule $testManifestBadFunctionsNullPath
4451
$results.Count | Should be 1
4552
$results[0].Extent.Text | Should be '$null'
4653
}
4754

55+
It "suggests corrections for FunctionsToExport with null" {
56+
Import-Module .\PSScriptAnalyzerTestHelper.psm1
57+
$violations = Run-PSScriptAnalyzerRule $testManifestBadFunctionsNullPath
58+
$violationFilepath = Join-path $testManifestPath $testManifestBadFunctionsNullPath
59+
Test-CorrectionExtent $violationFilepath $violations[0] 1 '$null' "@('Get-Foo', 'Get-Bar')"
60+
}
61+
4862
It "detects array element containing wildcard" {
4963
$results = Run-PSScriptAnalyzerRule $testManifestBadFunctionsWildcardInArrayPath
5064
$results.Count | Should be 3
@@ -68,6 +82,13 @@ Describe "UseManifestExportFields" {
6882
$results[0].Extent.Text | Should be "'*'"
6983
}
7084

85+
It "suggests corrections for AliasesToExport with wildcard" {
86+
Import-Module .\PSScriptAnalyzerTestHelper.psm1
87+
$violations = Run-PSScriptAnalyzerRule $testManifestBadAliasesWildcardPath
88+
$violationFilepath = Join-path $testManifestPath $testManifestBadAliasesWildcardPath
89+
Test-CorrectionExtent $violationFilepath $violations[0] 1 "'*'" "@('gfoo', 'gbar')"
90+
}
91+
7192
It "detects VariablesToExport with wildcard" {
7293
$results = Run-PSScriptAnalyzerRule $testManifestBadVariablesWildcardPath
7394
$results.Count | Should be 1

0 commit comments

Comments
 (0)