First of all, the case 1 + "foo" will be complicated, because in fact there is no implicit conversion: Int really, really has this + method ( unfortunately ).
So, you're out of luck if this is your use case, but you can do what you describe more generally. In the examples below, I will use the following setting:
case class Foo(i: Int) case class Bar(s: String) implicit def foo2bar(foo: Foo) = Bar(foo.i.toString)
First for an elegant approach:
object ConversionDetector { import scala.language.experimental.macros import scala.reflect.macros.Context def sniff[A](tree: _): Boolean = macro sniff_impl[A] def sniff_impl[A: c.WeakTypeTag](c: Context)(tree: c.Tree) = { // First we confirm that the code typechecks at all: c.typeCheck(tree, c.universe.weakTypeOf[A]) // Now we try it without views: c.literal( c.typeCheck(tree, c.universe.weakTypeOf[A], true, true, false).isEmpty ) } }
Works as desired:
scala> ConversionDetector.sniff[Bar](Foo(42)) res1: Boolean = true scala> ConversionDetector.sniff[Bar](foo2bar(Foo(42))) res2: Boolean = false
Unfortunately, this requires untyped macros, which are currently only available in Macro Paradise .
You can get what you want with the old old def macros in 2.10, but this is a bit of a hack:
object ConversionDetector { import scala.language.experimental.macros import scala.reflect.macros.Context def sniff[A](a: A) = macro sniff_impl[A] def sniff_impl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]) = { import c.universe._ c.literal( a.tree.exists { case app @ Apply(fun, _) => app.pos.column == fun.pos.column case _ => false } ) } }
And again:
scala> ConversionDetector.sniff[Bar](Foo(42)) res1: Boolean = true scala> ConversionDetector.sniff[Bar](foo2bar(Foo(42))) res2: Boolean = false
The trick is to look for places where we see the functional application in our abstract syntax tree, and then check if the positions of the child node Apply node and its fun the same column, which indicates that the method call is clearly not present in the source.