Skip to content

Commit bbc0975

Browse files
committed
Support exists in assert for Dotty
1 parent 281e06e commit bbc0975

File tree

2 files changed

+52
-11
lines changed

2 files changed

+52
-11
lines changed

scalactic.dotty/src/main/scala/org/scalactic/BooleanMacro.scala

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,33 @@ object BooleanMacro {
3333
case _ => false
3434
}
3535

36+
// use in `exists(_ == e)` or `exists(e == _)`
37+
//
38+
// Note: Scala2 implementation implicitly assumes `e` is side effect free,
39+
// we do the same. A better approach would be to check `e` is Ident or
40+
// Literal.
41+
//
42+
// {
43+
// def $anonfun(_$12: Int): Boolean = _$12.==(2)
44+
// closure($anonfun)
45+
// }
46+
object AnonFunction {
47+
def unapply(t: Term): Option[Term] = t match {
48+
case Block(
49+
ddef @
50+
DefDef(_, Nil, (ValDef(name, _, _) :: Nil) :: Nil, _,
51+
Some(Apply(Select(lhs, "=="), rhs :: Nil))
52+
) :: Nil,
53+
clos
54+
) if (clos.tpe.isFunctionType) => // walkaround: https://github.com/lampepfl/dotty/issues/6720
55+
(lhs, rhs) match {
56+
case (Ident(refName), _) if refName == name => Some(rhs)
57+
case (_, Ident(refName)) if refName == name => Some(lhs)
58+
case _ => None
59+
}
60+
case _ => None
61+
}
62+
}
3663

3764
condition.unseal.underlyingArgument match {
3865
case Apply(sel @ Select(Apply(qual, lhs :: Nil), op @ ("===" | "!==")), rhs :: Nil) =>
@@ -115,6 +142,21 @@ object BooleanMacro {
115142
case _ =>
116143
binaryDefault
117144
}
145+
case "exists" =>
146+
rhs match {
147+
case AnonFunction(rhsInner) => // see the assumption for `rhsInner` in `AnonFunction`
148+
let(lhs) { left =>
149+
val app = left.select(sel.symbol).appliedTo(rhs)
150+
let(app) { result =>
151+
val l = left.seal
152+
val r = rhsInner.seal
153+
val res = result.seal.cast[Boolean]
154+
val code = '{ Bool.existsMacroBool($l, $r, $res, $prettifier) }
155+
code.unseal
156+
}
157+
}.seal.cast[Bool]
158+
case _ => defaultCase
159+
}
118160
case _ =>
119161
binaryDefault
120162
}

scalatest-test/src/test/scala/org/scalatest/AssertionsSpec.scala

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1623,7 +1623,6 @@ class AssertionsSpec extends AnyFunSpec {
16231623
assert(e.failedCodeLineNumber == (Some(thisLineNumber - 4)))
16241624
}
16251625

1626-
// SKIP-DOTTY-START
16271626
it("should do nothing when is used to check l1.exists(_ == 3)") {
16281627
assert(l1.exists(_ == 3))
16291628
}
@@ -1650,7 +1649,6 @@ class AssertionsSpec extends AnyFunSpec {
16501649
assert(e.failedCodeFileName == (Some(fileName)))
16511650
assert(e.failedCodeLineNumber == (Some(thisLineNumber - 4)))
16521651
}
1653-
// SKIP-DOTTY-END
16541652

