Skip to content

Commit 2b6dab0

Browse files
author
Kapil Borle
committed
Merge branch 'AddSuggestedCorrections' into development
2 parents a467aff + b061db2 commit 2b6dab0

27 files changed

+829
-104
lines changed

Engine/Generic/CorrectionExtent.cs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Management.Automation.Language;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic
10+
{
11+
public class CorrectionExtent
12+
{
13+
public int EndColumnNumber
14+
{
15+
get
16+
{
17+
return endColumnNumber;
18+
}
19+
}
20+
21+
public int EndLineNumber
22+
{
23+
get
24+
{
25+
return endLineNumber;
26+
}
27+
}
28+
29+
public string File
30+
{
31+
get
32+
{
33+
return file;
34+
}
35+
}
36+
37+
public int StartColumnNumber
38+
{
39+
get
40+
{
41+
return startColumnNumber;
42+
}
43+
}
44+
45+
public int StartLineNumber
46+
{
47+
get
48+
{
49+
return startLineNumber;
50+
}
51+
}
52+
53+
public string Text
54+
{
55+
get
56+
{
57+
return text;
58+
}
59+
}
60+
61+
public string Description
62+
{
63+
get
64+
{
65+
return description;
66+
}
67+
}
68+
69+
private string file;
70+
private int startLineNumber;
71+
private int endLineNumber;
72+
private int startColumnNumber;
73+
private int endColumnNumber;
74+
private string text;
75+
private string description;
76+
77+
public CorrectionExtent(
78+
int startLineNumber,
79+
int endLineNumber,
80+
int startColumnNumber,
81+
int endColumnNumber,
82+
string text,
83+
string file)
84+
: this(
85+
startLineNumber,
86+
endLineNumber,
87+
startColumnNumber,
88+
endColumnNumber,
89+
text,
90+
file,
91+
null)
92+
{
93+
}
94+
95+
public CorrectionExtent(
96+
int startLineNumber,
97+
int endLineNumber,
98+
int startColumnNumber,
99+
int endColumnNumber,
100+
string text,
101+
string file,
102+
string description)
103+
{
104+
this.startLineNumber = startLineNumber;
105+
this.endLineNumber = endLineNumber;
106+
this.startColumnNumber = startColumnNumber;
107+
this.endColumnNumber = endColumnNumber;
108+
this.file = file;
109+
this.text = text;
110+
this.description = description;
111+
ThrowIfInvalidArguments();
112+
}
113+
114+
115+
116+
private void ThrowIfInvalidArguments()
117+
{
118+
ThrowIfNull<string>(file, "filename");
119+
ThrowIfNull<string>(text, "text");
120+
ThrowIfDecreasing(startLineNumber, endLineNumber, "start line number cannot be less than end line number");
121+
if (startLineNumber == endLineNumber)
122+
{
123+
ThrowIfDecreasing(StartColumnNumber, endColumnNumber, "start column number cannot be less than end column number for a one line extent");
124+
}
125+
}
126+
127+
private void ThrowIfDecreasing(int start, int end, string message)
128+
{
129+
if (start > end)
130+
{
131+
throw new ArgumentException(message);
132+
}
133+
}
134+
135+
private void ThrowIfNull<T>(T arg, string argName)
136+
{
137+
if (arg == null)
138+
{
139+
throw new ArgumentNullException(argName);
140+
}
141+
}
142+
143+
}
144+
}

Engine/Generic/DiagnosticRecord.cs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
// THE SOFTWARE.
1111
//
1212

13+
14+
1315
using System;
16+
using System.Collections.Generic;
1417
using System.Management.Automation.Language;
1518

1619
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic
@@ -27,6 +30,7 @@ public class DiagnosticRecord
2730
private DiagnosticSeverity severity;
2831
private string scriptPath;
2932
private string ruleSuppressionId;
33+
private List<CorrectionExtent> suggestedCorrections;
3034

3135
/// <summary>
3236
/// Represents a string from the rule about why this diagnostic was created.
@@ -90,43 +94,43 @@ public string RuleSuppressionID
9094
set { ruleSuppressionId = value; }
9195
}
9296

