未验证 提交 be35a9f2 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #43900 from CyrusNajmabadi/nrtDepProjectFinder

NRT enable dependent project finder.
......@@ -2,6 +2,8 @@
// 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.Concurrent;
using System.Collections.Generic;
......@@ -24,21 +26,29 @@ internal static class DependentProjectsFinder
/// <summary>
/// A helper struct used for keying in <see cref="s_dependentProjectsCache"/>.
/// </summary>
private struct DefinitionProject
private readonly struct DefinitionProject : IEquatable<DefinitionProject>
{
#pragma warning disable IDE0052 // Remove unread private members - DefinitionProject is used as a key for dictionaries.
private readonly ProjectId _sourceProjectId;
private readonly string _assemblyName;
#pragma warning restore IDE0052 // Remove unread private members
private readonly ProjectId? _sourceProjectId;
private readonly string? _assemblyName;
public DefinitionProject(ProjectId sourceProjectId, 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<ProjectId?>.Default.Equals(_sourceProjectId, other._sourceProjectId) &&
_assemblyName == other._assemblyName;
public override int GetHashCode()
=> Hash.Combine(_sourceProjectId, _assemblyName?.GetHashCode() ?? 0);
}
private struct DependentProject : IEquatable<DependentProject>
private readonly struct DependentProject : IEquatable<DependentProject>
{
public readonly ProjectId ProjectId;
public readonly bool HasInternalsAccess;
......@@ -49,8 +59,8 @@ public DependentProject(ProjectId dependentProjectId, bool hasInternalsAccess)
this.HasInternalsAccess = hasInternalsAccess;
}
public override bool Equals(object obj)
=> obj is DependentProject && this.Equals((DependentProject)obj);
public override bool Equals(object? obj)
=> obj is DependentProject project && this.Equals(project);
public override int GetHashCode()
=> Hash.Combine(HasInternalsAccess, ProjectId.GetHashCode());
......@@ -103,7 +113,7 @@ private static ImmutableArray<Project> GetAllProjects(Solution solution)
=> solution.Projects.ToImmutableArray();
private static ImmutableArray<Project> GetProjects(Solution solution, ImmutableArray<ProjectId> projectIds)
=> projectIds.SelectAsArray(id => solution.GetProject(id));
=> projectIds.SelectAsArray(id => solution.GetRequiredProject(id));
/// <summary>
/// This method computes the dependent projects that need to be searched for references of the given <paramref name="symbol"/>.
......@@ -136,6 +146,7 @@ private static ImmutableArray<Project> GetProjects(Solution solution, ImmutableA
// Find the projects that reference this assembly.
// If this is a source symbol from a project, try to find that project.
var sourceProject = solution.GetProject(containingAssembly, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
......@@ -167,7 +178,7 @@ private static ImmutableArray<Project> GetProjects(Solution solution, ImmutableA
private static async Task<ImmutableArray<DependentProject>> GetDependentProjectsCoreAsync(
ISymbol symbol,
Solution solution,
Project sourceProject,
Project? sourceProject,
SymbolVisibility visibility,
CancellationToken cancellationToken)
{
......@@ -213,7 +224,8 @@ private static ImmutableArray<Project> GetProjects(Solution solution, ImmutableA
return GetProjects(solution, projectIds);
}
private static async Task AddSubmissionDependentProjectsAsync(Solution solution, Project sourceProject, HashSet<DependentProject> dependentProjects, CancellationToken cancellationToken)
private static async Task AddSubmissionDependentProjectsAsync(
Solution solution, Project? sourceProject, HashSet<DependentProject> dependentProjects, CancellationToken cancellationToken)
{
var isSubmission = sourceProject != null && sourceProject.IsSubmission;
if (!isSubmission)
......@@ -226,26 +238,29 @@ private static async Task AddSubmissionDependentProjectsAsync(Solution solution,
// search only submission project
foreach (var projectId in solution.ProjectIds)
{
var project = solution.GetProject(projectId);
var project = solution.GetRequiredProject(projectId);
if (project.IsSubmission && project.SupportsCompilation)
{
cancellationToken.ThrowIfCancellationRequested();
// If we are referencing another project, store the link in the other direction
// so we walk across it later
var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var previous = compilation.ScriptCompilationInfo.PreviousScriptCompilation;
var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
var previous = compilation.ScriptCompilationInfo?.PreviousScriptCompilation;
if (previous != null)
{
var referencedProject = solution.GetProject(previous.Assembly, cancellationToken);
if (!projectIdsToReferencingSubmissionIds.TryGetValue(referencedProject.Id, out var referencingSubmissions))
if (referencedProject != null)
{
referencingSubmissions = new List<ProjectId>();
projectIdsToReferencingSubmissionIds.Add(referencedProject.Id, referencingSubmissions);
}
if (!projectIdsToReferencingSubmissionIds.TryGetValue(referencedProject.Id, out var referencingSubmissions))
{
referencingSubmissions = new List<ProjectId>();
projectIdsToReferencingSubmissionIds.Add(referencedProject.Id, referencingSubmissions);
}
referencingSubmissions.Add(project.Id);
referencingSubmissions.Add(project.Id);
}
}
}
}
......@@ -278,22 +293,23 @@ private static async Task AddSubmissionDependentProjectsAsync(Solution solution,
private static bool IsInternalsVisibleToAttribute(AttributeData attr)
{
var attrType = attr.AttributeClass;
if (attrType == null)
{
return false;
}
var attributeName = attr.AttributeClass.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted));
return attributeName == "System.Runtime.CompilerServices.InternalsVisibleToAttribute";
return attrType?.Name == nameof(InternalsVisibleToAttribute) &&
attrType.ContainingNamespace?.Name == nameof(System.Runtime.CompilerServices) &&
attrType.ContainingNamespace.ContainingNamespace?.Name == nameof(System.Runtime) &&
attrType.ContainingNamespace.ContainingNamespace.ContainingNamespace?.Name == nameof(System) &&
attrType.ContainingNamespace.ContainingNamespace.ContainingNamespace.ContainingNamespace?.IsGlobalNamespace == true;
}
private static async Task AddNonSubmissionDependentProjectsAsync(IAssemblySymbol sourceAssembly, Solution solution, Project sourceProject, HashSet<DependentProject> dependentProjects, CancellationToken cancellationToken)
private static async Task AddNonSubmissionDependentProjectsAsync(
IAssemblySymbol sourceAssembly,
Solution solution,
Project? sourceProject,
HashSet<DependentProject> dependentProjects,
CancellationToken cancellationToken)
{
var isSubmission = sourceProject != null && sourceProject.IsSubmission;
if (isSubmission)
{
return;
}
var internalsVisibleToMap = CreateInternalsVisibleToMap(sourceAssembly);
......@@ -304,7 +320,7 @@ private static async Task AddNonSubmissionDependentProjectsAsync(IAssemblySymbol
// things we want to find.
foreach (var projectId in solution.ProjectIds)
{
var project = solution.GetProject(projectId);
var project = solution.GetRequiredProject(projectId);
cancellationToken.ThrowIfCancellationRequested();
......@@ -326,7 +342,7 @@ private static async Task AddNonSubmissionDependentProjectsAsync(IAssemblySymbol
if (internalsVisibleToMap.Value.Contains(project.AssemblyName) &&
project.SupportsCompilation)
{
var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
var targetAssembly = compilation.Assembly;
if (sourceAssembly.Language != targetAssembly.Language)
......@@ -361,13 +377,9 @@ private static Lazy<HashSet<string>> CreateInternalsVisibleToMap(IAssemblySymbol
foreach (var attr in assembly.GetAttributes().Where(IsInternalsVisibleToAttribute))
{
var typeNameConstant = attr.ConstructorArguments.FirstOrDefault();
if (typeNameConstant.Type == null || typeNameConstant.Type.SpecialType != SpecialType.System_String)
{
continue;
}
var value = (string)typeNameConstant.Value;
if (value == null)
if (typeNameConstant.Type == null ||
typeNameConstant.Type.SpecialType != SpecialType.System_String ||
!(typeNameConstant.Value is string value))
{
continue;
}
......@@ -383,7 +395,11 @@ private static Lazy<HashSet<string>> CreateInternalsVisibleToMap(IAssemblySymbol
return internalsVisibleToMap;
}
private static bool HasReferenceTo(IAssemblySymbol containingAssembly, Project sourceProject, Project project, CancellationToken cancellationToken)
private static bool HasReferenceTo(
IAssemblySymbol containingAssembly,
Project? sourceProject,
Project project,
CancellationToken cancellationToken)
{
if (containingAssembly == null)
{
......@@ -431,16 +447,14 @@ public static bool HasReferenceToAssembly(this Project project, string assemblyN
// way for it to have an IAssemblySymbol. And without that, there is no way for it
// to have any sort of 'ReferenceTo' the provided 'containingAssembly' symbol.
if (!project.SupportsCompilation)
{
return null;
}
if (!project.TryGetCompilation(out var compilation))
{
// WORKAROUND:
// perf check metadata reference using newly created empty compilation with only metadata references.
compilation = project.LanguageServices.CompilationFactory.CreateCompilation(
project.AssemblyName, project.CompilationOptions);
compilation = project.LanguageServices.CompilationFactory!.CreateCompilation(
project.AssemblyName, project.CompilationOptions!);
compilation = compilation.AddReferences(project.MetadataReferences);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册