From 7fc77f83c873ce3fada0e01db4947caeb67527ab Mon Sep 17 00:00:00 2001 From: Eugene Burmako Date: Tue, 28 Jan 2014 18:37:18 +0300 Subject: sane semantics for Symbols.companionSymbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While playing with tests for Type.companionType, I figured out that companionSymbol isn’t what it seems to be: scala> ScalaPackage.companionSymbol res5: $r.intp.global.Symbol = scala> ScalaPackageClass.companionSymbol res6: $r.intp.global.Symbol = package scala Or even funnier observation: scala> class C; object C defined class C defined object C scala> val classC = typeOf[C].typeSymbol classC: $r.intp.global.Symbol = class C scala> val moduleC = classC.companionSymbol moduleC: $r.intp.global.Symbol = object C scala> classC.companionSymbol == moduleC res0: Boolean = true scala> moduleC.companionSymbol == classC res1: Boolean = true scala> moduleC.moduleClass.companionSymbol == moduleC res2: Boolean = true Of course, I rushed to clean this up, so that `companionSymbol` only returns something other than NoSymbol if the target has a companion in the common sense, not wrt the internal “class with the same name in the same package” convention of scalac, and that `companionSymbol` for module classes is a class, not a source module. Unfortunately it’s not that easy, because api.Symbol#companionSymbol has the same name as internal.Symbol#companionSymbol, so we can’t change the behavior of the former without changing the behavior of the latter. Therefore I deprecated api.Symbol#companionSymbol and introduced a replacement called api.Symbol#companion with sane semantics. --- src/reflect/scala/reflect/api/Symbols.scala | 11 +++++++++++ src/reflect/scala/reflect/internal/Symbols.scala | 7 +++++++ test/files/run/reflection-companion.check | 6 ++++++ test/files/run/reflection-companion.scala | 16 ++++++++++++++++ test/files/run/reflection-implClass.scala | 8 ++++---- test/files/run/t6989/Test_2.scala | 4 ++-- 6 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 test/files/run/reflection-companion.check create mode 100644 test/files/run/reflection-companion.scala diff --git a/src/reflect/scala/reflect/api/Symbols.scala b/src/reflect/scala/reflect/api/Symbols.scala index 8e26679e62..852d49ee28 100644 --- a/src/reflect/scala/reflect/api/Symbols.scala +++ b/src/reflect/scala/reflect/api/Symbols.scala @@ -335,10 +335,21 @@ trait Symbols { self: Universe => * For a module: the class with the same name in the same package. * For all others: NoSymbol * + * This API may return unexpected results for module classes, packages and package classes. + * Use `companion` instead in order to get predictable results. + * * @group Basics */ + @deprecated("Use `companion` instead", "2.11.0") def companionSymbol: Symbol + /** For a class: its companion object if exists. + * For a module or a module class: companion class of the module if exists. + * For a package or a package class: NoSymbol. + * For all others: NoSymbol. + */ + def companion: Symbol + /** The type signature of this symbol seen as a member of given type `site`. * * @group Basics diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 31fb7d2a6e..74528267d6 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -133,6 +133,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => def getter: Symbol = getter(owner) def setter: Symbol = setter(owner) + + def companion: Symbol = { + if (isModule && !isPackage) companionSymbol + else if (isModuleClass && !isPackageClass) sourceModule.companionSymbol + else if (isClass && !isModuleClass && !isPackageClass) companionSymbol + else NoSymbol + } } private[reflect] case class SymbolKind(accurate: String, sanitized: String, abbreviation: String) diff --git a/test/files/run/reflection-companion.check b/test/files/run/reflection-companion.check new file mode 100644 index 0000000000..5dbff9960e --- /dev/null +++ b/test/files/run/reflection-companion.check @@ -0,0 +1,6 @@ +C#MOD +C#CLS +C#CLS +NoSymbol#??? +NoSymbol#??? +NoSymbol#??? diff --git a/test/files/run/reflection-companion.scala b/test/files/run/reflection-companion.scala new file mode 100644 index 0000000000..0f62dead12 --- /dev/null +++ b/test/files/run/reflection-companion.scala @@ -0,0 +1,16 @@ +import scala.reflect.runtime.universe._ +import scala.reflect.runtime.{currentMirror => cm} + +class C +object C + +object Test extends App { + type T = C + + println(showRaw(symbolOf[C].companion, printKinds = true)) + println(showRaw(symbolOf[C].companion.companion, printKinds = true)) + println(showRaw(symbolOf[C.type].companion, printKinds = true)) + println(showRaw(symbolOf[T].companion, printKinds = true)) + println(showRaw(cm.staticPackage("scala").moduleClass.companion, printKinds = true)) + println(showRaw(cm.staticPackage("scala").companion, printKinds = true)) +} \ No newline at end of file diff --git a/test/files/run/reflection-implClass.scala b/test/files/run/reflection-implClass.scala index db211fd9a8..e11b8a2a16 100644 --- a/test/files/run/reflection-implClass.scala +++ b/test/files/run/reflection-implClass.scala @@ -16,13 +16,13 @@ object Test extends App with Outer { val s1 = implClass(classTag[Foo].runtimeClass) assert(s1 != NoSymbol) assert(s1.typeSignature != NoType) - assert(s1.companionSymbol.typeSignature != NoType) - assert(s1.companionSymbol.typeSignature.declaration(TermName("bar")) != NoSymbol) + assert(s1.companion.typeSignature != NoType) + assert(s1.companion.typeSignature.declaration(TermName("bar")) != NoSymbol) val s2 = implClass(classTag[Bar].runtimeClass) assert(s2 != NoSymbol) assert(s2.typeSignature != NoType) - assert(s2.companionSymbol.typeSignature != NoType) - assert(s2.companionSymbol.typeSignature.declaration(TermName("foo")) != NoSymbol) + assert(s2.companion.typeSignature != NoType) + assert(s2.companion.typeSignature.declaration(TermName("foo")) != NoSymbol) def implClass(clazz: Class[_]) = { val implClass = Class.forName(clazz.getName + "$class") cm.classSymbol(implClass) diff --git a/test/files/run/t6989/Test_2.scala b/test/files/run/t6989/Test_2.scala index e48e82422d..3f578158e8 100644 --- a/test/files/run/t6989/Test_2.scala +++ b/test/files/run/t6989/Test_2.scala @@ -11,9 +11,9 @@ import scala.reflect.runtime.universe._ package object foo { def testAll(): Unit = { test(typeOf[foo.PackagePrivateJavaClass].typeSymbol) - test(typeOf[foo.PackagePrivateJavaClass].typeSymbol.companionSymbol) + test(typeOf[foo.PackagePrivateJavaClass].typeSymbol.companion) test(typeOf[foo.JavaClass_1].typeSymbol) - test(typeOf[foo.JavaClass_1].typeSymbol.companionSymbol) + test(typeOf[foo.JavaClass_1].typeSymbol.companion) } def test(sym: Symbol): Unit = { -- cgit v1.2.3