Welcome to Scala version 2.10.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_09).Type in expressions to have them evaluated.Type :help for more information.scala> object M extends org.scalamock.Mockdefined module Mscala> M.mock[com.paulbutcher.test.JavaInterface2](null)------------TypeApply(Select(Block(List(ClassDef(Modifiers(FINAL), newTypeName("$anon"), List(), Template(List(TypeTree()), emptyValDef, List(DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))), DefDef(Modifiers(OVERRIDE), newTermName("generic"), List(TypeDef(Modifiers(DEFERRED | PARAM), newTypeName("T"), List(), TypeBoundsTree(TypeTree(), TypeTree()))), List(List(ValDef(Modifiers(PARAM), newTermName("x$1"), TypeTree(), EmptyTree))), TypeTree(), Apply(Select(Select(This(newTypeName("$anon")), newTermName("mock$generic$0")), newTermName("apply")), List(Ident(newTermName("x$1"))))), ValDef(Modifiers(), newTermName("mock$generic$0"), TypeTree(), Apply(Select(New(AppliedTypeTree(Ident(org.scalamock.MockFunction1), List(TypeTree(), TypeTree()))), nme.CONSTRUCTOR), List(Literal(Constant(null)), Apply(Select(Select(Ident(newTermName("scala")), newTermName("Symbol")), newTermName("apply")), List(Literal(Constant("generic"))))))))))), Apply(Select(New(Ident(newTypeName("$anon"))), nme.CONSTRUCTOR), List())), newTermName("asInstanceOf")), List(TypeTree()))------------{final class $anon extends com.paulbutcher.test.JavaInterface2 {def <init>() = {super.<init>();()};override def generic[T >: Nothing <: Any](x$1: T): Unit = $anon.this.mock$generic$0.apply(x$1);val mock$generic$0 = new MockFunction1[T, Unit](null, scala.Symbol.apply("generic"))};new $anon()}.asInstanceOf[com.paulbutcher.test.JavaInterface2]------------<console>:9: error: overloaded method value apply with alternatives:(v1: T(in value mock$generic$0))Unit <and>(v1: (some other)T(in value mock$generic$0))Unitcannot be applied to (T)M.mock[com.paulbutcher.test.JavaInterface2](null)^
You mean for this part? https://github.com/paulbutcher/ScalaMock/blob/master/core/src/main/scala/org/scalamock/Mock.scala#L181
Probably you'll get away with substituteSymbols.
From what I understand, one of the problems you'll need to solve is replacing usages of old symbols originating from the mockee meth with fresh symbols created exclusively for mocks. substituteSymbols is an easy solutions. Don't know any good examples, but its usage should be pretty straightforward.
scala> import org.scalamock.Implement._import org.scalamock.Implement._scala> trait Foo { def m(x: Int, y: Double): String }defined trait Fooscala> val f = implement[Foo]------------TypeApply(Select(Block(List(ClassDef(Modifiers(FINAL), newTypeName("$anon"), List(), Template(List(TypeTree()), emptyValDef, List(DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))), DefDef(Modifiers(OVERRIDE), newTermName("m"), List(), List(List(ValDef(Modifiers(PARAM), newTermName("x"), TypeTree(), EmptyTree), ValDef(Modifiers(PARAM), newTermName("y"), TypeTree(), EmptyTree))), TypeTree(), TypeApply(Select(Literal(Constant(null)), newTermName("asInstanceOf")), List(TypeTree()))))))), Apply(Select(New(Ident(newTypeName("$anon"))), nme.CONSTRUCTOR), List())), newTermName("asInstanceOf")), List(TypeTree()))------------{final class $anon extends Foo {
def <init>() = {super.<init>();()};
override def m(x: Int, y: Double): String = null.asInstanceOf[String]};new $anon()}.asInstanceOf[Foo]------------f: Foo = $anon$1@341db69cscala> f.m(1, 2)res1: String = null
I've written the code to define new type parameters for type-parameterized methods:And this seems to work fine. But I'm struggling to work out how to rewrite this:to use substituteSymbols.Working out which symbols to substitute is easy enough - it's just the typeParams of the method that's being implemented. But where do I get the symbols to substitute them with? I've created a set of TypeDefs for the new type parameters, but there's no way (that I'm aware of) to get a symbol from a TypeDef?I'm sure that I'm missing something simple - can you point me at it? :-)
You've distilled the essence of the problem, and it's a thorny one.
Long story short, I'm not sure you can implement your macro through the public macro API, and even if you used the internal API and started assigning symbols yourself, it will be challenging. But perhaps someone else will see something I'm missing.
You've distilled the essence of the problem, and it's a thorny one.
So I've made a start on knocking something together, and (so far at least) it looks like it might have legs. There's certainly a way to go before I cover all the cases, but I'm hopeful that it'll prove tractable:If there are lurking dragons that I should be aware of, I'd be very grateful if you could point them out.
But persisting for a bit longer is probably worthwhile as an educational exercise.
Oh - one further thought. Is there any way to have the macro runtime detect when a macro writer makes this kind of mistake? There was a discussion about something like this on scala-internals a while ago:The problem with the current status quo is that it's very easy to create something that:a) looks reasonable to the untrained eyeb) typechecks and compilesc) apparently works just fine, until you give it something "complicated"This is recipe for heartbreak on the part of a macro writer. Especially as things currently stand with limited documentation and examples. I've had to work things out by trial and error, and in the absence of a true understanding of "the right way" it's easy to assume that if something compiles and (apparently) works, it's right :-)