Skip to content

Commit f0bd654

Browse files
author
Kapil Borle
committed
Merge pull request #515 from PowerShell/AddSuggestedCorrectionMissingMemberManifest
Add suggested correction to PSMissingModuleManifestField rule
2 parents 2b6dab0 + 5da7ca2 commit f0bd654

4 files changed

+115
-5
lines changed

Rules/MissingModuleManifestField.cs

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic;
1818
using System.ComponentModel.Composition;
1919
using System.Globalization;
20+
using System.Text;
2021

2122
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules
2223
{
@@ -46,16 +47,89 @@ public IEnumerable<DiagnosticRecord> AnalyzeScript(Ast ast, string fileName)
4647
{
4748
if (Helper.IsMissingManifestMemberException(errorRecord))
4849
{
49-
System.Diagnostics.Debug.Assert(errorRecord.Exception != null && !String.IsNullOrWhiteSpace(errorRecord.Exception.Message), Strings.NullErrorMessage);
50-
yield return
51-
new DiagnosticRecord(errorRecord.Exception.Message, ast.Extent, GetName(), DiagnosticSeverity.Warning, fileName);
50+
System.Diagnostics.Debug.Assert(
51+
errorRecord.Exception != null && !String.IsNullOrWhiteSpace(errorRecord.Exception.Message),
52+
Strings.NullErrorMessage);
53+
var hashTableAst = ast.Find(x => x is HashtableAst, false);
54+
yield return new DiagnosticRecord(
55+
errorRecord.Exception.Message,
56+
hashTableAst.Extent,
57+
GetName(),
58+
DiagnosticSeverity.Warning,
59+
fileName,
60+
suggestedCorrections:GetCorrectionExtent(hashTableAst as HashtableAst));
5261
}
5362

5463
}
5564
}
5665
}
5766

5867
}
68+
69+
/// <summary>
70+
/// Gets the correction extent
71+
/// </summary>
72+
/// <param name="ast"></param>
73+
/// <returns>A List of CorrectionExtent</returns>
74+
private List<CorrectionExtent> GetCorrectionExtent(HashtableAst ast)
75+
{
76+
int startLineNumber;
77+
int startColumnNumber;
78+
79+
// for empty hashtable insert after after "@{"
80+
if (ast.KeyValuePairs.Count == 0)
81+
{
82+
// check if ast starts with "@{"
83+
if (ast.Extent.Text.IndexOf("@{") != 0)
84+
{
85+
return null;
86+
}
87+
startLineNumber = ast.Extent.StartLineNumber;
88+
startColumnNumber = ast.Extent.StartColumnNumber + 2; // 2 for "@{",
89+
}
90+
else // for non-empty hashtable insert after the last element
91+
{
92+
int maxLine = 0;
93+
int lastCol = 0;
94+
foreach (var keyVal in ast.KeyValuePairs)
95+
{
96+
if (keyVal.Item2.Extent.EndLineNumber > maxLine)
97+
{
98+
maxLine = keyVal.Item2.Extent.EndLineNumber;
99+
lastCol = keyVal.Item2.Extent.EndColumnNumber;
100+
}
101+
}
102+
startLineNumber = maxLine;
103+
startColumnNumber = lastCol;
104+
}
105+
106+
var correctionExtents = new List<CorrectionExtent>();
107+
string fieldName = "ModuleVersion";
108+
string fieldValue = "1.0.0.0";
109+
string description = string.Format(
110+
CultureInfo.CurrentCulture,
111+
Strings.MissingModuleManifestFieldCorrectionDescription,
112+
fieldName,
113+
fieldValue);
114+
var correctionTextTemplate = @"
115+
# Version number of this module.
116+
{0} = '{1}'
117+
";
118+
var correctionText = string.Format(
119+
correctionTextTemplate,
120+
fieldName,
121+
fieldValue);
122+
var correctionExtent = new CorrectionExtent(
123+
startLineNumber,
124+
startLineNumber,
125+
startColumnNumber,
126+
startColumnNumber,
127+
correctionText,
128+
ast.Extent.File,
129+
description);
130+
correctionExtents.Add(correctionExtent);
131+
return correctionExtents;
132+
}
59133

60134
/// <summary>
61135
/// GetName: Retrieves the name of this rule.

Rules/Strings.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Rules/Strings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,9 @@
816816
<data name="AvoidUsingPlainTextForPasswordCorrectionDescription" xml:space="preserve">
817817
<value>Set {0} type to SecureString</value>
818818
</data>
819+
<data name="MissingModuleManifestFieldCorrectionDescription" xml:space="preserve">
820+
<value>Add {0} = {1} to the module manifest</value>
821+
</data>
819822
<data name="UseToExportFieldsInManifestCorrectionDescription" xml:space="preserve">
820823
<value>Replace {0} with {1}</value>
821824
</data>

Tests/Rules/AvoidUnloadableModuleOrMissingRequiredFieldInManifest.tests.ps1

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,19 @@
22
$missingMessage = "The member 'ModuleVersion' is not present in the module manifest."
33
$missingName = "PSMissingModuleManifestField"
44
$directory = Split-Path -Parent $MyInvocation.MyCommand.Path
5-
$violations = Invoke-ScriptAnalyzer $directory\TestBadModule\TestBadModule.psd1 | Where-Object {$_.RuleName -eq $missingName}
5+
$violationFilepath = Join-Path $directory "TestBadModule\TestBadModule.psd1"
6+
$violations = Invoke-ScriptAnalyzer $violationFilepath | Where-Object {$_.RuleName -eq $missingName}
67
$noViolations = Invoke-ScriptAnalyzer $directory\TestGoodModule\TestGoodModule.psd1 | Where-Object {$_.RuleName -eq $missingName}
78

89
Describe "MissingRequiredFieldModuleManifest" {
10+
BeforeAll {
11+
Import-Module (Join-Path $directory "PSScriptAnalyzerTestHelper.psm1")
12+
}
13+
14+
AfterAll{
15+
Remove-Module PSScriptAnalyzerTestHelper
16+
}
17+
918
Context "When there are violations" {
1019
It "has 1 missing required field module manifest violation" {
1120
$violations.Count | Should Be 1
@@ -14,7 +23,22 @@ Describe "MissingRequiredFieldModuleManifest" {
1423
It "has the correct description message" {
1524
$violations.Message | Should Match $missingMessage
1625
}
17-
}
26+
27+
$numExpectedCorrections = 1
28+
It "has $numExpectedCorrections suggested corrections" {
29+
$violations.SuggestedCorrections.Count | Should Be $numExpectedCorrections
30+
}
31+
32+
33+
It "has the right suggested correction" {
34+
$expectedText = @'
35+
# Version number of this module.
36+
ModuleVersion = '1.0.0.0'
37+
'@
38+
$violations[0].SuggestedCorrections[0].Text | Should Match $expectedText
39+
Get-ExtentText $violations[0].SuggestedCorrections[0] $violationFilepath | Should Match ""
40+
}
41+
}
1842

1943
Context "When there are no violations" {
2044
It "returns no violations" {

0 commit comments

Comments
 (0)