Skip to content

Commit

Permalink
ScalaDoc : type parameter in extension method is wrong
Browse files Browse the repository at this point in the history
fixes #12813
  • Loading branch information
KacperFKorban committed Jul 28, 2021
1 parent c53cc91 commit b4f1ba5
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 70 deletions.
22 changes: 22 additions & 0 deletions scaladoc-testcases/src/tests/extensionParams.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package tests.extensionParams

extension [A](thiz: A)
def toTuple2[B](that: B): (A, B) = thiz -> that

extension [A](a: A)(using Int)
def f[B](b: B): (A, B) = ???

extension [A](a: A)(using Int)
def ff(b: A): (A, A) = ???

extension [A](a: A)(using Int)
def fff(using String)(b: A): (A, A) = ???

extension (a: Char)(using Int)
def ffff(using String)(b: Int): Unit = ???

extension (a: Char)(using Int)
def fffff[B](using String)(b: B): Unit = ???

extension [A <: List[Char]](a: A)(using Int)
def ffffff[B](b: B): (A, B) = ???
5 changes: 2 additions & 3 deletions scaladoc/src/dotty/tools/scaladoc/api.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ enum Modifier(val name: String, val prefix: Boolean):
case Opaque extends Modifier("opaque", true)
case Open extends Modifier("open", true)

case class ExtensionTarget(name: String, signature: Signature, dri: DRI, position: Long)
case class ExtensionTarget(name: String, typeParams: Seq[TypeParameter], argsLists: Seq[ParametersList], signature: Signature, dri: DRI, position: Long)
case class ImplicitConversion(from: DRI, to: DRI)
trait ImplicitConversionProvider { def conversion: Option[ImplicitConversion] }
trait Classlike

enum Kind(val name: String){
enum Kind(val name: String):
case RootPackage extends Kind("")
case Package extends Kind("package")
case Class(typeParams: Seq[TypeParameter], argsLists: Seq[ParametersList])
Expand All @@ -70,7 +70,6 @@ enum Kind(val name: String){
case Implicit(kind: Kind.Def | Kind.Val.type, conversion: Option[ImplicitConversion])
extends Kind(kind.name) with ImplicitConversionProvider
case Unknown extends Kind("Unknown")
}

enum Origin:
case ImplicitlyAddedBy(name: String, dri: DRI)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,14 @@ class MemberRenderer(signatureRenderer: SignatureRenderer)(using DocContext) ext
case _ => None
}.collect {
case (Some(on), members) =>
val sig = Signature(s"extension (${on.name}: ") ++ on.signature ++ Signature(")")
val typeSig = InlineSignatureBuilder()
.text("extension ")
.generics(on.typeParams)
.asInstanceOf[InlineSignatureBuilder].names.reverse
val argsSig = InlineSignatureBuilder()
.functionParameters(on.argsLists)
.asInstanceOf[InlineSignatureBuilder].names.reverse
val sig = typeSig ++ Signature(s"(${on.name}: ") ++ on.signature ++ Signature(")") ++ argsSig
MGroup(span(sig.map(renderElement)), members.sortBy(_.name).toSeq, on.name)
}.toSeq

