File: Debugging\AbstractDataTipInfoGetter.cs
Web Access
Project: src\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// 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.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
 
namespace Microsoft.CodeAnalysis.Debugging;
 
internal abstract class AbstractDataTipInfoGetter<
    TExpressionSyntax,
    TMemberExpressionSyntax,
    TInvocationExpressionSyntax>
    where TExpressionSyntax : SyntaxNode
    where TMemberExpressionSyntax : TExpressionSyntax
    where TInvocationExpressionSyntax : TExpressionSyntax
{
    protected static async ValueTask<(DebugDataTipInfoKind kind, TextSpan? expressionSpan)> ComputeKindAsync(
        Document document, TExpressionSyntax expression, bool includeKind, CancellationToken cancellationToken)
    {
        if (includeKind)
        {
            var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();
            var semanticModel = await document.GetRequiredNullableDisabledSemanticModelAsync(cancellationToken).ConfigureAwait(false);
 
            // Look to see if this is a linq method call. If so, return the invocation so the debugger can provide a
            // custom linq experience for it.
            if (expression.Parent is TMemberExpressionSyntax { Parent: TInvocationExpressionSyntax invocation } memberAccess &&
                syntaxFacts.GetRightSideOfDot(memberAccess) == expression &&
                IsLinqExtensionMethod(semanticModel.GetSymbolInfo(invocation, cancellationToken).Symbol))
            {
                return (DebugDataTipInfoKind.LinqExpression, invocation.Span);
            }
        }
 
        return default;
    }
 
    private static bool IsLinqExtensionMethod([NotNullWhen(true)] ISymbol? symbol)
        => symbol is IMethodSymbol
        {
            MethodKind: MethodKind.ReducedExtension,
            ContainingType.Name: nameof(Enumerable),
            ContainingType.ContainingNamespace.Name: nameof(System.Linq),
            ContainingType.ContainingNamespace.ContainingNamespace.Name: nameof(System),
            ContainingType.ContainingNamespace.ContainingNamespace.ContainingNamespace.IsGlobalNamespace: true,
        };
}