From 7026d2aa53f85f2af9c9469d7e8c53e4f9ffc180 Mon Sep 17 00:00:00 2001 From: sijarsu <> Date: Fri, 22 May 2020 10:19:46 +0200 Subject: [PATCH 1/3] initial --- .../macwire/internals/DependencyResolver.scala | 14 ++++++++++++++ .../test/resources/test-cases/_t142.success | 14 ++++++++++++++ .../com/softwaremill/macwire/WireTest.scala | 18 ++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 tests/src/test/resources/test-cases/_t142.success create mode 100644 tests/src/test/scala/com/softwaremill/macwire/WireTest.scala diff --git a/macros/src/main/scala/com/softwaremill/macwire/internals/DependencyResolver.scala b/macros/src/main/scala/com/softwaremill/macwire/internals/DependencyResolver.scala index 05e71df8..55f10ec1 100644 --- a/macros/src/main/scala/com/softwaremill/macwire/internals/DependencyResolver.scala +++ b/macros/src/main/scala/com/softwaremill/macwire/internals/DependencyResolver.scala @@ -19,6 +19,11 @@ private[macwire] class DependencyResolver[C <: blackbox.Context](val c: C, debug def resolve(param: Symbol, t: Type): Tree = { eligibleValues.findInFirstScope(t).toList match { + case Nil if isOption(t) => + getOptionArg(t).flatMap(u => eligibleValues.findInFirstScope(u).toList.headOption) match { //FIXME: handle multiple values (tests, tests...) + case Some(argTree) => q"Some($argTree)" + case None => q"None" + } case Nil => c.abort(c.enclosingPosition, s"Cannot find a value of type: [$t]") case value :: Nil => val forwardValues = eligibleValues.findInScope(t, LocalForward) @@ -31,6 +36,15 @@ private[macwire] class DependencyResolver[C <: blackbox.Context](val c: C, debug } } + private def isOption(t: Type): Boolean = getOptionArg(t).nonEmpty + + private def getOptionArg(t: Type): Option[Type] = t.baseType(typeOf[Option[_]].typeSymbol) match { + case TypeRef(_, _, arg :: Nil) => Some(arg) + case NoType => None + } + + + /** @return all the instances of type `t` that are accessible. */ def resolveAll(t: Type): Iterable[Tree] = { diff --git a/tests/src/test/resources/test-cases/_t142.success b/tests/src/test/resources/test-cases/_t142.success new file mode 100644 index 00000000..4941ee2c --- /dev/null +++ b/tests/src/test/resources/test-cases/_t142.success @@ -0,0 +1,14 @@ +class A() +class B(val oa: Option[A]) + +object TestSome { + val a = new A() + val b = wire[B] +} + +object TestNone { + val b = wire[B] +} + +require(TestSome.b.oa.contains(TestSome.a)) +require(TestNone.b.oa.isEmpty) diff --git a/tests/src/test/scala/com/softwaremill/macwire/WireTest.scala b/tests/src/test/scala/com/softwaremill/macwire/WireTest.scala new file mode 100644 index 00000000..f52de70d --- /dev/null +++ b/tests/src/test/scala/com/softwaremill/macwire/WireTest.scala @@ -0,0 +1,18 @@ +package com.softwaremill.macwire + +object WireTest extends App { + class A() + class B(val oa: Option[A]) + + object TestSome { + val a = new A() + val b = wire[B] + } + + object TestNone { + val b = wire[B] + } + + require(TestSome.b.oa.contains(TestSome.a)) + require(TestNone.b.oa.isEmpty) +} From 3935a4c08062542364678782d51594c1c5e1a206 Mon Sep 17 00:00:00 2001 From: sijarsu <> Date: Sun, 24 May 2020 19:23:01 +0200 Subject: [PATCH 2/3] cleanup --- .../internals/DependencyResolver.scala | 62 +++++++++++++------ .../resources/test-cases/_t142_nested.success | 20 ++++++ .../com/softwaremill/macwire/WireTest.scala | 18 ------ 3 files changed, 62 insertions(+), 38 deletions(-) create mode 100644 tests/src/test/resources/test-cases/_t142_nested.success delete mode 100644 tests/src/test/scala/com/softwaremill/macwire/WireTest.scala diff --git a/macros/src/main/scala/com/softwaremill/macwire/internals/DependencyResolver.scala b/macros/src/main/scala/com/softwaremill/macwire/internals/DependencyResolver.scala index 55f10ec1..4225029a 100644 --- a/macros/src/main/scala/com/softwaremill/macwire/internals/DependencyResolver.scala +++ b/macros/src/main/scala/com/softwaremill/macwire/internals/DependencyResolver.scala @@ -18,32 +18,54 @@ private[macwire] class DependencyResolver[C <: blackbox.Context](val c: C, debug */ def resolve(param: Symbol, t: Type): Tree = { - eligibleValues.findInFirstScope(t).toList match { - case Nil if isOption(t) => - getOptionArg(t).flatMap(u => eligibleValues.findInFirstScope(u).toList.headOption) match { //FIXME: handle multiple values (tests, tests...) - case Some(argTree) => q"Some($argTree)" - case None => q"None" - } - case Nil => c.abort(c.enclosingPosition, s"Cannot find a value of type: [$t]") + def resolve(t: Type, handler: (Type, List[Tree]) => Tree): Tree = { + val trees: List[Tree] = eligibleValues.findInFirstScope(t).toList match { + case Nil if isOption(t) => List(resolve(getOptionArg(t).get, optionHandler)) + case ts => ts + } + handler(t, trees) + } + + def basicHandler(t: Type, trees: List[Tree]): Tree = trees match { + case Nil => noValueError(t) case value :: Nil => - val forwardValues = eligibleValues.findInScope(t, LocalForward) - if (forwardValues.nonEmpty) { - c.warning(c.enclosingPosition, s"Found [$value] for parameter [${param.name}], " + - s"but a forward reference [${forwardValues.mkString(", ")}] was also eligible") - } + forwardValuesWarn(value) value - case values => c.abort(c.enclosingPosition, s"Found multiple values of type [$t]: [$values]") + case values => multipleValuesError(t, values) } - } - private def isOption(t: Type): Boolean = getOptionArg(t).nonEmpty + def optionHandler(t: Type, trees: List[Tree]): Tree = trees match { + case Nil => q"None" + case value :: Nil if value.equalsStructure(q"None") => q"None" + case value :: Nil => + forwardValuesWarn(value) + q"Some($value)" + case values => multipleValuesError(t, values) + } - private def getOptionArg(t: Type): Option[Type] = t.baseType(typeOf[Option[_]].typeSymbol) match { - case TypeRef(_, _, arg :: Nil) => Some(arg) - case NoType => None - } + def noValueError(t: Type): Nothing = + c.abort(c.enclosingPosition, s"Cannot find a value of type: [$t]") + + def multipleValuesError(t: Type, values: List[Tree]): Nothing = + c.abort(c.enclosingPosition, s"Found multiple values of type [$t]: [$values]") - + def forwardValuesWarn(value: Tree): Unit = { + val forwardValues = eligibleValues.findInScope(t, LocalForward) + if (forwardValues.nonEmpty) { + c.warning(c.enclosingPosition, s"Found [$value] for parameter [${param.name}], " + + s"but a forward reference [${forwardValues.mkString(", ")}] was also eligible") + } + } + + def isOption(t: Type): Boolean = getOptionArg(t).nonEmpty + + def getOptionArg(t: Type): Option[Type] = t.baseType(typeOf[Option[_]].typeSymbol) match { + case TypeRef(_, _, arg :: Nil) => Some(arg) + case NoType => None + } + + resolve(t, basicHandler) + } /** @return all the instances of type `t` that are accessible. */ diff --git a/tests/src/test/resources/test-cases/_t142_nested.success b/tests/src/test/resources/test-cases/_t142_nested.success new file mode 100644 index 00000000..fb4dcbd4 --- /dev/null +++ b/tests/src/test/resources/test-cases/_t142_nested.success @@ -0,0 +1,20 @@ +class A() +class B(val oa: Option[Option[A]]) + +object TestSome { + val a = Some(new A()) + val b = wire[B] +} + +object TestNestedSome { + val a = new A() + val b = wire[B] +} + +object TestNone { + val b = wire[B] +} + +require(TestSome.b.oa.contains(TestSome.a)) +require(TestNestedSome.b.oa.get.contains(TestNestedSome.a)) +require(TestNone.b.oa.isEmpty) diff --git a/tests/src/test/scala/com/softwaremill/macwire/WireTest.scala b/tests/src/test/scala/com/softwaremill/macwire/WireTest.scala deleted file mode 100644 index f52de70d..00000000 --- a/tests/src/test/scala/com/softwaremill/macwire/WireTest.scala +++ /dev/null @@ -1,18 +0,0 @@ -package com.softwaremill.macwire - -object WireTest extends App { - class A() - class B(val oa: Option[A]) - - object TestSome { - val a = new A() - val b = wire[B] - } - - object TestNone { - val b = wire[B] - } - - require(TestSome.b.oa.contains(TestSome.a)) - require(TestNone.b.oa.isEmpty) -} From eda0a5df8c41b9d6320455d2f48eded7e034747a Mon Sep 17 00:00:00 2001 From: sijarsu <> Date: Sun, 24 May 2020 20:27:42 +0200 Subject: [PATCH 3/3] cleanup tests --- .../src/test/resources/test-cases/_t142.success | 17 ++++++++++++----- .../resources/test-cases/_t142_nested.success | 10 +++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/tests/src/test/resources/test-cases/_t142.success b/tests/src/test/resources/test-cases/_t142.success index 4941ee2c..817bc0e0 100644 --- a/tests/src/test/resources/test-cases/_t142.success +++ b/tests/src/test/resources/test-cases/_t142.success @@ -1,14 +1,21 @@ class A() class B(val oa: Option[A]) -object TestSome { - val a = new A() - val b = wire[B] +object TestSomeOption { + val a = new A() + val oa = Some(new A()) + val b = wire[B] +} + +object TestSomeValue { + val a = new A() + val b = wire[B] } object TestNone { - val b = wire[B] + val b = wire[B] } -require(TestSome.b.oa.contains(TestSome.a)) +require(TestSomeOption.b.oa.contains(TestSomeOption.oa.get)) +require(TestSomeValue.b.oa.contains(TestSomeValue.a)) require(TestNone.b.oa.isEmpty) diff --git a/tests/src/test/resources/test-cases/_t142_nested.success b/tests/src/test/resources/test-cases/_t142_nested.success index fb4dcbd4..a28435f1 100644 --- a/tests/src/test/resources/test-cases/_t142_nested.success +++ b/tests/src/test/resources/test-cases/_t142_nested.success @@ -2,17 +2,17 @@ class A() class B(val oa: Option[Option[A]]) object TestSome { - val a = Some(new A()) - val b = wire[B] + val a = Some(new A()) + val b = wire[B] } object TestNestedSome { - val a = new A() - val b = wire[B] + val a = new A() + val b = wire[B] } object TestNone { - val b = wire[B] + val b = wire[B] } require(TestSome.b.oa.contains(TestSome.a))