Возможно, я мог бы указать на несколько моментов в кодовой базе, которые могли бы иметь отношение.
Во-первых, есть способ соотнести грамматику Scala Спецификации языка непосредственно с исходным кодом. Например, классы дел правило
TmplDef ::= ‘case’ ‘class’ ClassDef
относится к Parser.tmplDef
/** {{{
* TmplDef ::= [case] class ClassDef
* | [case] object ObjectDef
* | [override] trait TraitDef
* }}}
*/
def tmplDef(pos: Offset, mods: Modifiers): Tree = {
...
in.token match {
...
case CASECLASS =>
classDef(pos, (mods | Flags.CASE) withPosition (Flags.CASE, tokenRange(in.prev /*scanner skips on 'case' to 'class', thus take prev*/)))
...
}
}
Спецификация продолжается
Определение класса case ?[tps](ps1)…(ps?)
с параметрами типа tps и значениями параметров ps подразумевает определение объекта-компаньона, который служит объектом экстрактора.
object ? {
def apply[tps](ps1)…(ps?): ?[tps] = new ?[Ts](xs1)…(xs?)
def unapply[tps](?: ?[tps]) =
if (x eq null) scala.None
else scala.Some(?.xs11,…,?.xs1?)
}
, поэтому давайте попробуем охота на подразумеваемое определение из
def apply[tps](ps1)…(ps?): ?[tps] = new ?[Ts](xs1)…(xs?)
, которое является другим способом сказать синтезированное определение. Многообещающе, существует MethodSynthesis.scala
/** Logic related to method synthesis which involves cooperation between
* Namer and Typer.
*/
trait MethodSynthesis {
Таким образом, мы находим еще два потенциальных ключа Namer
и Typer
. Интересно, что там? Но сначала MethodSynthesis.scala
имеет только приблизительно 300 LO C, так что давайте немного просмотрим. Мы натыкаемся на многообещающую линию
val methDef = factoryMeth(classDef.mods & (AccessFlags | FINAL) | METHOD | IMPLICIT | SYNTHETIC, classDef.name.toTermName, classDef)
"factoryMeth
" ... там есть кольцо. Найти использования! Нас быстро ведут к
/** The apply method corresponding to a case class
*/
def caseModuleApplyMeth(cdef: ClassDef): DefDef = {
val inheritedMods = constrMods(cdef)
val mods =
if (applyShouldInheritAccess(inheritedMods))
(caseMods | (inheritedMods.flags & PRIVATE)).copy(privateWithin = inheritedMods.privateWithin)
else
caseMods
factoryMeth(mods, nme.apply, cdef)
}
Кажется, мы на правильном пути. Мы также отмечаем имя
nme.apply
, которое составляет
val apply: NameType = nameType("apply")
С нетерпением мы находим использование caseModuleApplyMeth
, и мы червоточины к Namer.addApplyUnapply
/** Given a case class
* case class C[Ts] (ps: Us)
* Add the following methods to toScope:
* 1. if case class is not abstract, add
* <synthetic> <case> def apply[Ts](ps: Us): C[Ts] = new C[Ts](ps)
* 2. add a method
* <synthetic> <case> def unapply[Ts](x: C[Ts]) = <ret-val>
* where <ret-val> is the caseClassUnapplyReturnValue of class C (see UnApplies.scala)
*
* @param cdef is the class definition of the case class
* @param namer is the namer of the module class (the comp. obj)
*/
def addApplyUnapply(cdef: ClassDef, namer: Namer): Unit = {
if (!cdef.symbol.hasAbstractFlag)
namer.enterSyntheticSym(caseModuleApplyMeth(cdef))
val primaryConstructorArity = treeInfo.firstConstructorArgs(cdef.impl.body).size
if (primaryConstructorArity <= MaxTupleArity)
namer.enterSyntheticSym(caseModuleUnapplyMeth(cdef))
}
Woohoo! Документация гласит:
<synthetic> <case> def apply[Ts](ps: Us): C[Ts] = new C[Ts](ps)
, которая кажется очень похожей на версию SLS
def apply[tps](ps1)…(ps?): ?[tps] = new ?[Ts](xs1)…(xs?)
Наш камень в темноте, кажется, привел нас к открытию.