diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjects/DefinitionProject.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjects/DefinitionProject.cs deleted file mode 100644 index 086ecb38d85cfd5d5c4f6d77932bb2255b7caa4b..0000000000000000000000000000000000000000 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjects/DefinitionProject.cs +++ /dev/null @@ -1,39 +0,0 @@ -// 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; -using System.Collections.Generic; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.FindSymbols.DependentProjects -{ - /// - /// The name of the assembly, and optionally also the project, that a symbol came from. Used in the to quickly find which set of projects in a solution should searched when - /// looking at symbols from that same location. - /// - internal readonly struct DefinitionProject : IEquatable - { - private readonly ProjectId? _sourceProjectId; - private readonly string _assemblyName; - - public DefinitionProject(ProjectId? sourceProjectId, string assemblyName) - { - _sourceProjectId = sourceProjectId; - _assemblyName = assemblyName; - } - - public override bool Equals(object? obj) - => obj is DefinitionProject project && Equals(project); - - public bool Equals(DefinitionProject other) - => EqualityComparer.Default.Equals(_sourceProjectId, other._sourceProjectId) && - _assemblyName == other._assemblyName; - - public override int GetHashCode() - => Hash.Combine(_sourceProjectId, _assemblyName.GetHashCode()); - } -} diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjects/DependentProject.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjects/DependentProject.cs deleted file mode 100644 index 8c98d3f3f0d4050a13c8838edbfd849df673001f..0000000000000000000000000000000000000000 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjects/DependentProject.cs +++ /dev/null @@ -1,36 +0,0 @@ -// 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; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.FindSymbols.DependentProjects -{ - /// - /// Represents a project that depends on some assembly, and if that project can see the internal symbols of that - /// assembly or not. - /// - internal readonly struct DependentProject : IEquatable - { - public readonly ProjectId ProjectId; - public readonly bool HasInternalsAccess; - - public DependentProject(ProjectId dependentProjectId, bool hasInternalsAccess) - { - this.ProjectId = dependentProjectId; - this.HasInternalsAccess = hasInternalsAccess; - } - - public override bool Equals(object? obj) - => obj is DependentProject project && this.Equals(project); - - public override int GetHashCode() - => Hash.Combine(HasInternalsAccess, ProjectId.GetHashCode()); - - public bool Equals(DependentProject other) - => HasInternalsAccess == other.HasInternalsAccess && ProjectId.Equals(other.ProjectId); - } -} diff --git a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs index 89b2336a7ae647fd10c66a42490a6d575d1ab827..917c5dc1aa103a54ef430cc32f3ee91390a7270d 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/FindReferences/DependentProjectsFinder.cs @@ -5,7 +5,6 @@ #nullable enable using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -13,29 +12,17 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.FindSymbols.DependentProjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols { - using DependentProjectMap = ConcurrentDictionary>>; - /// /// Provides helper methods for finding dependent projects across a solution that a given symbol can be referenced within. /// internal static partial class DependentProjectsFinder { - /// - /// Dependent projects cache. - /// For a given solution, maps from an assembly (source-project or metadata-assembly) to the set of projects referencing it. - /// Key: DefinitionProject, which contains the project-id for a source-project-assembly, or assembly-name for a metadata-assembly. - /// Value: List of DependentProjects, where each DependentProject contains a dependent project ID and a flag indicating whether the dependent project has internals access to definition project. - /// - private static readonly ConditionalWeakTable s_dependentProjectsCache = - new ConditionalWeakTable(); - public static async Task> GetDependentProjectsAsync( Solution solution, ISymbol symbol, IImmutableSet? projects, CancellationToken cancellationToken) { @@ -79,32 +66,17 @@ internal static partial class DependentProjectsFinder return ImmutableArray.Empty; // 1) Compute all the dependent projects (submission + non-submission) and their InternalsVisibleTo semantics to the definition project. - - var visibility = symbol.GetResultantVisibility(); - if (visibility == SymbolVisibility.Private) - { - // For private symbols, we only need the current project (and related submissions). No need to cache - // that, just simply compute and return the result. - return GetProjects(solution, await ComputeDependentProjectsAsync(solution, symbolOrigination, visibility, cancellationToken).ConfigureAwait(false)); - } - - // Otherwise, for non-private symbols, we cache the dependent projects for non-private symbols to speed up - // future calls. - var dependentProjectsMap = s_dependentProjectsCache.GetValue(solution, _ => new DependentProjectMap()); - - var asyncLazy = dependentProjectsMap.GetOrAdd( - new DefinitionProject(symbolOrigination.sourceProject?.Id, symbolOrigination.assembly.Name.ToLower()), - _ => AsyncLazy.Create(c => ComputeDependentProjectsAsync(solution, symbolOrigination, visibility, c), cacheResult: true)); - var dependentProjects = await asyncLazy.GetValueAsync(cancellationToken).ConfigureAwait(false); + var symbolVisibility = symbol.GetResultantVisibility(); + var dependentProjects = await ComputeDependentProjectsAsync( + solution, symbolOrigination, symbolVisibility, cancellationToken).ConfigureAwait(false); // 2) Filter the above computed dependent projects based on symbol visibility. - return GetProjects(solution, visibility == SymbolVisibility.Internal - ? dependentProjects.WhereAsArray(dp => dp.HasInternalsAccess) - : dependentProjects); - } + var filteredProjects = symbolVisibility == SymbolVisibility.Internal + ? dependentProjects.WhereAsArray(dp => dp.hasInternalsAccess) + : dependentProjects; - private static ImmutableArray GetProjects(Solution solution, ImmutableArray dependentProjects) - => dependentProjects.SelectAsArray(dp => solution.GetRequiredProject(dp.ProjectId)); + return filteredProjects.SelectAsArray(t => t.project); + } /// /// Returns a pair of data bout where originates from. It's GetProjects(Solution solution, ImmutableA return assembly == null ? default : (assembly, solution.GetProject(assembly, cancellationToken)); } - private static async Task> ComputeDependentProjectsAsync( + private static async Task> ComputeDependentProjectsAsync( Solution solution, (IAssemblySymbol assembly, Project? sourceProject) symbolOrigination, SymbolVisibility visibility, @@ -126,12 +98,12 @@ private static ImmutableArray GetProjects(Solution solution, ImmutableA { cancellationToken.ThrowIfCancellationRequested(); - var dependentProjects = new HashSet(); + var dependentProjects = new HashSet<(Project, bool hasInternalsAccess)>(); // If a symbol was defined in source, then it is always visible to the project it // was defined in. if (symbolOrigination.sourceProject != null) - dependentProjects.Add(new DependentProject(symbolOrigination.sourceProject.Id, hasInternalsAccess: true)); + dependentProjects.Add((symbolOrigination.sourceProject, hasInternalsAccess: true)); // If it's not private, then we need to find possible references. if (visibility != SymbolVisibility.Private) @@ -145,7 +117,7 @@ private static ImmutableArray GetProjects(Solution solution, ImmutableA } private static async Task AddSubmissionDependentProjectsAsync( - Solution solution, Project? sourceProject, HashSet dependentProjects, CancellationToken cancellationToken) + Solution solution, Project? sourceProject, HashSet<(Project project, bool hasInternalsAccess)> dependentProjects, CancellationToken cancellationToken) { if (sourceProject?.IsSubmission != true) return; @@ -187,7 +159,7 @@ private static ImmutableArray GetProjects(Solution solution, ImmutableA // and 2, even though 2 doesn't have a direct reference to 1. Hence we need to take // our current set of projects and find the transitive closure over backwards // submission previous references. - var projectIdsToProcess = new Stack(dependentProjects.Select(dp => dp.ProjectId)); + var projectIdsToProcess = new Stack(dependentProjects.Select(dp => dp.project.Id)); while (projectIdsToProcess.Count > 0) { @@ -197,9 +169,9 @@ private static ImmutableArray GetProjects(Solution solution, ImmutableA { foreach (var pId in submissionIds) { - if (!dependentProjects.Any(dp => dp.ProjectId == pId)) + if (!dependentProjects.Any(dp => dp.project.Id == pId)) { - dependentProjects.Add(new DependentProject(pId, hasInternalsAccess: true)); + dependentProjects.Add((solution.GetRequiredProject(pId), hasInternalsAccess: true)); projectIdsToProcess.Push(pId); } } @@ -220,7 +192,7 @@ private static bool IsInternalsVisibleToAttribute(AttributeData attr) private static void AddNonSubmissionDependentProjects( Solution solution, (IAssemblySymbol assembly, Project? sourceProject) symbolOrigination, - HashSet dependentProjects, + HashSet<(Project project, bool hasInternalsAccess)> dependentProjects, CancellationToken cancellationToken) { if (symbolOrigination.sourceProject?.IsSubmission == true) @@ -239,8 +211,8 @@ private static bool IsInternalsVisibleToAttribute(AttributeData attr) // Ok, we have some project that at least references this assembly. Add it to the result, keeping track // if it can see internals or not as well. internalsVisibleToSet ??= GetInternalsVisibleToSet(symbolOrigination.assembly); - var internalsVisibleTo = internalsVisibleToSet.Contains(project.AssemblyName); - dependentProjects.Add(new DependentProject(project.Id, internalsVisibleTo)); + var hasInternalsAccess = internalsVisibleToSet.Contains(project.AssemblyName); + dependentProjects.Add((project, hasInternalsAccess)); } }