Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat code actions forall calc #6044

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
8 changes: 8 additions & 0 deletions Source/DafnyCore/AST/Grammar/Printer/Printer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ public static string ExprToString(DafnyOptions options, Expression expr, [CanBeN
return wr.ToString();
}

public static string ForallExprRangeToString(DafnyOptions options, ForallExpr expr,
[CanBeNull] PrintFlags printFlags = null) {
using var wr = new StringWriter();
var pr = new Printer(wr, options, printFlags: printFlags);
pr.PrintQuantifierDomain(expr.BoundVars, expr.Attributes, expr.Range);
return wr.ToString();
}

public static string ExprListToString(DafnyOptions options, List<Expression> expressions, [CanBeNull] PrintFlags printFlags = null) {
Contract.Requires(expressions != null);
using var wr = new StringWriter();
Expand Down
241 changes: 216 additions & 25 deletions Source/DafnyLanguageServer.Test/CodeActions/CodeActionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Castle.Core.Internal;
using Microsoft.Dafny.LanguageServer.IntegrationTest.Util;
using Xunit;
using Xunit.Abstractions;
Expand Down Expand Up @@ -108,14 +109,104 @@ method Dac(c: L)
}");
}

[Fact]
public async Task TestCalcIntroduction() {
await TestCodeAction(@"
method Test() {
assert 1 =><= 2(>Insert a calc statement-> by {
calc {
1;
2;
}
}:::;<)
}");
}

[Fact]
public async Task TestCalcIntroductionEquiv() {
await TestCodeAction(@"
method Test() {
assert true <=><=> false(>Insert a calc statement-> by {
calc <==> {
true;
false;
}
}:::;<)
}");
}

[Fact]
public async Task TestForallIntroduction() {
await TestCodeAction(@"
method Test() {
assert for><all i | i % 4 == 1 :: i % 2 == 0(>Insert a forall statement-> by {
forall i: int | i % 4 == 1 ensures i % 2 == 0 {
assert i % 2 == 0;
}
}:::;<)
}");
}

[Fact]
public async Task TestForallIntroductionFunction() {
await TestCodeAction(@"
function Test(): int {
assert for><all i | i % 4 == 1 :: i % 2 == 0(>Insert a forall statement-> by {
forall i: int | i % 4 == 1 ensures i % 2 == 0 {
assert i % 2 == 0;
}
}:::;<)
1
}");
}

[Fact]
public async Task GitIssue4401CorrectInsertionPlace() {
await TestCodeAction(@"
predicate P(i: int)

method Test() {(>Insert explicit failing assertion->
assert exists x: int :: P(x);<)
var x :><| P(x);
method Test() {
var x :><| P(x)(>Insert explicit failing assertion-> by {
assert exists x: int :: P(x);
}:::;<)
}");
}

[Fact]
public async Task InsertIntoByStatement() {
await TestCodeAction(@"
predicate P(i: int)

function Test(i: int): int
requires P(i) {
i
}

method TestMethod() {
assert Test><(1) == 1 by {
(>Insert explicit failing assertion->assert P(1);
<)calc {
1;
1;
}
}
}");
}

[Fact]
public async Task InsertIntoEmptyByStatement() {
await TestCodeAction(@"
predicate P(i: int)

function Test(i: int): int
requires P(i) {
i
}

method TestMethod() {
assert Test><(1) == 1 by {
(>Insert explicit failing assertion-> assert P(1);
<)}
}");
}

Expand All @@ -126,9 +217,10 @@ module Test {
class TheTest {
predicate P(i: int)

method Test() {(>Insert explicit failing assertion->
assert exists x: int :: P(x);<)
var x :><| P(x);
method Test() {
var x :><| P(x)(>Insert explicit failing assertion-> by {
assert exists x: int :: P(x);
}:::;<)
}
}
}");
Expand Down Expand Up @@ -254,17 +346,50 @@ match e
}

[Fact]
public async Task ExplicitDivisionByZero() {
public async Task ExplicitDivisionByZeroFunction() {
await TestCodeAction(@"
method Foo(i: int)
function Foo(i: int): int
{
(>Insert explicit failing assertion->assert i + 1 != 0;
<)var x := 2>< / (i + 1);
x
}");
}

[Fact]
public async Task ExplicitDivisionByZeroMethod() {
await TestCodeAction(@"
method Foo(i: int)
{
var x := 2>< / (i + 1)(>Insert explicit failing assertion-> by {
assert i + 1 != 0;
}:::;<)
}");
}

[Fact]
public async Task ExplicitDivisionImp() {
public async Task ExplicitDivisionImpFunction() {
await TestCodeAction(@"
function Foo(b: bool, i: int, j: int): bool
{
var x := b ==> (>Insert explicit failing assertion->assert i + 1 != 0;
<)2 ></ (i + 1) == j;
x
}");
}

[Fact]
public async Task ExplicitDivisionImpImpMethod() {
await TestCodeAction(@"
method Foo(b: bool, i: int, j: int)
{
var x := b ==> b ==> (>Insert explicit failing assertion->assert i + 1 != 0;
<)2 ></ (i + 1) == j;
}");
}

[Fact]
public async Task ExplicitDivisionImpMethod() {
await TestCodeAction(@"
method Foo(b: bool, i: int, j: int)
{
Expand All @@ -278,28 +403,53 @@ public async Task ExplicitDivisionImp2() {
await TestCodeAction(@"
method Foo(b: bool, i: int, j: int)
{
(>Insert explicit failing assertion->assert i + 1 != 0;
<)var x := 2 ></ (i + 1) == j ==> b;
var x := 2 ></ (i + 1) == j ==> b(>Insert explicit failing assertion-> by {
assert i + 1 != 0;
}:::;<)
}");
}

[Fact]
public async Task ExplicitDivisionAnd() {
public async Task ExplicitDivisionAndFunction() {
await TestCodeAction(@"
method Foo(b: bool, i: int, j: int)
function Foo(b: bool, i: int, j: int): bool
{
var x := b && (>Insert explicit failing assertion->assert i + 1 != 0;
<)2 ></ (i + 1) == j;
x
}");
}


[Fact]
public async Task ExplicitDivisionAnd2() {
public async Task ExplicitDivisionAndMethod() {
await TestCodeAction(@"
method Foo(b: bool, i: int, j: int)
{
var x := b && (>Insert explicit failing assertion->assert i + 1 != 0;
<)2 ></ (i + 1) == j;
}");
}

[Fact]
public async Task ExplicitDivisionAnd2Function() {
await TestCodeAction(@"
function Foo(b: bool, i: int, j: int): bool
{
(>Insert explicit failing assertion->assert i + 1 != 0;
<)var x := 2 ></ (i + 1) == j && b;
x
}");
}

[Fact]
public async Task ExplicitDivisionAnd2Method() {
await TestCodeAction(@"
method Foo(b: bool, i: int, j: int)
{
var x := 2 ></ (i + 1) == j && b(>Insert explicit failing assertion-> by {
assert i + 1 != 0;
}:::;<)
}");
}

Expand All @@ -319,35 +469,69 @@ public async Task ExplicitDivisionOr2() {
await TestCodeAction(@"
method Foo(b: bool, i: int, j: int)
{
(>Insert explicit failing assertion->assert i + 1 != 0;
<)var x := 2 ></ (i + 1) == j || b;
var x := 2 ></ (i + 1) == j || b(>Insert explicit failing assertion-> by {
assert i + 1 != 0;
}:::;<)
}");
}



[Fact]
public async Task ExplicitDivisionAddParentheses() {
await TestCodeAction(@"
method Foo(b: bool, i: int, j: int)
{
var x := 2 ></ match b case true => i + 1 case false => i - 1(>Insert explicit failing assertion-> by {
assert (match b case true => i + 1 case false => i - 1) != 0;
}:::;<)
}");
}

[Fact]
public async Task ExplicitDivisionAddParentheses2() {
await TestCodeAction(@"
function Foo(b: bool, i: int, j: int): int
{
(>Insert explicit failing assertion->assert (match b case true => i + 1 case false => i - 1) != 0;
<)var x := 2 ></ match b case true => i + 1 case false => i - 1;
x
}");
}

[Fact]
public async Task ExplicitDivisionExp() {
public async Task ExplicitDivisionExpMethod() {
await TestCodeAction(@"
method Foo(b: bool, i: int, j: int)
{
var x := b <== 2 ></ (i + 1) == j(>Insert explicit failing assertion-> by {
assert i + 1 != 0;
}:::;<)
}");
}

[Fact]
public async Task ExplicitDivisionExpFunction() {
await TestCodeAction(@"
function Foo(b: bool, i: int, j: int): int
{
(>Insert explicit failing assertion->assert i + 1 != 0;
<)var x := b <== 2 ></ (i + 1) == j;
2
}");
}

[Fact]
public async Task ExplicitDivisionExp2Function() {
await TestCodeAction(@"
function Foo(b: bool, i: int, j: int): bool
{
var x := (>Insert explicit failing assertion->(assert i + 1 != 0;
2 / (i + 1) == j):::2 ></ (i + 1) == j<) <== b;
x
}");
}

[Fact]
public async Task ExplicitDivisionExp2() {
public async Task ExplicitDivisionExp2Method() {
await TestCodeAction(@"
method Foo(b: bool, i: int, j: int)
{
Expand All @@ -357,7 +541,7 @@ method Foo(b: bool, i: int, j: int)
}

[Fact]
public async Task ExplicitDivisionByZeroFunction() {
public async Task ExplicitDivisionByZeroIfFunction() {
await TestCodeAction(@"
function Foo(i: int): int
{
Expand All @@ -372,7 +556,7 @@ function Foo(i: int): int


[Fact]
public async Task ExplicitDivisionByZeroFunctionLetExpr() {
public async Task ExplicitDivisionByZeroMatchFunctionLetExpr() {
await TestCodeAction(@"
function Foo(i: int): int
{
Expand Down Expand Up @@ -410,7 +594,9 @@ private async Task TestCodeAction(string source) {
out var ranges);
var documentItem = await CreateOpenAndWaitForResolve(output);
var diagnostics = await GetLastDiagnostics(documentItem);
Assert.Equal(ranges.Count, diagnostics.Length);
if (ranges.Count != diagnostics.Length) {
Assert.True(ranges.Count == diagnostics.Length, string.Join("\n", diagnostics.Select(d => d.ToString())));
}

if (positions.Count != ranges.Count) {
positions = ranges.Select(r => r.Range.Start).ToList();
Expand Down Expand Up @@ -441,8 +627,13 @@ private async Task TestCodeAction(string source) {
}
}

Assert.True(found,
$"Did not find the code action '{expectedTitle}'. Available were:{string.Join(",", otherTitles)}");
if (otherTitles.IsNullOrEmpty()) {
// Parsing gone wrong, we display diagnostics
Assert.True(false, output + "\n" + string.Join("\n", diagnostics.Select(d => d.ToString())));
} else {
Assert.True(found,
$"Did not find the code action '{expectedTitle}'. Available were:{string.Join(",", otherTitles)}");
}
}
}

Expand Down
Loading
Loading