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..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,17 +18,53 @@ 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 => 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) } + + 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) + } + + 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.success b/tests/src/test/resources/test-cases/_t142.success new file mode 100644 index 00000000..817bc0e0 --- /dev/null +++ b/tests/src/test/resources/test-cases/_t142.success @@ -0,0 +1,21 @@ +class A() +class B(val oa: Option[A]) + +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] +} + +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 new file mode 100644 index 00000000..a28435f1 --- /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)