From 0a1bef14caf89aae97ae6bee7e8021640c333831 Mon Sep 17 00:00:00 2001 From: Georgii Kovalev Date: Wed, 30 Apr 2025 11:34:11 +0500 Subject: [PATCH 1/2] fix assertTrue on new instance creation --- project/BuildHelper.scala | 2 +- .../zio/test/SmartAssertionScala3Spec.scala | 95 +++++++++++++++++++ .../scala/zio/test/SmartAssertionSpec.scala | 28 ++++++ .../src/main/scala-3/zio/test/Macros.scala | 43 ++++++--- 4 files changed, 152 insertions(+), 16 deletions(-) create mode 100644 test-tests/shared/src/test/scala-3/zio/test/SmartAssertionScala3Spec.scala diff --git a/project/BuildHelper.scala b/project/BuildHelper.scala index 4b1ad86f5703..e2673bc9aae9 100644 --- a/project/BuildHelper.scala +++ b/project/BuildHelper.scala @@ -177,7 +177,7 @@ object BuildHelper { case Some((2, 13)) => List("2.13+", "2.12-2.13") case Some((3, _)) => - List("2.13+") + List("2.13+", "3") case _ => List() } diff --git a/test-tests/shared/src/test/scala-3/zio/test/SmartAssertionScala3Spec.scala b/test-tests/shared/src/test/scala-3/zio/test/SmartAssertionScala3Spec.scala new file mode 100644 index 000000000000..609eaedb1402 --- /dev/null +++ b/test-tests/shared/src/test/scala-3/zio/test/SmartAssertionScala3Spec.scala @@ -0,0 +1,95 @@ +package zio.test + +object SmartAssertionScala3Spec extends ZIOBaseSpec { + + override def spec = + suite("SmartAssertionScala3Spec")( + suite("new instance creation")( + test("anonymous class (trait) with overload and type args - new instance") { + trait ClassWithOverload[X] { + def overloaded: Int = 1 + def overloaded(x: Int): Int = 1 + } + assertTrue(new ClassWithOverload[Int]() {}.overloaded == 1) + }, + test("anonymous class with overload - new instance") { + class ClassWithOverload { + def overloaded: Int = 1 + def overloaded(x: Int): Int = 1 + } + assertTrue(new ClassWithOverload() {}.overloaded == 1) + }, + test("anonymous class (trait) with overload - new instance") { + trait ClassWithOverload { + def overloaded: Int = 1 + def overloaded(x: Int): Int = 1 + } + assertTrue(new ClassWithOverload() {}.overloaded == 1) + }, + test("trait with parameter and overloaded methods") { + trait TraitOverloadedWithParameter(x: Int) { + def overloaded: Int = 1 + + def overloaded(x: Int) = 1 + } + + assertTrue(new TraitOverloadedWithParameter(1) {}.overloaded == 1) + }, + test("trait with overloaded methods with type args") { + trait TraitOverloadedAndTypeArgs[A] { + def overloaded: Int = 1 + + def overloaded(x: Int) = 1 + } + + assertTrue(new TraitOverloadedAndTypeArgs[Int] {}.overloaded == 1) + }, + test("trait with parameter and overloaded methods with type args") { + trait TraitOverloadedWithParameterAndTypeArgs[A](x: A) { + def overloaded: Int = 1 + + def overloaded(x: Int) = 1 + } + + assertTrue(new TraitOverloadedWithParameterAndTypeArgs[Int](1) {}.overloaded == 1) + }, + test("inlined trait with overloaded methods and parameter and type arg") { + trait TraitOverloadedWithParameterAndTypeArgs[A](x: A) { + def overloaded: Int = 1 + + def overloaded(x: Int) = 1 + } + @scala.annotation.nowarn + inline def create = new TraitOverloadedWithParameterAndTypeArgs[Int](1) {} + + assertTrue(create.overloaded == 1) + }, + test("inlined trait with overloaded methods and parameter") { + trait TraitOverloadedWithParameter(x: Int) { + def overloaded: Int = 1 + + def overloaded(x: Int) = 1 + } + @scala.annotation.nowarn + inline def create = + new TraitOverloadedWithParameter(1) {} + + assertTrue(create.overloaded == 1) + }, + test("inlined class with overloaded methods and parameter") { + class ClassOverloadedWithParameter(x: Int) { + def overloaded: Int = 1 + + def overloaded(x: Int) = 1 + } + + @scala.annotation.nowarn + inline def create = + new ClassOverloadedWithParameter(1) + + assertTrue(create.overloaded == 1) + } + ) + ) + +} diff --git a/test-tests/shared/src/test/scala/zio/test/SmartAssertionSpec.scala b/test-tests/shared/src/test/scala/zio/test/SmartAssertionSpec.scala index 51bf22982ae6..cb7ad4f875ad 100644 --- a/test-tests/shared/src/test/scala/zio/test/SmartAssertionSpec.scala +++ b/test-tests/shared/src/test/scala/zio/test/SmartAssertionSpec.scala @@ -628,6 +628,34 @@ object SmartAssertionSpec extends ZIOBaseSpec { val exit: Exit[String, Int] = Exit.succeed(1) assertTrue(exit.isSuccess) }, + test("class with overload - new instance") { + class ClassWithOverload { + def overloaded: Int = 1 + def overloaded(x: Int): Int = x + } + assertTrue(new ClassWithOverload().overloaded == 1) + }, + test("class with overload with type args - new instance") { + class ClassWithOverload[A] { + def overloaded: Int = 1 + def overloaded(x: Int): Int = x + } + assertTrue(new ClassWithOverload[Int]().overloaded == 1) + }, + test("class with overload with args - new instance") { + class ClassWithOverload(x: Int) { + def overloaded: Int = x + def overloaded(x: Int): Int = x + } + assertTrue(new ClassWithOverload(1).overloaded == 1) + }, + test("class with overload with args and type args - new instance") { + class ClassWithOverload[A](x: Int) { + def overloaded: Int = x + def overloaded(x: Int): Int = x + } + assertTrue(new ClassWithOverload[Int](1).overloaded == 1) + }, test("equalTo on java.lang.Boolean works") { val jBool = java.lang.Boolean.FALSE assertTrue(jBool == false) diff --git a/test/shared/src/main/scala-3/zio/test/Macros.scala b/test/shared/src/main/scala-3/zio/test/Macros.scala index 1939fe0449a9..59b810e7d184 100644 --- a/test/shared/src/main/scala-3/zio/test/Macros.scala +++ b/test/shared/src/main/scala-3/zio/test/Macros.scala @@ -345,29 +345,42 @@ object SmartAssertMacros { } case Unseal(method @ MethodCall(lhs, name, tpeArgs, args)) => - def body(param: Term) = + def body(param: Term): Term = (tpeArgs, args) match { case (Nil, None) => try Select.unique(param, name) catch { case _: AssertionError => - def getFieldOrMethod(s: Symbol) = - s.fieldMembers + def getFieldOrMethod(tpe: TypeRepr, owner: Tree): Select = { + val s = tpe.typeSymbol + val member = s.fieldMembers .find(f => f.name == name) .orElse(s.methodMember(name).filter(_.declarations.nonEmpty).headOption) + .getOrElse( + throw new Error(s"Could not resolve $name on ${owner.show(using Printer.TreeStructure)}") + ) + Select(param, member) + } - // Tries to find directly the referenced method on lhs's type (or if lhs is method, on lhs's returned type) - lhs.symbol.tree match { - case DefDef(_, _, tpt, _) => - getFieldOrMethod(tpt.symbol) match { - case Some(fieldOrMethod) => Select(param, fieldOrMethod) - case None => throw new Error(s"Could not resolve $name on $tpt") - } - case _ => - getFieldOrMethod(lhs.symbol) match { - case Some(fieldOrMethod) => Select(param, fieldOrMethod) - case None => throw new Error(s"Could not resolve $name on $lhs") - } + lhs.underlyingArgument match { + case Block(List(cls: ClassDef), term) => + // if this is new instance of anonymous class - take symbol from it instead of block + getFieldOrMethod(term.tpe, term) + + case Typed(Block(List(cls: ClassDef), term), _) => + getFieldOrMethod(term.tpe, term) + + // Tries to find directly the referenced method on lhs's type (or if lhs is method, on lhs's returned type) + case lhs => + if lhs.symbol == Symbol.noSymbol then + report.errorAndAbort(s"Can't get symbol of ${lhs.show(using Printer.TreeStructure)}") + else + lhs.symbol.tree match { + case DefDef(_, _, tpt, _) => + getFieldOrMethod(tpt.tpe, tpt) + case _ => + getFieldOrMethod(lhs.tpe, lhs) + } } } case (tpeArgs, Some(args)) => Select.overloaded(param, name, tpeArgs, args) From e4bec9951a6f1aa2036d28c6f29c45296d936366 Mon Sep 17 00:00:00 2001 From: Georgii Kovalev Date: Thu, 1 May 2025 11:36:08 +0500 Subject: [PATCH 2/2] replace error with report.errorAndAbort --- test/shared/src/main/scala-3/zio/test/Macros.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/shared/src/main/scala-3/zio/test/Macros.scala b/test/shared/src/main/scala-3/zio/test/Macros.scala index 59b810e7d184..8b2d8c1be85b 100644 --- a/test/shared/src/main/scala-3/zio/test/Macros.scala +++ b/test/shared/src/main/scala-3/zio/test/Macros.scala @@ -357,7 +357,7 @@ object SmartAssertMacros { .find(f => f.name == name) .orElse(s.methodMember(name).filter(_.declarations.nonEmpty).headOption) .getOrElse( - throw new Error(s"Could not resolve $name on ${owner.show(using Printer.TreeStructure)}") + report.errorAndAbort(s"Could not resolve $name on ${owner.show(using Printer.TreeStructure)}") ) Select(param, member) }