diff --git a/scaladoc-testcases/src/tests/extensionParams.scala b/scaladoc-testcases/src/tests/extensionParams.scala index 231a8a1fefbf..7892676af2c4 100644 --- a/scaladoc-testcases/src/tests/extensionParams.scala +++ b/scaladoc-testcases/src/tests/extensionParams.scala @@ -1,22 +1,54 @@ package tests.extensionParams +trait Animal + 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: Int)(using Int) + 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 + = ??? + + def f12(using Int)(b: A)(c: String): Number + = ??? -extension [A <: List[Char]](a: A)(using Int) - def ffffff[B](b: B): (A, B) = ??? +extension (using String)(using Unit)(a: Animal)(using Int)(using Number) + def f11(b: Any)(c: Any): Any + = ??? diff --git a/scaladoc/resources/dotty_res/styles/scalastyle.css b/scaladoc/resources/dotty_res/styles/scalastyle.css index 546524e6779a..a10ee68e2d45 100644 --- a/scaladoc/resources/dotty_res/styles/scalastyle.css +++ b/scaladoc/resources/dotty_res/styles/scalastyle.css @@ -988,8 +988,7 @@ footer .socials { color: var(--type); } -.signature *[t="t"] { - /* Types with links */ +.signature *[t="t"] { /* Types with links */ color: var(--type-link); } diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala index 6316e14a399f..f26041a5ac00 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/MemberRenderer.scala @@ -319,8 +319,8 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext .functionParameters(on.argsLists) .content val sig = typeSig ++ Signature(Plain(s"(${on.name}: ")) ++ on.signature ++ Signature(Plain(")")) ++ argsSig - MGroup(span(cls := "groupHeader")(sig.map(renderElement(_))), members.sortBy(_.name).toSeq, on.name) - }.toSeq + MGroup(span(cls := "groupHeader")(sig.map(renderElement(_))), members.sortBy(_.name).toSeq, on.name) -> on.position + }.toSeq.sortBy(_._2).map(_._1) div(cls := "membersList expand")( h2(cls := "h500")("Members list"), diff --git a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala index 78dd7fb7faf7..d42d1ae3c239 100644 --- a/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala +++ b/scaladoc/src/dotty/tools/scaladoc/renderers/Resources.scala @@ -166,7 +166,15 @@ trait Resources(using ctx: DocContext) extends Locations, Writer: def extensionTarget(member: Member): String = member.kind match - case Kind.Extension(on, _) => flattenToText(on.signature) + case Kind.Extension(on, _) => + val typeSig = SignatureBuilder() + .keyword("extension ") + .generics(on.typeParams) + .content + val argsSig = SignatureBuilder() + .functionParameters(on.argsLists) + .content + flattenToText(typeSig ++ argsSig) case _ => "" def processPage(page: Page, pageFQName: List[String]): Seq[(JSON, Seq[String])] = diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala index 9f87a23dee06..b383fe8fca5d 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala @@ -519,14 +519,13 @@ 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 = + MemberInfo(polyType.paramNames.zip(polyType.paramBounds).toMap, memberInfo.paramLists, 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)) - def findParamRefs(t: TypeRepr): Seq[ParamRef] = t match case paramRef: ParamRef => Seq(paramRef) case AppliedType(_, args) => args.flatMap(findParamRefs) @@ -563,7 +562,7 @@ trait ClassLikeSupport: MemberInfo(memberInfo.genericTypes, memberInfo.paramLists, 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 diff --git a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala index ca3dac7e12f8..b4a1fc197d9a 100644 --- a/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala +++ b/scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala @@ -147,28 +147,43 @@ object SymOps: def extendedSymbol: Option[reflect.ValDef] = import reflect.* - Option.when(sym.isExtensionMethod){ - val termParamss = sym.tree.asInstanceOf[DefDef].termParamss - if sym.isLeftAssoc || termParamss.size == 1 then termParamss(0).params(0) - else termParamss(1).params(0) + if sym.isExtensionMethod then + sym.extendedTermParamLists.find(param => !param.isImplicit && !param.isGiven).flatMap(_.params.headOption) + else None + + def splitExtensionParamList: (List[reflect.ParamClause], List[reflect.ParamClause]) = + import reflect.* + + def getPositionStartOption(pos: Option[Position]): Option[Int] = pos.flatMap { + case dotty.tools.dotc.util.NoSourcePosition => None + case pos: Position => Some(pos.start) } + def comparePositionStarts(posA: Option[Position], posB: Option[Position]): Option[Boolean] = + for { + startA <- getPositionStartOption(posA) + startB <- getPositionStartOption(posB) + } yield startA < startB + + sym.tree match + case tree: DefDef => + tree.paramss.partition(_.params.headOption.flatMap(param => + comparePositionStarts(param.symbol.pos, tree.symbol.pos)).getOrElse(false) + ) + case _ => Nil -> Nil + def extendedTypeParams: List[reflect.TypeDef] = import reflect.* - val method = sym.tree.asInstanceOf[DefDef] - method.leadingTypeParams + sym.tree match + case tree: DefDef => + tree.leadingTypeParams + case _ => Nil 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 - } - else - List.empty + sym.splitExtensionParamList._1.collect { + case tpc: TermParamClause => tpc + } def nonExtensionTermParamLists: List[reflect.TermParamClause] = import reflect.* @@ -185,14 +200,7 @@ object SymOps: } 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 + sym.splitExtensionParamList._2 def nonExtensionLeadingTypeParams: List[reflect.TypeDef] = import reflect.* diff --git a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala index 7da1bb9b7e03..ab7c2189e5d5 100644 --- a/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala +++ b/scaladoc/test/dotty/tools/scaladoc/signatures/TranslatableSignaturesTestCases.scala @@ -41,6 +41,8 @@ class MergedPackageSignatures extends SignatureTest("mergedPackage", SignatureTe class ExtensionMethodSignature extends SignatureTest("extensionMethodSignatures", SignatureTest.all) +class ExtensionMethodParamsSignature extends SignatureTest("extensionParams", SignatureTest.all) + class ClassModifiers extends SignatureTest("classModifiers", SignatureTest.classlikeKinds) class EnumSignatures extends SignatureTest("enumSignatures", SignatureTest.all)