summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVlad Ureche <vlad.ureche@gmail.com>2012-04-14 12:40:03 +0200
committerVlad Ureche <vlad.ureche@gmail.com>2012-04-14 12:40:03 +0200
commitcb2468a8a0971207ceb33c7bb9d2596bdbac8072 (patch)
treedde2544e7d309e03cffb2511a0a92872f8f3f9ee
parent95ad0bbddb09f1177cf8cd6f0e8ef2f7135bf4ae (diff)
downloadscala-cb2468a8a0971207ceb33c7bb9d2596bdbac8072.tar.gz
scala-cb2468a8a0971207ceb33c7bb9d2596bdbac8072.tar.bz2
scala-cb2468a8a0971207ceb33c7bb9d2596bdbac8072.zip
Reimplemented shadowing between class members
and implicit pimped on members. Thanks to Lukas for pointing it out!
-rw-r--r--src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala22
-rw-r--r--test/scaladoc/resources/implicits-base-res.scala66
-rw-r--r--test/scaladoc/resources/implicits-shadowing-res.scala64
-rw-r--r--test/scaladoc/run/implicits-shadowing.check1
-rw-r--r--test/scaladoc/run/implicits-shadowing.scala70
5 files changed, 189 insertions, 34 deletions
diff --git a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
index 23bef02bed..0e44933ac6 100644
--- a/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
+++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactoryImplicitSupport.scala
@@ -137,8 +137,8 @@ trait ModelFactoryImplicitSupport {
// Members inherited by implicit conversions cannot override actual members
memberSyms = memberSyms.filterNot((sym1: Symbol) =>
- existingMembers.exists(sym2 => sym1.name == sym2.name &&
- isSameType(toType.memberInfo(sym1), sym.info.memberInfo(sym2))))
+ existingMembers.exists(sym2 => sym1.name == sym2.name &&
+ !isDistinguishableFrom(toType.memberInfo(sym1), sym.info.memberInfo(sym2))))
debug(" -> full type: " + toType)
if (constraints.length != 0) {
@@ -498,4 +498,22 @@ trait ModelFactoryImplicitSupport {
(aSym.isMethod || aSym.isGetter || aSym.isSetter) &&
(aSym.nameString != "getClass")
}
+
+ /* To put it very bluntly: checks if you can call implicitly added method with t1 when t2 is already there in the
+ * class. We suppose the name of the two members coincides
+ *
+ * The trick here is that the resultType does not matter - the condition for removal it that paramss have the same
+ * structure (A => B => C may not override (A, B) => C) and that all the types involved are
+ * of the implcit conversion's member are subtypes of the parent members' parameters */
+ def isDistinguishableFrom(t1: Type, t2: Type): Boolean =
+ if (t1.paramss.map(_.length) == t2.paramss.map(_.length)) {
+ for ((t1p, t2p) <- t1.paramss.flatten zip t2.paramss.flatten)
+ if (!isSubType(t1 memberInfo t1p, t2 memberInfo t2p))
+ return true // if on the corresponding parameter you give a type that is in t1 but not in t2
+ // example:
+ // def foo(a: Either[Int, Double]): Int = 3
+ // def foo(b: Left[T1]): Int = 6
+ // a.foo(Right(4.5d)) prints out 3 :)
+ false
+ } else true // the member structure is different foo(3, 5) vs foo(3)(5)
} \ No newline at end of file
diff --git a/test/scaladoc/resources/implicits-base-res.scala b/test/scaladoc/resources/implicits-base-res.scala
index ce86ba8918..3e3d0f01a6 100644
--- a/test/scaladoc/resources/implicits-base-res.scala
+++ b/test/scaladoc/resources/implicits-base-res.scala
@@ -11,20 +11,22 @@ trait MyNumeric[R]
* - tests the complete type inference
* - the following inherited methods should appear:
* {{{
- * def convToGtColonDoubleA: Double // pimpA3: with a constraint that T <: Double
- * def convToIntA: Int // pimpA2: with a constraint that T = Int
- * def convToManifestA: T // pimpA7: with 2 constraints: T: Manifest and T <: Double
- * def convToMyNumericA: T // pimpA6: with a constraint that there is x: MyNumeric[T] implicit in scope
- * def convToNumericA: T // pimpA1: with a constraint that there is x: Numeric[T] implicit in scope
- * def convToPimpedA: Bar[Foo[T]] // pimpA5: no constraints
- * def convToPimpedA: S // pimpA4: with 3 constraints: T = Foo[Bar[S]], S: Foo and S: Bar
- * def convToTraversableOps: T // pimpA7: with 2 constraints: T: Manifest and T <: Double
- * // should not be abstract!
+ * def convToGtColonDoubleA(x: Double) // pimpA3: with a constraint that T <: Double
+ * def convToIntA(x: Int) // pimpA2: with a constraint that T = Int
+ * def convToManifestA(x: T) // pimpA7: with 2 constraints: T: Manifest and T <: Double
+ * def convToMyNumericA(x: T) // pimpA6: with a constraint that there is x: MyNumeric[T] implicit in scope
+ * def convToNumericA(x: T) // pimpA1: with a constraint that there is x: Numeric[T] implicit in scope
+ * def convToPimpedA(x: Bar[Foo[T]]) // pimpA5: no constraints
+ * def convToPimpedA(x: S) // pimpA4: with 3 constraints: T = Foo[Bar[S]], S: Foo and S: Bar
+ * def convToTraversableOps(x: T) // pimpA7: with 2 constraints: T: Manifest and T <: Double
+ * // should not be abstract!
* }}}
*/
class A[T] {
/** This should prevent the implicitly inherited `def convToPimpedA: T` from `pimpA0` from showing up */
- def convToPimpedA: T = sys.error("Let's check it out!")
+ def convToPimpedA(x: T): T = sys.error("Let's check it out!")
+ /** This should check implicit member elimination in the case of subtyping */
+ def foo(a: T, b: AnyRef): T
}
/** Companion object with implicit transformations */
object A {
@@ -38,7 +40,7 @@ object A {
implicit def pimpA5[Z](a: A[Z]): PimpedA[Bar[Foo[Z]]] = sys.error("not implemented")
implicit def pimpA6[Z: MyNumeric](a: A[Z]) = new MyNumericA[Z](a)
// TODO: Add H <: Double and see why it crashes for C and D -- context bounds, need to check!
- implicit def pimpA7[H <: Double : Manifest](a: A[H]) = new ManifestA[H](a) with MyTraversableOps[H] { def convToTraversableOps: H = sys.error("no") }
+ implicit def pimpA7[H <: Double : Manifest](a: A[H]) = new ManifestA[H](a) with MyTraversableOps[H] { def convToTraversableOps(x: H): H = sys.error("no") }
}
@@ -46,13 +48,13 @@ object A {
* - tests the existential type solving
* - the following inherited methods should appear:
* {{{
- * def convToGtColonDoubleA: Double // pimpA3: no constraints
- * def convToManifestA: Double // pimpA7: no constraints
- * def convToMyNumericA: Double // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[Double] implicit in scope
- * def convToNumericA: Double // pimpA1: no constraintsd
- * def convToPimpedA: Bar[Foo[Double]] // pimpA5: no constraints
- * def convToTraversableOps: Double // pimpA7: no constraints
- * // should not be abstract!
+ * def convToGtColonDoubleA(x: Double) // pimpA3: no constraints
+ * def convToManifestA(x: Double) // pimpA7: no constraints
+ * def convToMyNumericA(x: Double) // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[Double] implicit in scope
+ * def convToNumericA(x: Double) // pimpA1: no constraintsd
+ * def convToPimpedA(x: Bar[Foo[Double]]) // pimpA5: no constraints
+ * def convToTraversableOps(x: Double) // pimpA7: no constraints
+ * // should not be abstract!
* }}}
*/
class B extends A[Double]
@@ -63,10 +65,10 @@ object B extends A
* - tests asSeenFrom
* - the following inherited methods should appear:
* {{{
- * def convToIntA: Int // pimpA2: no constraints
- * def convToMyNumericA: Int // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[Int] implicit in scope
- * def convToNumericA: Int // pimpA1: no constraints
- * def convToPimpedA: Bar[Foo[Int]] // pimpA5: no constraints
+ * def convToIntA(x: Int) // pimpA2: no constraints
+ * def convToMyNumericA(x: Int) // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[Int] implicit in scope
+ * def convToNumericA(x: Int) // pimpA1: no constraints
+ * def convToPimpedA(x: Bar[Foo[Int]]) // pimpA5: no constraints
* }}}
*/
class C extends A[Int]
@@ -77,9 +79,9 @@ object C extends A
* - tests implicit elimination
* - the following inherited methods should appear:
* {{{
- * def convToMyNumericA: String // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[String] implicit in scope
- * def convToNumericA: String // pimpA1: (if showAll is set) with a constraint that there is x: Numeric[String] implicit in scope
- * def convToPimpedA: Bar[Foo[String]] // pimpA5: no constraints
+ * def convToMyNumericA(x: String) // pimpA6: (if showAll is set) with a constraint that there is x: MyNumeric[String] implicit in scope
+ * def convToNumericA(x: String) // pimpA1: (if showAll is set) with a constraint that there is x: Numeric[String] implicit in scope
+ * def convToPimpedA(x: Bar[Foo[String]]) // pimpA5: no constraints
* }}}
*/
class D extends A[String]
@@ -92,7 +94,7 @@ object D extends A
* - A, B and C should be implicitly converted to this */
class PimpedA[V](a: A[V]) {
/** The convToPimpedA: V documentation... */
- def convToPimpedA: V = sys.error("Not implemented")
+ def convToPimpedA(x: V): V = sys.error("Not implemented")
}
/** NumericA class <br/>
@@ -100,7 +102,7 @@ class PimpedA[V](a: A[V]) {
* - A, B and C should be implicitly converted to this */
class NumericA[U: Numeric](a: A[U]) {
/** The convToNumericA: U documentation... */
- def convToNumericA: U = implicitly[Numeric[U]].zero
+ def convToNumericA(x: U): U = implicitly[Numeric[U]].zero
}
/** IntA class <br/>
@@ -108,7 +110,7 @@ class NumericA[U: Numeric](a: A[U]) {
* - A and C should be implicitly converted to this */
class IntA(a: A[Int]) {
/** The convToIntA: Int documentation... */
- def convToIntA: Int = 0
+ def convToIntA(x: Int): Int = 0
}
/** GtColonDoubleA class <br/>
@@ -116,7 +118,7 @@ class IntA(a: A[Int]) {
* - A and B should be implicitly converted to this */
class GtColonDoubleA(a: A[T] forSome { type T <: Double }) {
/** The convToGtColonDoubleA: Double documentation... */
- def convToGtColonDoubleA: Double = 0
+ def convToGtColonDoubleA(x: Double): Double = 0
}
/** MyNumericA class <br/>
@@ -124,7 +126,7 @@ class GtColonDoubleA(a: A[T] forSome { type T <: Double }) {
* - A should be implicitly converted to this */
class MyNumericA[U: MyNumeric](a: A[U]) {
/** The convToMyNumericA: U documentation... */
- def convToMyNumericA: U = sys.error("dunno")
+ def convToMyNumericA(x: U): U = sys.error("dunno")
}
/** ManifestA class <br/>
@@ -132,7 +134,7 @@ class MyNumericA[U: MyNumeric](a: A[U]) {
* - A, B, C, D should be implicitly converted to this */
class ManifestA[W: Manifest](a: A[W]) {
/** The convToManifestA: W documentation... */
- def convToManifestA: W = sys.error("dunno")
+ def convToManifestA(x: W): W = sys.error("dunno")
}
/** MyTraversableOps class <br/>
@@ -140,6 +142,6 @@ class ManifestA[W: Manifest](a: A[W]) {
*/
trait MyTraversableOps[S] {
/** The convToTraversableOps: S documentation... */
- def convToTraversableOps: S
+ def convToTraversableOps(x: S): S
}
diff --git a/test/scaladoc/resources/implicits-shadowing-res.scala b/test/scaladoc/resources/implicits-shadowing-res.scala
new file mode 100644
index 0000000000..c5e9493bf3
--- /dev/null
+++ b/test/scaladoc/resources/implicits-shadowing-res.scala
@@ -0,0 +1,64 @@
+/**
+ * Test scaladoc implicits distinguishing -- supress all members by implicit conversion that are shadowed by the
+ * class' own members
+ *
+ * {{{
+ * scala> class A { def foo(t: String) = 4 }
+ * defined class A
+ *
+ * scala> class B { def foo(t: Any) = 5 }
+ * defined class B
+ *
+ * scala> implicit def AtoB(a:A) = new B
+ * AtoB: (a: A)B
+ *
+ * scala> val a = new A
+ * a: A = A@28f553e3
+ *
+ * scala> a.foo("T")
+ * res1: Int = 4
+ *
+ * scala> a.foo(4)
+ * res2: Int = 5
+ * }}}
+ */
+package scala.test.scaladoc.implicits.shadowing
+import language.implicitConversions // according to SIP18
+
+/** conv5, conv8, conv9, conv10, conv11 should be visible */
+class A[T] {
+ def conv1: AnyRef = ???
+ def conv2: T = ???
+ def conv3(l: Int): AnyRef = ???
+ def conv4(l: AnyRef): AnyRef = ???
+ def conv5(l: String): AnyRef = ???
+ def conv6(l: AnyRef): AnyRef = ???
+ def conv7(l: AnyRef): String = ???
+ def conv8(l: String)(m: String): AnyRef = ???
+ def conv9(l: AnyRef)(m: AnyRef): AnyRef = ???
+ def conv10(l: T): T = ???
+ def conv11(l: T): T = ???
+}
+/** conv5, conv8, conv9, conv11 should be visible */
+class B extends A[Int]
+/** conv5, conv8, conv9, conv10, conv11 should be visible */
+class C extends A[Double]
+/** conv5, conv8, conv9, conv10 should be visible */
+class D extends A[AnyRef]
+
+class Z[T] {
+ def conv1: AnyRef = ???
+ def conv2: T = ???
+ def conv3(p: Int): AnyRef = ???
+ def conv4(p: String): AnyRef = ???
+ def conv5(p: AnyRef): AnyRef = ???
+ def conv6(p: AnyRef): String = ???
+ def conv7(p: AnyRef): AnyRef = ???
+ def conv8(p: String, q: String): AnyRef = ???
+ def conv9(p: AnyRef, q: AnyRef): AnyRef = ???
+ def conv10(p: Int): T = ???
+ def conv11(p: String): T = ???
+}
+object A {
+ implicit def AtoZ[T](a: A[T]) = new Z[T]
+}
diff --git a/test/scaladoc/run/implicits-shadowing.check b/test/scaladoc/run/implicits-shadowing.check
new file mode 100644
index 0000000000..619c56180b
--- /dev/null
+++ b/test/scaladoc/run/implicits-shadowing.check
@@ -0,0 +1 @@
+Done.
diff --git a/test/scaladoc/run/implicits-shadowing.scala b/test/scaladoc/run/implicits-shadowing.scala
new file mode 100644
index 0000000000..7835223d21
--- /dev/null
+++ b/test/scaladoc/run/implicits-shadowing.scala
@@ -0,0 +1,70 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+
+ // test a file instead of a piece of code
+ override def resourceFile = "implicits-shadowing-res.scala"
+
+ // start implicits
+ def scaladocSettings = "-implicits"
+
+ def testModel(root: Package) = {
+ // get the quick access implicit defs in scope (_package(s), _class(es), _trait(s), object(s) _method(s), _value(s))
+ import access._
+
+ // SEE THE test/resources/implicits-chaining-res.scala FOR THE EXPLANATION OF WHAT'S CHECKED HERE:
+ val base = root._package("scala")._package("test")._package("scaladoc")._package("implicits")._object("shadowing")
+ var conv: ImplicitConversion = null
+
+//// class A ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ val A = base._class("A")
+
+ conv = A._conversion(base._object("A").qualifiedName + ".AtoZ")
+ assert(conv.members.length == 5)
+ conv._member("conv5")
+ conv._member("conv8")
+ conv._member("conv9")
+ conv._member("conv10")
+ conv._member("conv11")
+ assert(conv.constraints.length == 0)
+
+//// class B ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ val B = base._class("B")
+
+ conv = B._conversion(base._object("A").qualifiedName + ".AtoZ")
+ assert(conv.members.length == 4)
+ conv._member("conv5")
+ conv._member("conv8")
+ conv._member("conv9")
+ conv._member("conv11")
+ assert(conv.constraints.length == 0)
+
+//// class C ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ val C = base._class("C")
+
+ conv = C._conversion(base._object("A").qualifiedName + ".AtoZ")
+ assert(conv.members.length == 5)
+ conv._member("conv5")
+ conv._member("conv8")
+ conv._member("conv9")
+ conv._member("conv10")
+ conv._member("conv11")
+ assert(conv.constraints.length == 0)
+
+//// class D ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ val D = base._class("D")
+
+ conv = D._conversion(base._object("A").qualifiedName + ".AtoZ")
+ assert(conv.members.length == 4)
+ conv._member("conv5")
+ conv._member("conv8")
+ conv._member("conv9")
+ conv._member("conv10")
+ assert(conv.constraints.length == 0)
+ }
+} \ No newline at end of file