2013-03-19 20 views

Trả lời

3

Trước hết, trường hợp 1 + "foo" sẽ là khó khăn vì không có thực sự bất kỳ chuyển đổi ngầm xảy ra ở đó: Int tự really, truly does have this + method (unfortunately).

Vì vậy, bạn không may mắn nếu đó là trường hợp sử dụng của bạn, nhưng bạn có thể làm những gì bạn mô tả thường xuyên hơn. Tôi sẽ giả thiết lập sau trong ví dụ của tôi dưới đây:

case class Foo(i: Int) 
case class Bar(s: String) 
implicit def foo2bar(foo: Foo) = Bar(foo.i.toString) 

đầu tiên cho phương pháp tiếp cận thanh lịch:

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 
    ) 
    } 
} 

Những công trình như mong muốn:

scala> ConversionDetector.sniff[Bar](Foo(42)) 
res1: Boolean = true 

scala> ConversionDetector.sniff[Bar](foo2bar(Foo(42))) 
res2: Boolean = false 

Đáng tiếc là điều này đòi hỏi các macro untyped, hiện chỉ có sẵn trong Macro Paradise.

Bạn có thể có được những gì bạn muốn với đồng bằng cũ def macro trong 2.10, nhưng đó là một chút của một 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 
     } 
    ) 
    } 
} 

Và một lần nữa:

scala> ConversionDetector.sniff[Bar](Foo(42)) 
res1: Boolean = true 

scala> ConversionDetector.sniff[Bar](foo2bar(Foo(42))) 
res2: Boolean = false 

Bí quyết là để tìm kiếm nơi nơi chúng ta thấy ứng dụng chức năng trong cây cú pháp trừu tượng của chúng ta, và sau đó kiểm tra xem các vị trí của nút Apply và con của nó có cùng một cột hay không, cho biết rằng lời gọi phương thức không hiển thị rõ ràng trong nguồn.

+0

Cảm ơn đã trả lời. Tôi không biết rằng 'Int' có một phương thức' + 'chấp nhận' String' nhưng tôi có nghĩa là trường hợp chung. Tôi đã suy nghĩ về việc sử dụng 'Vị trí quá, nhưng điều này thực sự có mùi một chút 'hacky'. Tôi nghĩ sẽ rất tuyệt nếu 'Tree's có một số lá cờ có thể nói" cây này được trình biên dịch tự động suy ra ". – ghik

+1

Tôi vừa tìm thấy một [bình luận] (https://github.com/scala/scala/blob/master/src/reflect/scala/reflect/internal/Trees.scala#L431) trong các nguồn 'scala-reflect' mà nói về một số cờ tiềm năng trên AST 'Áp dụng' sẽ chỉ ra chuyển đổi ngầm định. Và có vẻ như bây giờ có một lớp riêng biệt để chỉ ra điều này (nó là nội bộ, thật không may). – ghik

+0

Thật không may bằng cách sử dụng thủ thuật 'Position' sẽ có nghĩa là mã của bạn sẽ bị thổi khi được sử dụng trong REPL. @ghik, tôi nghĩ rằng bạn đang đi đúng hướng với bình luận cuối cùng của mình, câu trả lời của EugeneBurmako là gì. –

2

Đó là một hack, nhưng nó có thể giúp bạn:

import scala.reflect.macros.Context 
import language.experimental.macros 

object Macros { 
    def impl(c: Context)(x: c.Expr[Int]) = { 
    import c.universe._ 
    val hasInferredImplicitArgs = x.tree.isInstanceOf[scala.reflect.internal.Trees#ApplyToImplicitArgs] 
    val isAnImplicitConversion = x.tree.isInstanceOf[scala.reflect.internal.Trees#ApplyImplicitView] 
    println(s"x = ${x.tree}, args = $hasInferredImplicitArgs, view = $isAnImplicitConversion") 
    c.literalUnit 
    } 

    def foo(x: Int) = macro impl 
} 

import language.implicitConversions 
import scala.reflect.ClassTag 

object Test extends App { 
    def bar[T: ClassTag](x: T) = x 
    implicit def foo(x: String): Int = augmentString(x).toInt 
    Macros.foo(2) 
    Macros.foo(bar(2)) 
    Macros.foo("2") 
} 

08:30 ~/Projects/210x/sandbox (2.10.x)$ ss 
x = 2, args = false, view = false 
x = Test.this.bar[Int](2)(ClassTag.Int), args = true, view = false 
x = Test.this.foo("2"), args = false, view = true