| File: Debugging\DataTipInfoGetter.cs | Web Access |
| Project: src\roslyn\src\Features\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.Features.csproj (Microsoft.CodeAnalysis.CSharp.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; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Debugging; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Debugging; internal sealed class DataTipInfoGetter : AbstractDataTipInfoGetter< ExpressionSyntax, MemberAccessExpressionSyntax, InvocationExpressionSyntax> { public static async Task<DebugDataTipInfo> GetInfoAsync( Document document, int position, bool includeKind, CancellationToken cancellationToken) { try { var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(position); if (token.Parent is not ExpressionSyntax expression) { return token.IsKind(SyntaxKind.IdentifierToken) ? new DebugDataTipInfo(token.Span, text: null) : default; } else if (expression is TypeSyntax typeSyntax && typeSyntax.IsVar) { // If the user is hovering over 'var', then pass back the full type name that 'var' binds to. var semanticModel = await document.GetRequiredNullableDisabledSemanticModelAsync(cancellationToken).ConfigureAwait(false); var type = semanticModel.GetTypeInfo(typeSyntax, cancellationToken).Type; var text = type?.ToNameDisplayString(); return new DebugDataTipInfo(typeSyntax.Span, text); } else if (expression is LiteralExpressionSyntax) { // If the user hovers over a literal, give them a DataTip for the type of the literal they're hovering // over. Partial semantics should always be sufficient because the (unconverted) type of a literal can // always easily be determined. document = document.WithFrozenPartialSemantics(cancellationToken); var semanticModel = await document.GetRequiredNullableDisabledSemanticModelAsync(cancellationToken).ConfigureAwait(false); var type = semanticModel.GetTypeInfo(expression, cancellationToken).Type; return type == null ? default : new DebugDataTipInfo(expression.Span, type.ToNameDisplayString()); } else { var (kind, fullExpressionSpan) = await ComputeKindAsync(document, expression, includeKind, cancellationToken).ConfigureAwait(false); if (expression.IsRightSideOfDotOrArrow()) { // NOTE: There may not be an ExpressionSyntax corresponding to the range we want. // For example, for input a?.$$B?.C, we want span [|a?.B|]?.C. var current = expression.GetRootConditionalAccessExpression() ?? expression; var span = current == expression ? expression.GetRequiredParent().Span : TextSpan.FromBounds(current.SpanStart, expression.Span.End); return new DebugDataTipInfo(span, fullExpressionSpan ?? span, Text: null, kind); } // NOTE(cyrusn): This behavior is to mimic what we did in Dev10, I'm not sure if it's // necessary or not. if (expression is InvocationExpressionSyntax invocation) expression = invocation.Expression; return new DebugDataTipInfo(expression.Span, fullExpressionSpan ?? expression.Span, Text: null, kind); } } catch (Exception e) when (FatalError.ReportAndCatchUnlessCanceled(e, cancellationToken)) { return default; } } }