提交 9ecb210e 编写于 作者: T Tomáš Matoušek 提交者: GitHub

Better error reporting for VsLanguageDebugInfo (#21651)

Better error reporting for VsLanguageDebugInfo
上级 196eea0a
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
......@@ -12,6 +13,6 @@ internal interface IBreakpointResolutionService : ILanguageService
{
Task<BreakpointResolutionResult> ResolveBreakpointAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken = default);
Task<IEnumerable<BreakpointResolutionResult>> ResolveBreakpointsAsync(Solution solution, string name, CancellationToken cancellationToken = default);
Task<ImmutableArray<BreakpointResolutionResult>> ResolveBreakpointsAsync(Solution solution, string name, CancellationToken cancellationToken = default);
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.EditAndContinue;
using Microsoft.CodeAnalysis.Editor.Implementation.Debugging;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Text;
......@@ -15,29 +18,33 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.Debugging
[ExportLanguageService(typeof(IBreakpointResolutionService), LanguageNames.CSharp), Shared]
internal partial class CSharpBreakpointResolutionService : IBreakpointResolutionService
{
internal static async Task<BreakpointResolutionResult> GetBreakpointAsync(Document document, int position, CancellationToken cancellationToken)
/// <summary>
/// Returns null if a breakpoint can't be placed at the specified position.
/// </summary>
public async Task<BreakpointResolutionResult> ResolveBreakpointAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken)
{
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
if (!BreakpointSpans.TryGetBreakpointSpan(tree, position, cancellationToken, out var span))
try
{
return null;
}
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
if (!BreakpointSpans.TryGetBreakpointSpan(tree, textSpan.Start, cancellationToken, out var span))
{
return null;
}
if (span.Length == 0)
{
return BreakpointResolutionResult.CreateLineResult(document);
}
if (span.Length == 0)
return BreakpointResolutionResult.CreateSpanResult(document, span);
}
catch (Exception e) when (FatalError.ReportWithoutCrashUnlessCanceled(e))
{
return BreakpointResolutionResult.CreateLineResult(document);
return null;
}
return BreakpointResolutionResult.CreateSpanResult(document, span);
}
public Task<BreakpointResolutionResult> ResolveBreakpointAsync(
Document document, TextSpan textSpan, CancellationToken cancellationToken)
{
return GetBreakpointAsync(document, textSpan.Start, cancellationToken);
}
public Task<IEnumerable<BreakpointResolutionResult>> ResolveBreakpointsAsync(Solution solution, string name, CancellationToken cancellationToken)
public Task<ImmutableArray<BreakpointResolutionResult>> ResolveBreakpointsAsync(Solution solution, string name, CancellationToken cancellationToken)
{
return new BreakpointResolver(solution, name).DoAsync(cancellationToken);
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
......@@ -9,6 +10,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.VisualStudio.LanguageServices.Implementation.Debugging;
......@@ -71,13 +73,23 @@ internal partial class CSharpProximityExpressionsService : IProximityExpressions
return true;
}
/// <summary>
/// Returns null indicating a failure.
/// </summary>
public async Task<IList<string>> GetProximityExpressionsAsync(
Document document,
int position,
CancellationToken cancellationToken)
{
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
return Do(tree, position, cancellationToken);
try
{
var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
return Do(tree, position, cancellationToken);
}
catch (Exception e) when (FatalError.ReportWithoutCrashUnlessCanceled(e))
{
return null;
}
}
// Internal for testing purposes
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
......@@ -7,6 +8,7 @@
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editor.Implementation.Debugging;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
......@@ -16,76 +18,83 @@ internal static class DataTipInfoGetter
{
internal static async Task<DebugDataTipInfo> GetInfoAsync(Document document, int position, CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(position);
var expression = token.Parent as ExpressionSyntax;
if (expression == null)
try
{
return token.IsKind(SyntaxKind.IdentifierToken)
? new DebugDataTipInfo(token.Span, text: null)
: default(DebugDataTipInfo);
}
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(position);
if (expression.IsAnyLiteralExpression())
{
// 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.
var semanticModel = await document.GetPartialSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var type = semanticModel.GetTypeInfo(expression, cancellationToken).Type;
return type == null
? default(DebugDataTipInfo)
: new DebugDataTipInfo(expression.Span, type.ToNameDisplayString());
}
var expression = token.Parent as ExpressionSyntax;
if (expression == null)
{
return token.IsKind(SyntaxKind.IdentifierToken)
? new DebugDataTipInfo(token.Span, text: null)
: default;
}
if (expression.IsRightSideOfDotOrArrow())
{
var curr = expression;
while (true)
if (expression.IsAnyLiteralExpression())
{
// 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.
var semanticModel = await document.GetPartialSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var type = semanticModel.GetTypeInfo(expression, cancellationToken).Type;
return type == null
? default
: new DebugDataTipInfo(expression.Span, type.ToNameDisplayString());
}
if (expression.IsRightSideOfDotOrArrow())
{
var conditionalAccess = curr.GetParentConditionalAccessExpression();
if (conditionalAccess == null)
var curr = expression;
while (true)
{
var conditionalAccess = curr.GetParentConditionalAccessExpression();
if (conditionalAccess == null)
{
break;
}
curr = conditionalAccess;
}
if (curr == expression)
{
break;
// NB: Parent.Span, not Span as below.
return new DebugDataTipInfo(expression.Parent.Span, text: null);
}
curr = conditionalAccess;
// 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.
return new DebugDataTipInfo(TextSpan.FromBounds(curr.SpanStart, expression.Span.End), text: null);
}
if (curr == expression)
// NOTE(cyrusn): This behavior is to mimic what we did in Dev10, I'm not sure if it's
// necessary or not.
if (expression.IsKind(SyntaxKind.InvocationExpression))
{
// NB: Parent.Span, not Span as below.
return new DebugDataTipInfo(expression.Parent.Span, text: null);
expression = ((InvocationExpressionSyntax)expression).Expression;
}
// 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.
return new DebugDataTipInfo(TextSpan.FromBounds(curr.SpanStart, expression.Span.End), text: null);
}
string textOpt = null;
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.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var type = semanticModel.GetTypeInfo(typeSyntax, cancellationToken).Type;
if (type != null)
{
textOpt = type.ToNameDisplayString();
}
}
// NOTE(cyrusn): This behavior is to mimic what we did in Dev10, I'm not sure if it's
// necessary or not.
if (expression.IsKind(SyntaxKind.InvocationExpression))
{
expression = ((InvocationExpressionSyntax)expression).Expression;
return new DebugDataTipInfo(expression.Span, textOpt);
}
string textOpt = null;
if (expression is TypeSyntax typeSyntax && typeSyntax.IsVar)
catch (Exception e) when (FatalError.ReportWithoutCrashUnlessCanceled(e))
{
// If the user is hovering over 'var', then pass back the full type name that 'var'
// binds to.
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var type = semanticModel.GetTypeInfo(typeSyntax, cancellationToken).Type;
if (type != null)
{
textOpt = type.ToNameDisplayString();
}
return default;
}
return new DebugDataTipInfo(expression.Span, textOpt);
}
}
}
......@@ -6,12 +6,14 @@
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.TextManager.Interop;
using Roslyn.Utilities;
using TextSpan = Microsoft.VisualStudio.TextManager.Interop.TextSpan;
namespace Microsoft.VisualStudio.LanguageServices.Implementation
......@@ -32,7 +34,19 @@ internal abstract class AbstractVsTextViewFilter<TPackage, TLanguageService> : A
_languageService = languageService;
}
public virtual int GetDataTipText(TextSpan[] pSpan, out string pbstrText)
int IVsTextViewFilter.GetDataTipText(TextSpan[] pSpan, out string pbstrText)
{
try
{
return GetDataTipTextImpl(pSpan, out pbstrText);
}
catch (Exception e) when (FatalError.ReportWithoutCrash(e) && false)
{
throw ExceptionUtilities.Unreachable;
}
}
protected virtual int GetDataTipTextImpl(TextSpan[] pSpan, out string pbstrText)
{
pbstrText = null;
......@@ -45,7 +59,7 @@ public virtual int GetDataTipText(TextSpan[] pSpan, out string pbstrText)
return VSConstants.E_FAIL;
}
var vsBuffer = this.EditorAdaptersFactory.GetBufferAdapter(subjectBuffer);
var vsBuffer = EditorAdaptersFactory.GetBufferAdapter(subjectBuffer);
// TODO: broken in REPL
if (vsBuffer == null)
......@@ -59,15 +73,22 @@ public virtual int GetDataTipText(TextSpan[] pSpan, out string pbstrText)
return VSConstants.E_FAIL;
}
public int GetPairExtents(int iLine, int iIndex, TextSpan[] pSpan)
int IVsTextViewFilter.GetPairExtents(int iLine, int iIndex, TextSpan[] pSpan)
{
int result = VSConstants.S_OK;
_languageService.Package.ComponentModel.GetService<IWaitIndicator>().Wait(
"Intellisense",
allowCancel: true,
action: c => result = GetPairExtentsWorker(iLine, iIndex, pSpan, c.CancellationToken));
try
{
int result = VSConstants.S_OK;
_languageService.Package.ComponentModel.GetService<IWaitIndicator>().Wait(
"Intellisense",
allowCancel: true,
action: c => result = GetPairExtentsWorker(iLine, iIndex, pSpan, c.CancellationToken));
return result;
return result;
}
catch (Exception e) when (FatalError.ReportWithoutCrash(e) && false)
{
throw ExceptionUtilities.Unreachable;
}
}
private int GetPairExtentsWorker(int iLine, int iIndex, TextSpan[] pSpan, CancellationToken cancellationToken)
......@@ -84,10 +105,8 @@ private int GetPairExtentsWorker(int iLine, int iIndex, TextSpan[] pSpan, Cancel
cancellationToken);
}
public int GetWordExtent(int iLine, int iIndex, uint dwFlags, TextSpan[] pSpan)
{
return VSConstants.E_NOTIMPL;
}
int IVsTextViewFilter.GetWordExtent(int iLine, int iIndex, uint dwFlags, TextSpan[] pSpan)
=> VSConstants.E_NOTIMPL;
#region Edit and Continue
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Implementation.Debugging;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
......@@ -70,12 +73,6 @@ internal abstract partial class AbstractBreakpointResolver
protected abstract IEnumerable<ISymbol> GetMembers(INamedTypeSymbol type, string name);
protected abstract bool HasMethodBody(IMethodSymbol method, CancellationToken cancellationToken);
public async Task<IEnumerable<BreakpointResolutionResult>> DoAsync(CancellationToken cancellationToken)
{
var methods = await this.ResolveMethodsAsync(cancellationToken).ConfigureAwait(false);
return methods.Select(CreateBreakpoint).ToImmutableArrayOrEmpty();
}
private BreakpointResolutionResult CreateBreakpoint(ISymbol methodSymbol)
{
var location = methodSymbol.Locations.First(loc => loc.IsInSource);
......@@ -87,59 +84,72 @@ private BreakpointResolutionResult CreateBreakpoint(ISymbol methodSymbol)
return BreakpointResolutionResult.CreateSpanResult(document, textSpan, vsDebugName);
}
private async Task<IEnumerable<ISymbol>> ResolveMethodsAsync(CancellationToken cancellationToken)
public async Task<ImmutableArray<BreakpointResolutionResult>> DoAsync(CancellationToken cancellationToken)
{
this.ParseText(out var nameParts, out var parameterCount);
// Notes: In C#, indexers can't be resolved by any name. This is acceptable, because the old language
// service wasn't able to resolve them either. In VB, parameterized properties will work in
// the same way as any other property.
// Destructors in C# can be resolved using the method name "Finalize". The resulting string
// representation will use C# language format ("C.~C()"). I verified that this works with
// "Break at Function" (breakpoint is correctly set and can be hit), so I don't see a reason
// to prohibit this (even though the old language service didn't support it).
var members = await FindMembersAsync(nameParts, cancellationToken).ConfigureAwait(false);
// Filter down the list of symbols to "applicable methods", specifically:
// - "regular" methods
// - constructors
// - destructors
// - properties
// - operators?
// - conversions?
// where "applicable" means that the method or property represents a valid place to set a breakpoint
// and that it has the expected number of parameters
return members.Where(m => IsApplicable(m, parameterCount, cancellationToken));
try
{
ParseText(out var nameParts, out var parameterCount);
// Notes: In C#, indexers can't be resolved by any name. This is acceptable, because the old language
// service wasn't able to resolve them either. In VB, parameterized properties will work in
// the same way as any other property.
// Destructors in C# can be resolved using the method name "Finalize". The resulting string
// representation will use C# language format ("C.~C()"). I verified that this works with
// "Break at Function" (breakpoint is correctly set and can be hit), so I don't see a reason
// to prohibit this (even though the old language service didn't support it).
var members = await FindMembersAsync(nameParts, cancellationToken).ConfigureAwait(false);
// Filter down the list of symbols to "applicable methods", specifically:
// - "regular" methods
// - constructors
// - destructors
// - properties
// - operators?
// - conversions?
// where "applicable" means that the method or property represents a valid place to set a breakpoint
// and that it has the expected number of parameters
return members.Where(m => IsApplicable(m, parameterCount, cancellationToken)).
Select(CreateBreakpoint).ToImmutableArrayOrEmpty();
}
catch (Exception e) when (FatalError.ReportWithoutCrashUnlessCanceled(e))
{
return ImmutableArray<BreakpointResolutionResult>.Empty;
}
}
private async Task<IEnumerable<ISymbol>> FindMembersAsync(
IList<NameAndArity> nameParts, CancellationToken cancellationToken)
{
if (nameParts.Count == 0)
try
{
// If there were no name parts, then we don't have any members to return.
// We only expect to hit this condition when the name provided does not parse.
return SpecializedCollections.EmptyList<ISymbol>();
}
else if (nameParts.Count == 1)
{
// They're just searching for a method name. Have to look through every type to find
// it.
return FindMembers(await GetAllTypesAsync(cancellationToken).ConfigureAwait(false), nameParts[0]);
}
else if (nameParts.Count == 2)
{
// They have a type name and a method name. Find a type with a matching name and a
// method in that type.
var types = await GetAllTypesAsync(cancellationToken).ConfigureAwait(false);
types = types.Where(t => MatchesName(t, nameParts[0], _identifierComparer));
return FindMembers(types, nameParts[1]);
switch (nameParts.Count)
{
case 0:
// If there were no name parts, then we don't have any members to return.
// We only expect to hit this condition when the name provided does not parse.
return SpecializedCollections.EmptyList<ISymbol>();
case 1:
// They're just searching for a method name. Have to look through every type to find
// it.
return FindMembers(await GetAllTypesAsync(cancellationToken).ConfigureAwait(false), nameParts[0]);
case 2:
// They have a type name and a method name. Find a type with a matching name and a
// method in that type.
var types = await GetAllTypesAsync(cancellationToken).ConfigureAwait(false);
types = types.Where(t => MatchesName(t, nameParts[0], _identifierComparer));
return FindMembers(types, nameParts[1]);
default:
// They have a namespace or nested type qualified name. Walk up to the root namespace trying to match.
var containers = await _solution.GetGlobalNamespacesAsync(cancellationToken).ConfigureAwait(false);
return FindMembers(containers, nameParts.ToArray());
}
}
else
catch (Exception e) when (FatalError.ReportWithoutCrashUnlessCanceled(e))
{
// They have a namespace or nested type qualified name. Walk up to the root namespace trying to match.
var containers = await _solution.GetGlobalNamespacesAsync(cancellationToken).ConfigureAwait(false);
return FindMembers(containers, nameParts.ToArray());
return ImmutableArray<ISymbol>.Empty;
}
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
using Roslyn.Utilities;
using IVsEnumBSTR = Microsoft.VisualStudio.TextManager.Interop.IVsEnumBSTR;
using IVsTextBuffer = Microsoft.VisualStudio.TextManager.Interop.IVsTextBuffer;
using TextSpan = Microsoft.VisualStudio.TextManager.Interop.TextSpan;
......@@ -10,39 +12,88 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService
{
internal partial class AbstractLanguageService<TPackage, TLanguageService> : IVsLanguageDebugInfo
{
public int GetLanguageID(IVsTextBuffer pBuffer, int iLine, int iCol, out Guid pguidLanguageID)
int IVsLanguageDebugInfo.GetLanguageID(IVsTextBuffer pBuffer, int iLine, int iCol, out Guid pguidLanguageID)
{
return this.LanguageDebugInfo.GetLanguageID(pBuffer, iLine, iCol, out pguidLanguageID);
try
{
return LanguageDebugInfo.GetLanguageID(pBuffer, iLine, iCol, out pguidLanguageID);
}
catch (Exception e) when (FatalError.ReportWithoutCrash(e) && false)
{
throw ExceptionUtilities.Unreachable;
}
}
public int GetLocationOfName(string pszName, out string pbstrMkDoc, out TextSpan pspanLocation)
int IVsLanguageDebugInfo.GetLocationOfName(string pszName, out string pbstrMkDoc, out TextSpan pspanLocation)
{
return this.LanguageDebugInfo.GetLocationOfName(pszName, out pbstrMkDoc, out pspanLocation);
try
{
return LanguageDebugInfo.GetLocationOfName(pszName, out pbstrMkDoc, out pspanLocation);
}
catch (Exception e) when (FatalError.ReportWithoutCrash(e) && false)
{
throw ExceptionUtilities.Unreachable;
}
}
public int GetNameOfLocation(IVsTextBuffer pBuffer, int iLine, int iCol, out string pbstrName, out int piLineOffset)
int IVsLanguageDebugInfo.GetNameOfLocation(IVsTextBuffer pBuffer, int iLine, int iCol, out string pbstrName, out int piLineOffset)
{
return this.LanguageDebugInfo.GetNameOfLocation(pBuffer, iLine, iCol, out pbstrName, out piLineOffset);
try
{
return LanguageDebugInfo.GetNameOfLocation(pBuffer, iLine, iCol, out pbstrName, out piLineOffset);
}
catch (Exception e) when (FatalError.ReportWithoutCrash(e) && false)
{
throw ExceptionUtilities.Unreachable;
}
}
public int GetProximityExpressions(IVsTextBuffer pBuffer, int iLine, int iCol, int cLines, out IVsEnumBSTR ppEnum)
int IVsLanguageDebugInfo.GetProximityExpressions(IVsTextBuffer pBuffer, int iLine, int iCol, int cLines, out IVsEnumBSTR ppEnum)
{
return this.LanguageDebugInfo.GetProximityExpressions(pBuffer, iLine, iCol, cLines, out ppEnum);
try
{
return LanguageDebugInfo.GetProximityExpressions(pBuffer, iLine, iCol, cLines, out ppEnum);
}
catch (Exception e) when (FatalError.ReportWithoutCrash(e) && false)
{
throw ExceptionUtilities.Unreachable;
}
}
public int IsMappedLocation(IVsTextBuffer pBuffer, int iLine, int iCol)
int IVsLanguageDebugInfo.IsMappedLocation(IVsTextBuffer pBuffer, int iLine, int iCol)
{
return this.LanguageDebugInfo.IsMappedLocation(pBuffer, iLine, iCol);
try
{
return LanguageDebugInfo.IsMappedLocation(pBuffer, iLine, iCol);
}
catch (Exception e) when (FatalError.ReportWithoutCrash(e) && false)
{
throw ExceptionUtilities.Unreachable;
}
}
public int ResolveName(string pszName, uint dwFlags, out IVsEnumDebugName ppNames)
int IVsLanguageDebugInfo.ResolveName(string pszName, uint dwFlags, out IVsEnumDebugName ppNames)
{
return this.LanguageDebugInfo.ResolveName(pszName, dwFlags, out ppNames);
try
{
return LanguageDebugInfo.ResolveName(pszName, dwFlags, out ppNames);
}
catch (Exception e) when (FatalError.ReportWithoutCrash(e) && false)
{
throw ExceptionUtilities.Unreachable;
}
}
public int ValidateBreakpointLocation(IVsTextBuffer pBuffer, int iLine, int iCol, TextSpan[] pCodeSpan)
int IVsLanguageDebugInfo.ValidateBreakpointLocation(IVsTextBuffer pBuffer, int iLine, int iCol, TextSpan[] pCodeSpan)
{
return this.LanguageDebugInfo.ValidateBreakpointLocation(pBuffer, iLine, iCol, pCodeSpan);
try
{
return LanguageDebugInfo.ValidateBreakpointLocation(pBuffer, iLine, iCol, pCodeSpan);
}
catch (Exception e) when (FatalError.ReportWithoutCrash(e) && false)
{
throw ExceptionUtilities.Unreachable;
}
}
}
}
......@@ -26,7 +26,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService
{
internal abstract partial class AbstractLanguageService<TPackage, TLanguageService>
{
internal class VsLanguageDebugInfo : IVsLanguageDebugInfo
internal sealed class VsLanguageDebugInfo
{
private readonly Guid _languageId;
private readonly TLanguageService _languageService;
......
......@@ -2,6 +2,7 @@
using System;
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Internal.Log;
......@@ -48,16 +49,9 @@ public VisualStudioWaitIndicator(SVsServiceProvider serviceProvider)
{
return WaitIndicatorResult.Canceled;
}
catch (AggregateException e)
catch (AggregateException aggregate) when (aggregate.InnerExceptions.All(e => e is OperationCanceledException))
{
if (e.InnerExceptions[0] is OperationCanceledException operationCanceledException)
{
return WaitIndicatorResult.Canceled;
}
else
{
throw;
}
return WaitIndicatorResult.Canceled;
}
}
}
......
......@@ -43,7 +43,7 @@ protected override ITextBuffer GetSubjectBufferContainingCaret()
return _subjectBuffer;
}
public override int GetDataTipText(TextSpan[] pSpan, out string pbstrText)
protected override int GetDataTipTextImpl(TextSpan[] pSpan, out string pbstrText)
{
if (pSpan == null || pSpan.Length != 1)
{
......@@ -78,7 +78,7 @@ public override int GetDataTipText(TextSpan[] pSpan, out string pbstrText)
// Next, we'll check to see if there is actually a DataTip for this candidate.
// If there is, we'll map this span back to the DataBuffer and return it.
pSpan[0] = candidateSpan.ToVsTextSpan();
int hr = base.GetDataTipText(pSpan, out pbstrText);
int hr = base.GetDataTipTextImpl(pSpan, out pbstrText);
if (ErrorHandler.Succeeded(hr))
{
var subjectSpan = _subjectBuffer.CurrentSnapshot.GetSpan(pSpan[0]);
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Immutable
Imports System.Composition
Imports System.Threading
Imports System.Threading.Tasks
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editor.Implementation.Debugging
Imports Microsoft.CodeAnalysis.ErrorReporting
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic
......@@ -18,56 +20,62 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Debugging
Implements IBreakpointResolutionService
Friend Shared Async Function GetBreakpointAsync(document As Document, position As Integer, length As Integer, cancellationToken As CancellationToken) As Task(Of BreakpointResolutionResult)
Dim tree = Await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(False)
Try
Dim tree = Await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(False)
' Non-zero length means that the span is passed by the debugger and we may need validate it.
' In a rare VB case, this span may contain multiple methods, e.g.,
'
' [Sub Goo() Handles A
'
' End Sub
'
' Sub Bar() Handles B]
'
' End Sub
'
' It happens when IDE services (e.g., NavBar or CodeFix) inserts a new method at the beginning of the existing one
' which happens to have a breakpoint on its head. In this situation, we should attempt to validate the span of the existing method,
' not that of a newly-prepended method.
' Non-zero length means that the span is passed by the debugger and we may need validate it.
' In a rare VB case, this span may contain multiple methods, e.g.,
'
' [Sub Goo() Handles A
'
' End Sub
'
' Sub Bar() Handles B]
'
' End Sub
'
' It happens when IDE services (e.g., NavBar or CodeFix) inserts a new method at the beginning of the existing one
' which happens to have a breakpoint on its head. In this situation, we should attempt to validate the span of the existing method,
' not that of a newly-prepended method.
Dim descendIntoChildren As Func(Of SyntaxNode, Boolean) =
Dim descendIntoChildren As Func(Of SyntaxNode, Boolean) =
Function(n)
Return (Not n.IsKind(SyntaxKind.ConstructorBlock)) _
AndAlso (Not n.IsKind(SyntaxKind.SubBlock))
End Function
If length > 0 Then
Dim root = Await tree.GetRootAsync(cancellationToken).ConfigureAwait(False)
Dim item = root.DescendantNodes(New TextSpan(position, length), descendIntoChildren:=descendIntoChildren).OfType(Of MethodBlockSyntax).Skip(1).LastOrDefault()
If item IsNot Nothing Then
position = item.SpanStart
If length > 0 Then
Dim root = Await tree.GetRootAsync(cancellationToken).ConfigureAwait(False)
Dim item = root.DescendantNodes(New TextSpan(position, length), descendIntoChildren:=descendIntoChildren).OfType(Of MethodBlockSyntax).Skip(1).LastOrDefault()
If item IsNot Nothing Then
position = item.SpanStart
End If
End If
End If
Dim span As TextSpan
If Not BreakpointSpans.TryGetBreakpointSpan(tree, position, cancellationToken, span) Then
Return Nothing
End If
Dim span As TextSpan
If Not BreakpointSpans.TryGetBreakpointSpan(tree, position, cancellationToken, span) Then
Return Nothing
End If
If span.Length = 0 Then
Return BreakpointResolutionResult.CreateLineResult(document)
End If
If span.Length = 0 Then
Return BreakpointResolutionResult.CreateLineResult(document)
End If
Return BreakpointResolutionResult.CreateSpanResult(document, span)
Return BreakpointResolutionResult.CreateSpanResult(document, span)
Catch e As Exception When FatalError.ReportWithoutCrashUnlessCanceled(e)
Return Nothing
End Try
End Function
Public Function ResolveBreakpointAsync(document As Document, textSpan As TextSpan, Optional cancellationToken As CancellationToken = Nothing) As Task(Of BreakpointResolutionResult) Implements IBreakpointResolutionService.ResolveBreakpointAsync
Return GetBreakpointAsync(document, textSpan.Start, textSpan.Length, cancellationToken)
End Function
Public Function ResolveBreakpointsAsync(solution As Solution,
name As String,
Optional cancellationToken As CancellationToken = Nothing) As Task(Of IEnumerable(Of BreakpointResolutionResult)) Implements IBreakpointResolutionService.ResolveBreakpointsAsync
Public Function ResolveBreakpointsAsync(
solution As Solution,
name As String,
Optional cancellationToken As CancellationToken = Nothing) As Task(Of ImmutableArray(Of BreakpointResolutionResult)) Implements IBreakpointResolutionService.ResolveBreakpointsAsync
Return New BreakpointResolver(solution, name).DoAsync(cancellationToken)
End Function
End Class
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册