-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Improve cross-project add-using results #63449
Conversation
var service = _solution.Services.GetService<ISymbolTreeInfoCacheService>(); | ||
var info = await service.TryGetMetadataSymbolTreeInfoAsync(_solution, _metadataReference, CancellationToken).ConfigureAwait(false); | ||
var service = _solution.Services.GetService<SymbolTreeInfoCacheService>(); | ||
var info = await service.TryGetPotentiallyStaleMetadataSymbolTreeInfoAsync(_solution, _metadataReference, CancellationToken).ConfigureAwait(false); |
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.
here we now allow for a stale index to come back. if we get no index at all, that's fine and we gracefully handle it. If we get a stale index, that's also fine as FindAsync then simply uses the index data to try to find matching symbols in _assembly. It is completely ok with that 'Find' step not finding anything and still returning no results.
@@ -86,7 +86,7 @@ public static async ValueTask UpdateCacheAsync(Project project, CancellationToke | |||
await GetUpToDateCacheEntryAsync(relevantProject, cacheService, cancellationToken).ConfigureAwait(false); | |||
|
|||
foreach (var peReference in GetAllRelevantPeReferences(project)) | |||
await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(project.Solution, peReference, loadOnly: false, cancellationToken).ConfigureAwait(false); |
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 loadOnly flag entirely went away. There are now just two distinct types of callers. Those that want the completely up-to-date index (and are willing to compute it), and those that are are ok with a stale index and will either use what is already there, or will try to load whatever index we last stored to disk if not.
/// necessary. The secondary purpose is to generate a minimal set of symbols when there is a match, though that | ||
/// will still incur a heavy cost (for example, getting the <see cref="IAssemblySymbol"/> root symbol for a | ||
/// particular project). | ||
/// </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.
i figured it was finally time to have a comment for this :)
/// created. | ||
/// </summary> | ||
private readonly Task<SpellChecker> _spellCheckerTask; | ||
private readonly SpellChecker _spellChecker; |
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 used to be lazy when everything was in-proc and memory/cpu was very tight. Now that this is all OOP, it's just much simpler to have no lazyness in the internal data of this index. measuring things in the benchmarks showed no issue moving this to not be delayed. Also, since this is used by add-using, it's highly likely for this to be needed almost immediately as the add-using codepath runs for tons of different user error scenarios.
@@ -126,7 +116,6 @@ private static string GetMetadataNameWithoutBackticks(MetadataReader reader, Str | |||
SolutionServices services, | |||
SolutionKey solutionKey, | |||
PortableExecutableReference reference, | |||
Checksum checksum, |
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.
no need to pass checksums along. they are already cached in dictionaries that we have access to so it can just be retrieved when necessary.
@@ -50,15 +50,12 @@ public override SymbolReference CreateReference<T>(SymbolResult<T> searchResult) | |||
SymbolFilter filter, SearchQuery searchQuery) | |||
{ | |||
var service = _solution.Services.GetService<SymbolTreeInfoCacheService>(); | |||
var info = await service.TryGetMetadataSymbolTreeInfoAsync(_solution, _metadataReference, CancellationToken).ConfigureAwait(false); | |||
var info = await service.TryGetPotentiallyStaleMetadataSymbolTreeInfoAsync(_solution, _metadataReference, CancellationToken).ConfigureAwait(false); |
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.
renamed to make the staleness possibility obvious.
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.
Is PotentiallyStale
more clear than Cached
? Caches always can be stale :)
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 be ok with that :)
|
||
// We must have an index since we passed in loadOnly: false here. | ||
Contract.ThrowIfNull(info); |
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.
probably not necessary (we have NRT on these apis). but it doesn't hurt to still have this check.
return Task.FromResult(new SpellChecker( | ||
checksum, sortedNodes.Select(n => n.Name.AsMemory()))); | ||
} | ||
private static SpellChecker CreateSpellChecker(Checksum checksum, ImmutableArray<Node> sortedNodes) |
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.
is this really needed anymore?
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 actually called by a few places :) so i'dlike to leave in.
if (_metadataIdToInfo.TryGetValue(metadataId, out var metadataInfo) && | ||
metadataInfo.SymbolTreeInfo.Checksum == checksum) | ||
{ | ||
// See if the last value produced exactly matches what the caller is asking for. If so, return 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.
This comment is wrong now isn't it? It just checks if we have any cached value
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.
indeedly.
if (_projectIdToInfo.TryGetValue(project.Id, out var projectInfo) && | ||
projectInfo.Checksum == checksum) | ||
{ | ||
// See if the last value produced exactly matches what the caller is asking for. If so, return 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.
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.
can fix in followup PR :)
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.
love all the added comments. Overall this looks correct and easier to understand
Thanks. Will make a followup for the naming/comment issues. |
FOllowup to #63438
Add-using supports adding references to source symbols located in sibling projects, and metadata-references from sibling projects. Traditionally, this was done by building a name-index for all those projects and references, and then having add-using search those indices to look for results.
This worked decently, but had a catch. It would only produce results if the index was fully up to date. So if you made changes in a project, you might not see any results from that project in other projects until we got around to reanalyzing that project.
This PR updates add-using to now allow using those indices even if they're stale. This means that if you are tryign to add a using for an type that already existed in a project which is stale, that will still work instead of you getting nothing.
Note: this is also safe in the case that the index contains information that is no longer applicable. That's because we just use the index to help find the info for a particular name to then lookup the symbol with that name in the project/reference. We still then try to find the symbol given that info. If we don't find it, that's fine and it just means we won't have an add-using option. It's a little extra CPU but should be generally quite rare. And it's a lot better to pay that tiny price occasionally if it allows us to use stale indices instead of waiting on them to be rebuilt.