-
Notifications
You must be signed in to change notification settings - Fork 226
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 codelens crash when a file does not exist #732
Changes from 3 commits
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 |
---|---|---|
|
@@ -8,9 +8,11 @@ | |
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Specialized; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Management.Automation; | ||
using System.Management.Automation.Language; | ||
using System.Runtime.InteropServices; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
|
||
|
@@ -294,72 +296,83 @@ public async Task<FindReferencesResult> FindReferencesOfSymbol( | |
ScriptFile[] referencedFiles, | ||
Workspace workspace) | ||
{ | ||
if (foundSymbol != null) | ||
if (foundSymbol == null) | ||
{ | ||
int symbolOffset = referencedFiles[0].GetOffsetAtPosition( | ||
foundSymbol.ScriptRegion.StartLineNumber, | ||
foundSymbol.ScriptRegion.StartColumnNumber); | ||
return null; | ||
} | ||
|
||
// Make sure aliases have been loaded | ||
await GetAliases(); | ||
int symbolOffset = referencedFiles[0].GetOffsetAtPosition( | ||
foundSymbol.ScriptRegion.StartLineNumber, | ||
foundSymbol.ScriptRegion.StartColumnNumber); | ||
|
||
// We want to look for references first in referenced files, hence we use ordered dictionary | ||
var fileMap = new OrderedDictionary(StringComparer.OrdinalIgnoreCase); | ||
foreach (ScriptFile file in referencedFiles) | ||
{ | ||
fileMap.Add(file.FilePath, file); | ||
} | ||
// Make sure aliases have been loaded | ||
await GetAliases(); | ||
|
||
var allFiles = workspace.EnumeratePSFiles(); | ||
foreach (var file in allFiles) | ||
// We want to look for references first in referenced files, hence we use ordered dictionary | ||
// TODO: File system case-sensitivity is based on filesystem not OS, but OS is a much cheaper heuristic | ||
var fileMap = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) | ||
? new OrderedDictionary() | ||
: new OrderedDictionary(StringComparer.OrdinalIgnoreCase); | ||
foreach (ScriptFile file in referencedFiles) | ||
{ | ||
fileMap.Add(file.FilePath, file); | ||
} | ||
|
||
foreach (string file in workspace.EnumeratePSFiles()) | ||
{ | ||
if (!fileMap.Contains(file)) | ||
{ | ||
if (!fileMap.Contains(file)) | ||
ScriptFile scriptFile; | ||
try | ||
{ | ||
fileMap.Add(file, new ScriptFile(file, null, this.powerShellContext.LocalPowerShellVersion.Version)); | ||
scriptFile = workspace.GetFile(file); | ||
} | ||
catch (IOException) | ||
{ | ||
// If the file has ceased to exist for some reason, we just skip it | ||
continue; | ||
} | ||
|
||
fileMap.Add(file, scriptFile); | ||
} | ||
} | ||
|
||
List<SymbolReference> symbolReferences = new List<SymbolReference>(); | ||
foreach (var fileName in fileMap.Keys) | ||
{ | ||
var file = (ScriptFile)fileMap[fileName]; | ||
IEnumerable<SymbolReference> symbolReferencesinFile = | ||
AstOperations | ||
.FindReferencesOfSymbol( | ||
file.ScriptAst, | ||
foundSymbol, | ||
CmdletToAliasDictionary, | ||
AliasToCmdletDictionary) | ||
.Select( | ||
reference => | ||
var symbolReferences = new List<SymbolReference>(); | ||
foreach (object fileName in fileMap.Keys) | ||
{ | ||
var file = (ScriptFile)fileMap[fileName]; | ||
IEnumerable<SymbolReference> symbolReferencesinFile = | ||
AstOperations | ||
.FindReferencesOfSymbol( | ||
file.ScriptAst, | ||
foundSymbol, | ||
CmdletToAliasDictionary, | ||
AliasToCmdletDictionary) | ||
.Select(reference => | ||
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. did you want to try to clean up the LINQ usage here? We can call that out of scope if you want. 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.
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 remember when I hit this before I went and searched if we can avoid the closure allocation by turning this into a static (or instance...) method call, but I didn't find any good information on it... Would be nice if we could retain the readable composability of LINQ 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. Actually, I'll open a new PR on the LINQ refactoring here, since there are other methods that would benefit. I'll see if I can do a quick benchmark before we merge |
||
{ | ||
try | ||
{ | ||
try | ||
{ | ||
reference.SourceLine = | ||
file.GetLine(reference.ScriptRegion.StartLineNumber); | ||
} | ||
catch (ArgumentOutOfRangeException e) | ||
{ | ||
reference.SourceLine = string.Empty; | ||
this.logger.WriteException("Found reference is out of range in script file", e); | ||
} | ||
|
||
reference.FilePath = file.FilePath; | ||
return reference; | ||
}); | ||
|
||
symbolReferences.AddRange(symbolReferencesinFile); | ||
} | ||
reference.SourceLine = file.GetLine(reference.ScriptRegion.StartLineNumber); | ||
} | ||
catch (ArgumentOutOfRangeException e) | ||
{ | ||
reference.SourceLine = string.Empty; | ||
this.logger.WriteException("Found reference is out of range in script file", e); | ||
} | ||
|
||
return | ||
new FindReferencesResult | ||
{ | ||
SymbolFileOffset = symbolOffset, | ||
SymbolName = foundSymbol.SymbolName, | ||
FoundReferences = symbolReferences | ||
}; | ||
reference.FilePath = file.FilePath; | ||
return reference; | ||
}); | ||
|
||
symbolReferences.AddRange(symbolReferencesinFile); | ||
} | ||
else { return null; } | ||
|
||
return new FindReferencesResult | ||
{ | ||
SymbolFileOffset = symbolOffset, | ||
SymbolName = foundSymbol.SymbolName, | ||
FoundReferences = symbolReferences | ||
}; | ||
} | ||
|
||
/// <summary> | ||
|
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.
this is the change that will increase perf, yeah @SeeminglyScience?
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 that constructor for
ScriptFile
(which is now unused, but is public, so...) does a call toFile.ReadAllText()
-- so it always reads off the file system. I'm always averse to something like touching the filesystem in a constructor (and don't really understand why it's done in this constructor, since there's another one that takes a full string), but the real problem is that it's likely we already have the script file contents loaded, and if we don't, this will cache them too