diff --git a/src/Workspaces/Core/Portable/FindSymbols/IRemoteSymbolFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/IRemoteSymbolFinder.cs index 8e45ed79e59fb..24169be4667a2 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/IRemoteSymbolFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/IRemoteSymbolFinder.cs @@ -3,7 +3,7 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Remote; @@ -17,19 +17,19 @@ Task FindReferencesAsync(PinnedSolutionInfo solutionInfo, SerializableSymbolAndP Task FindLiteralReferencesAsync(PinnedSolutionInfo solutionInfo, object value, TypeCode typeCode, CancellationToken cancellationToken); - Task> FindAllDeclarationsWithNormalQueryAsync( + Task> FindAllDeclarationsWithNormalQueryAsync( PinnedSolutionInfo solutionInfo, ProjectId projectId, string name, SearchKind searchKind, SymbolFilter criteria, CancellationToken cancellationToken); - Task> FindSolutionSourceDeclarationsWithNormalQueryAsync( + Task> FindSolutionSourceDeclarationsWithNormalQueryAsync( PinnedSolutionInfo solutionInfo, string name, bool ignoreCase, SymbolFilter criteria, CancellationToken cancellationToken); - Task> FindProjectSourceDeclarationsWithNormalQueryAsync( + Task> FindProjectSourceDeclarationsWithNormalQueryAsync( PinnedSolutionInfo solutionInfo, ProjectId projectId, string name, bool ignoreCase, SymbolFilter criteria, CancellationToken cancellationToken); - Task> FindSolutionSourceDeclarationsWithPatternAsync( + Task> FindSolutionSourceDeclarationsWithPatternAsync( PinnedSolutionInfo solutionInfo, string pattern, SymbolFilter criteria, CancellationToken cancellationToken); - Task> FindProjectSourceDeclarationsWithPatternAsync( + Task> FindProjectSourceDeclarationsWithPatternAsync( PinnedSolutionInfo solutionInfo, ProjectId projectId, string pattern, SymbolFilter criteria, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindReferencesServerCallback.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindReferencesServerCallback.cs index 6fe716fd5772d..b7ae067e29832 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindReferencesServerCallback.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder.FindReferencesServerCallback.cs @@ -15,15 +15,20 @@ public static partial class SymbolFinder /// Callback object we pass to the OOP server to hear about the result /// of the FindReferencesEngine as it executes there. /// - internal sealed class FindReferencesServerCallback + internal sealed class FindReferencesServerCallback : IEqualityComparer { private readonly Solution _solution; private readonly IStreamingFindReferencesProgress _progress; private readonly CancellationToken _cancellationToken; private readonly object _gate = new object(); - private readonly Dictionary _definitionMap = - new Dictionary(); + + /// + /// Note: for purposes of Equality/Hashing, all that we use is the underlying SymbolKey. That's because FAR + /// only cares if it is looking at the same symbol, it don't care if the symbol came from a different + /// project or not. + /// + private readonly Dictionary _definitionMap; public FindReferencesServerCallback( Solution solution, @@ -33,6 +38,7 @@ public FindReferencesServerCallback( _solution = solution; _progress = progress; _cancellationToken = cancellationToken; + _definitionMap = new Dictionary(this); } public Task AddItemsAsync(int count) => _progress.ProgressTracker.AddItemsAsync(count); @@ -85,6 +91,12 @@ public async Task OnReferenceFoundAsync( await _progress.OnReferenceFoundAsync(symbolAndProjectId, referenceLocation).ConfigureAwait(false); } + + bool IEqualityComparer.Equals(SerializableSymbolAndProjectId x, SerializableSymbolAndProjectId y) + => y.SymbolKeyData.Equals(x.SymbolKeyData); + + int IEqualityComparer.GetHashCode(SerializableSymbolAndProjectId obj) + => obj.SymbolKeyData.GetHashCode(); } } } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindReferences_Current.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindReferences_Current.cs index fbbae24ec72e0..74f071a6c16ea 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindReferences_Current.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolFinder_FindReferences_Current.cs @@ -46,7 +46,7 @@ internal static async Task FindReferencesAsync( solution, new object[] { - SerializableSymbolAndProjectId.Dehydrate(symbolAndProjectId), + SerializableSymbolAndProjectId.Dehydrate(solution, symbolAndProjectId.Symbol, cancellationToken), documents?.Select(d => d.Id).ToArray(), SerializableFindReferencesSearchOptions.Dehydrate(options), }, diff --git a/src/Workspaces/Core/Portable/Remote/RemoteArguments.cs b/src/Workspaces/Core/Portable/Remote/RemoteArguments.cs index cc9e5a7d1cb7a..9a1b616882dfe 100644 --- a/src/Workspaces/Core/Portable/Remote/RemoteArguments.cs +++ b/src/Workspaces/Core/Portable/Remote/RemoteArguments.cs @@ -37,35 +37,30 @@ public FindReferencesSearchOptions Rehydrate() } } - internal class SerializableSymbolAndProjectId : IEquatable + internal class SerializableSymbolAndProjectId { public string SymbolKeyData; public ProjectId ProjectId; - public override int GetHashCode() - => Hash.Combine(SymbolKeyData, ProjectId.GetHashCode()); - - public override bool Equals(object obj) - => Equals(obj as SerializableSymbolAndProjectId); - - public bool Equals(SerializableSymbolAndProjectId other) - => other != null && SymbolKeyData.Equals(other.SymbolKeyData) && ProjectId.Equals(other.ProjectId); - public static SerializableSymbolAndProjectId Dehydrate( - IAliasSymbol alias, Document document) + IAliasSymbol alias, Document document, CancellationToken cancellationToken) { return alias == null ? null - : Dehydrate(new SymbolAndProjectId(alias, document.Project.Id)); + : Dehydrate(document.Project.Solution, alias, cancellationToken); } public static SerializableSymbolAndProjectId Dehydrate( - SymbolAndProjectId symbolAndProjectId) + Solution solution, ISymbol symbol, CancellationToken cancellationToken) { + var symbolKey = symbol.GetSymbolKey(cancellationToken); + var projectId = solution.GetExactProjectId(symbol); + Contract.ThrowIfNull(projectId, WorkspacesResources.Symbols_project_could_not_be_found_in_the_provided_solution); + return new SerializableSymbolAndProjectId { - SymbolKeyData = symbolAndProjectId.Symbol.GetSymbolKey().ToString(), - ProjectId = symbolAndProjectId.ProjectId + SymbolKeyData = symbolKey.ToString(), + ProjectId = projectId, }; } @@ -163,12 +158,12 @@ internal class SerializableReferenceLocation public CandidateReason CandidateReason { get; set; } public static SerializableReferenceLocation Dehydrate( - ReferenceLocation referenceLocation) + ReferenceLocation referenceLocation, CancellationToken cancellationToken) { return new SerializableReferenceLocation { Document = referenceLocation.Document.Id, - Alias = SerializableSymbolAndProjectId.Dehydrate(referenceLocation.Alias, referenceLocation.Document), + Alias = SerializableSymbolAndProjectId.Dehydrate(referenceLocation.Alias, referenceLocation.Document, cancellationToken), Location = referenceLocation.Location.SourceSpan, IsImplicit = referenceLocation.IsImplicit, SymbolUsageInfo = SerializableSymbolUsageInfo.Dehydrate(referenceLocation.SymbolUsageInfo), diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs index ee75b5ba7beb5..219a199485557 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Solution.cs @@ -126,6 +126,29 @@ private static Project CreateProject(ProjectId projectId, Solution solution) return projectState == null ? null : GetProject(projectState.Id); } + /// + /// Given a returns the of the it came + /// from. Returns if does not come from . + /// + /// + /// This function differs from in terms of how it + /// treats s. Specifically, say there is the following: + /// + /// + /// Project-A, containing Symbol-A. + /// Project-B, with a reference to Project-A, and usage of Symbol-A. + /// + /// + /// It is possible (with retargeting, and other complex cases) that Symbol-A from Project-B will be a different + /// symbol than Symbol-A from Project-A. However, + /// will always try to return Project-A for either of the Symbol-A's, as it prefers to return the original + /// Source-Project of the original definition, not the project that actually produced the symbol. For many + /// features this is an acceptable abstraction. However, for some cases (Find-References in particular) it is + /// necessary to resolve symbols back to the actual project/compilation that produced them for correctness. + /// + internal ProjectId? GetExactProjectId(ISymbol symbol) + => _state.GetExactProjectId(symbol); + /// /// True if the solution contains the document in one of its projects /// diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.State.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.State.cs index 03466e2521705..d98698cc435e4 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.State.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.State.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Immutable; using System.Linq; +using System.Runtime.CompilerServices; using Microsoft.CodeAnalysis; using Roslyn.Utilities; @@ -30,7 +31,10 @@ private class State /// /// The base that starts with everything empty. /// - public static readonly State Empty = new State(compilation: null, declarationOnlyCompilation: null, generatorDriver: new TrackedGeneratorDriver(null)); + public static readonly State Empty = new State( + compilation: null, declarationOnlyCompilation: null, + generatorDriver: new TrackedGeneratorDriver(null), + assemblyAndModuleSet: null); /// /// A strong reference to the declaration-only compilation. This compilation isn't used to produce symbols, @@ -46,6 +50,21 @@ private class State public TrackedGeneratorDriver GeneratorDriver { get; } + /// + /// Weak table of the assembly and module symbols that this compilation tracker has created. This can + /// be used to determine which project an assembly symbol came from after the fact. This is needed as + /// the compilation an assembly came from can be GC'ed and further requests to get that compilation (or + /// any of it's assemblies) may produce new assembly symbols. + /// + /// + /// Ideally this would just be ConditionalWeakSet<ISymbol>. Effectively we just want to + /// hold onto the symbols as long as someone else is keeping them alive. And we don't actually need + /// them to map to anything. We just use their existence to know if our project was the project it came + /// from. However, ConditionalWeakTable is the best tool we have, so we simulate a set by just using a + /// table and mapping the keys to the value. + /// + public readonly ConditionalWeakTable? AssemblyAndModuleSet; + /// /// Specifies whether and all compilations it depends on contain full information or not. This can return /// if the state isn't at the point where it would know, and it's necessary to transition to to figure that out. @@ -57,7 +76,11 @@ private class State /// public virtual ValueSource>? FinalCompilation => null; - protected State(ValueSource>? compilation, Compilation? declarationOnlyCompilation, TrackedGeneratorDriver generatorDriver) + protected State( + ValueSource>? compilation, + Compilation? declarationOnlyCompilation, + TrackedGeneratorDriver generatorDriver, + ConditionalWeakTable? assemblyAndModuleSet) { // Declaration-only compilations should never have any references Contract.ThrowIfTrue(declarationOnlyCompilation != null && declarationOnlyCompilation.ExternalReferences.Any()); @@ -65,6 +88,7 @@ protected State(ValueSource>? compilation, Compilation? de Compilation = compilation; DeclarationOnlyCompilation = declarationOnlyCompilation; GeneratorDriver = generatorDriver; + AssemblyAndModuleSet = assemblyAndModuleSet; } public static State Create( @@ -90,6 +114,25 @@ public static ValueSource> CreateValueSource( ? new WeakValueSource(compilation) : (ValueSource>)new ConstantValueSource>(compilation); } + + public static ConditionalWeakTable GetAssemblyAndModuleSet(Compilation compilation) + { + var result = new ConditionalWeakTable(); + + var compAssembly = compilation.Assembly; + result.Add(compAssembly, null); + + foreach (var reference in compilation.References) + { + var symbol = compilation.GetAssemblyOrModuleSymbol(reference); + if (symbol == null) + continue; + + result.Add(symbol, null); + } + + return result; + } } /// @@ -106,7 +149,8 @@ public InProgressState( ImmutableArray<(ProjectState state, CompilationAndGeneratorDriverTranslationAction action)> intermediateProjects) : base(compilation: new ConstantValueSource>(inProgressCompilation), declarationOnlyCompilation: null, - generatorDriver: inProgressGeneratorDriver) + generatorDriver: inProgressGeneratorDriver, + GetAssemblyAndModuleSet(inProgressCompilation)) { Contract.ThrowIfTrue(intermediateProjects.IsDefault); Contract.ThrowIfFalse(intermediateProjects.Length > 0); @@ -121,7 +165,10 @@ public InProgressState( private sealed class LightDeclarationState : State { public LightDeclarationState(Compilation declarationOnlyCompilation) - : base(compilation: null, declarationOnlyCompilation: declarationOnlyCompilation, generatorDriver: new TrackedGeneratorDriver(null)) + : base(compilation: null, + declarationOnlyCompilation: declarationOnlyCompilation, + generatorDriver: new TrackedGeneratorDriver(null), + assemblyAndModuleSet: null) { } } @@ -133,7 +180,10 @@ public LightDeclarationState(Compilation declarationOnlyCompilation) private sealed class FullDeclarationState : State { public FullDeclarationState(Compilation declarationCompilation, TrackedGeneratorDriver generatorDriver) - : base(new WeakValueSource(declarationCompilation), declarationCompilation.Clone().RemoveAllReferences(), generatorDriver) + : base(new WeakValueSource(declarationCompilation), + declarationCompilation.Clone().RemoveAllReferences(), + generatorDriver, + GetAssemblyAndModuleSet(declarationCompilation)) { } } @@ -160,8 +210,12 @@ public FinalState( ValueSource> compilationWithoutGeneratedFilesSource, Compilation compilationWithoutGeneratedFiles, TrackedGeneratorDriver generatorDriver, - bool hasSuccessfullyLoaded) - : base(compilationWithoutGeneratedFilesSource, compilationWithoutGeneratedFiles.Clone().RemoveAllReferences(), generatorDriver) + bool hasSuccessfullyLoaded, + ConditionalWeakTable? compilationAssemblies) + : base(compilationWithoutGeneratedFilesSource, + compilationWithoutGeneratedFiles.Clone().RemoveAllReferences(), + generatorDriver, + compilationAssemblies) { HasSuccessfullyLoaded = hasSuccessfullyLoaded; FinalCompilation = finalCompilationSource; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs index 69d01dff8860e..a937aa92e4f3f 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.CompilationTracker.cs @@ -100,6 +100,14 @@ public bool HasCompilation } } + public bool ContainsAssemblyOrModule(ISymbol assemblyOrModule) + { + Debug.Assert(assemblyOrModule.Kind == SymbolKind.Assembly || assemblyOrModule.Kind == SymbolKind.NetModule); + var state = this.ReadState(); + var assemblyAndModuleSet = state.AssemblyAndModuleSet; + return assemblyAndModuleSet != null && assemblyAndModuleSet.TryGetValue(assemblyOrModule, out _); + } + /// /// Creates a new instance of the compilation info, retaining any already built /// compilation state as the now 'old' state @@ -188,7 +196,8 @@ public CompilationTracker FreezePartialStateWithTree(SolutionState solution, Doc new ConstantValueSource>(inProgressCompilation), inProgressCompilation, generatorDriver: new TrackedGeneratorDriver(null), - hasSuccessfullyLoaded: false)); + hasSuccessfullyLoaded: false, + State.GetAssemblyAndModuleSet(inProgressCompilation))); } /// @@ -710,7 +719,8 @@ private async Task FinalizeCompilationAsync( State.CreateValueSource(compilationWithoutGeneratedFiles, solution.Services), compilationWithoutGeneratedFiles, generatorDriver, - hasSuccessfullyLoaded), + hasSuccessfullyLoaded, + State.GetAssemblyAndModuleSet(compilation)), solution.Services); return new CompilationInfo(compilation, hasSuccessfullyLoaded); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.SymbolToProjectId.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.SymbolToProjectId.cs new file mode 100644 index 0000000000000..c0f9834ef284f --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.SymbolToProjectId.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis +{ + internal partial class SolutionState + { + /// + public ProjectId? GetExactProjectId(ISymbol? symbol) + { + LazyInitialization.EnsureInitialized(ref _assemblyOrModuleSymbolToProjectId, s_createTable); + + // Walk up the symbol so we can get to the containing namespace/assembly that will be used to map + // back to a project. + + while (symbol != null) + { + var result = GetProjectIdDirectly(symbol, _assemblyOrModuleSymbolToProjectId); + if (result != null) + return result; + + symbol = symbol.ContainingSymbol; + } + + return null; + } + + private ProjectId? GetProjectIdDirectly( + ISymbol symbol, ConditionalWeakTable assemblyOrModuleSymbolToProjectId) + { + if (symbol.IsKind(SymbolKind.Namespace, out INamespaceSymbol? ns)) + { + if (ns.ContainingCompilation != null) + { + // A namespace that spans a compilation. These don't belong to an assembly/module directly. + // However, as we're looking for the project this corresponds to, we can look for the + // source-module component (the first in the constituent namespaces) and then search using that. + return GetExactProjectId(ns.ConstituentNamespaces[0]); + } + } + else if (symbol.IsKind(SymbolKind.Assembly) || + symbol.IsKind(SymbolKind.NetModule)) + { + if (!assemblyOrModuleSymbolToProjectId.TryGetValue(symbol, out var projectId)) + { + foreach (var (id, tracker) in _projectIdToTrackerMap) + { + if (tracker.ContainsAssemblyOrModule(symbol)) + { + projectId = id; + break; + } + } + + // Have to lock as there's no atomic AddOrUpdate in netstandard2.0 and we could throw if two + // threads tried to add the same item. +#if NETSTANDARD + lock (assemblyOrModuleSymbolToProjectId) + { + assemblyOrModuleSymbolToProjectId.Remove(symbol); + assemblyOrModuleSymbolToProjectId.Add(symbol, projectId); + } +#else + assemblyOrModuleSymbolToProjectId.AddOrUpdate(symbol, projectId); +#endif + } + + return projectId; + } + + return null; + } + } +} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index a9434d23c40c6..46acdd99680f9 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -56,6 +56,14 @@ internal partial class SolutionState // holds on data calculated based on the AnalyzerReferences list private readonly Lazy _lazyAnalyzers; + /// + /// Cache we use to map between assembly and module symbols and the project they came from. That way if we + /// are asked about many symbols from the same assembly/module we can answer the question quickly after + /// computing for the first one. Created on demand. + /// + private ConditionalWeakTable? _assemblyOrModuleSymbolToProjectId; + private static readonly Func> s_createTable = () => new ConditionalWeakTable(); + private SolutionState( BranchId branchId, int workspaceVersion, diff --git a/src/Workspaces/Core/Portable/WorkspacesResources.resx b/src/Workspaces/Core/Portable/WorkspacesResources.resx index 1dde52b6f9d25..39e05a55ac404 100644 --- a/src/Workspaces/Core/Portable/WorkspacesResources.resx +++ b/src/Workspaces/Core/Portable/WorkspacesResources.resx @@ -553,4 +553,7 @@ Unknown + + Symbol's project could not be found in the provided solution + \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf index 927aed0ecac19..cd1c8908e9287 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf @@ -172,6 +172,11 @@ Přidávání projektů se nepodporuje. + + Symbol's project could not be found in the provided solution + Symbol's project could not be found in the provided solution + + The project already contains the specified reference. The project already contains the specified reference. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf index 8edd7c4742e15..64377f1c43998 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf @@ -172,6 +172,11 @@ Das Hinzufügen von Projekten wird nicht unterstützt. + + Symbol's project could not be found in the provided solution + Symbol's project could not be found in the provided solution + + The project already contains the specified reference. The project already contains the specified reference. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf index 10a857869d5d1..90b817c0b25bf 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf @@ -172,6 +172,11 @@ No se admite la adición de proyectos. + + Symbol's project could not be found in the provided solution + Symbol's project could not be found in the provided solution + + The project already contains the specified reference. The project already contains the specified reference. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf index 1e9a44690981e..9a7247c1d20c3 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf @@ -172,6 +172,11 @@ L'ajout de projets n'est pas pris en charge. + + Symbol's project could not be found in the provided solution + Symbol's project could not be found in the provided solution + + The project already contains the specified reference. The project already contains the specified reference. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf index e390333bf5a71..96ec6d11bc0df 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf @@ -172,6 +172,11 @@ L'aggiunta di progetti non è supportata. + + Symbol's project could not be found in the provided solution + Symbol's project could not be found in the provided solution + + The project already contains the specified reference. The project already contains the specified reference. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf index 3b3ff9d7ec695..20b168f0c7d18 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf @@ -172,6 +172,11 @@ プロジェクトの追加はサポートされていません。 + + Symbol's project could not be found in the provided solution + Symbol's project could not be found in the provided solution + + The project already contains the specified reference. The project already contains the specified reference. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf index f82e78f4a7042..791183aadef82 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf @@ -172,6 +172,11 @@ 프로젝트 추가가 지원되지 않습니다. + + Symbol's project could not be found in the provided solution + Symbol's project could not be found in the provided solution + + The project already contains the specified reference. The project already contains the specified reference. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf index ce51a1359c4fc..c0817d3f8b274 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf @@ -172,6 +172,11 @@ Dodawanie projektów nie jest obsługiwane. + + Symbol's project could not be found in the provided solution + Symbol's project could not be found in the provided solution + + The project already contains the specified reference. The project already contains the specified reference. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf index e97d1c1ff8249..593d1ff0828c0 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf @@ -172,6 +172,11 @@ Não há suporte para adicionar projetos. + + Symbol's project could not be found in the provided solution + Symbol's project could not be found in the provided solution + + The project already contains the specified reference. The project already contains the specified reference. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf index e1a5d5082256e..757fa537a55c1 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf @@ -172,6 +172,11 @@ Добавление проектов не поддерживается. + + Symbol's project could not be found in the provided solution + Symbol's project could not be found in the provided solution + + The project already contains the specified reference. The project already contains the specified reference. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf index 65b15ef347614..980ccc20171ed 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf @@ -172,6 +172,11 @@ Projelerin eklenmesi desteklenmiyor. + + Symbol's project could not be found in the provided solution + Symbol's project could not be found in the provided solution + + The project already contains the specified reference. The project already contains the specified reference. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf index 974afac279fcd..700d7440735dc 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf @@ -172,6 +172,11 @@ 不支持添加项目。 + + Symbol's project could not be found in the provided solution + Symbol's project could not be found in the provided solution + + The project already contains the specified reference. The project already contains the specified reference. diff --git a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf index 97722d4bec82c..b097edeb49440 100644 --- a/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf +++ b/src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf @@ -172,6 +172,11 @@ 不支援新增專案。 + + Symbol's project could not be found in the provided solution + Symbol's project could not be found in the provided solution + + The project already contains the specified reference. The project already contains the specified reference. diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_SymbolFinder.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_SymbolFinder.cs index e1636d7b252ea..07b3fdb8debf5 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_SymbolFinder.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_SymbolFinder.cs @@ -3,12 +3,12 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; @@ -34,7 +34,7 @@ public Task FindReferencesAsync( var symbolAndProjectId = await symbolAndProjectIdArg.TryRehydrateAsync( solution, cancellationToken).ConfigureAwait(false); - var progressCallback = new FindReferencesProgressCallback(EndPoint, cancellationToken); + var progressCallback = new FindReferencesProgressCallback(solution, EndPoint, cancellationToken); if (!symbolAndProjectId.HasValue) { @@ -74,7 +74,17 @@ await SymbolFinder.FindLiteralReferencesInCurrentProcessAsync( }, cancellationToken); } - public Task> FindAllDeclarationsWithNormalQueryAsync( + private ImmutableArray Convert(ImmutableArray items, Solution solution, CancellationToken cancellationToken) + { + using var _ = ArrayBuilder.GetInstance(out var result); + + foreach (var item in items) + result.Add(SerializableSymbolAndProjectId.Dehydrate(solution, item.Symbol, cancellationToken)); + + return result.ToImmutable(); + } + + public Task> FindAllDeclarationsWithNormalQueryAsync( PinnedSolutionInfo solutionInfo, ProjectId projectId, string name, @@ -94,12 +104,12 @@ public Task> FindAllDeclarationsWithNormal var result = await DeclarationFinder.FindAllDeclarationsWithNormalQueryInCurrentProcessAsync( project, query, criteria, cancellationToken).ConfigureAwait(false); - return (IList)result.SelectAsArray(SerializableSymbolAndProjectId.Dehydrate); + return Convert(result, solution, cancellationToken); } }, cancellationToken); } - public Task> FindSolutionSourceDeclarationsWithNormalQueryAsync( + public Task> FindSolutionSourceDeclarationsWithNormalQueryAsync( PinnedSolutionInfo solutionInfo, string name, bool ignoreCase, @@ -114,12 +124,12 @@ public Task> FindSolutionSourceDeclaration var result = await DeclarationFinder.FindSourceDeclarationsWithNormalQueryInCurrentProcessAsync( solution, name, ignoreCase, criteria, cancellationToken).ConfigureAwait(false); - return (IList)result.SelectAsArray(SerializableSymbolAndProjectId.Dehydrate); + return Convert(result, solution, cancellationToken); } }, cancellationToken); } - public Task> FindProjectSourceDeclarationsWithNormalQueryAsync( + public Task> FindProjectSourceDeclarationsWithNormalQueryAsync( PinnedSolutionInfo solutionInfo, ProjectId projectId, string name, @@ -137,12 +147,12 @@ public Task> FindProjectSourceDeclarations var result = await DeclarationFinder.FindSourceDeclarationsWithNormalQueryInCurrentProcessAsync( project, name, ignoreCase, criteria, cancellationToken).ConfigureAwait(false); - return (IList)result.SelectAsArray(SerializableSymbolAndProjectId.Dehydrate); + return Convert(result, solution, cancellationToken); } }, cancellationToken); } - public Task> FindSolutionSourceDeclarationsWithPatternAsync( + public Task> FindSolutionSourceDeclarationsWithPatternAsync( PinnedSolutionInfo solutionInfo, string pattern, SymbolFilter criteria, CancellationToken cancellationToken) { return RunServiceAsync(async () => @@ -154,12 +164,12 @@ public Task> FindSolutionSourceDeclaration var result = await DeclarationFinder.FindSourceDeclarationsWithPatternInCurrentProcessAsync( solution, pattern, criteria, cancellationToken).ConfigureAwait(false); - return (IList)result.SelectAsArray(SerializableSymbolAndProjectId.Dehydrate); + return Convert(result, solution, cancellationToken); } }, cancellationToken); } - public Task> FindProjectSourceDeclarationsWithPatternAsync( + public Task> FindProjectSourceDeclarationsWithPatternAsync( PinnedSolutionInfo solutionInfo, ProjectId projectId, string pattern, SymbolFilter criteria, CancellationToken cancellationToken) { return RunServiceAsync(async () => @@ -172,7 +182,7 @@ public Task> FindProjectSourceDeclarations var result = await DeclarationFinder.FindSourceDeclarationsWithPatternInCurrentProcessAsync( project, pattern, criteria, cancellationToken).ConfigureAwait(false); - return (IList)result.SelectAsArray(SerializableSymbolAndProjectId.Dehydrate); + return Convert(result, solution, cancellationToken); } }, cancellationToken); } @@ -203,13 +213,15 @@ public Task ItemCompletedAsync() private sealed class FindReferencesProgressCallback : IStreamingFindReferencesProgress, IStreamingProgressTracker { + private readonly Solution _solution; private readonly RemoteEndPoint _endPoint; private readonly CancellationToken _cancellationToken; public IStreamingProgressTracker ProgressTracker { get; } - public FindReferencesProgressCallback(RemoteEndPoint endPoint, CancellationToken cancellationToken) + public FindReferencesProgressCallback(Solution solution, RemoteEndPoint endPoint, CancellationToken cancellationToken) { + _solution = solution; _endPoint = endPoint; _cancellationToken = cancellationToken; ProgressTracker = this; @@ -228,15 +240,19 @@ public Task OnFindInDocumentCompletedAsync(Document document) => _endPoint.InvokeAsync(nameof(SymbolFinder.FindReferencesServerCallback.OnFindInDocumentCompletedAsync), new object[] { document.Id }, _cancellationToken); public Task OnDefinitionFoundAsync(SymbolAndProjectId definition) - => _endPoint.InvokeAsync(nameof(SymbolFinder.FindReferencesServerCallback.OnDefinitionFoundAsync), new object[] { SerializableSymbolAndProjectId.Dehydrate(definition) }, _cancellationToken); + => _endPoint.InvokeAsync( + nameof(SymbolFinder.FindReferencesServerCallback.OnDefinitionFoundAsync), + new object[] { SerializableSymbolAndProjectId.Dehydrate(_solution, definition.Symbol, _cancellationToken) }, _cancellationToken); public Task OnReferenceFoundAsync(SymbolAndProjectId definition, ReferenceLocation reference) - { - return _endPoint.InvokeAsync( + => _endPoint.InvokeAsync( nameof(SymbolFinder.FindReferencesServerCallback.OnReferenceFoundAsync), - new object[] { SerializableSymbolAndProjectId.Dehydrate(definition), SerializableReferenceLocation.Dehydrate(reference) }, + new object[] + { + SerializableSymbolAndProjectId.Dehydrate(_solution, definition.Symbol, _cancellationToken), + SerializableReferenceLocation.Dehydrate(reference, _cancellationToken), + }, _cancellationToken); - } public Task AddItemsAsync(int count) => _endPoint.InvokeAsync(nameof(SymbolFinder.FindReferencesServerCallback.AddItemsAsync), new object[] { count }, _cancellationToken);