Expand Down
17 changes: 12 additions & 5 deletions scaladoc/src/dotty/tools/scaladoc/tasty/ClassLikeSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ trait ClassLikeSupport:
val sgn = Inkuire.ExternalSignature(
signature = Inkuire.Signature(
receiver = receiver,
arguments = methodSymbol.nonExtensionParamLists.collect {
arguments = methodSymbol.nonExtensionTermParamLists.collect {
case tpc@TermParamClause(params) if !tpc.isImplicit && !tpc.isGiven => params //TODO [Inkuire] Implicit parameters
}.flatten.map(_.tpt.asInkuire(vars)),
result = defdef.returnTpt.asInkuire(vars),
Expand Down Expand Up @@ -242,11 +242,18 @@ trait ClassLikeSupport:
private def isDocumentableExtension(s: Symbol) =
!s.isHiddenByVisibility && !s.isSyntheticFunc && s.isExtensionMethod

private def parseMember(c: ClassDef)(s: Tree): Option[Member] = processTreeOpt(s)(s match
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.map { case (paramList, index) =>
ParametersList(paramList.params.map(mkParameter(_, memberInfo = memberInfo.paramLists(index))), paramListModifier(paramList.params))
}
val target = ExtensionTarget(
extSym.symbol.normalizedName,
typeParams,
termParams,
extSym.tpt.asSignature,
extSym.tpt.symbol.dri,
extSym.symbol.pos.get.start
Expand Down Expand Up @@ -302,7 +309,7 @@ trait ClassLikeSupport:
Some(parseClasslike(c))

case _ => None
)
}

private def parseGivenClasslike(c: ClassDef): Member = {
val parsedClasslike = parseClasslike(c)
Expand Down Expand Up @@ -470,8 +477,8 @@ trait ClassLikeSupport:
specificKind: (Kind.Def => Kind) = identity
): Member =
val method = methodSymbol.tree.asInstanceOf[DefDef]
val paramLists: List[TermParamClause] = methodSymbol.nonExtensionParamLists
val genericTypes = if (methodSymbol.isClassConstructor) Nil else method.leadingTypeParams
val paramLists: List[TermParamClause] = methodSymbol.nonExtensionTermParamLists
val genericTypes: List[TypeDef] = if (methodSymbol.isClassConstructor) Nil else methodSymbol.nonExtensionLeadingTypeParams

val memberInfo = unwrapMemberInfo(c, methodSymbol)

Expand Down
48 changes: 43 additions & 5 deletions scaladoc/src/dotty/tools/scaladoc/tasty/SymOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,52 @@ object SymOps:
else termParamss(1).params(0)
}

def nonExtensionParamLists: List[reflect.TermParamClause] =
def extendedTypeParams: List[reflect.TypeDef] =
import reflect.*
val method = sym.tree.asInstanceOf[DefDef]
method.leadingTypeParams

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

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.termParamss
if sym.isLeftAssoc || params.size == 1 then params.tail
else params.head :: params.tail.drop(1)
else method.termParamss
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

end extension

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class ImplicitMembersExtensionTransformer(using DocContext) extends(Module => Mo

val MyDri = c.dri
def collectApplicableMembers(source: Member): Seq[Member] = source.members.flatMap {
case m @ Member(_, _, _, Kind.Extension(ExtensionTarget(_, _, MyDri, _), _), Origin.RegularlyDefined) =>
case m @ Member(_, _, _, Kind.Extension(ExtensionTarget(_, _, _, _, MyDri, _), _), Origin.RegularlyDefined) =>
val kind = m.kind match
case d: Kind.Def => d
case _ => Kind.Def(Nil, Nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,74 +24,74 @@ trait SignatureBuilder extends ScalaSignatureUtils {
def memberName(name: String, dri: DRI) = text(name)

def list[E](
elements: Seq[E],
prefix: String = "",
suffix: String = "",
separator: String = ", ",
)(
elemOp: (SignatureBuilder, E) => SignatureBuilder
): SignatureBuilder = elements match {
case Nil => this
case head :: tail =>
tail.foldLeft(elemOp(text(prefix), head))((b, e) => elemOp(b.text(separator), e)).text(suffix)
}
elements: Seq[E],
prefix: String = "",
suffix: String = "",
separator: String = ", ",
)(
elemOp: (SignatureBuilder, E) => SignatureBuilder
): SignatureBuilder = elements match {
case Nil => this
case head :: tail =>
tail.foldLeft(elemOp(text(prefix), head))((b, e) => elemOp(b.text(separator), e)).text(suffix)
}

def annotationsBlock(d: Member): SignatureBuilder =
d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation)}

def annotationsInline(d: Parameter): SignatureBuilder =
d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation) }
d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation)}

def annotationsInline(t: TypeParameter): SignatureBuilder =
t.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation) }
def annotationsInline(d: Parameter): SignatureBuilder =
d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation) }

private def buildAnnotation(a: Annotation): SignatureBuilder =
text("@").driLink(a.dri.location.split('.').last, a.dri).buildAnnotationParams(a).text(" ")
def annotationsInline(t: TypeParameter): SignatureBuilder =
t.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation) }

