-
Notifications
You must be signed in to change notification settings - Fork 223
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
Fix formatter crash when script contains parse errors #728
Conversation
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.
Looking good. Just a couple questions before I sign off
|
||
if (result == null) | ||
{ | ||
_logger.Write(LogLevel.Error, $"Formatter returned null result"); |
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.
Nit: do you need the $
? It doesn't seem to be a template string.
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.
Yeah started out writing a different message
/// Wraps the result of an execution of PowerShell to send back through | ||
/// asynchronous calls. | ||
/// </summary> | ||
private class PowerShellResult |
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.
I feel like this class could be put in some utility class rather than in this file - perhaps if InvokePowerShellAsync
is used elsewhere.
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.
I agree, but InvokePowerShellAsync
is a method on ScriptAnalysis
. I didn't want to engage in heavy refactoring
@@ -479,10 +526,12 @@ private PSObject[] InvokePowerShell(string command, IDictionary<string, object> | |||
powerShell.AddParameter(kvp.Key, kvp.Value); | |||
} | |||
|
|||
var result = new PSObject[0]; | |||
PowerShellResult result = null; |
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.
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 comment
The 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 null
at the call site.
But, perhaps it's better to do something here like throw an analysis exception, or something else we catch??
result = powerShell.Invoke()?.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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like we think they can't actually be null
. Would be so much nicer if the type system were able to track that...
@@ -342,7 +387,7 @@ public IEnumerable<string> GetPSScriptAnalyzerRules() | |||
else | |||
{ | |||
// Return an empty marker list | |||
return new ScriptFileMarker[0]; | |||
return s_emptyScriptMarkerResult; |
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.
👍
@@ -360,7 +405,7 @@ public IEnumerable<string> GetPSScriptAnalyzerRules() | |||
else | |||
{ | |||
// Return an empty marker list | |||
return new ScriptFileMarker[0]; | |||
return s_emptyScriptMarkerResult; |
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.
👍
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.
LGTM
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.
LGTM, just a couple things to look at.
/// 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[] |
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.
Don't know how everyone feels about this, but you can ditch the new string[]
and just keep the braces for a field initialization. e.g
private static readonly string[] s_includedRules =
{
"MyRules",
};
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.
Oooh!
/// <summary> | ||
/// An empty diagnostic result to return when a script fails analysis. | ||
/// </summary> | ||
private static readonly PSObject[] s_emptyDiagnosticResult = new PSObject[0]; |
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.
Should we use Array.Empty<PSObject>()
here? That way the empty array will be cached (or pulled from cache)
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.
I would prefer that -- so long as .NET 4.5.1 supports that
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.
Looks like it doesn't.
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.
Aww :/
Probably not worth setting up then since we'll get it when we move to standard.
/// <summary> | ||
/// An empty script marker result to return when no script markers can be returned. | ||
/// </summary> | ||
private static readonly ScriptFileMarker[] s_emptyScriptMarkerResult = new ScriptFileMarker[0]; |
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>()
try | ||
{ | ||
result = powerShell.Invoke()?.ToArray(); | ||
PSObject[] output = powerShell.Invoke()?.ToArray(); | ||
ErrorRecord[] errors = powerShell.Streams?.Error?.ToArray(); |
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.
Might be able to drop the null conditionals, as far as I know Streams
and Error
are always populated.
Come to think of it, I don't think powerShell.Invoke()
can return null
either, just an empty collection.
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.
That's what I was hoping for
@@ -515,22 +527,25 @@ private void LogAvailablePssaFeatures() | |||
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 comment
The 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 comment
The 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 null
for the dictionary (and we didn't handle it), I think defaulting to null
and making the callee handle it is probably more efficient, safer and easier to use.
@@ -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 comment
The reason will be displayed to describe this comment to others. Learn more.
consider an empty dictionary instead of null
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.
LGTM just a small nit
Addresses PowerShell/PSScriptAnalyzer#1057 and PowerShell/PSScriptAnalyzer#826 (comment).
When the formatter was run, it we would try to format even a document with parse errors. Now if there are parse errors, the formatter will do nothing (silently). Ideally we should make VSCode report that formatting failed and why, although this would mean returning a different format result.