Skip to content

Commit

Permalink
Log an error instead of crashing in FindTokenRangeFromLspRange (#4869)
Browse files Browse the repository at this point in the history
Fixes #4818

### How has this been tested?
Not. There's no known reproduction for the issue. This change uses
defensive coding to reduce the chance of a crash

<small>By submitting this pull request, I confirm that my contribution
is made under the terms of the [MIT
license](https://github.com/dafny-lang/dafny/blob/master/LICENSE.txt).</small>
  • Loading branch information
keyboardDrummer authored Feb 9, 2024
1 parent 68f96d0 commit 29c4525
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 14 deletions.
6 changes: 3 additions & 3 deletions Source/DafnyLanguageServer/Handlers/DafnyCodeActionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ public override async Task<CommandOrCodeActionContainer> Handle(CodeActionParams

private DafnyCodeActionProvider[] GetDafnyCodeActionProviders() {
return new List<DafnyCodeActionProvider>() {
new VerificationDafnyCodeActionProvider()
, new ErrorMessageDafnyCodeActionProvider()
, new ImplicitFailingAssertionCodeActionProvider(options)
new VerificationDafnyCodeActionProvider(logger)
, new ErrorMessageDafnyCodeActionProvider(logger)
, new ImplicitFailingAssertionCodeActionProvider(logger, options)
}
.Concat(
options.Plugins.SelectMany(plugin =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.Dafny.LanguageServer.Handlers;
using Microsoft.Dafny.LanguageServer.Plugins;
using Microsoft.Dafny.LanguageServer.Util;
using Microsoft.Dafny.LanguageServer.Workspace;
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;

Expand All @@ -18,6 +20,9 @@ public class ErrorMessageDafnyCodeActionProvider : DiagnosticDafnyCodeActionProv
var actions = new List<DafnyCodeAction>();
if (actionSigs != null) {
var range = FindTokenRangeFromLspRange(input, diagnostic.Range);
if (range == null) {
return actions;
}
foreach (var sig in actionSigs) {
var dafnyActions = sig(range);
actions.AddRange(dafnyActions.Select(dafnyAction => new InstantDafnyCodeAction(dafnyAction.Title, new[] { diagnostic }, dafnyAction.Edits)));
Expand All @@ -26,13 +31,6 @@ public class ErrorMessageDafnyCodeActionProvider : DiagnosticDafnyCodeActionProv
return actions;
}

public static RangeToken FindTokenRangeFromLspRange(IDafnyCodeActionInput input, Range range) {
var start = range.Start;
var startNode = input.Program.FindNode<Node>(input.Uri.ToUri(), start.ToDafnyPosition());
var startToken = startNode.CoveredTokens.First(t => t.line - 1 == start.Line && t.col - 1 == start.Character);
var end = range.End;
var endNode = input.Program.FindNode<Node>(input.Uri.ToUri(), end.ToDafnyPosition());
var endToken = endNode.CoveredTokens.FirstOrDefault(t => t.line - 1 == end.Line && t.col - 1 + t.val.Length == end.Character);
return new RangeToken(startToken, endToken);
public ErrorMessageDafnyCodeActionProvider(ILogger<DafnyCodeActionHandler> logger) : base(logger) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.Dafny.LanguageServer.Handlers;
using Microsoft.Dafny.LanguageServer.Plugins;
using Microsoft.Dafny.LanguageServer.Workspace;
using Microsoft.Dafny.LanguageServer.Workspace.Notifications;
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;

Expand All @@ -16,9 +18,9 @@ namespace Microsoft.Dafny.LanguageServer.Language;
/// indicated on the '{' -- meaning there is no explicit return.
/// </summary>
class ImplicitFailingAssertionCodeActionProvider : DiagnosticDafnyCodeActionProvider {
private DafnyOptions options;
private readonly DafnyOptions options;

public ImplicitFailingAssertionCodeActionProvider(DafnyOptions options) {
public ImplicitFailingAssertionCodeActionProvider(ILogger<DafnyCodeActionHandler> logger, DafnyOptions options) : base(logger) {
this.options = options;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Dafny.LanguageServer.Handlers;
using Microsoft.Dafny.LanguageServer.Plugins;
using Microsoft.Dafny.LanguageServer.Workspace;
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;

Expand All @@ -29,7 +31,10 @@ class VerificationDafnyCodeActionProvider : DiagnosticDafnyCodeActionProvider {
return null;
}

var range = ErrorMessageDafnyCodeActionProvider.FindTokenRangeFromLspRange(input, relatedInformation.Location.Range);
var range = FindTokenRangeFromLspRange(input, relatedInformation.Location.Range);
if (range == null) {
return null;
}
var expression = range.PrintOriginal();
var statement = $"assert {expression};";
var edit = DafnyCodeActionHelpers.InsertAtEndOfBlock(input, diagnostic.Range.Start, statement);
Expand All @@ -46,4 +51,7 @@ class VerificationDafnyCodeActionProvider : DiagnosticDafnyCodeActionProvider {
};

}

public VerificationDafnyCodeActionProvider(ILogger<DafnyCodeActionHandler> logger) : base(logger) {
}
}
24 changes: 24 additions & 0 deletions Source/DafnyLanguageServer/Plugins/DafnyCodeActionProvider.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.Dafny.LanguageServer.Handlers;
using Microsoft.Dafny.LanguageServer.Language;
using Microsoft.Dafny.LanguageServer.Util;
using Microsoft.Extensions.Logging;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;

Expand Down Expand Up @@ -45,6 +49,12 @@ protected static RangeToken InsertAfter(IToken tok) {
/// that are being touched by the selection
/// </summary>
public abstract class DiagnosticDafnyCodeActionProvider : DafnyCodeActionProvider {
private ILogger<DafnyCodeActionHandler> logger;

protected DiagnosticDafnyCodeActionProvider(ILogger<DafnyCodeActionHandler> logger) {
this.logger = logger;
}

public override IEnumerable<DafnyCodeAction> GetDafnyCodeActions(IDafnyCodeActionInput input, Range selection) {
if (input.Program == null) {
return System.Array.Empty<DafnyCodeAction>();
Expand Down Expand Up @@ -74,4 +84,18 @@ public override IEnumerable<DafnyCodeAction> GetDafnyCodeActions(IDafnyCodeActio
/// <param name="selection">Where the user's caret is</param>
protected abstract IEnumerable<DafnyCodeAction>? GetDafnyCodeActions(IDafnyCodeActionInput input,
Diagnostic diagnostic, Range selection);

public RangeToken? FindTokenRangeFromLspRange(IDafnyCodeActionInput input, Range range) {
var start = range.Start;
var startNode = input.Program.FindNode<Node>(input.Uri.ToUri(), start.ToDafnyPosition());
var startToken = startNode.CoveredTokens.FirstOrDefault(t => t.line - 1 == start.Line && t.col - 1 == start.Character);
if (startToken == null) {
logger.LogError($"Could not find starting token for position {start} in node {startNode}");
return null;
}
var end = range.End;
var endNode = input.Program.FindNode<Node>(input.Uri.ToUri(), end.ToDafnyPosition());
var endToken = endNode.CoveredTokens.FirstOrDefault(t => t.line - 1 == end.Line && t.col - 1 + t.val.Length == end.Character);
return new RangeToken(startToken, endToken);
}
}
1 change: 1 addition & 0 deletions docs/dev/news/4818.fix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Use defensive coding to prevent a crash in the IDE that could occur in the context of code actions.

0 comments on commit 29c4525

Please sign in to comment.