diff --git a/scala3doc-testcases/src/tests/complexNames.scala b/scala3doc-testcases/src/tests/complexNames.scala index df0e515d497d..686473204026 100644 --- a/scala3doc-testcases/src/tests/complexNames.scala +++ b/scala3doc-testcases/src/tests/complexNames.scala @@ -2,16 +2,23 @@ package tests package complexNames +import scala.annotation.StaticAnnotation + +class `*** Annotation` extends StaticAnnotation +class `OtherAnnotation` extends StaticAnnotation + +class `*** Type` +class `OtherType` + abstract class A: def ++(other: A): A def +:(other: Int): A def :+(other: Int): A - // scala3doc has problems with names in backticks - // def `multi word name`: Int - // def `*** name with arbitrary chars ^%`: Int - // def `mischievous(param:Int)`(otherParam: Int): String - // def withMischievousParams(`param: String, param2`: String): String + def `multi word name`: Int + def `*** name with arbitrary chars ^%`: Int + def `mischievous(param:Int)`(otherParam: Int): String + def withMischievousParams(`param: String, param2`: String): String def complexName_^*(param: String): A @@ -19,8 +26,23 @@ abstract class A: def `+++:`(other: Int): A //expected: def +++:(other: Int): A def `:+++`(other: Int): A //expected: def :+++(other: Int): A - def `abc_^^_&&`: A //expected: def abc_^^_&&: A + def `abc_^^_&&`: A def `abc_def`: A //expected: def abc_def: A def `abc_def_++`: A //expected: def abc_def_++: A - // def `++_abc`: A - // def `abc_++_--`: A + def `++_abc`: A + def `abc_++_--`: A + + @`*** Annotation` def withStrangeAnnotation: A + @`OtherAnnotation` def withOtherAnnotation: A //expected: @OtherAnnotation def withOtherAnnotation: A + @OtherAnnotation def withOtherAnnotation2: A + + def withStrangeType: `*** Type` + def withOtherType: `OtherType` //expected: def withOtherType: OtherType + def withOtherType2: OtherType + + def `class`: A + def `case`: A + def `=>`: A + def `=>:`(other: A): A //expected: def =>:(other: A): A + def `caseclass`: A //expected: def caseclass: A + def `Class`: A //expected: def Class: A diff --git a/scala3doc/src/dotty/dokka/model/extras.scala b/scala3doc/src/dotty/dokka/model/extras.scala index 5e49f617c929..2cbfd65223b1 100644 --- a/scala3doc/src/dotty/dokka/model/extras.scala +++ b/scala3doc/src/dotty/dokka/model/extras.scala @@ -19,7 +19,7 @@ case class MethodExtension(parametersListSizes: Seq[Int]) extends ExtraProperty[ object MethodExtension extends BaseKey[DFunction, MethodExtension] -case class ParameterExtension(isExtendedSymbol: Boolean, isGrouped: Boolean) extends ExtraProperty[DParameter]: +case class ParameterExtension(isExtendedSymbol: Boolean, isGrouped: Boolean, prefix: String) extends ExtraProperty[DParameter]: override def getKey = ParameterExtension object ParameterExtension extends BaseKey[DParameter, ParameterExtension] diff --git a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala index d1e0c4e78275..d6435f56be9c 100644 --- a/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala +++ b/scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala @@ -125,9 +125,9 @@ trait ClassLikeSupport: .map { _ => parseMethod(dd.symbol, kind = Kind.Given(getGivenInstance(dd).map(_.asSignature), None)) } - + case dd: DefDef if !dd.symbol.isHiddenByVisibility && dd.symbol.isExported => - val exportedTarget = dd.rhs.collect { + val exportedTarget = dd.rhs.collect { case a: Apply => a.fun.asInstanceOf[Select] case s: Select => s } @@ -137,7 +137,7 @@ trait ClassLikeSupport: case Select(qualifier: Ident, _) => qualifier.tpe.typeSymbol.normalizedName }.getOrElse("instance") val dri = dd.rhs.collect { - case s: Select if s.symbol.isDefDef => s.symbol.dri + case s: Select if s.symbol.isDefDef => s.symbol.dri }.orElse(exportedTarget.map(_.qualifier.tpe.typeSymbol.dri)) Some(parseMethod(dd.symbol, kind = Kind.Exported).withOrigin(Origin.ExportedFrom(s"$instanceName.$functionName", dri))) @@ -350,13 +350,13 @@ trait ClassLikeSupport: def parseArgument(argument: ValDef, prefix: Symbol => String, isExtendedSymbol: Boolean = false, isGrouped: Boolean = false): DParameter = new DParameter( argument.symbol.dri, - prefix(argument.symbol) + argument.symbol.normalizedName, + argument.symbol.normalizedName, argument.symbol.documentation.asJava, null, argument.tpt.dokkaType, ctx.sourceSet.toSet, PropertyContainer.Companion.empty() - .plus(ParameterExtension(isExtendedSymbol, isGrouped)) + .plus(ParameterExtension(isExtendedSymbol, isGrouped, prefix(argument.symbol))) .plus(MemberExtension.empty.copy(annotations = argument.symbol.getAnnotations())) ) diff --git a/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala b/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala index 0a503135ce2c..65ea00ca4fd8 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaContentBuilder.scala @@ -498,7 +498,7 @@ class ScalaPageContentBuilder( DocumentableElement( buildAnnotations(documentable), signatureBuilder.preName.reverse, - documentable.getName, + hackEscapedName(documentable.getName), signatureBuilder.names.reverse, docs.fold(Nil)(d => reset().rawComment(d.getRoot)), originInfo, diff --git a/scala3doc/src/dotty/dokka/translators/ScalaSignatureUtils.scala b/scala3doc/src/dotty/dokka/translators/ScalaSignatureUtils.scala index c6e9fab30846..be016726f912 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaSignatureUtils.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaSignatureUtils.scala @@ -7,6 +7,8 @@ import org.jetbrains.dokka.model.properties.WithExtraProperties import org.jetbrains.dokka.pages._ import collection.JavaConverters._ import dotty.dokka.model.api.{Kind, _} +import dotty.tools.dotc.core.StdNames.nme.keywords +import dotty.tools.dotc.core.Names.termName case class InlineSignatureBuilder(names: Signature = Nil, preName: Signature = Nil) extends SignatureBuilder: override def text(str: String): SignatureBuilder = copy(names = str +: names) @@ -19,7 +21,7 @@ object InlineSignatureBuilder: trait SignatureBuilder extends ScalaSignatureUtils { def text(str: String): SignatureBuilder - def name(str: String, dri: DRI) = driLink(str, dri) + def name(str: String, dri: DRI) = driLink(hackEscapedName(str), dri) def driLink(text: String, dri: DRI): SignatureBuilder def signature(s: Signature) = s.foldLeft(this){ (b, e) => e match @@ -47,7 +49,8 @@ trait SignatureBuilder extends ScalaSignatureUtils { d.annotations.foldLeft(this){ (bdr, annotation) => bdr.buildAnnotation(annotation) } private def buildAnnotation(a: Annotation): SignatureBuilder = - text("@").driLink(a.dri.getClassNames, a.dri).buildAnnotationParams(a).text(" ") + val name = hackEscapedName(a.dri.getClassNames) + text("@").driLink(name, a.dri).buildAnnotationParams(a).text(" ") private def buildAnnotationParams(a: Annotation): SignatureBuilder = if !a.params.isEmpty then @@ -81,7 +84,7 @@ trait SignatureBuilder extends ScalaSignatureUtils { tc.getProjections.asScala.foldLeft(this) { (bdr, elem) => elem match { case text: UnresolvedBound => bdr.text(text.getName) case link: TypeParameter => - bdr.driLink(link.getName, link.getDri) + bdr.driLink(hackEscapedName(link.getName), link.getDri) case other => bdr.text(s"TODO($other)") } @@ -105,7 +108,7 @@ trait SignatureBuilder extends ScalaSignatureUtils { else if !method.kind.isInstanceOf[Kind.Extension] || from != receiverPos then val b = builder.list(method.getParameters.subList(from, toIndex).asScala.toList, "(", ")"){ (bdr, param) => bdr .annotationsInline(param) - .text(param.getName) + .text(prefixFor(param) + hackEscapedName(param.getName)) .text(": ") .typeSignature(param.getType) } @@ -113,8 +116,21 @@ trait SignatureBuilder extends ScalaSignatureUtils { else (builder, toIndex) } bldr + + private def prefixFor(param: DParameter): String = param.get(ParameterExtension).prefix } trait ScalaSignatureUtils: extension (tokens: Seq[String]) def toSignatureString(): String = tokens.filter(_.trim.nonEmpty).mkString(""," "," ") + +private[dokka] val ignoredKeywords: Set[String] = Set("this") + +// TODO: remove after adding name abstraction to reflection api +private[dokka] def hackEscapedName(name: String) = + val simpleIdentifierRegex = raw"(?:\w+_[^\[\(\s_]+)|\w+|[^\[\(\s\w_]+".r + name match + case n if ignoredKeywords(n) => n + case n if keywords(termName(n)) => s"`$n`" + case simpleIdentifierRegex() => name + case n => s"`$n`" \ No newline at end of file diff --git a/scala3doc/test/dotty/dokka/ScaladocTest.scala b/scala3doc/test/dotty/dokka/ScaladocTest.scala index a57b322dbe3f..e70412f4b34e 100644 --- a/scala3doc/test/dotty/dokka/ScaladocTest.scala +++ b/scala3doc/test/dotty/dokka/ScaladocTest.scala @@ -22,11 +22,22 @@ abstract class ScaladocTest(val name: String): private def args = Scala3doc.Args( name = "test", - tastyFiles = tastyFiles(name), + tastyFiles = tastyFiles, output = getTempDir().getRoot, projectVersion = Some("1.0") ) + private def tastyFiles = + def listFilesSafe(dir: File) = Option(dir.listFiles).getOrElse { + throw AssertionError(s"$dir not found. The test name is incorrect or scala3doc-testcases were not recompiled.") + } + def collectFiles(dir: File): List[File] = listFilesSafe(dir).toList.flatMap { + case f if f.isDirectory => collectFiles(f) + case f if f.getName endsWith ".tasty" => f :: Nil + case _ => Nil + } + collectFiles(File(s"${BuildInfo.test_testcasesOutputDir}/tests/$name")) + @Rule def collector = _collector private val _collector = new ErrorCollector();