File: FindSymbols\ReferenceLocationExtensions.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Shared.Extensions;
 
namespace Microsoft.CodeAnalysis.FindSymbols;
 
internal static class ReferenceLocationExtensions
{
    public static async Task<Dictionary<ISymbol, 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>>();
 
        foreach (var projectGroup in projectGroups)
        {
            cancellationToken.ThrowIfCancellationRequested();
 
            var project = projectGroup.Key;
            if (project.SupportsCompilation)
            {
                var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
 
                foreach (var documentGroup in projectGroup)
                {
                    var document = documentGroup.Key;
                    var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
                    AddSymbols(semanticModel, documentGroup, result);
                }
 
                // Keep compilation alive so that GetSemanticModelAsync remains cheap
                GC.KeepAlive(compilation);
            }
        }
 
        return result;
    }
 
    private static void AddSymbols(
        SemanticModel semanticModel,
        IEnumerable<ReferenceLocation> references,
        Dictionary<ISymbol, List<Location>> result)
    {
        foreach (var reference in references)
        {
            var containingSymbol = GetEnclosingMethodOrPropertyOrField(semanticModel, reference);
            if (containingSymbol != null)
            {
                if (!result.TryGetValue(containingSymbol, out var locations))
                {
                    locations = [];
                    result.Add(containingSymbol, locations);
                }
 
                locations.Add(reference.Location);
            }
        }
    }
 
    private static ISymbol? GetEnclosingMethodOrPropertyOrField(
        SemanticModel semanticModel,
        ReferenceLocation reference)
    {
        var enclosingSymbol = semanticModel.GetEnclosingSymbol(reference.Location.SourceSpan.Start);
 
        for (var current = enclosingSymbol; current != null; current = current.ContainingSymbol)
        {
            if (current.Kind == SymbolKind.Field)
            {
                return current;
            }
 
            if (current.Kind == SymbolKind.Property)
            {
                return current;
            }
 
            if (current.Kind == SymbolKind.Method)
            {
                var method = (IMethodSymbol)current;
                if (method.IsAccessor())
                {
                    return method.AssociatedSymbol;
                }
 
                if (method.MethodKind != MethodKind.AnonymousFunction)
                {
                    return method;
                }
            }
        }
 
        return null;
    }
}