Vor allem die 1 + "foo"
Fall wird schwierig sein, weil es nicht wirklich jede implizite Konvertierung dort geschieht, ist: Int
selbst really, truly does have this +
method (unfortunately).
Sie haben also kein Glück, wenn das Ihr Anwendungsfall ist, aber es ist möglich, das zu tun, was Sie allgemein beschreiben.
case class Foo(i: Int)
case class Bar(s: String)
implicit def foo2bar(foo: Foo) = Bar(foo.i.toString)
Zuerst für den eleganten Ansatz:: Ich werde die folgende Einstellung in meinen Beispielen unten nehmen
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
)
}
}
die wie gewünscht funktioniert:
scala> ConversionDetector.sniff[Bar](Foo(42))
res1: Boolean = true
scala> ConversionDetector.sniff[Bar](foo2bar(Foo(42)))
res2: Boolean = false
Leider ist dieses nicht typisierten Makros erfordert, welche derzeit nur in Macro Paradise verfügbar sind.
Sie bekommen, was Sie in 2.10 mit einfachen alten def
Makros wollen, aber es ist ein bisschen wie ein 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
}
)
}
}
Und wieder:
scala> ConversionDetector.sniff[Bar](Foo(42))
res1: Boolean = true
scala> ConversionDetector.sniff[Bar](foo2bar(Foo(42)))
res2: Boolean = false
Der Trick ist, um die Plätze zu suchen Wo wir in unserem abstrakten Syntaxbaum Funktionsanwendungen sehen und dann prüfen, ob die Positionen des Apply
-Knotens und seines untergeordneten Elements fun
dieselbe Spalte haben, was darauf hinweist, dass der Methodenaufruf nicht explizit in der Quelle vorhanden ist.
Dank für die Beantwortung. Ich wusste nicht, dass "Int" eine Methode '+' hat, die 'String' akzeptiert, aber ich meinte den allgemeinen Fall trotzdem. Ich habe darüber nachgedacht, 'Position's auch zu benutzen, aber das riecht tatsächlich ein bisschen 'hacky'. Ich denke, es wäre cool, wenn 'Tree's eine Flagge hätte, die sagen würde" dieser Baum wurde automatisch vom Compiler abgeleitet ". – ghik
Ich habe gerade einen [Kommentar] (https://github.com/scala/scala/blob/master/src/reflect/scala/reflect/internal/Trees.scala#L431) in 'scala-reflect' Quellen gefunden, die sprechen über ein potentielles Flag auf 'Apply' AST, das eine implizite Konvertierung anzeigt. Und es sieht jetzt so aus, dass es eine separate Klasse gibt, um dies anzuzeigen (leider ist es intern). – ghik
Leider führt die Verwendung des 'Position'-Tricks dazu, dass Ihr Code bei der Verwendung in der REPL explodiert. @ghik, ich denke du bist auf dem richtigen Weg mit deinem letzten Kommentar, auf den @ EugeneBurmakos Antwort näher eingeht. –