-
Notifications
You must be signed in to change notification settings - Fork 234
Fix formatter crash when script contains parse errors #728
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,8 +30,7 @@ public class AnalysisService : IDisposable | |
/// Defines the list of Script Analyzer rules to include by default if | ||
/// no settings file is specified. | ||
/// </summary> | ||
private static readonly string[] s_includedRules = new string[] | ||
{ | ||
private static readonly string[] s_includedRules = { | ||
"PSUseToExportFieldsInManifest", | ||
"PSMisleadingBacktick", | ||
"PSAvoidUsingCmdletAliases", | ||
|
@@ -58,6 +57,8 @@ public class AnalysisService : IDisposable | |
/// </summary> | ||
private static readonly ScriptFileMarker[] s_emptyScriptMarkerResult = new ScriptFileMarker[0]; | ||
|
||
private static readonly string[] s_emptyGetRuleResult = new string[0]; | ||
|
||
/// <summary> | ||
/// The indentation to add when the logger lists errors. | ||
/// </summary> | ||
|
@@ -299,9 +300,15 @@ public async Task<ScriptFileMarker[]> GetSemanticMarkersAsync( | |
/// </summary> | ||
public IEnumerable<string> GetPSScriptAnalyzerRules() | ||
{ | ||
PowerShellResult getRuleResult = InvokePowerShell("Get-ScriptAnalyzerRule"); | ||
if (getRuleResult == null) | ||
{ | ||
_logger.Write(LogLevel.Warning, "Get-ScriptAnalyzerRule returned null result"); | ||
return s_emptyGetRuleResult; | ||
} | ||
|
||
var ruleNames = new List<string>(); | ||
IEnumerable<PSObject> ruleObjects = InvokePowerShell("Get-ScriptAnalyzerRule", new Dictionary<string, object>()).Output; | ||
foreach (var rule in ruleObjects) | ||
foreach (var rule in getRuleResult.Output) | ||
{ | ||
ruleNames.Add((string)rule.Members["RuleName"].Value); | ||
} | ||
|
@@ -341,7 +348,7 @@ public async Task<string> Format( | |
|
||
if (result == null) | ||
{ | ||
_logger.Write(LogLevel.Error, $"Formatter returned null result"); | ||
_logger.Write(LogLevel.Error, "Formatter returned null result"); | ||
return null; | ||
} | ||
|
||
|
@@ -425,11 +432,16 @@ private void LogAvailablePssaFeatures() | |
// If we already know the module that was imported, save some work | ||
if (_pssaModuleInfo == null) | ||
{ | ||
PSObject[] modules = InvokePowerShell( | ||
PowerShellResult getModuleResult = InvokePowerShell( | ||
"Get-Module", | ||
new Dictionary<string, object>{ {"Name", PSSA_MODULE_NAME} })?.Output; | ||
new Dictionary<string, object>{ {"Name", PSSA_MODULE_NAME} }); | ||
|
||
_pssaModuleInfo = modules | ||
if (getModuleResult == null) | ||
{ | ||
throw new AnalysisServiceLoadException("Get-Module call to find PSScriptAnalyzer module failed"); | ||
} | ||
|
||
_pssaModuleInfo = getModuleResult.Output | ||
.Select(m => m.BaseObject) | ||
.OfType<PSModuleInfo>() | ||
.FirstOrDefault(); | ||
|
@@ -515,22 +527,25 @@ private async Task<PSObject[]> GetDiagnosticRecordsAsync<TSettings>( | |
return diagnosticRecords; | ||
} | ||
|
||
private PowerShellResult InvokePowerShell(string command, IDictionary<string, object> paramArgMap) | ||
private PowerShellResult InvokePowerShell(string command, IDictionary<string, object> paramArgMap = null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the default param could be an empty dictionary and then you don't need he null check on line 536 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I made this change because one of the calls to this method had to create a new empty dictionary just to call the method. A null check is much cheaper than creating a new dictionary. I think the question boils down to "do we make the caller or the callee handle the complexity?". Since a caller could always pass in |
||
{ | ||
using (var powerShell = System.Management.Automation.PowerShell.Create()) | ||
{ | ||
powerShell.RunspacePool = _analysisRunspacePool; | ||
powerShell.AddCommand(command); | ||
foreach (var kvp in paramArgMap) | ||
if (paramArgMap != null) | ||
{ | ||
powerShell.AddParameter(kvp.Key, kvp.Value); | ||
foreach (KeyValuePair<string, object> kvp in paramArgMap) | ||
{ | ||
powerShell.AddParameter(kvp.Key, kvp.Value); | ||
} | ||
} | ||
|
||
PowerShellResult result = null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need to be set to null? Just want to make sure we don't cause a null pointer exception somewhere else :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it has to be set to something here, and creating a new PowerShell result would require creating two new arrays as well. Doesn't seem right. I think I've dealt with the possibility that any result here could return But, perhaps it's better to do something here like throw an analysis exception, or something else we catch?? |
||
try | ||
{ | ||
PSObject[] output = powerShell.Invoke()?.ToArray(); | ||
ErrorRecord[] errors = powerShell.Streams?.Error?.ToArray(); | ||
PSObject[] output = powerShell.Invoke().ToArray(); | ||
ErrorRecord[] errors = powerShell.Streams.Error.ToArray(); | ||
result = new PowerShellResult(output, errors, powerShell.HadErrors); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I notice that both output and errors could potentially be set to null here... Is that going to be ok? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like we think they can't actually be |
||
} | ||
catch (CommandNotFoundException ex) | ||
|
@@ -552,7 +567,7 @@ private PowerShellResult InvokePowerShell(string command, IDictionary<string, ob | |
} | ||
} | ||
|
||
private async Task<PowerShellResult> InvokePowerShellAsync(string command, IDictionary<string, object> paramArgMap) | ||
private async Task<PowerShellResult> InvokePowerShellAsync(string command, IDictionary<string, object> paramArgMap = null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. consider an empty dictionary instead of null |
||
{ | ||
var task = Task.Run(() => | ||
{ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
echo:
Array.Empty<ScriptFileMarker>()