Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ScalaDoc : type parameter in extension method is wrong #13191

Merged
merged 1 commit into from
Aug 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,75 +24,75 @@ trait SignatureBuilder extends ScalaSignatureUtils {
def memberName(name: String, dri: DRI) = text(name)

def list[E](
elements: Seq[E],
prefix: String = "",
suffix: String = "",
separator: String = ", ",
forcePrefixAndSuffix: Boolean = false
)(
elemOp: (SignatureBuilder, E) => SignatureBuilder
): SignatureBuilder = elements match {
case Nil => if forcePrefixAndSuffix then this.text(prefix + suffix) else 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 = ", ",
forcePrefixAndSuffix: Boolean = false
)(
elemOp: (SignatureBuilder, E) => SignatureBuilder
): SignatureBuilder = elements match {
case Nil => if forcePrefixAndSuffix then this.text(prefix + suffix) else 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}", ")", forcePrefixAndSuffix = true){ (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}", ")", forcePrefixAndSuffix = true) { (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