diff --git a/scaladoc-testcases/src/tests/extensionParams.scala b/scaladoc-testcases/src/tests/extensionParams.scala index 231a8a1fefbf..31747e3d9f63 100644 --- a/scaladoc-testcases/src/tests/extensionParams.scala +++ b/scaladoc-testcases/src/tests/extensionParams.scala @@ -1,22 +1,44 @@ package tests.extensionParams extension [A](thiz: A) - def toTuple2[B](that: B): (A, B) = thiz -> that - + def toTuple2[B](that: B): (A, B) + = thiz -> that extension [A](a: A)(using Int) - def f[B](b: B): (A, B) = ??? + def f1[B](b: B): (A, B) + = ??? extension [A](a: A)(using Int) - def ff(b: A): (A, A) = ??? + def f2(b: A): (A, A) + = ??? extension [A](a: A)(using Int) - def fff(using String)(b: A): (A, A) = ??? + def f3(using String)(b: A): (A, A) + = ??? extension (a: Char)(using Int) - def ffff(using String)(b: Int): Unit = ??? + def f4(using String)(b: Int): Unit + = ??? extension (a: Char)(using Int) - def fffff[B](using String)(b: B): Unit = ??? + def f5[B](using String)(b: B): Unit + = ??? extension [A <: List[Char]](a: A)(using Int) - def ffffff[B](b: B): (A, B) = ??? + def f6[B](b: B): (A, B) + = ??? + +extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number) + def f7[B, C](b: B)(c: C): (A, B) + = ??? + +extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number) + def f8(b: Any)(c: Any): Any + = ??? + +extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number) + def f9[B, C](using Int)(b: B)(c: C): (A, B) + = ??? + +extension [A <: List[Char]](using String)(using Unit)(a: A)(using Int)(using Number) + def f10(using Int)(b: Any)(c: Any): Any + = ??? diff --git a/scaladoc/src/dotty/tools/scaladoc/api.scala b/scaladoc/src/dotty/tools/scaladoc/api.scala index 314dcc3c7261..98f60036ed8c 100644 --- a/scaladoc/src/dotty/tools/scaladoc/api.scala +++ b/scaladoc/src/dotty/tools/scaladoc/api.scala @@ -43,7 +43,7 @@ enum Modifier(val name: String, val prefix: Boolean): case Transparent extends Modifier("transparent", true) case Infix extends Modifier("infix", true) -case class ExtensionTarget(name: String, typeParams: Seq[TypeParameter], argsLists: Seq[ParametersList], signature: Signature, dri: DRI, position: Long) +case class ExtensionTarget(name: String, typeParams: Seq[TypeParameter], argsLists: Seq[TermParametersList], signature: Signature, dri: DRI, position: Long) case class ImplicitConversion(from: DRI, to: DRI) trait ImplicitConversionProvider { def conversion: Option[ImplicitConversion] } trait Classlike @@ -51,14 +51,14 @@ trait Classlike enum Kind(val name: String): case RootPackage extends Kind("") case Package extends Kind("package") - case Class(typeParams: Seq[TypeParameter], argsLists: Seq[ParametersList]) + case Class(typeParams: Seq[TypeParameter], argsLists: Seq[TermParametersList]) extends Kind("class") with Classlike case Object extends Kind("object") with Classlike - case Trait(typeParams: Seq[TypeParameter], argsLists: Seq[ParametersList]) + case Trait(typeParams: Seq[TypeParameter], argsLists: Seq[TermParametersList]) extends Kind("trait") with Classlike - case Enum(typeParams: Seq[TypeParameter], argsLists: Seq[ParametersList]) extends Kind("enum") with Classlike + case Enum(typeParams: Seq[TypeParameter], argsLists: Seq[TermParametersList]) extends Kind("enum") with Classlike case EnumCase(kind: Object.type | Kind.Type | Val.type | Class) extends Kind("case") - case Def(typeParams: Seq[TypeParameter], argsLists: Seq[ParametersList]) + case Def(params: Seq[TypeParametersList | TermParametersList]) // TODO: sc extends Kind("def") case Extension(on: ExtensionTarget, m: Kind.Def) extends Kind("def") case Constructor(base: Kind.Def) extends Kind("def") @@ -94,12 +94,15 @@ object Annotation: case class LinkParameter(name: Option[String] = None, dri: DRI, value: String) extends AnnotationParameter case class UnresolvedParameter(name: Option[String] = None, unresolvedText: String) extends AnnotationParameter -case class ParametersList( - parameters: Seq[Parameter], + +// type ParametersList = TermParametersList | TypeParametersList // TODO: sc + +case class TermParametersList( + parameters: Seq[TermParameter], modifiers: String ) -case class Parameter( +case class TermParameter( annotations: Seq[Annotation], modifiers: String, name: Option[String], @@ -109,6 +112,10 @@ case class Parameter( isGrouped: Boolean = false ) +case class TypeParametersList( + parameters: Seq[TypeParameter] +) + case class TypeParameter( annotations: Seq[Annotation], variance: "" | "+" | "-", diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 31e27e234332..798d95b27cd4 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -9,7 +9,7 @@ import scala.quoted._ import SymOps._ import NameNormalizer._ -import SyntheticsSupport._ +import SyntheticsSupport.{memberInfo => ssMemberInfo, _} import dotty.tools.dotc.core.NameKinds trait ClassLikeSupport: @@ -45,7 +45,7 @@ trait ClassLikeSupport: .filter(s => s.exists && !s.isHiddenByVisibility) .map( _.tree.asInstanceOf[DefDef]) constr.fold(Nil)( - _.termParamss.map(pList => ParametersList(pList.params.map(p => mkParameter(p, parameterModifier)), paramListModifier(pList.params))) + _.termParamss.map(pList => TermParametersList(pList.params.map(p => mkParameter(p, parameterModifier)), paramListModifier(pList.params))) ) if classDef.symbol.flags.is(Flags.Module) then Kind.Object @@ -126,21 +126,103 @@ trait ClassLikeSupport: private def isDocumentableExtension(s: Symbol) = !s.isHiddenByVisibility && !s.isSyntheticFunc && s.isExtensionMethod + private def _debugStringParamLists(name: String, l: List[Any]) = l.mkString(s"$name:\n ","\n ", "\n") + private def parseMember(c: ClassDef)(s: Tree): Option[Member] = processTreeOpt(s) { s match case dd: DefDef if isDocumentableExtension(dd.symbol) => dd.symbol.extendedSymbol.map { extSym => val memberInfo = unwrapMemberInfo(c, dd.symbol) - val typeParams = dd.symbol.extendedTypeParams.map(mkTypeArgument(_, memberInfo.genericTypes)) - val termParams = dd.symbol.extendedTermParamLists.zipWithIndex.flatMap { case (paramList, index) => + //val typeParams = dd.symbol.extendedTypeParamList.map(mkTypeArgument(_, memberInfo.typeParamLists.headOption.getOrElse(Map()))) //assumes provided type param is always LHS y + //println(dd.name) + //println("MemberInfo:\n" + memberInfo.paramLists) + //println("extended...:\n" + dd.symbol.extendedTermParamLists) + /*val termParams = dd.symbol.extendedTermParamLists.zipWithIndex.flatMap { case (paramList, index) => memberInfo.paramLists(index) match - case EvidenceOnlyParameterList => Nil - case info: RegularParameterList => - Seq(ParametersList(paramList.params.map(mkParameter(_, memberInfo = info)), paramListModifier(paramList.params))) + case Right(EvidenceOnlyParameterList) => Nil + case Right(info: RegularParameterList) => + Seq(TermParametersList(paramList.params.map(mkParameter(_, memberMap = info)), paramListModifier(paramList.params))) + case Left(types: TypeParameterList) => + throw new Error(s"Mismatch between extendedTermParamLists and memberInfo.paramLists: \n ${dd.symbol.extendedTermParamLists.mkString("extendedTermParamLists:\n ","\n ", "\n")} \n ${memberInfo.paramLists.mkString("memberInfo.paramLists:\n ","\n ", "\n")}") + }*/ + val mismatchString = s"""Mismatch between extendedParamLists and memberInfo.extendedParamLists: + |${dd.symbol.extendedParamLists.mkString("extendedParamLists:\n ","\n ", "\n")} + |${memberInfo.paramLists.mkString("memberInfo.paramLists:\n ","\n ", "\n")} + |""".stripMargin + // We match clauses in dd.symbol.extendedParamLists and X by the'r first element (which we call the representative) + // TODO: handle the case EvidenceOnlyParameterList + val paramss: List[Either[List[TypeParameter], TermParametersList]] = dd.symbol.extendedParamLists.flatMap{ + case TermParamClause(terms) => + val representative: ValDef = terms.head // RegularParameterList = Map[String, TypeRepr] + val ValDef(name: String, _, _) = representative + + val candidates = memberInfo.paramLists.collect{ case Right(info: RegularParameterList) => info }.filter( _ contains name ) + val extradebug = s""" + |representative: $representative + |name: $name + |candidates: $candidates + |size: ${candidates.size} + | + |""" + + if(candidates.size != 1){ + if(candidates.size == 0 && memberInfo.paramLists.exists{ case Right(EvidenceOnlyParameterList) => true; case _ => false}){ + // In this case it is possible the matching info was actually the EvidenceOnlyParameterList + None + } else{ + throw new Error(extradebug + mismatchString) + } + }else{ + val info = candidates.head + Some(Right(TermParametersList(terms.map(mkParameter(_, memberMap = info)), paramListModifier(terms)))) + } + + case TypeParamClause(types) => + val representative = types.head + val TypeDef(name: String, _) = representative + + val candidates = memberInfo.paramLists.collect{ case Left(info: TypeParameterList) => info }.filter( _ contains name ) // TypeBounds + + if(candidates.size != 1){ + throw new Error(mismatchString) + }else{ + val info = candidates.head + Some(Left(types.map(mkTypeArgument(_, info)))) + } + } + + /* + extendedParamLists: + List(TypeDef(A,TypeBoundsTree(TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),Nothing)],TypeTree[TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),Any)],EmptyTree))) + List(ValDef(a,Ident(A),EmptyTree)) + List(ValDef(x$2,Ident(Int),EmptyTree)) + + memberInfo.paramLists: + Left(Map(A -> TypeBounds(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),Nothing),TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),Any)))) + Right(Map(a -> TypeParamRef(A))) + Right(Map(x$2 -> TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),Int))) + Left(Map(B -> TypeBounds(TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),Nothing),TypeRef(TermRef(ThisType(TypeRef(NoPrefix,module class )),object scala),Any)))) + Right(Map(b -> TypeParamRef(B))) + */ + + // not valid assumption, will have to find members with the same name ! + //assert(dd.symbol.extendedParamLists.length == memberInfo.paramLists.length, mismatchString) + /* + val paramss: List[Either[List[TypeParameter], TermParametersList]] = (dd.symbol.extendedParamLists zip memberInfo.paramLists).flatMap{ + case (terms: TermParamClause, Right(EvidenceOnlyParameterList)) => None + case (terms: TermParamClause, Right(info: RegularParameterList)) => + Some(Right(TermParametersList(terms.params.map(mkParameter(_, memberMap = info)), paramListModifier(terms.params)))) + case (TypeParamClause(types), Left(info: TypeParameterList)) => + Some(Left(types.map(mkTypeArgument(_, info)))) + case _ => + throw new Error(mismatchString) } + */ + val typeParams = paramss.collect{case Left(types: List[TypeParameter]) => types}.lift(0).getOrElse(List()) + val termParamss = paramss.collect{case Right(terms: TermParametersList) => terms} val target = ExtensionTarget( extSym.symbol.normalizedName, typeParams, - termParams, + termParamss, extSym.tpt.asSignature, extSym.tpt.symbol.dri, extSym.symbol.pos.get.start @@ -327,32 +409,65 @@ trait ClassLikeSupport: specificKind: (Kind.Def => Kind) = identity ): Member = val method = methodSymbol.tree.asInstanceOf[DefDef] - val paramLists: List[TermParamClause] = methodSymbol.nonExtensionTermParamLists - val genericTypes: List[TypeDef] = if (methodSymbol.isClassConstructor) Nil else methodSymbol.nonExtensionLeadingTypeParams + val allParamss: List[ParamClause] = method.paramss + val nonExtensionParamss: List[ParamClause] = methodSymbol.nonExtensionParamLists + //val paramLists: List[TermParamClause] = methodSymbol.nonExtensionTermParamLists + //val genericTypes: List[TypeDef] = if (methodSymbol.isClassConstructor) Nil else methodSymbol.nonExtensionTypeParamList val memberInfo = unwrapMemberInfo(c, methodSymbol) + assert(allParamss.length == memberInfo.paramLists.length) + + // assumes method is left-associative + // finds indices in memberInfo.paramLists corresponding to nonExtensionParamss + // probably not going to work, as they are related but not == + val nonExtensionParamssWithIndices = allParamss.zipWithIndex.dropWhile(_._1 != nonExtensionParamss.head) + val basicKind: Kind.Def = Kind.Def( + nonExtensionParamssWithIndices map ( (clause, i) => (clause, memberInfo.paramLists(i)) ) flatMap { + case (terms: TermParamClause, Right(EvidenceOnlyParameterList)) => None + case (terms: TermParamClause, Right(info: RegularParameterList)) => + Some( TermParametersList( + terms.params.map( + mkParameter(_, paramPrefix, memberMap = info) + ), + paramListModifier(terms.params) + )) + case (types: TypeParamClause, Left(info: TypeParameterList)) => + Some( TypeParametersList( + types.params.map( + mkTypeArgument(_, info, memberInfo.contextBounds) + ) + )) + case (_, _) => + throw new Error(s"Mismatch between nonExtensionParamssWithIndices and memberInfo.paramLists: \n $nonExtensionParamssWithIndices \n ${memberInfo.paramLists}") + + + } + ) + /* + val basicKind: Kind.Def = Kind.Def( // TODO: sc genericTypes.map(mkTypeArgument(_, memberInfo.genericTypes, memberInfo.contextBounds)), paramLists.zipWithIndex.flatMap { (pList, index) => memberInfo.paramLists(index) match - case EvidenceOnlyParameterList => Nil - case info: RegularParameterList => - Seq(ParametersList(pList.params.map( - mkParameter(_, paramPrefix, memberInfo = info)), paramListModifier(pList.params) - )) + } ) + */ + + val numberOfTermParams = nonExtensionParamss.zipWithIndex.findLast{case (_: TermParamClause, _) => true case _ => false}.map((_, i) => i+1).getOrElse(0) + val firstTermParams: Option[TermParamClause] = nonExtensionParamss.collectFirst{case tpc: TermParamClause => tpc } + val firstTermParamsCount = firstTermParams.map(_.params.size).getOrElse(0) val methodKind = if methodSymbol.isClassConstructor then Kind.Constructor(basicKind) else if methodSymbol.flags.is(Flags.Implicit) then extractImplicitConversion(method.returnTpt.tpe) match - case Some(conversion) if paramLists.size == 0 || (paramLists.size == 1 && paramLists.head.params.size == 0) => + case Some(conversion) if numberOfTermParams == 0 || (numberOfTermParams == 1 && firstTermParamsCount == 0) => Kind.Implicit(basicKind, Some(conversion)) - case None if paramLists.size == 1 && paramLists(0).params.size == 1 => + case None if numberOfTermParams == 1 && firstTermParamsCount == 1 => Kind.Implicit(basicKind, Some( ImplicitConversion( - paramLists(0).params(0).tpt.tpe.typeSymbol.dri, + firstTermParams.get.params(0).tpt.tpe.typeSymbol.dri, method.returnTpt.tpe.typeSymbol.dri ) )) @@ -372,23 +487,23 @@ trait ClassLikeSupport: prefix: Symbol => String = _ => "", isExtendedSymbol: Boolean = false, isGrouped: Boolean = false, - memberInfo: Map[String, TypeRepr] = Map.empty) = + memberMap: Map[String, TypeRepr] = Map.empty) = val inlinePrefix = if argument.symbol.flags.is(Flags.Inline) then "inline " else "" val nameIfNotSynthetic = Option.when(!argument.symbol.flags.is(Flags.Synthetic))(argument.symbol.normalizedName) val name = argument.symbol.normalizedName - Parameter( + TermParameter( argument.symbol.getAnnotations(), inlinePrefix + prefix(argument.symbol), nameIfNotSynthetic, argument.symbol.dri, - memberInfo.get(name).fold(argument.tpt.asSignature)(_.asSignature), + memberMap.get(name).fold(argument.tpt.asSignature)(_.asSignature), isExtendedSymbol, isGrouped ) def mkTypeArgument( argument: TypeDef, - memberInfo: Map[String, TypeBounds] = Map.empty, + memberMap: Map[String, TypeBounds] = Map.empty, contextBounds: Map[String, DSignature] = Map.empty ): TypeParameter = val variancePrefix: "+" | "-" | "" = @@ -398,7 +513,7 @@ trait ClassLikeSupport: val name = argument.symbol.normalizedName val normalizedName = if name.matches("_\\$\\d*") then "_" else name - val boundsSignature = memberInfo.get(name).fold(argument.rhs.asSignature)(_.asSignature) + val boundsSignature = memberMap.get(name).fold(argument.rhs.asSignature)(_.asSignature) val signature = contextBounds.get(name) match case None => boundsSignature case Some(contextBoundsSignature) => @@ -471,18 +586,22 @@ trait ClassLikeSupport: object EvidenceOnlyParameterList type RegularParameterList = Map[String, TypeRepr] - type ParameterList = RegularParameterList | EvidenceOnlyParameterList.type + type TermParameterList = RegularParameterList | EvidenceOnlyParameterList.type + type TypeParameterList = Map[String, TypeBounds] + type ParameterList = Either[TypeParameterList, TermParameterList] case class MemberInfo( - genericTypes: Map[String, TypeBounds], paramLists: List[ParameterList], res: TypeRepr, contextBounds: Map[String, DSignature] = Map.empty, - ) + ){ + //def typeParamLists: List[TypeParameterList] = paramLists.collect{case Left(tps) => tps} + } def unwrapMemberInfo(c: ClassDef, symbol: Symbol): MemberInfo = - val baseTypeRepr = memberInfo(c, symbol) + + val defCoord = symbol.pos.get.start def isSyntheticEvidence(name: String) = if !name.startsWith(NameKinds.EvidenceParamName.separator) then false else @@ -495,12 +614,14 @@ trait ClassLikeSupport: // Documenting method slightly different then its definition is withing the 'undefiend behaviour'. symbol.paramSymss.flatten.find(_.name == name).exists(_.flags.is(Flags.Implicit)) - def handlePolyType(polyType: PolyType): MemberInfo = - MemberInfo(polyType.paramNames.zip(polyType.paramBounds).toMap, List.empty, polyType.resType) + def handlePolyType(memberInfo: MemberInfo, polyType: PolyType): MemberInfo = + val paramList = Left(polyType.paramNames.zip(polyType.paramBounds).toMap) + + memberInfo.copy(memberInfo.paramLists :+ paramList, polyType.resType) def handleMethodType(memberInfo: MemberInfo, methodType: MethodType): MemberInfo = val rawParams = methodType.paramNames.zip(methodType.paramTypes).toMap - val (evidences, notEvidences) = rawParams.partition(e => isSyntheticEvidence(e._1)) + val (evidences, notEvidences) = rawParams.partition(e => isSyntheticEvidence(e._1)) def findParamRefs(t: TypeRepr): Seq[ParamRef] = t match @@ -514,7 +635,7 @@ trait ClassLikeSupport: val PolyType(names, _, _) = ref.binder names(ref.paramNum) - val (paramsThatLookLikeContextBounds, contextBounds) = + val (paramsThatLookLikeContextBounds, newContextBounds) = evidences.partitionMap { case (_, AppliedType(tpe, List(typeParam: ParamRef))) => Right(nameForRef(typeParam) -> tpe.asSignature) @@ -529,22 +650,24 @@ trait ClassLikeSupport: val newParams = notEvidences ++ paramsThatLookLikeContextBounds - val newLists: List[ParameterList] = if newParams.isEmpty && contextBounds.nonEmpty - then memberInfo.paramLists ++ Seq(EvidenceOnlyParameterList) - else memberInfo.paramLists ++ Seq(newParams) - - MemberInfo(memberInfo.genericTypes, newLists , methodType.resType, contextBounds.toMap) + val paramList: ParameterList = Right(if newParams.isEmpty && newContextBounds.nonEmpty + then EvidenceOnlyParameterList + else newParams + ) + + memberInfo.copy(memberInfo.paramLists :+ paramList, methodType.resType, contextBounds = newContextBounds.toMap) def handleByNameType(memberInfo: MemberInfo, byNameType: ByNameType): MemberInfo = - MemberInfo(memberInfo.genericTypes, memberInfo.paramLists, byNameType.underlying) + memberInfo.copy(res = byNameType.underlying) def recursivelyCalculateMemberInfo(memberInfo: MemberInfo): MemberInfo = memberInfo.res match - case p: PolyType => recursivelyCalculateMemberInfo(handlePolyType(p)) + case p: PolyType => recursivelyCalculateMemberInfo(handlePolyType(memberInfo, p)) case m: MethodType => recursivelyCalculateMemberInfo(handleMethodType(memberInfo, m)) case b: ByNameType => handleByNameType(memberInfo, b) case _ => memberInfo - - recursivelyCalculateMemberInfo(MemberInfo(Map.empty, List.empty, baseTypeRepr)) + + val baseTypeRepr: TypeRepr = ssMemberInfo(c, symbol) + recursivelyCalculateMemberInfo(MemberInfo(List.empty, baseTypeRepr)) private def paramListModifier(parameters: Seq[ValDef]): String = if parameters.size > 0 then diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala index e412475cbf2c..17367418aef7 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala @@ -142,7 +142,15 @@ object SymOps: import reflect._ sym.flags.is(Flags.Artifact) - def isLeftAssoc: Boolean = !sym.name.endsWith(":") + /** + * note that this is not the right criterion: + * An extension method is treated as a right-associative operator (as in SLS ยง6.12.3) + * if it has a name ending in : and is immediately followed by a single parameter. + * https://docs.scala-lang.org/scala3/reference/contextual/right-associative-extension-methods.html + */ + def isRightAssoc: Boolean = sym.name.endsWith(":") + + def isLeftAssoc: Boolean = !sym.isRightAssoc def extendedSymbol: Option[reflect.ValDef] = import reflect.* @@ -152,52 +160,68 @@ object SymOps: else termParamss(1).params(0) } - def extendedTypeParams: List[reflect.TypeDef] = - import reflect.* - val method = sym.tree.asInstanceOf[DefDef] - method.leadingTypeParams + def splitExtensionParamLists: (List[reflect.ParamClause], List[reflect.ParamClause]) = + if sym.isRightAssoc && sym.isExtensionMethod then + val unswapped@(extPart, defPart) = sym.splitExtensionParamListsAssumingLeftAssoc + def nonUsingClauses(clauses: List[reflect.ParamClause]) = clauses.zipWithIndex.collect{case (terms: reflect.TermParamClause, i) if !terms.isGiven => (terms, i)} + val extNonUsingClause = nonUsingClauses(extPart) + val defNonUsingClauses = nonUsingClauses(defPart) + assert(extNonUsingClause.size == 1) - def extendedTermParamLists: List[reflect.TermParamClause] = - import reflect.* - if sym.nonExtensionLeadingTypeParams.nonEmpty then - sym.nonExtensionParamLists.takeWhile { - case _: TypeParamClause => false - case _ => true - }.collect { - case tpc: TermParamClause => tpc - } + if defNonUsingClauses.lift(0).map(_._1.params.size != 1).getOrElse(true) // was not really right associative, see comment of isRightAssoc + then unswapped + else + val (first, i1) = extNonUsingClause(0) + val (second, i2) = defNonUsingClauses(0) // since cond is false, we know lift(0) returned Some(_) + (extPart.updated(i1, second), defPart.updated(i2, first)) else - List.empty + sym.splitExtensionParamListsAssumingLeftAssoc + + /** + * This uses the assumption that there is the following "pos hierachy": extension paramss < DefDef < extMethod paramss + * /!\ where DefDef is the tree containing the paramss + * It wouldn't really make sense for the Def's position not to be either the "def" or the method name, + * but is not enforced + */ + def splitExtensionParamListsAssumingLeftAssoc: (List[reflect.ParamClause], List[reflect.ParamClause]) = + val method = sym.tree.asInstanceOf[reflect.DefDef] + val paramss = method.paramss //List[ParamClause[T]] //ParamClause[T] = List[ValDef[T]] | List[TypeDef[T]] + val defCoord = method.symbol.pos.get.start //.span.point + + val res = paramss.span{ + case reflect.TypeParamClause(params) => params.head.symbol.pos.get.start < defCoord //.span.start + case reflect.TermParamClause(params) => + params.headOption + .map(_.symbol.pos.get.start < defCoord) //.span.start + .getOrElse(false) // () is only allowed on the RHS of extensions + } + //println(method.name) + //println(res._1.map(_.params.map(_.show)).mkString("ExtensionPart:\n","\n","\n")) + //println(res._2.map(_.params.map(_.show)).mkString("NonExtensionPart:\n","\n","\n")) + res + + + def extendedParamLists: List[reflect.ParamClause] = + sym.splitExtensionParamLists._1 + + def extendedTermParamLists: List[reflect.TermParamClause] = + sym.extendedParamLists.collect{case terms: reflect.TermParamClause => terms} + + def extendedTypeParamList: List[reflect.TypeDef] = + val typeParamss: List[reflect.TypeParamClause] = sym.extendedParamLists.collect{case types: reflect.TypeParamClause => types} + typeParamss.headOption.map(_.params).getOrElse(List()) // only one type param clause on LHS - def nonExtensionTermParamLists: List[reflect.TermParamClause] = - import reflect.* - if sym.nonExtensionLeadingTypeParams.nonEmpty then - sym.nonExtensionParamLists.dropWhile { - case _: TypeParamClause => false - case _ => true - }.drop(1).collect { - case tpc: TermParamClause => tpc - } - else - sym.nonExtensionParamLists.collect { - case tpc: TermParamClause => tpc - } def nonExtensionParamLists: List[reflect.ParamClause] = - import reflect.* - val method = sym.tree.asInstanceOf[DefDef] - if sym.isExtensionMethod then - val params = method.paramss - val toDrop = if method.leadingTypeParams.nonEmpty then 2 else 1 - if sym.isLeftAssoc || params.size == 1 then params.drop(toDrop) - else params.head :: params.tail.drop(toDrop) - else method.paramss - - def nonExtensionLeadingTypeParams: List[reflect.TypeDef] = - import reflect.* - sym.nonExtensionParamLists.collectFirst { - case TypeParamClause(params) => params - }.toList.flatten + sym.splitExtensionParamLists._2 + + def nonExtensionTermParamLists: List[reflect.TermParamClause] = + sym.nonExtensionParamLists.collect{case terms: reflect.TermParamClause => terms} + + def nonExtensionTypeParamList: List[reflect.TypeDef] = + val typeParamss: List[reflect.TypeParamClause] = sym.nonExtensionParamLists.collect{case types: reflect.TypeParamClause => types} + typeParamss.headOption.map(_.params).getOrElse(List()) // only one type param clause on RHS + end extension diff --git a/scaladoc/src/dotty/tools/scaladoc/transformers/ImplicitMembersExtensionTransformer.scala b/scaladoc/src/dotty/tools/scaladoc/transformers/ImplicitMembersExtensionTransformer.scala index 0fa6b9c5d3cf..0e5de09ff0f6 100644 --- a/scaladoc/src/dotty/tools/scaladoc/transformers/ImplicitMembersExtensionTransformer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/transformers/ImplicitMembersExtensionTransformer.scala @@ -29,7 +29,7 @@ class ImplicitMembersExtensionTransformer(using DocContext) extends(Module => Mo case m @ Member(_, _, _, Kind.Extension(ExtensionTarget(_, _, _, _, MyDri, _), _), Origin.RegularlyDefined) => val kind = m.kind match case d: Kind.Def => d - case _ => Kind.Def(Nil, Nil) + case _ => Kind.Def(Nil) Seq(m.withOrigin(Origin.ExtensionFrom(source.name, source.dri)).withKind(kind)) case m @ Member(_, _, _, conversionProvider: ImplicitConversionProvider, Origin.RegularlyDefined) => diff --git a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala index 0639b5af09bd..4ba448f0b075 100644 --- a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala +++ b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureProvider.scala @@ -118,28 +118,25 @@ object ScalaSignatureProvider: parentsSignature(clazz, selfSignature) - private def extensionSignature(extension: Member, fun: Kind.Def, builder: SignatureBuilder): SignatureBuilder = + private def extensionSignature(extension: Member, fun: Kind.Def, builder: SignatureBuilder): SignatureBuilder = // TODO: sc val withSignature = builder .modifiersAndVisibility(extension, "def") .name(extension.name, extension.dri) - .generics(fun.typeParams) - .functionParameters(fun.argsLists) + .functionParameters2(fun.params) withSignature.plain(":").plain(" ").signature(extension.signature) - private def givenMethodSignature(method: Member, body: Kind.Def, builder: SignatureBuilder): SignatureBuilder = method.kind match + private def givenMethodSignature(method: Member, body: Kind.Def, builder: SignatureBuilder): SignatureBuilder = method.kind match // TODO: sc case Kind.Given(_, Some(instance), _) => builder.keyword("given ") .name(method.name, method.dri) - .generics(body.typeParams) - .functionParameters(body.argsLists) + .functionParameters2(body.params) .plain(": ") .signature(instance) case _ => builder.keyword("given ") .name(method.name, method.dri) - .generics(body.typeParams) - .functionParameters(body.argsLists) + .functionParameters2(body.params) private def givenValSignature(field: Member, builder: SignatureBuilder): SignatureBuilder = field.kind match case Kind.Given(_, Some(instance), _) => @@ -150,12 +147,12 @@ object ScalaSignatureProvider: case _ => builder.keyword("given ").name(field.name, field.dri) - private def methodSignature(method: Member, cls: Kind.Def, builder: SignatureBuilder): SignatureBuilder = + private def methodSignature(method: Member, cls: Kind.Def, builder: SignatureBuilder): SignatureBuilder = // TODO: sc val bdr = builder .modifiersAndVisibility(method, "def") .name(method.name, method.dri) - .generics(cls.typeParams) - .functionParameters(cls.argsLists) + .functionParameters2(cls.params) + if !method.kind.isInstanceOf[Kind.Constructor] then bdr.plain(": ").signature(method.signature) else bdr diff --git a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala index 7a5dc2310c0e..28b082e1f7ff 100644 --- a/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala +++ b/scaladoc/src/dotty/tools/scaladoc/translators/ScalaSignatureUtils.scala @@ -44,7 +44,7 @@ trait SignatureBuilder extends ScalaSignatureUtils { def annotationsBlock(d: Member): SignatureBuilder = d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation)} - def annotationsInline(d: Parameter): SignatureBuilder = + def annotationsInline(d: TermParameter): SignatureBuilder = d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation) } def annotationsInline(t: TypeParameter): SignatureBuilder = @@ -87,7 +87,7 @@ trait SignatureBuilder extends ScalaSignatureUtils { bdr.annotationsInline(e).keyword(e.variance).tpe(e.name, Some(e.dri)).signature(e.signature) } - def functionParameters(params: Seq[ParametersList]) = + def functionParameters(params: Seq[TermParametersList]) = if params.isEmpty then this.plain("") else if params.size == 1 && params(0).parameters == Nil then this.plain("()") else this.list(params, separator = List(Plain(""))) { (bld, pList) => @@ -98,8 +98,23 @@ trait SignatureBuilder extends ScalaSignatureUtils { name.signature(p.signature) } } + /* + def functionParameter = + (bdr: SignatureBuilder, param: TypeParametersList | TermParametersList) => param match { + case types: TypeParameterList => generics(ts) + case terms: TermParametersList => functionParameters(terms) + } */ + + def functionParameters2(params: Seq[TypeParametersList | TermParametersList]) = params.foldLeft(this){ (bdr, params) => params match { + case types: TypeParametersList => bdr.generics(types.parameters) + case terms: TermParametersList => bdr.functionParameters(Seq(terms)) + }} // TODO: sc + + } + + trait ScalaSignatureUtils: extension (tokens: Seq[String]) def toSignatureString(): String = tokens.filter(_.trim.nonEmpty).mkString(""," "," ") diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala index d85d2d960d2c..5daff1a63e55 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala @@ -1,5 +1,7 @@ package dotty.tools.scaladoc.signatures +import java.security.Signature + class GenericSignaftures extends SignatureTest("genericSignatures", Seq("class")) class ObjectSignatures extends SignatureTest("objectSignatures", Seq("object")) @@ -22,6 +24,8 @@ class GenericMethodsTest extends SignatureTest("genericMethods", Seq("def")) class MethodsAndConstructors extends SignatureTest("methodsAndConstructors", Seq("def")) +class ExtensionParams extends SignatureTest("extensionParams", SignatureTest.all) + class TypesSignatures extends SignatureTest("typesSignatures", SignatureTest.all) class FieldsSignatures extends SignatureTest("fieldsSignatures", SignatureTest.all.filterNot(_ == "object"))