16551653
it("should do nothing when is used to check !l1.exists(_ == 5)") {
16561654
assert(!l1.exists(_ == 5))
@@ -1659,8 +1657,7 @@ class AssertionsSpec extends AnyFunSpec {
16591657
it("should do nothing when is used to check !l1.exists(5 == _)") {
16601658
assert(!l1.exists(5 == _))
16611659
}
1662-
// SKIP-DOTTY-START
1663-
// TODO: better handle exists
1660+
16641661
it("should throw TestFailedException with correct message and stack depth when is used to check !l1.exists(_ == 3)") {
16651662
val e = intercept[TestFailedException] {
16661663
assert(!l1.exists(_ == 3))
@@ -1679,6 +1676,8 @@ class AssertionsSpec extends AnyFunSpec {
16791676
assert(e.failedCodeLineNumber == (Some(thisLineNumber - 4)))
16801677
}
16811678

1679+
// SKIP-DOTTY-START
1680+
// different printing of anonymous functions
16821681
it("should throw TestFailedException with correct message and stack depth when is used to check l1.exists(_ > 3)") {
16831682
val e = intercept[TestFailedException] {
16841683
assert(l1.exists(_ > 3))
@@ -1705,6 +1704,7 @@ class AssertionsSpec extends AnyFunSpec {
17051704
assert(e.failedCodeFileName == (Some(fileName)))
17061705
assert(e.failedCodeLineNumber == (Some(thisLineNumber - 4)))
17071706
}
1707+
// SKIP-DOTTY-END
17081708

17091709
it("should throw TestFailedException with correct message and stack depth when is used to check l3.exists(false)") {
17101710
val e = intercept[TestFailedException] {
@@ -1714,7 +1714,6 @@ class AssertionsSpec extends AnyFunSpec {
17141714
assert(e.failedCodeFileName == (Some(fileName)))
17151715
assert(e.failedCodeLineNumber == (Some(thisLineNumber - 4)))
17161716
}
1717-
// SKIP-DOTTY-END
17181717

17191718
def woof(f: => Unit) = "woof"
17201719
def meow(x: Int = 0, y: Int = 3) = "meow"
@@ -4549,8 +4548,6 @@ class AssertionsSpec extends AnyFunSpec {
45494548
assert(e.failedCodeLineNumber == (Some(thisLineNumber - 4)))
45504549
}
45514550

4552-
// SKIP-DOTTY-START
4553-
// missing support for exists
45544551
it("should do nothing when is used to check l1.exists(_ == 3)") {
45554552
assume(l1.exists(_ == 3))
45564553
}
@@ -4603,6 +4600,8 @@ class AssertionsSpec extends AnyFunSpec {
46034600
assert(e.failedCodeLineNumber == (Some(thisLineNumber - 4)))
46044601
}
46054602

4603+
// SKIP-DOTTY-START
4604+
// different printing of anonymous function
46064605
it("should throw TestCanceledException with correct message and stack depth when is used to check l1.exists(_ > 3)") {
46074606
val e = intercept[TestCanceledException] {
46084607
assume(l1.exists(_ > 3))
@@ -4629,6 +4628,7 @@ class AssertionsSpec extends AnyFunSpec {
46294628
assert(e.failedCodeFileName == (Some(fileName)))
46304629
assert(e.failedCodeLineNumber == (Some(thisLineNumber - 4)))
46314630
}
4631+
// SKIP-DOTTY-END
46324632

46334633
it("should throw TestCanceledException with correct message and stack depth when is used to check l3.exists(false)") {
46344634
val e = intercept[TestCanceledException] {
@@ -4638,7 +4638,6 @@ class AssertionsSpec extends AnyFunSpec {
46384638
assert(e.failedCodeFileName == (Some(fileName)))
46394639
assert(e.failedCodeLineNumber == (Some(thisLineNumber - 4)))
46404640
}
4641-
// SKIP-DOTTY-END
46424641

46434642
def woof(f: => Unit) = "woof"
46444643
def meow(x: Int = 0, y: Int = 3) = "meow"
@@ -6012,8 +6011,6 @@ class AssertionsSpec extends AnyFunSpec {
60126011
assert(e.failedCodeLineNumber == (Some(thisLineNumber - 4)))
60136012
}
60146013

6015-
// SKIP-DOTTY-START
6016-
// missing support for exists
60176014
it("should do nothing when is used to check l1.exists(_ == 3)") {
60186015
assume(l1.exists(_ == 3), ", dude")
60196016
}
@@ -6066,6 +6063,8 @@ class AssertionsSpec extends AnyFunSpec {
60666063
assert(e.failedCodeLineNumber == (Some(thisLineNumber - 4)))
60676064
}
60686065

6066+
// SKIP-DOTTY-START
6067+
// different printing for anonymous functions
60696068
it("should throw TestCanceledException with correct message and stack depth when is used to check l1.exists(_ > 3)") {
60706069
val e = intercept[TestCanceledException] {
60716070
assume(l1.exists(_ > 3), ", dude")
@@ -6092,6 +6091,7 @@ class AssertionsSpec extends AnyFunSpec {
60926091
assert(e.failedCodeFileName == (Some(fileName)))
60936092
assert(e.failedCodeLineNumber == (Some(thisLineNumber - 4)))
60946093
}
6094+
// SKIP-DOTTY-END
60956095

60966096
it("should throw TestCanceledException with correct message and stack depth when is used to check l3.exists(false)") {
60976097
val e = intercept[TestCanceledException] {
@@ -6101,7 +6101,6 @@ class AssertionsSpec extends AnyFunSpec {
61016101
assert(e.failedCodeFileName == (Some(fileName)))
61026102
assert(e.failedCodeLineNumber == (Some(thisLineNumber - 4)))
61036103
}
6104-
// SKIP-DOTTY-END
61056104

61066105
def woof(f: => Unit) = "woof"
61076106
def meow(x: Int = 0, y: Int = 3) = "meow"

0 commit comments

Comments
 (0)