提交 c79beff3 编写于 作者: C Cyrus Najmabadi

Move many more symbolic apis over to SymbolAndProjectId

上级 2b611fde
......@@ -146,11 +146,13 @@ public ValueTask<ITypeSymbol> DetermineReturnTypeAsync(CancellationToken cancell
var compilation = Document.SemanticModel.Compilation;
var allTypeParameters = availableMethodTypeParameters.Concat(availableTypeParameters);
var availableTypeParameterNames = allTypeParameters.Select(t => t.Name).ToSet();
var typeArgumentToTypeParameterMap = GetTypeArgumentToTypeParameterMap(cancellationToken);
typeSymbol = typeSymbol.RemoveAnonymousTypes(compilation);
typeSymbol = await typeSymbol.ReplaceTypeParametersBasedOnTypeConstraintsAsync(compilation, allTypeParameters, Document.Document.Project.Solution, cancellationToken).ConfigureAwait(false);
typeSymbol = await ReplaceTypeParametersBasedOnTypeConstraintsAsync(
Document.Project, typeSymbol, compilation, availableTypeParameterNames, cancellationToken).ConfigureAwait(false);
return typeSymbol.RemoveUnavailableTypeParameters(compilation, allTypeParameters)
.RemoveUnnamedErrorTypes(compilation)
.SubstituteTypes(typeArgumentToTypeParameterMap, new TypeGenerator());
......
// 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.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.GenerateMember.GenerateParameterizedMember
{
internal partial class AbstractGenerateParameterizedMemberService<TService, TSimpleNameSyntax, TExpressionSyntax, TInvocationExpressionSyntax>
{
private static async ValueTask<ITypeSymbol> ReplaceTypeParametersBasedOnTypeConstraintsAsync(
Project project,
ITypeSymbol type,
Compilation compilation,
ISet<string> availableTypeParameterNames,
CancellationToken cancellationToken)
{
var visitor = new DetermineSubstitutionsVisitor(
compilation, availableTypeParameterNames, project, cancellationToken);
await visitor.Visit(type).ConfigureAwait(false);
return type.SubstituteTypes(visitor.Substitutions, compilation);
}
private class DetermineSubstitutionsVisitor : AsyncSymbolVisitor
{
public readonly Dictionary<ITypeSymbol, ITypeSymbol> Substitutions =
new Dictionary<ITypeSymbol, ITypeSymbol>();
private readonly CancellationToken _cancellationToken;
private readonly Compilation _compilation;
private readonly ISet<string> _availableTypeParameterNames;
private readonly Project _project;
public DetermineSubstitutionsVisitor(
Compilation compilation, ISet<string> availableTypeParameterNames, Project project, CancellationToken cancellationToken)
{
_compilation = compilation;
_availableTypeParameterNames = availableTypeParameterNames;
_project = project;
_cancellationToken = cancellationToken;
}
public override ValueTask VisitDynamicType(IDynamicTypeSymbol symbol)
=> default;
public override ValueTask VisitArrayType(IArrayTypeSymbol symbol)
=> symbol.ElementType.Accept(this);
public override async ValueTask VisitNamedType(INamedTypeSymbol symbol)
{
foreach (var typeArg in symbol.TypeArguments)
await typeArg.Accept(this).ConfigureAwait(false);
}
public override ValueTask VisitPointerType(IPointerTypeSymbol symbol)
=> symbol.PointedAtType.Accept(this);
public override async ValueTask VisitTypeParameter(ITypeParameterSymbol symbol)
{
if (_availableTypeParameterNames.Contains(symbol.Name))
return;
switch (symbol.ConstraintTypes.Length)
{
case 0:
// If there are no constraint then there is no replacement required.
return;
case 1:
// If there is one constraint which is a INamedTypeSymbol then return the INamedTypeSymbol
// because the TypeParameter is expected to be of that type
// else return the original symbol
if (symbol.ConstraintTypes.ElementAt(0) is INamedTypeSymbol namedType)
Substitutions.Add(symbol, namedType);
return;
}
var commonDerivedType = await DetermineCommonDerivedTypeAsync(symbol).ConfigureAwait(false);
if (commonDerivedType != null)
Substitutions.Add(symbol, commonDerivedType);
}
private async ValueTask<ITypeSymbol> DetermineCommonDerivedTypeAsync(ITypeParameterSymbol symbol)
{
if (!symbol.ConstraintTypes.All(t => t is INamedTypeSymbol))
return null;
var solution = _project.Solution;
var projects = solution.Projects.ToImmutableHashSet();
var commonTypes = await GetDerivedAndImplementedTypesAsync(
(INamedTypeSymbol)symbol.ConstraintTypes[0], projects).ConfigureAwait(false);
for (int i = 1; i < symbol.ConstraintTypes.Length; i++)
{
var currentTypes = await GetDerivedAndImplementedTypesAsync(
(INamedTypeSymbol)symbol.ConstraintTypes[i], projects).ConfigureAwait(false);
commonTypes.IntersectWith(currentTypes);
if (commonTypes.Count == 0)
return null;
}
// If there was any intersecting derived type among the constraint types then pick the first of the lot.
if (commonTypes.Count == 0)
return null;
var commonType = commonTypes.First();
// If the resultant intersecting type contains any Type arguments that could be replaced
// using the type constraints then recursively update the type until all constraints are appropriately handled
var substitutedType = await ReplaceTypeParametersBasedOnTypeConstraintsAsync(
_project, commonType, _compilation, _availableTypeParameterNames, _cancellationToken).ConfigureAwait(false);
var similarTypes = SymbolFinder.FindSimilarSymbols(substitutedType, _compilation, _cancellationToken);
if (similarTypes.Any())
return similarTypes.First();
similarTypes = SymbolFinder.FindSimilarSymbols(commonType, _compilation, _cancellationToken);
return similarTypes.FirstOrDefault() ?? symbol;
}
private async Task<ISet<INamedTypeSymbol>> GetDerivedAndImplementedTypesAsync(
INamedTypeSymbol constraintType, IImmutableSet<Project> projects)
{
var solution = _project.Solution;
var symbolAndProjectId = SymbolAndProjectId.Create(constraintType, _project.Id);
var derivedClasses = await SymbolFinder.FindDerivedClassesAsync(
symbolAndProjectId, solution, projects, _cancellationToken).ConfigureAwait(false);
var implementedTypes = await DependentTypeFinder.FindTransitivelyImplementingStructuresAndClassesAsync(
symbolAndProjectId, solution, projects, _cancellationToken).ConfigureAwait(false);
return derivedClasses.Concat(implementedTypes).Select(t => t.Symbol).ToSet();
}
}
}
}
......@@ -10,6 +10,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Elfie.Model;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
......@@ -34,7 +35,7 @@ internal sealed partial class GraphBuilder
private readonly Dictionary<GraphNode, Project> _nodeToContextProjectMap = new Dictionary<GraphNode, Project>();
private readonly Dictionary<GraphNode, Document> _nodeToContextDocumentMap = new Dictionary<GraphNode, Document>();
private readonly Dictionary<GraphNode, ISymbol> _nodeToSymbolMap = new Dictionary<GraphNode, ISymbol>();
private readonly Dictionary<GraphNode, SymbolAndProjectId> _nodeToSymbolMap = new Dictionary<GraphNode, SymbolAndProjectId>();
/// <summary>
/// The input solution. Never null.
......@@ -121,7 +122,7 @@ private async Task PopulateMapsForSymbolInputNodeAsync(GraphNode inputNode)
var symbol = symbolId.Value.Resolve(compilation).Symbol;
if (symbol != null)
{
_nodeToSymbolMap.Add(inputNode, symbol);
_nodeToSymbolMap.Add(inputNode, new SymbolAndProjectId(symbol, project.Id));
}
var documentId = (DocumentId)inputNode[RoslynGraphProperties.ContextDocumentId];
......@@ -160,7 +161,7 @@ public Document GetContextDocument(GraphNode node)
}
}
public ISymbol GetSymbol(GraphNode node)
public SymbolAndProjectId GetSymbol(GraphNode node)
{
using (_gate.DisposableWait())
{
......@@ -169,15 +170,17 @@ public ISymbol GetSymbol(GraphNode node)
}
}
public Task<GraphNode> AddNodeForSymbolAsync(ISymbol symbol, GraphNode relatedNode)
public Task<GraphNode> AddNodeForSymbolAsync(SymbolAndProjectId symbol, GraphNode relatedNode)
{
// The lack of a lock here is acceptable, since each of the functions lock, and GetContextProject/GetContextDocument
// never change for the same input.
return AddNodeForSymbolAsync(symbol, GetContextProject(relatedNode), GetContextDocument(relatedNode));
}
public async Task<GraphNode> AddNodeForSymbolAsync(ISymbol symbol, Project contextProject, Document contextDocument)
public async Task<GraphNode> AddNodeForSymbolAsync(SymbolAndProjectId symbolAndProjectId, Project contextProject, Document contextDocument)
{
var symbol = symbolAndProjectId.Symbol;
// Figure out what the location for this node should be. We'll arbitrarily pick the
// first one, unless we have a contextDocument to restrict it
var preferredLocation = symbol.Locations.FirstOrDefault(l => l.SourceTree != null);
......@@ -230,7 +233,7 @@ public async Task<GraphNode> AddNodeForSymbolAsync(ISymbol symbol, Project conte
// we won't double-count.
_createdNodes.Add(node);
_nodeToSymbolMap[node] = symbol;
_nodeToSymbolMap[node] = symbolAndProjectId;
_nodeToContextProjectMap[node] = contextProject;
_nodeToContextDocumentMap[node] = contextDocument;
......
......@@ -2,17 +2,13 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.VisualStudio.GraphModel;
using Microsoft.VisualStudio.GraphModel.CodeSchema;
using Microsoft.VisualStudio.GraphModel.Schemas;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.Progression
......@@ -26,7 +22,7 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
foreach (var node in context.InputNodes)
{
var symbol = graphBuilder.GetSymbol(node);
if (symbol != null)
if (symbol.Symbol != null)
{
foreach (var newSymbol in await GetCalledMethodSymbolsAsync(symbol, solution, cancellationToken).ConfigureAwait(false))
{
......@@ -41,11 +37,12 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
return graphBuilder;
}
private static async Task<IEnumerable<ISymbol>> GetCalledMethodSymbolsAsync(ISymbol symbol, Solution solution, CancellationToken cancellationToken)
private static async Task<ImmutableArray<SymbolAndProjectId>> GetCalledMethodSymbolsAsync(
SymbolAndProjectId symbolAndProjectId, Solution solution, CancellationToken cancellationToken)
{
var symbols = new List<ISymbol>();
using var _ = ArrayBuilder<SymbolAndProjectId>.GetInstance(out var symbols);
foreach (var reference in symbol.DeclaringSyntaxReferences)
foreach (var reference in symbolAndProjectId.Symbol.DeclaringSyntaxReferences)
{
var semanticModel = await solution.GetDocument(reference.SyntaxTree).GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
foreach (var syntaxNode in (await reference.GetSyntaxAsync(cancellationToken).ConfigureAwait(false)).DescendantNodes())
......@@ -56,12 +53,12 @@ private static async Task<IEnumerable<ISymbol>> GetCalledMethodSymbolsAsync(ISym
if (newSymbol != null && newSymbol is IMethodSymbol &&
(newSymbol.CanBeReferencedByName || ((IMethodSymbol)newSymbol).MethodKind == MethodKind.Constructor))
{
symbols.Add(newSymbol);
symbols.Add(symbolAndProjectId.WithSymbol(newSymbol));
}
}
}
return symbols;
return symbols.ToImmutable();
}
}
}
......@@ -25,7 +25,7 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
{
var symbol = graphBuilder.GetSymbol(node);
if (symbol != null)
if (symbol.Symbol != null)
{
var containsChildren = SymbolContainment.GetContainedSymbols(symbol).Any();
graphBuilder.AddDeferredPropertySet(node, DgmlNodeProperties.ContainsChildren, containsChildren);
......
......@@ -33,7 +33,7 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
var symbol = graphBuilder.GetSymbol(node);
if (symbol != null)
if (symbol.Symbol != null)
{
foreach (var newSymbol in SymbolContainment.GetContainedSymbols(symbol))
{
......
......@@ -23,7 +23,10 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
foreach (var node in context.InputNodes)
{
var symbol = graphBuilder.GetSymbol(node);
if (symbol is INamedTypeSymbol || symbol is IMethodSymbol || symbol is IPropertySymbol || symbol is IEventSymbol)
if (symbol.Symbol is INamedTypeSymbol ||
symbol.Symbol is IMethodSymbol ||
symbol.Symbol is IPropertySymbol ||
symbol.Symbol is IEventSymbol)
{
var implementations = await SymbolFinder.FindImplementationsAsync(symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
......
......@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
......@@ -24,13 +25,15 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
foreach (var node in context.InputNodes)
{
var symbol = graphBuilder.GetSymbol(node);
if (symbol is INamedTypeSymbol namedType)
if (symbol.Symbol is INamedTypeSymbol namedType)
{
var implementedSymbols = namedType.AllInterfaces;
var implementedSymbols = namedType.AllInterfaces.SelectAsArray(i => (SymbolAndProjectId)symbol.WithSymbol(i));
await AddImplementedSymbolsAsync(graphBuilder, node, implementedSymbols).ConfigureAwait(false);
}
else if (symbol is IMethodSymbol || symbol is IPropertySymbol || symbol is IEventSymbol)
else if (symbol.Symbol is IMethodSymbol ||
symbol.Symbol is IPropertySymbol ||
symbol.Symbol is IEventSymbol)
{
var implements = await SymbolFinder.FindImplementedInterfaceMembersAsync(symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
await AddImplementedSymbolsAsync(graphBuilder, node, implements).ConfigureAwait(false);
......@@ -41,7 +44,9 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
}
}
private static async Task AddImplementedSymbolsAsync(GraphBuilder graphBuilder, GraphNode node, IEnumerable<ISymbol> implementedSymbols)
private static async Task AddImplementedSymbolsAsync(
GraphBuilder graphBuilder, GraphNode node,
ImmutableArray<SymbolAndProjectId> implementedSymbols)
{
foreach (var interfaceType in implementedSymbols)
{
......
......@@ -19,29 +19,29 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
foreach (var node in context.InputNodes)
{
if (!(graphBuilder.GetSymbol(node) is INamedTypeSymbol namedType))
{
var symbolAndProjectId = graphBuilder.GetSymbol(node);
if (!(symbolAndProjectId.Symbol is INamedTypeSymbol namedType))
continue;
}
if (namedType.TypeKind == TypeKind.Class)
{
var derivedTypes = await DependentTypeFinder.FindImmediatelyDerivedClassesAsync(namedType, solution, cancellationToken).ConfigureAwait(false);
var derivedTypes = await DependentTypeFinder.FindImmediatelyDerivedClassesAsync(
symbolAndProjectId.WithSymbol(namedType), solution, cancellationToken).ConfigureAwait(false);
foreach (var derivedType in derivedTypes)
{
var symbolNode = await graphBuilder.AddNodeForSymbolAsync(
derivedType.Symbol, relatedNode: node).ConfigureAwait(false);
derivedType, relatedNode: node).ConfigureAwait(false);
graphBuilder.AddLink(symbolNode, CodeLinkCategories.InheritsFrom, node);
}
}
else if (namedType.TypeKind == TypeKind.Interface)
{
var derivedTypes = await DependentTypeFinder.FindImmediatelyDerivedAndImplementingTypesAsync(
namedType, solution, cancellationToken).ConfigureAwait(false);
symbolAndProjectId.WithSymbol(namedType), solution, cancellationToken).ConfigureAwait(false);
foreach (var derivedType in derivedTypes)
{
var symbolNode = await graphBuilder.AddNodeForSymbolAsync(
derivedType.Symbol, relatedNode: node).ConfigureAwait(false);
derivedType, relatedNode: node).ConfigureAwait(false);
graphBuilder.AddLink(symbolNode, CodeLinkCategories.InheritsFrom, node);
}
}
......
......@@ -31,12 +31,13 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
foreach (var node in nodesToProcess)
{
if (graphBuilder.GetSymbol(node) is INamedTypeSymbol namedType)
var symbolAndProjectId = graphBuilder.GetSymbol(node);
if (symbolAndProjectId.Symbol is INamedTypeSymbol namedType)
{
if (namedType.BaseType != null)
{
var baseTypeNode = await graphBuilder.AddNodeForSymbolAsync(namedType.BaseType, relatedNode: node).ConfigureAwait(false);
var baseTypeNode = await graphBuilder.AddNodeForSymbolAsync(
symbolAndProjectId.WithSymbol(namedType.BaseType), relatedNode: node).ConfigureAwait(false);
newNodes.Add(baseTypeNode);
graphBuilder.AddLink(node, CodeLinkCategories.InheritsFrom, baseTypeNode);
}
......@@ -44,7 +45,8 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
{
foreach (var baseNode in namedType.OriginalDefinition.AllInterfaces.Distinct())
{
var baseTypeNode = await graphBuilder.AddNodeForSymbolAsync(baseNode, relatedNode: node).ConfigureAwait(false);
var baseTypeNode = await graphBuilder.AddNodeForSymbolAsync(
symbolAndProjectId.WithSymbol(baseNode), relatedNode: node).ConfigureAwait(false);
newNodes.Add(baseTypeNode);
graphBuilder.AddLink(node, CodeLinkCategories.InheritsFrom, baseTypeNode);
}
......
......@@ -24,13 +24,13 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
foreach (var node in context.InputNodes)
{
var symbol = graphBuilder.GetSymbol(node);
if (symbol != null)
if (symbol.Symbol != null)
{
var callers = await SymbolFinder.FindCallersAsync(symbol, solution, cancellationToken).ConfigureAwait(false);
foreach (var caller in callers.Where(c => c.IsDirect))
{
var callerNode = await graphBuilder.AddNodeForSymbolAsync(caller.CallingSymbol, relatedNode: node).ConfigureAwait(false);
var callerNode = await graphBuilder.AddNodeForSymbolAsync(caller.CallingSymbolAndProjectId, relatedNode: node).ConfigureAwait(false);
graphBuilder.AddLink(callerNode, CodeLinkCategories.Calls, node);
}
}
......
......@@ -20,13 +20,13 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
foreach (var node in context.InputNodes)
{
var symbol = graphBuilder.GetSymbol(node);
if (symbol != null)
if (symbol.Symbol != null)
{
var overriddenMember = symbol.OverriddenMember();
var overriddenMember = symbol.Symbol.OverriddenMember();
if (overriddenMember != null)
{
var symbolNode = await graphBuilder.AddNodeForSymbolAsync(overriddenMember, relatedNode: node).ConfigureAwait(false);
var symbolNode = await graphBuilder.AddNodeForSymbolAsync(
symbol.WithSymbol(overriddenMember), relatedNode: node).ConfigureAwait(false);
graphBuilder.AddLink(node, RoslynGraphCategories.Overrides, symbolNode);
}
}
......
......@@ -22,12 +22,15 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
foreach (var node in context.InputNodes)
{
var symbol = graphBuilder.GetSymbol(node);
if (symbol is IMethodSymbol || symbol is IPropertySymbol || symbol is IEventSymbol)
if (symbol.Symbol is IMethodSymbol ||
symbol.Symbol is IPropertySymbol ||
symbol.Symbol is IEventSymbol)
{
var overrides = await SymbolFinder.FindOverridesAsync(symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
foreach (var o in overrides)
{
var symbolNode = await graphBuilder.AddNodeForSymbolAsync(o, relatedNode: node).ConfigureAwait(false);
var symbolNode = await graphBuilder.AddNodeForSymbolAsync(
symbol.WithSymbol(o.Symbol), relatedNode: node).ConfigureAwait(false);
graphBuilder.AddLink(symbolNode, RoslynGraphCategories.Overrides, node);
}
}
......
......@@ -50,25 +50,30 @@ private async Task ProcessProjectAsync(Project project, GraphBuilder graphBuilde
{
cancellationToken.ThrowIfCancellationRequested();
if (symbol is INamedTypeSymbol namedType)
if (symbol.Symbol is INamedTypeSymbol namedType)
{
await AddLinkedNodeForTypeAsync(project, namedType, graphBuilder, symbol.DeclaringSyntaxReferences.Select(d => d.SyntaxTree)).ConfigureAwait(false);
await AddLinkedNodeForTypeAsync(
project, symbol.WithSymbol(namedType), graphBuilder,
symbol.Symbol.DeclaringSyntaxReferences.Select(d => d.SyntaxTree)).ConfigureAwait(false);
}
else
{
await AddLinkedNodeForMemberAsync(project, symbol, graphBuilder).ConfigureAwait(false);
await AddLinkedNodeForMemberAsync(
project, symbol, graphBuilder).ConfigureAwait(false);
}
}
}
}
}
private async Task<GraphNode> AddLinkedNodeForTypeAsync(Project project, INamedTypeSymbol namedType, GraphBuilder graphBuilder, IEnumerable<SyntaxTree> syntaxTrees)
private async Task<GraphNode> AddLinkedNodeForTypeAsync(
Project project, SymbolAndProjectId<INamedTypeSymbol> namedType, GraphBuilder graphBuilder, IEnumerable<SyntaxTree> syntaxTrees)
{
// If this named type is contained in a parent type, then just link farther up
if (namedType.ContainingType != null)
if (namedType.Symbol.ContainingType != null)
{
var parentTypeNode = await AddLinkedNodeForTypeAsync(project, namedType.ContainingType, graphBuilder, syntaxTrees).ConfigureAwait(false);
var parentTypeNode = await AddLinkedNodeForTypeAsync(
project, namedType.WithSymbol(namedType.Symbol.ContainingType), graphBuilder, syntaxTrees).ConfigureAwait(false);
var typeNode = await graphBuilder.AddNodeForSymbolAsync(namedType, relatedNode: parentTypeNode).ConfigureAwait(false);
graphBuilder.AddLink(parentTypeNode, GraphCommonSchema.Contains, typeNode);
......@@ -92,20 +97,24 @@ private async Task<GraphNode> AddLinkedNodeForTypeAsync(Project project, INamedT
}
}
private async Task<GraphNode> AddLinkedNodeForMemberAsync(Project project, ISymbol member, GraphBuilder graphBuilder)
private async Task<GraphNode> AddLinkedNodeForMemberAsync(
Project project, SymbolAndProjectId symbolAndProjectId, GraphBuilder graphBuilder)
{
var member = symbolAndProjectId.Symbol;
Contract.ThrowIfNull(member.ContainingType);
var trees = member.DeclaringSyntaxReferences.Select(d => d.SyntaxTree);
var parentTypeNode = await AddLinkedNodeForTypeAsync(project, member.ContainingType, graphBuilder, trees).ConfigureAwait(false);
var memberNode = await graphBuilder.AddNodeForSymbolAsync(member, relatedNode: parentTypeNode).ConfigureAwait(false);
var parentTypeNode = await AddLinkedNodeForTypeAsync(
project, symbolAndProjectId.WithSymbol(member.ContainingType), graphBuilder, trees).ConfigureAwait(false);
var memberNode = await graphBuilder.AddNodeForSymbolAsync(
symbolAndProjectId, relatedNode: parentTypeNode).ConfigureAwait(false);
graphBuilder.AddLink(parentTypeNode, GraphCommonSchema.Contains, memberNode);
return memberNode;
}
internal async Task<ImmutableArray<ISymbol>> FindNavigableSourceSymbolsAsync(
internal async Task<ImmutableArray<SymbolAndProjectId>> FindNavigableSourceSymbolsAsync(
Project project, CancellationToken cancellationToken)
{
ImmutableArray<SymbolAndProjectId> declarations;
......@@ -131,14 +140,14 @@ private async Task<GraphNode> AddLinkedNodeForMemberAsync(Project project, ISymb
throw ExceptionUtilities.Unreachable;
}
var symbols = declarations.SelectAsArray(d => d.Symbol);
using var _ = ArrayBuilder<SymbolAndProjectId>.GetInstance(out var results);
var results = ArrayBuilder<ISymbol>.GetInstance();
foreach (var symbol in symbols)
foreach (var declaration in declarations)
{
cancellationToken.ThrowIfCancellationRequested();
var symbol = declaration.Symbol;
// Ignore constructors and namespaces. We don't want to expose them through this API.
if (symbol.IsConstructor() ||
symbol.IsStaticConstructor() ||
......@@ -153,7 +162,7 @@ private async Task<GraphNode> AddLinkedNodeForMemberAsync(Project project, ISymb
continue;
}
results.Add(symbol);
results.Add(declaration);
// also report matching constructors (using same match result as type)
if (symbol is INamedTypeSymbol namedType)
......@@ -163,7 +172,7 @@ private async Task<GraphNode> AddLinkedNodeForMemberAsync(Project project, ISymb
// only constructors that were explicitly declared
if (!constructor.IsImplicitlyDeclared)
{
results.Add(constructor);
results.Add(declaration.WithSymbol(constructor));
}
}
}
......@@ -171,11 +180,11 @@ private async Task<GraphNode> AddLinkedNodeForMemberAsync(Project project, ISymb
// report both parts of partial methods
if (symbol is IMethodSymbol method && method.PartialImplementationPart != null)
{
results.Add(method);
results.Add(declaration.WithSymbol(method));
}
}
return results.ToImmutableAndFree();
return results.ToImmutable();
}
}
}
......@@ -4,11 +4,14 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
......@@ -30,11 +33,11 @@ public static async Task<IEnumerable<SyntaxNode>> GetContainedSyntaxNodesAsync(D
return progressionLanguageService.GetTopLevelNodesFromDocument(root, cancellationToken);
}
public static async Task<IEnumerable<ISymbol>> GetContainedSymbolsAsync(Document document, CancellationToken cancellationToken)
public static async Task<ImmutableArray<SymbolAndProjectId>> GetContainedSymbolsAsync(Document document, CancellationToken cancellationToken)
{
var syntaxNodes = await GetContainedSyntaxNodesAsync(document, cancellationToken).ConfigureAwait(false);
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var symbols = new List<ISymbol>();
using var _ = ArrayBuilder<SymbolAndProjectId>.GetInstance(out var symbols);
foreach (var syntaxNode in syntaxNodes)
{
......@@ -45,11 +48,11 @@ public static async Task<IEnumerable<ISymbol>> GetContainedSymbolsAsync(Document
!string.IsNullOrEmpty(symbol.Name) &&
IsTopLevelSymbol(symbol))
{
symbols.Add(symbol);
symbols.Add(new SymbolAndProjectId(symbol, document.Project.Id));
}
}
return symbols;
return symbols.ToImmutable();
}
private static bool IsTopLevelSymbol(ISymbol symbol)
......@@ -68,10 +71,9 @@ private static bool IsTopLevelSymbol(ISymbol symbol)
}
}
public static IEnumerable<ISymbol> GetContainedSymbols(ISymbol symbol)
public static IEnumerable<SymbolAndProjectId> GetContainedSymbols(SymbolAndProjectId symbolAndProjectId)
{
if (symbol is INamedTypeSymbol namedType)
if (symbolAndProjectId.Symbol is INamedTypeSymbol namedType)
{
foreach (var member in namedType.GetMembers())
{
......@@ -80,7 +82,6 @@ public static IEnumerable<ISymbol> GetContainedSymbols(ISymbol symbol)
continue;
}
if (member is IMethodSymbol method && method.AssociatedSymbol != null)
{
continue;
......@@ -88,7 +89,7 @@ public static IEnumerable<ISymbol> GetContainedSymbols(ISymbol symbol)
if (!string.IsNullOrEmpty(member.Name))
{
yield return member;
yield return symbolAndProjectId.WithSymbol(member);
}
}
}
......
......@@ -56,7 +56,7 @@ internal static partial class DependentTypeFinder
private static readonly RelatedTypeCache s_typeToImmediatelyDerivedAndImplementingTypesMap = new RelatedTypeCache();
private static async Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindTypesFromCacheOrComputeAsync(
INamedTypeSymbol type,
SymbolAndProjectId<INamedTypeSymbol> type,
Solution solution,
IImmutableSet<Project> projects,
RelatedTypeCache cache,
......@@ -69,7 +69,7 @@ internal static partial class DependentTypeFinder
// Do a quick lookup first to avoid the allocation. If it fails, go through the
// slower allocating path.
var key = (type.GetSymbolKey(), projects);
var key = (type.Symbol.GetSymbolKey(), projects);
if (!dictionary.TryGetValue(key, out var lazy))
{
lazy = dictionary.GetOrAdd(key,
......@@ -134,15 +134,14 @@ internal static partial class DependentTypeFinder
/// Used for implementing the Inherited-By relation for progression.
/// </summary>
public static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindImmediatelyDerivedClassesAsync(
INamedTypeSymbol type,
SymbolAndProjectId<INamedTypeSymbol> type,
Solution solution,
CancellationToken cancellationToken)
{
return FindTypesFromCacheOrComputeAsync(
type, solution, projects: null,
cache: s_typeToImmediatelyDerivedClassesMap,
findAsync: c => FindDerivedClassesAsync(
SymbolAndProjectId.Create(type, projectId: null), solution, projects: null,
findAsync: c => FindDerivedClassesAsync(type, solution, projects: null,
transitive: false, cancellationToken: c),
cancellationToken: cancellationToken);
}
......@@ -151,16 +150,15 @@ internal static partial class DependentTypeFinder
/// This is an internal implementation of <see cref="SymbolFinder.FindDerivedClassesAsync(SymbolAndProjectId{INamedTypeSymbol}, Solution, IImmutableSet{Project}, CancellationToken)"/>, which is a publically callable method.
/// </summary>
public static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindTransitivelyDerivedClassesAsync(
INamedTypeSymbol type,
SymbolAndProjectId<INamedTypeSymbol> type,
Solution solution,
IImmutableSet<Project> projects,
CancellationToken cancellationToken)
{
return FindTypesFromCacheOrComputeAsync(
type, solution, projects, s_typeToTransitivelyDerivedClassesMap,
c => FindDerivedClassesAsync(
SymbolAndProjectId.Create(type, projectId: null), solution, projects,
transitive: true, cancellationToken: c),
c => FindDerivedClassesAsync(type, solution, projects,
transitive: true, c),
cancellationToken);
}
......@@ -195,7 +193,7 @@ bool sourceTypeImmediatelyMatches(SymbolAndProjectIdSet set, INamedTypeSymbol me
/// <see cref="INamedTypeSymbol"/>s
/// </summary>
public static async Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindTransitivelyImplementingStructuresAndClassesAsync(
INamedTypeSymbol type,
SymbolAndProjectId<INamedTypeSymbol> type,
Solution solution,
IImmutableSet<Project> projects,
CancellationToken cancellationToken)
......@@ -212,7 +210,7 @@ bool sourceTypeImmediatelyMatches(SymbolAndProjectIdSet set, INamedTypeSymbol me
/// <see cref="INamedTypeSymbol"/>s
/// </summary>
public static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindTransitivelyImplementingStructuresClassesAndInterfacesAsync(
INamedTypeSymbol type,
SymbolAndProjectId<INamedTypeSymbol> type,
Solution solution,
IImmutableSet<Project> projects,
CancellationToken cancellationToken)
......@@ -224,21 +222,20 @@ bool sourceTypeImmediatelyMatches(SymbolAndProjectIdSet set, INamedTypeSymbol me
}
private static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindTransitivelyImplementingStructuresClassesAndInterfacesWorkerAsync(
INamedTypeSymbol type,
SymbolAndProjectId<INamedTypeSymbol> type,
Solution solution,
IImmutableSet<Project> projects,
CancellationToken cancellationToken)
{
return FindDerivedAndImplementingTypesAsync(
SymbolAndProjectId.Create(type, projectId: null), solution, projects,
transitive: true, cancellationToken: cancellationToken);
type, solution, projects, transitive: true, cancellationToken);
}
/// <summary>
/// Used for implementing the Inherited-By relation for progression.
/// </summary>
public static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindImmediatelyDerivedAndImplementingTypesAsync(
INamedTypeSymbol type,
SymbolAndProjectId<INamedTypeSymbol> type,
Solution solution,
CancellationToken cancellationToken)
{
......@@ -246,8 +243,7 @@ bool sourceTypeImmediatelyMatches(SymbolAndProjectIdSet set, INamedTypeSymbol me
type, solution, projects: null,
cache: s_typeToImmediatelyDerivedAndImplementingTypesMap,
findAsync: c => FindDerivedAndImplementingTypesAsync(
SymbolAndProjectId.Create(type, projectId: null), solution, projects: null,
transitive: false, cancellationToken: c),
type, solution, projects: null, transitive: false, c),
cancellationToken: cancellationToken);
}
......
......@@ -13,13 +13,13 @@ namespace Microsoft.CodeAnalysis.FindSymbols
{
internal static class ReferenceLocationExtensions
{
public static async Task<Dictionary<ISymbol, List<Location>>> FindReferencingSymbolsAsync(
public static async Task<Dictionary<SymbolAndProjectId, List<Location>>> FindReferencingSymbolsAsync(
this IEnumerable<ReferenceLocation> referenceLocations,
CancellationToken cancellationToken)
{
var documentGroups = referenceLocations.GroupBy(loc => loc.Document);
var projectGroups = documentGroups.GroupBy(g => g.Key.Project);
var result = new Dictionary<ISymbol, List<Location>>();
var result = new Dictionary<SymbolAndProjectId, List<Location>>();
foreach (var projectGroup in projectGroups)
{
......@@ -48,17 +48,18 @@ internal static class ReferenceLocationExtensions
Document document,
SemanticModel semanticModel,
IEnumerable<ReferenceLocation> references,
Dictionary<ISymbol, List<Location>> result)
Dictionary<SymbolAndProjectId, List<Location>> result)
{
foreach (var reference in references)
{
var containingSymbol = GetEnclosingMethodOrPropertyOrField(semanticModel, reference);
if (containingSymbol != null)
{
if (!result.TryGetValue(containingSymbol, out var locations))
var symbolAndProjectId = new SymbolAndProjectId(containingSymbol, document.Project.Id);
if (!result.TryGetValue(symbolAndProjectId, out var locations))
{
locations = new List<Location>();
result.Add(containingSymbol, locations);
result.Add(symbolAndProjectId, locations);
}
locations.Add(reference.Location);
......
......@@ -20,7 +20,9 @@ public struct SymbolCallerInfo
/// <summary>
/// The symbol that is calling the symbol being called.
/// </summary>
public ISymbol CallingSymbol { get; }
public ISymbol CallingSymbol => CallingSymbolAndProjectId.Symbol;
internal SymbolAndProjectId CallingSymbolAndProjectId { get; }
/// <summary>
/// The locations inside the calling symbol where the called symbol is referenced.
......@@ -30,7 +32,9 @@ public struct SymbolCallerInfo
/// <summary>
/// The symbol being called.
/// </summary>
public ISymbol CalledSymbol { get; }
public ISymbol CalledSymbol => CalledSymbolAndProjectId.Symbol;
internal SymbolAndProjectId CalledSymbolAndProjectId { get; }
/// <summary>
/// True if the CallingSymbol is directly calling CalledSymbol. False if it is calling a
......@@ -40,11 +44,14 @@ public struct SymbolCallerInfo
/// </summary>
public bool IsDirect { get; }
internal SymbolCallerInfo(ISymbol callingSymbol, ISymbol calledSymbol, IEnumerable<Location> locations, bool isDirect)
: this()
internal SymbolCallerInfo(
SymbolAndProjectId callingSymbol,
SymbolAndProjectId calledSymbol,
IEnumerable<Location> locations,
bool isDirect)
{
this.CallingSymbol = callingSymbol;
this.CalledSymbol = calledSymbol;
CallingSymbolAndProjectId = callingSymbol;
CalledSymbolAndProjectId = calledSymbol;
this.IsDirect = isDirect;
this.Locations = locations;
}
......
......@@ -19,19 +19,6 @@ namespace Microsoft.CodeAnalysis.FindSymbols
{
public static partial class SymbolFinder
{
/// <summary>
/// Find symbols for members that override the specified member symbol.
/// </summary>
public static async Task<IEnumerable<ISymbol>> FindOverridesAsync(
ISymbol symbol, Solution solution, IImmutableSet<Project> projects = null, CancellationToken cancellationToken = default)
{
var result = await FindOverridesAsync(
SymbolAndProjectId.Create(symbol, projectId: null),
solution, projects, cancellationToken).ConfigureAwait(false);
return result.SelectAsArray(s => s.Symbol);
}
internal static async Task<ImmutableArray<SymbolAndProjectId>> FindOverridesAsync(
SymbolAndProjectId symbolAndProjectId, Solution solution, IImmutableSet<Project> projects = null, CancellationToken cancellationToken = default)
{
......@@ -79,18 +66,6 @@ public static partial class SymbolFinder
return false;
}
/// <summary>
/// Find symbols for declarations that implement members of the specified interface symbol
/// </summary>
public static async Task<IEnumerable<ISymbol>> FindImplementedInterfaceMembersAsync(
ISymbol symbol, Solution solution, IImmutableSet<Project> projects = null, CancellationToken cancellationToken = default)
{
var result = await FindImplementedInterfaceMembersAsync(
SymbolAndProjectId.Create(symbol, projectId: null),
solution, projects, cancellationToken).ConfigureAwait(false);
return result.SelectAsArray(s => s.Symbol);
}
internal static async Task<ImmutableArray<SymbolAndProjectId>> FindImplementedInterfaceMembersAsync(
SymbolAndProjectId symbolAndProjectId, Solution solution, IImmutableSet<Project> projects = null, CancellationToken cancellationToken = default)
{
......@@ -180,51 +155,17 @@ public static partial class SymbolFinder
return type.Symbol.AllInterfaces.Select(type.WithSymbol);
}
/// <summary>
/// Finds the derived classes of the given type. Implementations of an interface are not considered "derived", but can be found
/// with <see cref="FindImplementationsAsync(ISymbol, Solution, IImmutableSet{Project}, CancellationToken)"/>.
/// </summary>
/// <param name="type">The symbol to find derived types of.</param>
/// <param name="solution">The solution to search in.</param>
/// <param name="projects">The projects to search. Can be null to search the entire solution.</param>
/// <param name="cancellationToken"></param>
/// <returns>The derived types of the symbol. The symbol passed in is not included in this list.</returns>
public static async Task<IEnumerable<INamedTypeSymbol>> FindDerivedClassesAsync(
INamedTypeSymbol type, Solution solution, IImmutableSet<Project> projects = null, CancellationToken cancellationToken = default)
{
var result = await FindDerivedClassesAsync(
SymbolAndProjectId.Create(type, projectId: null),
solution, projects, cancellationToken).ConfigureAwait(false);
return result.SelectAsArray(s => s.Symbol);
}
internal static Task<ImmutableArray<SymbolAndProjectId<INamedTypeSymbol>>> FindDerivedClassesAsync(
SymbolAndProjectId<INamedTypeSymbol> typeAndProjectId, Solution solution, IImmutableSet<Project> projects = null, CancellationToken cancellationToken = default)
{
var type = typeAndProjectId.Symbol;
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
if (solution == null)
{
throw new ArgumentNullException(nameof(solution));
}
return DependentTypeFinder.FindTransitivelyDerivedClassesAsync(type, solution, projects, cancellationToken);
}
/// <summary>
/// Finds the symbols that implement an interface or interface member.
/// </summary>
public static async Task<IEnumerable<ISymbol>> FindImplementationsAsync(
ISymbol symbol, Solution solution, IImmutableSet<Project> projects = null, CancellationToken cancellationToken = default)
{
var result = await FindImplementationsAsync(
SymbolAndProjectId.Create(symbol, projectId: null),
solution, projects, cancellationToken).ConfigureAwait(false);
return result.SelectAsArray(s => s.Symbol);
return DependentTypeFinder.FindTransitivelyDerivedClassesAsync(typeAndProjectId, solution, projects, cancellationToken);
}
internal static async Task<ImmutableArray<SymbolAndProjectId>> FindImplementationsAsync(
......@@ -235,7 +176,8 @@ public static partial class SymbolFinder
var symbol = symbolAndProjectId.Symbol;
if (symbol is INamedTypeSymbol namedTypeSymbol)
{
var implementingTypes = await DependentTypeFinder.FindTransitivelyImplementingStructuresAndClassesAsync(namedTypeSymbol, solution, projects, cancellationToken).ConfigureAwait(false);
var implementingTypes = await DependentTypeFinder.FindTransitivelyImplementingStructuresAndClassesAsync(
symbolAndProjectId.WithSymbol(namedTypeSymbol), solution, projects, cancellationToken).ConfigureAwait(false);
return implementingTypes.Select(s => (SymbolAndProjectId)s)
.Where(IsAccessible)
.ToImmutableArray();
......@@ -243,7 +185,8 @@ public static partial class SymbolFinder
else if (symbol.IsImplementableMember())
{
var containingType = symbol.ContainingType.OriginalDefinition;
var allTypes = await DependentTypeFinder.FindTransitivelyImplementingStructuresClassesAndInterfacesAsync(containingType, solution, projects, cancellationToken).ConfigureAwait(false);
var allTypes = await DependentTypeFinder.FindTransitivelyImplementingStructuresClassesAndInterfacesAsync(
symbolAndProjectId.WithSymbol(containingType), solution, projects, cancellationToken).ConfigureAwait(false);
ImmutableArray<SymbolAndProjectId>.Builder results = null;
foreach (var t in allTypes.Convert<INamedTypeSymbol, ITypeSymbol>())
......@@ -288,25 +231,26 @@ private static bool IsAccessible(SymbolAndProjectId symbolAndProjectId)
/// <summary>
/// Finds all the callers of a specified symbol.
/// </summary>
public static Task<IEnumerable<SymbolCallerInfo>> FindCallersAsync(
ISymbol symbol, Solution solution, CancellationToken cancellationToken = default)
internal static Task<IEnumerable<SymbolCallerInfo>> FindCallersAsync(
SymbolAndProjectId symbolAndProjectId, Solution solution, CancellationToken cancellationToken = default)
{
return FindCallersAsync(symbol, solution, documents: null, cancellationToken: cancellationToken);
return FindCallersAsync(symbolAndProjectId, solution, documents: null, cancellationToken: cancellationToken);
}
/// <summary>
/// Finds all the callers of a specified symbol.
/// </summary>
public static async Task<IEnumerable<SymbolCallerInfo>> FindCallersAsync(ISymbol symbol, Solution solution, IImmutableSet<Document> documents, CancellationToken cancellationToken = default)
internal static async Task<IEnumerable<SymbolCallerInfo>> FindCallersAsync(
SymbolAndProjectId symbolAndProjectId, Solution solution, IImmutableSet<Document> documents, CancellationToken cancellationToken = default)
{
symbol = symbol.OriginalDefinition;
var foundSymbol = await FindSourceDefinitionAsync(symbol, solution, cancellationToken).ConfigureAwait(false);
symbol = foundSymbol ?? symbol;
symbolAndProjectId = symbolAndProjectId.WithSymbol(symbolAndProjectId.Symbol.OriginalDefinition);
var foundSymbol = await FindSourceDefinitionAsync(symbolAndProjectId, solution, cancellationToken).ConfigureAwait(false);
symbolAndProjectId = foundSymbol.Symbol != null ? foundSymbol : symbolAndProjectId;
var references = await FindCallReferencesAsync(solution, symbol, documents, cancellationToken).ConfigureAwait(false);
var references = await FindCallReferencesAsync(solution, symbolAndProjectId, documents, cancellationToken).ConfigureAwait(false);
var directReference = references.Where(
r => SymbolEquivalenceComparer.Instance.Equals(symbol, r.Definition)).FirstOrDefault();
r => SymbolEquivalenceComparer.Instance.Equals(symbolAndProjectId.Symbol, r.Definition)).FirstOrDefault();
var indirectReferences = references.WhereAsArray(r => r != directReference);
......@@ -329,26 +273,29 @@ async Task AddReferencingSymbols(ReferencedSymbol reference, bool isDirect)
var result = await reference.Locations.FindReferencingSymbolsAsync(cancellationToken).ConfigureAwait(false);
foreach (var (callingSymbol, locations) in result)
{
results.Add(new SymbolCallerInfo(callingSymbol, reference.Definition, locations, isDirect));
results.Add(new SymbolCallerInfo(callingSymbol, reference.DefinitionAndProjectId, locations, isDirect));
}
}
}
private static async Task<ImmutableArray<ReferencedSymbol>> FindCallReferencesAsync(
Solution solution,
ISymbol symbol,
SymbolAndProjectId symbolAndProjectId,
IImmutableSet<Document> documents,
CancellationToken cancellationToken = default)
{
var symbol = symbolAndProjectId.Symbol;
if (symbol != null)
{
if (symbol.Kind == SymbolKind.Event ||
symbol.Kind == SymbolKind.Method ||
symbol.Kind == SymbolKind.Property)
{
var result = await FindReferencesAsync(
symbol, solution, documents, cancellationToken).ConfigureAwait(false);
return result.ToImmutableArray();
var collector = new StreamingProgressCollector();
await FindReferencesAsync(
symbolAndProjectId, solution, collector, documents,
FindReferencesSearchOptions.Default, cancellationToken).ConfigureAwait(false);
return collector.GetReferencedSymbols();
}
}
......
// 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.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;
using System.Diagnostics;
namespace Microsoft.CodeAnalysis.FindSymbols
{
public static partial class SymbolFinder
{
/// <summary>
/// Find symbols for members that override the specified member symbol.
/// </summary>
public static async Task<IEnumerable<ISymbol>> FindOverridesAsync(
ISymbol symbol, Solution solution, IImmutableSet<Project> projects = null, CancellationToken cancellationToken = default)
{
var result = await FindOverridesAsync(
SymbolAndProjectId.Create(symbol, projectId: null),
solution, projects, cancellationToken).ConfigureAwait(false);
return result.SelectAsArray(s => s.Symbol);
}
/// <summary>
/// Find symbols for declarations that implement members of the specified interface symbol
/// </summary>
public static async Task<IEnumerable<ISymbol>> FindImplementedInterfaceMembersAsync(
ISymbol symbol, Solution solution, IImmutableSet<Project> projects = null, CancellationToken cancellationToken = default)
{
var result = await FindImplementedInterfaceMembersAsync(
SymbolAndProjectId.Create(symbol, projectId: null),
solution, projects, cancellationToken).ConfigureAwait(false);
return result.SelectAsArray(s => s.Symbol);
}
/// <summary>
/// Finds the derived classes of the given type. Implementations of an interface are not considered "derived", but can be found
/// with <see cref="FindImplementationsAsync(ISymbol, Solution, IImmutableSet{Project}, CancellationToken)"/>.
/// </summary>
/// <param name="type">The symbol to find derived types of.</param>
/// <param name="solution">The solution to search in.</param>
/// <param name="projects">The projects to search. Can be null to search the entire solution.</param>
/// <param name="cancellationToken"></param>
/// <returns>The derived types of the symbol. The symbol passed in is not included in this list.</returns>
public static async Task<IEnumerable<INamedTypeSymbol>> FindDerivedClassesAsync(
INamedTypeSymbol type, Solution solution, IImmutableSet<Project> projects = null, CancellationToken cancellationToken = default)
{
var result = await FindDerivedClassesAsync(
SymbolAndProjectId.Create(type, projectId: null),
solution, projects, cancellationToken).ConfigureAwait(false);
return result.SelectAsArray(s => s.Symbol);
}
/// <summary>
/// Finds the symbols that implement an interface or interface member.
/// </summary>
public static async Task<IEnumerable<ISymbol>> FindImplementationsAsync(
ISymbol symbol, Solution solution, IImmutableSet<Project> projects = null, CancellationToken cancellationToken = default)
{
var result = await FindImplementationsAsync(
SymbolAndProjectId.Create(symbol, projectId: null),
solution, projects, cancellationToken).ConfigureAwait(false);
return result.SelectAsArray(s => s.Symbol);
}
/// <summary>
/// Finds all the callers of a specified symbol.
/// </summary>
public static Task<IEnumerable<SymbolCallerInfo>> FindCallersAsync(
ISymbol symbol, Solution solution, CancellationToken cancellationToken = default)
{
return FindCallersAsync(
SymbolAndProjectId.Create(symbol, projectId: null),
solution, documents: null, cancellationToken);
}
/// <summary>
/// Finds all the callers of a specified symbol.
/// </summary>
public static Task<IEnumerable<SymbolCallerInfo>> FindCallersAsync(
ISymbol symbol, Solution solution, IImmutableSet<Document> documents, CancellationToken cancellationToken = default)
{
return FindCallersAsync(
SymbolAndProjectId.Create(symbol, projectId: null),
solution, documents, cancellationToken);
}
}
}
// 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.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.FindSymbols;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Shared.Extensions
{
internal partial class ITypeSymbolExtensions
{
private class ReplaceTypeParameterBasedOnTypeConstraintVisitor : AsyncSymbolVisitor<ITypeSymbol>
{
private readonly CancellationToken _cancellationToken;
private readonly Compilation _compilation;
private readonly ISet<string> _availableTypeParameterNames;
private readonly Solution _solution;
public ReplaceTypeParameterBasedOnTypeConstraintVisitor(Compilation compilation, ISet<string> availableTypeParameterNames, Solution solution, CancellationToken cancellationToken)
{
_compilation = compilation;
_availableTypeParameterNames = availableTypeParameterNames;
_solution = solution;
_cancellationToken = cancellationToken;
}
protected override ITypeSymbol DefaultResult => throw new NotImplementedException();
public override ValueTask<ITypeSymbol> VisitDynamicType(IDynamicTypeSymbol symbol)
=> new ValueTask<ITypeSymbol>(symbol);
public override async ValueTask<ITypeSymbol> VisitArrayType(IArrayTypeSymbol symbol)
{
var elementType = await symbol.ElementType.Accept(this).ConfigureAwait(false);
if (elementType != null && elementType.Equals(symbol.ElementType))
{
return symbol;
}
return _compilation.CreateArrayTypeSymbol(elementType, symbol.Rank);
}
public override async ValueTask<ITypeSymbol> VisitNamedType(INamedTypeSymbol symbol)
{
var arguments = await SpecializedTasks.WhenAll(symbol.TypeArguments.Select(t => t.Accept(this))).ConfigureAwait(false);
if (arguments.SequenceEqual(symbol.TypeArguments))
{
return symbol;
}
return symbol.ConstructedFrom.Construct(arguments.ToArray());
}
public override async ValueTask<ITypeSymbol> VisitPointerType(IPointerTypeSymbol symbol)
{
var elementType = await symbol.PointedAtType.Accept(this).ConfigureAwait(false);
if (elementType != null && elementType.Equals(symbol.PointedAtType))
{
return symbol;
}
return _compilation.CreatePointerTypeSymbol(elementType);
}
public override async ValueTask<ITypeSymbol> VisitTypeParameter(ITypeParameterSymbol symbol)
{
if (_availableTypeParameterNames.Contains(symbol.Name))
{
return symbol;
}
switch (symbol.ConstraintTypes.Length)
{
case 0:
// If there are no constraint then there is no replacement required
// Just return the symbol
return symbol;
case 1:
// If there is one constraint which is a INamedTypeSymbol then return the INamedTypeSymbol
// because the TypeParameter is expected to be of that type
// else return the original symbol
return symbol.ConstraintTypes.ElementAt(0) as INamedTypeSymbol ?? (ITypeSymbol)symbol;
// More than one
default:
if (symbol.ConstraintTypes.All(t => t is INamedTypeSymbol))
{
var immutableProjects = _solution.Projects.ToImmutableHashSet();
var derivedImplementedTypesOfEachConstraintType = await Task.WhenAll(symbol.ConstraintTypes.Select(async ct =>
{
var derivedAndImplementedTypes = new List<INamedTypeSymbol>();
var derivedClasses = await SymbolFinder.FindDerivedClassesAsync((INamedTypeSymbol)ct, _solution, immutableProjects, _cancellationToken).ConfigureAwait(false);
var implementedTypes = await DependentTypeFinder.FindTransitivelyImplementingStructuresAndClassesAsync((INamedTypeSymbol)ct, _solution, immutableProjects, _cancellationToken).ConfigureAwait(false);
return derivedClasses.Concat(implementedTypes.Select(s => s.Symbol)).ToList();
})).ConfigureAwait(false);
var intersectingTypes = derivedImplementedTypesOfEachConstraintType.Aggregate((x, y) => x.Intersect(y).ToList());
// If there was any intersecting derived type among the constraint types then pick the first of the lot.
if (intersectingTypes.Any())
{
var resultantIntersectingType = intersectingTypes.First();
// If the resultant intersecting type contains any Type arguments that could be replaced
// using the type constraints then recursively update the type until all constraints are appropriately handled
var typeConstraintConvertedType = await resultantIntersectingType.Accept(this).ConfigureAwait(false);
var knownSimilarTypesInCompilation = SymbolFinder.FindSimilarSymbols(typeConstraintConvertedType, _compilation, _cancellationToken);
if (knownSimilarTypesInCompilation.Any())
{
return knownSimilarTypesInCompilation.First();
}
var resultantSimilarKnownTypes = SymbolFinder.FindSimilarSymbols(resultantIntersectingType, _compilation, _cancellationToken);
return resultantSimilarKnownTypes.FirstOrDefault() ?? (ITypeSymbol)symbol;
}
}
return symbol;
}
}
}
}
}
......@@ -230,20 +230,6 @@ where SymbolEquivalenceComparer.Instance.Equals(explicitInterfaceMethod, constru
return type?.Accept(new AnonymousTypeRemover(compilation));
}
[return: NotNullIfNotNull(parameterName: "type")]
public static ValueTask<ITypeSymbol?> ReplaceTypeParametersBasedOnTypeConstraintsAsync(
this ITypeSymbol? type,
Compilation compilation,
IEnumerable<ITypeParameterSymbol> availableTypeParameters,
Solution solution,
CancellationToken cancellationToken)
{
#pragma warning disable CA2012 // Use ValueTasks correctly (https://github.com/dotnet/roslyn-analyzers/issues/3384)
return type?.Accept(new ReplaceTypeParameterBasedOnTypeConstraintVisitor(compilation, availableTypeParameters.Select(t => t.Name).ToSet(), solution, cancellationToken))
#pragma warning restore CA2012 // Use ValueTasks correctly
?? new ValueTask<ITypeSymbol?>((ITypeSymbol?)null);
}
[return: NotNullIfNotNull(parameterName: "type")]
public static ITypeSymbol? RemoveUnnamedErrorTypes(
this ITypeSymbol? type,
......
......@@ -28,10 +28,10 @@ public static IEnumerable<INamedTypeSymbol> GetBaseTypesAndThis(this INamedTypeS
}
}
public static IEnumerable<ITypeParameterSymbol> GetAllTypeParameters(this INamedTypeSymbol? symbol)
public static ImmutableArray<ITypeParameterSymbol> GetAllTypeParameters(this INamedTypeSymbol? symbol)
{
var stack = GetContainmentStack(symbol);
return stack.SelectMany(n => n.TypeParameters);
return stack.SelectMany(n => n.TypeParameters).ToImmutableArray();
}
public static IEnumerable<ITypeSymbol> GetAllTypeArguments(this INamedTypeSymbol? symbol)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册