97+
/// <summary>
98+
/// Returns suggested correction
99+
/// return value can be null
100+
/// </summary>
101+
public IEnumerable<CorrectionExtent> SuggestedCorrections
102+
{
103+
get { return suggestedCorrections; }
104+
}
105+
93106
/// <summary>
94107
/// DiagnosticRecord: The constructor for DiagnosticRecord class.
95108
/// </summary>
96109
public DiagnosticRecord()
97110
{
98111

99112
}
100-
113+
101114
/// <summary>
102-
/// DiagnosticRecord: The constructor for DiagnosticRecord class.
115+
/// DiagnosticRecord: The constructor for DiagnosticRecord class that takes in suggestedCorrection
103116
/// </summary>
104117
/// <param name="message">A string about why this diagnostic was created</param>
105118
/// <param name="extent">The place in the script this diagnostic refers to</param>
106119
/// <param name="ruleName">The name of the rule that created this diagnostic</param>
107120
/// <param name="severity">The severity of this diagnostic</param>
108-
/// <param name="scriptName">The path of the script file being analyzed</param>
109-
public DiagnosticRecord(string message, IScriptExtent extent, string ruleName, DiagnosticSeverity severity, string scriptPath, string ruleId = null)
121+
/// <param name="scriptPath">The full path of the script file being analyzed</param>
122+
/// <param name="suggestedCorrections">The correction suggested by the rule to replace the extent text</param>
123+
public DiagnosticRecord(string message, IScriptExtent extent, string ruleName, DiagnosticSeverity severity, string scriptPath, string ruleId = null, List<CorrectionExtent> suggestedCorrections = null)
110124
{
111125
Message = message;
112126
RuleName = ruleName;
113127
Extent = extent;
114128
Severity = severity;
115129
ScriptPath = scriptPath;
116-
ruleSuppressionId = ruleId;
130+
RuleSuppressionID = ruleId;
131+
this.suggestedCorrections = suggestedCorrections;
117132
}
118133

119-
/// <summary>
120-
/// Copy Constructor
121-
/// </summary>
122-
/// <param name="record"></param>
123-
public DiagnosticRecord(DiagnosticRecord diagnosticRecord)
124-
{
125-
if (diagnosticRecord == null)
126-
{
127-
throw new ArgumentNullException("diagnosticRecord");
128-
}
129-
}
130134
}
131135

132136

Engine/Helper.cs

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using System.Management.Automation.Language;
2020
using System.Globalization;
2121
using Microsoft.Windows.PowerShell.ScriptAnalyzer.Generic;
22+
using System.Management.Automation.Runspaces;
2223

2324
namespace Microsoft.Windows.PowerShell.ScriptAnalyzer
2425
{
@@ -104,14 +105,14 @@ internal set
104105
private string[] functionScopes = new string[] { "global:", "local:", "script:", "private:"};
105106

106107
private string[] variableScopes = new string[] { "global:", "local:", "script:", "private:", "variable:", ":"};
107-
108108
#endregion
109109

110110
/// <summary>
111111
/// Initializes the Helper class.
112112
/// </summary>
113113
private Helper()
114114
{
115+
115116
}
116117

117118
/// <summary>
@@ -229,6 +230,61 @@ public bool IsDscResourceModule(string filePath)
229230

230231
return false;
231232
}
233+
234+
/// <summary>
235+
/// Gets the module manifest
236+
/// </summary>
237+
/// <param name="filePath"></param>
238+
/// <param name="errorRecord"></param>
239+
/// <returns>Returns a object of type PSModuleInfo</returns>
240+
public PSModuleInfo GetModuleManifest(string filePath, out IEnumerable<ErrorRecord> errorRecord)
241+
{
242+
errorRecord = null;
243+
PSModuleInfo psModuleInfo = null;
244+
Collection<PSObject> psObj = null;
245+
var ps = System.Management.Automation.PowerShell.Create();
246+
try
247+
{
248+
ps.AddCommand("Test-ModuleManifest");
249+
ps.AddParameter("Path", filePath);
250+
ps.AddParameter("WarningAction", ActionPreference.SilentlyContinue);
251+
psObj = ps.Invoke();
252+
}
253+
catch (CmdletInvocationException e)
254+
{
255+
// Invoking Test-ModuleManifest on a module manifest that doesn't have all the valid keys
256+
// throws a NullReferenceException. This is probably a bug in Test-ModuleManifest and hence
257+
// we consume it to allow execution of the of this method.
258+
if (e.InnerException == null || e.InnerException.GetType() != typeof(System.NullReferenceException))
259+
{
260+
throw;
261+
}
262+
}
263+
if (ps.HadErrors && ps.Streams != null && ps.Streams.Error != null)
264+
{
265+
var errorRecordArr = new ErrorRecord[ps.Streams.Error.Count];
266+
ps.Streams.Error.CopyTo(errorRecordArr, 0);
267+
errorRecord = errorRecordArr;
268+
}
269+
if (psObj != null && psObj.Any() && psObj[0] != null)
270+
{
271+
psModuleInfo = psObj[0].ImmediateBaseObject as PSModuleInfo;
272+
}
273+
ps.Dispose();
274+
return psModuleInfo;
275+
}
276+
277+
/// <summary>
278+
/// Checks if the error record is MissingMemberException
279+
/// </summary>
280+
/// <param name="errorRecord"></param>
281+
/// <returns>Returns a boolean value indicating the presence of MissingMemberException</returns>
282+
public static bool IsMissingManifestMemberException(ErrorRecord errorRecord)
283+
{
284+
return errorRecord.CategoryInfo != null
285+
&& errorRecord.CategoryInfo.Category == ErrorCategory.ResourceUnavailable
286+
&& string.Equals("MissingMemberException", errorRecord.CategoryInfo.Reason, StringComparison.OrdinalIgnoreCase);
287+
}
232288

