public static async Task <DefinitionItem?> GetDefinitionAsync(Solution solution, StackFrameCompilationUnit compilationUnit, StackFrameSymbolPart symbolPart, CancellationToken cancellationToken) { // MemberAccessExpression is [Expression].[Identifier], and Identifier is the // method name. var typeExpression = compilationUnit.MethodDeclaration.MemberAccessExpression.Left; // typeExpression.ToString() returns the full expression (or identifier) // including arity for generic types. var fullyQualifiedTypeName = typeExpression.ToString(); var typeName = typeExpression is StackFrameQualifiedNameNode qualifiedName ? qualifiedName.Right.ToString() : typeExpression.ToString(); RoslynDebug.AssertNotNull(fullyQualifiedTypeName); var methodNode = compilationUnit.MethodDeclaration.MemberAccessExpression.Right; var methodTypeArguments = compilationUnit.MethodDeclaration.TypeArguments; var methodArguments = compilationUnit.MethodDeclaration.ArgumentList; // // Do a first pass to find projects with the type name to check first // using var _ = PooledObjects.ArrayBuilder <Project> .GetInstance(out var candidateProjects); foreach (var project in solution.Projects) { if (!project.SupportsCompilation) { continue; } var containsSymbol = await project.ContainsSymbolsWithNameAsync( typeName, SymbolFilter.Type, cancellationToken).ConfigureAwait(false); if (containsSymbol) { var method = await TryGetBestMatchAsync(project, fullyQualifiedTypeName, methodNode, methodArguments, methodTypeArguments, cancellationToken).ConfigureAwait(false); if (method is not null) { return(GetDefinition(method)); } } else { candidateProjects.Add(project); } } // // Do a second pass to check the remaining compilations // for the symbol, which may be a metadata symbol in the compilation // foreach (var project in candidateProjects) { var method = await TryGetBestMatchAsync(project, fullyQualifiedTypeName, methodNode, methodArguments, methodTypeArguments, cancellationToken).ConfigureAwait(false); if (method is not null) { return(GetDefinition(method)); } } return(null); // // Local Functions // DefinitionItem GetDefinition(IMethodSymbol method) { ISymbol symbol = method; if (symbolPart == StackFrameSymbolPart.ContainingType) { symbol = method.ContainingType; } return(symbol.ToNonClassifiedDefinitionItem( solution, FindReferencesSearchOptions.Default with { UnidirectionalHierarchyCascade = true }, includeHiddenLocations: true)); }
public static async Task <DefinitionItem?> GetDefinitionAsync(Solution solution, StackFrameCompilationUnit compilationUnit, StackFrameSymbolPart symbolPart, CancellationToken cancellationToken) { // MemberAccessExpression is [Expression].[Identifier], and Identifier is the // method name. var typeExpression = compilationUnit.MethodDeclaration.MemberAccessExpression.Left; // typeExpression.ToString() returns the full expression (or identifier) // including arity for generic types. var fullyQualifiedTypeName = typeExpression.ToString(); var typeName = typeExpression is StackFrameQualifiedNameNode qualifiedName ? qualifiedName.Right.ToString() : typeExpression.ToString(); RoslynDebug.AssertNotNull(fullyQualifiedTypeName); var methodIdentifier = compilationUnit.MethodDeclaration.MemberAccessExpression.Right; var methodTypeArguments = compilationUnit.MethodDeclaration.TypeArguments; var methodArguments = compilationUnit.MethodDeclaration.ArgumentList; var methodName = methodIdentifier.ToString(); // // Do a first pass to find projects with the type name to check first // using var _ = PooledObjects.ArrayBuilder <Project> .GetInstance(out var candidateProjects); foreach (var project in solution.Projects) { if (!project.SupportsCompilation) { continue; } var containsSymbol = await project.ContainsSymbolsWithNameAsync( typeName, SymbolFilter.Type, cancellationToken).ConfigureAwait(false); if (containsSymbol) { var matchingMethods = await GetMatchingMembersFromCompilationAsync(project).ConfigureAwait(false); if (matchingMethods.Any()) { return(await GetDefinitionAsync(matchingMethods[0]).ConfigureAwait(false)); } } else { candidateProjects.Add(project); } } // // Do a second pass to check the remaining compilations // for the symbol, which may be a metadata symbol in the compilation // foreach (var project in candidateProjects) { var matchingMethods = await GetMatchingMembersFromCompilationAsync(project).ConfigureAwait(false); if (matchingMethods.Any()) { return(await GetDefinitionAsync(matchingMethods[0]).ConfigureAwait(false)); } } return(null); // // Local Functions // async Task <ImmutableArray <IMethodSymbol> > GetMatchingMembersFromCompilationAsync(Project project) { var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); var type = compilation.GetTypeByMetadataName(fullyQualifiedTypeName); if (type is null) { return(ImmutableArray <IMethodSymbol> .Empty); } var members = type.GetMembers(); return(members .OfType <IMethodSymbol>() .Where(m => m.Name == methodName) .Where(m => MatchTypeArguments(m.TypeArguments, methodTypeArguments)) .Where(m => MatchParameters(m.Parameters, methodArguments)) .ToImmutableArrayOrEmpty()); } Task <DefinitionItem> GetDefinitionAsync(IMethodSymbol method) { ISymbol symbol = method; if (symbolPart == StackFrameSymbolPart.ContainingType) { symbol = method.ContainingType; } return(symbol.ToNonClassifiedDefinitionItemAsync(solution, includeHiddenLocations: true, cancellationToken)); } }