private def buildAnnotationParams(a: Annotation): SignatureBuilder =
if !a.params.isEmpty then
val params = a.params.filterNot {
case Annotation.LinkParameter(_, _, text) => text == "_"
case _ => false
}
list(params, "(", ")", ", "){ (bdr, param) => bdr.buildAnnotationParameter(param)}
else this
private def buildAnnotation(a: Annotation): SignatureBuilder =
text("@").driLink(a.dri.location.split('.').last, a.dri).buildAnnotationParams(a).text(" ")

private def addParameterName(txt: Option[String]): SignatureBuilder = txt match {
case Some(name) => this.text(s"$name = ")
case _ => this
private def buildAnnotationParams(a: Annotation): SignatureBuilder =
if !a.params.isEmpty then
val params = a.params.filterNot {
case Annotation.LinkParameter(_, _, text) => text == "_"
case _ => false
}
list(params, "(", ")", ", "){ (bdr, param) => bdr.buildAnnotationParameter(param)}
else this

private def buildAnnotationParameter(a: Annotation.AnnotationParameter): SignatureBuilder = a match {
case Annotation.PrimitiveParameter(name, value) =>
addParameterName(name).text(value)
case Annotation.LinkParameter(name, dri, text) =>
addParameterName(name).driLink(text, dri)
case Annotation.UnresolvedParameter(name, value) =>
addParameterName(name).text(value)
}
private def addParameterName(txt: Option[String]): SignatureBuilder = txt match {
case Some(name) => this.text(s"$name = ")
case _ => this
}

def modifiersAndVisibility(t: Member, kind: String) =
val (prefixMods, suffixMods) = t.modifiers.partition(_.prefix)
val all = prefixMods.map(_.name) ++ Seq(t.visibility.asSignature) ++ suffixMods.map(_.name)
private def buildAnnotationParameter(a: Annotation.AnnotationParameter): SignatureBuilder = a match {
case Annotation.PrimitiveParameter(name, value) =>
addParameterName(name).text(value)
case Annotation.LinkParameter(name, dri, text) =>
addParameterName(name).driLink(text, dri)
case Annotation.UnresolvedParameter(name, value) =>
addParameterName(name).text(value)
}

text(all.toSignatureString()).text(kind + " ")
def modifiersAndVisibility(t: Member, kind: String) =
val (prefixMods, suffixMods) = t.modifiers.partition(_.prefix)
val all = prefixMods.map(_.name) ++ Seq(t.visibility.asSignature) ++ suffixMods.map(_.name)

def generics(on: Seq[TypeParameter]) = list(on.toList, "[", "]"){ (bdr, e) =>
bdr.annotationsInline(e).text(e.variance).memberName(e.name, e.dri).signature(e.signature)
}
text(all.toSignatureString()).text(kind + " ")

def functionParameters(params: Seq[ParametersList]) =
if params.isEmpty then this.text("")
else if params.size == 1 && params(0).parameters == Nil then this.text("()")
else this.list(params, separator = ""){ (bld, pList) =>
bld.list(pList.parameters, s"(${pList.modifiers}", ")"){ (bld, p) =>
val annotationsAndModifiers = bld.annotationsInline(p)
.text(p.modifiers)
val name = p.name.fold(annotationsAndModifiers)(annotationsAndModifiers.memberName(_, p.dri).text(": "))
name.signature(p.signature)
}
def generics(on: Seq[TypeParameter]) = list(on.toList, "[", "]"){ (bdr, e) =>
bdr.annotationsInline(e).text(e.variance).memberName(e.name, e.dri).signature(e.signature)
}

def functionParameters(params: Seq[ParametersList]) =
if params.isEmpty then this.text("")
else if params.size == 1 && params(0).parameters == Nil then this.text("()")
else this.list(params, separator = ""){ (bld, pList) =>
bld.list(pList.parameters, s"(${pList.modifiers}", ")"){ (bld, p) =>
val annotationsAndModifiers = bld.annotationsInline(p)
.text(p.modifiers)
val name = p.name.fold(annotationsAndModifiers)(annotationsAndModifiers.memberName(_, p.dri).text(": "))
name.signature(p.signature)
}
}
}

trait ScalaSignatureUtils:
Expand Down

0 comments on commit b4f1ba5

Please sign in to comment.