233289
/// <summary>
234290
/// Get the list of exported function by analyzing the ast
@@ -1328,7 +1384,7 @@ public static string[] ProcessCustomRulePaths(string[] rulePaths, SessionState s
13281384
return outPaths.ToArray();
13291385

13301386
}
1331-
1387+
13321388
/// <summary>
13331389
/// Check if the function name starts with one of potentailly state changing verbs
13341390
/// </summary>
@@ -1458,7 +1514,7 @@ public bool GetNamedArgumentAttributeValue(NamedAttributeArgumentAst namedAttrib
14581514
return false;
14591515
}
14601516

1461-
#endregion
1517+
#endregion Methods
14621518
}
14631519

14641520

Engine/ScriptAnalyzerEngine.csproj

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,23 +34,23 @@
3434
<RootNamespace>Microsoft.Windows.PowerShell.ScriptAnalyzer</RootNamespace>
3535
</PropertyGroup>
3636
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PSV3 Debug|AnyCPU'">
37-
<DebugSymbols>true</DebugSymbols>
38-
<OutputPath>bin\PSV3 Debug\</OutputPath>
39-
<DefineConstants>TRACE;DEBUG;PSV3</DefineConstants>
40-
<DebugType>full</DebugType>
41-
<PlatformTarget>AnyCPU</PlatformTarget>
42-
<ErrorReport>prompt</ErrorReport>
43-
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
44-
</PropertyGroup>
45-
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PSV3 Release|AnyCPU'">
46-
<OutputPath>bin\PSV3 Release\</OutputPath>
47-
<DefineConstants>TRACE;PSV3</DefineConstants>
48-
<Optimize>true</Optimize>
49-
<DebugType>pdbonly</DebugType>
50-
<PlatformTarget>AnyCPU</PlatformTarget>
51-
<ErrorReport>prompt</ErrorReport>
52-
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
53-
</PropertyGroup>
37+
<DebugSymbols>true</DebugSymbols>
38+
<OutputPath>bin\PSV3 Debug\</OutputPath>
39+
<DefineConstants>TRACE;DEBUG;PSV3</DefineConstants>
40+
<DebugType>full</DebugType>
41+
<PlatformTarget>AnyCPU</PlatformTarget>
42+
<ErrorReport>prompt</ErrorReport>
43+
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
44+
</PropertyGroup>
45+
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PSV3 Release|AnyCPU'">
46+
<OutputPath>bin\PSV3 Release\</OutputPath>
47+
<DefineConstants>TRACE;PSV3</DefineConstants>
48+
<Optimize>true</Optimize>
49+
<DebugType>pdbonly</DebugType>
50+
<PlatformTarget>AnyCPU</PlatformTarget>
51+
<ErrorReport>prompt</ErrorReport>
52+
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
53+
</PropertyGroup>
5454
<ItemGroup>
5555
<Reference Include="Microsoft.CSharp" />
5656
<Reference Include="System" />
@@ -70,6 +70,7 @@
7070
<Compile Include="Commands\InvokeScriptAnalyzerCommand.cs" />
7171
<Compile Include="Generic\AvoidCmdletGeneric.cs" />
7272
<Compile Include="Generic\AvoidParameterGeneric.cs" />
73+
<Compile Include="Generic\CorrectionExtent.cs" />
7374
<Compile Include="Generic\SuppressedRecord.cs" />
7475
<Compile Include="Generic\DiagnosticRecord.cs" />
7576
<Compile Include="Generic\ExternalRule.cs" />
@@ -101,7 +102,7 @@
101102
<Compile Include="VariableAnalysisBase.cs" />
102103
</ItemGroup>
103104
<ItemGroup>
104-
<None Include="PSScriptAnalyzer.psm1" />
105+
<None Include="PSScriptAnalyzer.psm1" />
105106
<None Include="PSScriptAnalyzer.psd1" />
106107
<None Include="ScriptAnalyzer.format.ps1xml" />
107108
<None Include="ScriptAnalyzer.types.ps1xml" />

0 commit comments

Comments
 (0)