From 0b1ac85956e211221bff50cff94805c750bdacd7 Mon Sep 17 00:00:00 2001 From: "Andrei.Tyrin" Date: Mon, 26 Dec 2022 21:17:07 +0100 Subject: [PATCH 1/4] Add virtual default constructor --- .../psi/DefaultPsiToDocumentableTranslator.kt | 51 ++++++++--- .../DefaultPsiToDocumentableTranslatorTest.kt | 91 ++++++++++++++++++- 2 files changed, 130 insertions(+), 12 deletions(-) diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt index 574fb2e88a..645b2e1920 100644 --- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt @@ -16,14 +16,15 @@ import org.jetbrains.dokka.analysis.KotlinAnalysis import org.jetbrains.dokka.analysis.PsiDocumentableSource import org.jetbrains.dokka.analysis.from import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.base.translators.psi.parsers.JavaDocumentationParser import org.jetbrains.dokka.base.translators.psi.parsers.JavadocParser import org.jetbrains.dokka.base.translators.typeConstructorsBeingExceptions import org.jetbrains.dokka.base.translators.unquotedValue -import org.jetbrains.dokka.links.* +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.nextTarget +import org.jetbrains.dokka.links.withClass +import org.jetbrains.dokka.links.withEnumEntryExtra import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.AnnotationTarget -import org.jetbrains.dokka.model.Nullable import org.jetbrains.dokka.model.doc.DocumentationNode import org.jetbrains.dokka.model.doc.Param import org.jetbrains.dokka.model.properties.PropertyContainer @@ -45,7 +46,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs import java.io.File class DefaultPsiToDocumentableTranslator( - context: DokkaContext + context: DokkaContext, ) : AsyncSourceToDocumentableTranslator { private val kotlinAnalysis: KotlinAnalysis = context.plugin().querySingle { kotlinAnalysis } @@ -94,8 +95,8 @@ class DefaultPsiToDocumentableTranslator( class DokkaPsiParser( private val sourceSetData: DokkaSourceSet, - facade: DokkaResolutionFacade, - private val logger: DokkaLogger + private val facade: DokkaResolutionFacade, + private val logger: DokkaLogger, ) { private val javadocParser = JavadocParser(logger, facade) private val syntheticDocProvider = SyntheticElementDocumentationProvider(javadocParser, facade) @@ -276,10 +277,19 @@ class DefaultPsiToDocumentableTranslator( JavaClassKindTypes.CLASS ) } - }) + ancestry.interfaces.map { TypeConstructorWithKind(it.typeConstructor, JavaClassKindTypes.INTERFACE) }).toSourceSetDependent() + }) + ancestry.interfaces.map { + TypeConstructorWithKind( + it.typeConstructor, + JavaClassKindTypes.INTERFACE + ) + }).toSourceSetDependent() val modifiers = getModifier().toSourceSetDependent() val implementedInterfacesExtra = ImplementedInterfaces(ancestry.allImplementedInterfaces().toSourceSetDependent()) + + fun Array.defaultCtorIfEmpty() = + if (isEmpty()) arrayOf(psi.createDefaultConstructor()) else this + when { isAnnotationType -> DAnnotation( @@ -303,6 +313,7 @@ class DefaultPsiToDocumentableTranslator( .toAnnotations() ) ) + isEnum -> DEnum( dri = dri, name = name.orEmpty(), @@ -341,6 +352,7 @@ class DefaultPsiToDocumentableTranslator( .toAnnotations() ) ) + isInterface -> DInterface( dri = dri, name = name.orEmpty(), @@ -362,10 +374,11 @@ class DefaultPsiToDocumentableTranslator( .toAnnotations() ) ) + else -> DClass( dri = dri, name = name.orEmpty(), - constructors = constructors.map { parseFunction(it, true) }, + constructors = constructors.defaultCtorIfEmpty().map { parseFunction(it, true) }, functions = allFunctions.await(), properties = allFields.await(), classlikes = classlikes.await(), @@ -390,14 +403,29 @@ class DefaultPsiToDocumentableTranslator( } } + /** + * PSI doesn't return a default constructor if class doesn't contain an explicit one. + * This method create synthetic constructor + * Visibility modifier is preserved from the class. + */ + private fun PsiClass.createDefaultConstructor(): PsiMethod { + val psiElementFactory = JavaPsiFacade.getElementFactory(facade.project) + val signature = when (val classVisibility = getVisibility()) { + JavaVisibility.Default -> name.orEmpty() + else -> "${classVisibility.name} $name" + } + return psiElementFactory.createConstructor(signature, this) + } + private fun AncestryNode.exceptionInSupertypesOrNull(): ExceptionInSupertypes? = - typeConstructorsBeingExceptions().takeIf { it.isNotEmpty() }?.let { ExceptionInSupertypes(it.toSourceSetDependent()) } + typeConstructorsBeingExceptions().takeIf { it.isNotEmpty() } + ?.let { ExceptionInSupertypes(it.toSourceSetDependent()) } private fun parseFunction( psi: PsiMethod, isConstructor: Boolean = false, inheritedFrom: DRI? = null, - parentDRI: DRI? = null + parentDRI: DRI? = null, ): DFunction { val dri = parentDRI?.let { dri -> DRI.from(psi).copy(packageName = dri.packageName, classNames = dri.classNames) @@ -427,7 +455,8 @@ class DefaultPsiToDocumentableTranslator( }, documentation = docs.toSourceSetDependent(), expectPresentInSet = null, - sources = PsiDocumentableSource(psi).toSourceSetDependent(), + // isPhysical detects the virtual methods without real sources. Here it used for default constructor + sources = if (psi.isPhysical) PsiDocumentableSource(psi).toSourceSetDependent() else emptyMap(), visibility = psi.getVisibility().toSourceSetDependent(), type = psi.returnType?.let { getBound(type = it) } ?: Void, generics = psi.mapTypeParameters(dri), diff --git a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt index 711b9c02cc..04fadfdaf1 100644 --- a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt +++ b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt @@ -1,5 +1,6 @@ package translators +import com.jetbrains.rd.util.first import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest @@ -8,7 +9,6 @@ import org.jetbrains.dokka.links.PointingToDeclaration import org.jetbrains.dokka.model.* import org.jetbrains.dokka.model.doc.* import org.jetbrains.dokka.plugability.DokkaPlugin -import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions.* import org.junit.jupiter.api.Test import utils.assertNotNull @@ -741,4 +741,93 @@ class DefaultPsiToDocumentableTranslatorTest : BaseAbstractTest() { } } } + + @Test + fun `should have public default constructor in public class`() { + testInline( + """ + |/src/main/java/test/A.java + |package test; + |public class A { + |} + """.trimIndent(), + configuration + ) { + documentablesMergingStage = { module -> + val testedClass = module.findClasslike(packageName = "test", "A") as DClass + + assertEquals(1, testedClass.constructors.size, "Expect 1 default constructor") + assertTrue( + testedClass.constructors.first().parameters.isEmpty(), + "Expect default constructor doesn't have params" + ) + assertEquals(JavaVisibility.Public, testedClass.constructors.first().constructorVisibility()) + } + } + } + + @Test + fun `should have package-private default constructor in package-private class`() { + testInline( + """ + |/src/main/java/test/A.java + |package test; + |class A { + |} + """.trimIndent(), + configuration + ) { + documentablesMergingStage = { module -> + val testedClass = module.findClasslike(packageName = "test", "A") as DClass + + assertEquals(1, testedClass.constructors.size, "Expect 1 default constructor") + assertEquals(JavaVisibility.Default, testedClass.constructors.first().constructorVisibility()) + } + } + } + + @Test + fun `should have private default constructor in private nested class`() { + testInline( + """ + |/src/main/java/test/A.java + |package test; + |class A { + | private static class PrivateNested{} + |} + """.trimIndent(), + configuration + ) { + documentablesMergingStage = { module -> + val parentClass = module.findClasslike(packageName = "test", "A") as DClass + val testedClass = parentClass.classlikes.single { it.name == "PrivateNested" } as DClass + + assertEquals(1, testedClass.constructors.size, "Expect 1 default constructor") + assertEquals(JavaVisibility.Private, testedClass.constructors.first().constructorVisibility()) + } + } + } + + @Test + fun `should not have a default constructor because have explicit private`() { + testInline( + """ + |/src/main/java/test/A.java + |package test; + |public class A { + | private A(){} + |} + """.trimIndent(), + configuration + ) { + documentablesMergingStage = { module -> + val testedClass = module.findClasslike(packageName = "test", "A") as DClass + + assertEquals(1, testedClass.constructors.size, "Expect 1 declared constructor") + assertEquals(JavaVisibility.Private, testedClass.constructors.first().constructorVisibility()) + } + } + } } + +private fun DFunction.constructorVisibility() = visibility.first().value From a88e53045d84b9d6adec7405c5c20bece85074d2 Mon Sep 17 00:00:00 2001 From: "Andrei.Tyrin" Date: Tue, 27 Dec 2022 16:10:51 +0100 Subject: [PATCH 2/4] Update tests --- .../base/src/test/kotlin/model/JavaTest.kt | 13 +++-- .../InheritedAccessorsSignatureTest.kt | 48 +++++++++++++------ .../test/kotlin/signatures/SignatureTest.kt | 8 +++- .../javadoc/search/JavadocIndexSearchTest.kt | 11 +++-- .../src/test/kotlin/KotlinAsJavaPluginTest.kt | 27 ++++++++--- 5 files changed, 74 insertions(+), 33 deletions(-) diff --git a/plugins/base/src/test/kotlin/model/JavaTest.kt b/plugins/base/src/test/kotlin/model/JavaTest.kt index 47a259431c..b3ee2726c9 100644 --- a/plugins/base/src/test/kotlin/model/JavaTest.kt +++ b/plugins/base/src/test/kotlin/model/JavaTest.kt @@ -13,7 +13,6 @@ import utils.AbstractModelTest import utils.assertNotNull import utils.name import kotlin.test.assertEquals -import org.jetbrains.dokka.links.Callable as DRICallable class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") { val configuration = dokkaConfiguration { @@ -48,7 +47,7 @@ class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") { ) { with((this / "java" / "Test").cast()) { name equals "Test" - children counts 1 + children counts 2 // default constructor and function with((this / "fn").cast()) { name equals "fn" val params = parameters.map { it.documentation.values.first().children.first() as Param } @@ -118,7 +117,7 @@ class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") { ) { with((this / "java" / "Test").cast()) { name equals "Test" - children counts 1 + children counts 2 // default constructor and function with((this / "arrayToString").cast()) { name equals "arrayToString" @@ -219,10 +218,10 @@ class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") { """, configuration = configuration ) { with((this / "java" / "InnerClass").cast()) { - children counts 1 + children counts 2 // default constructor and inner class with((this / "D").cast()) { name equals "D" - children counts 0 + children counts 1 // default constructor } } } @@ -239,7 +238,7 @@ class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") { ) { with((this / "java" / "Foo").cast()) { name equals "Foo" - children counts 1 + children counts 2 // default constructor and function with((this / "bar").cast()) { name equals "bar" @@ -263,7 +262,7 @@ class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") { """, configuration = configuration ) { with((this / "java" / "Test").cast()) { - children counts 2 + children counts 3 // default constructor + 2 props with((this / "i").cast()) { getter equals null diff --git a/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt b/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt index a75c00fcfc..6a77a39912 100644 --- a/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt +++ b/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt @@ -62,11 +62,11 @@ class InheritedAccessorsSignatureTest : BaseAbstractTest() { writerPlugin.writer.renderedContent("root/test/-a/index.html").let { javaClassContent -> val signatures = javaClassContent.signature().toList() assertEquals( - 2, signatures.size, - "Expected 2 signatures: class signature and property" + 3, signatures.size, + "Expected 3 signatures: class signature, default constructor and property" ) - val property = signatures[1] + val property = signatures[2] property.match( "open var ", A("a"), ":", A("Int"), ignoreSpanWithTokenStyle = true @@ -109,9 +109,13 @@ class InheritedAccessorsSignatureTest : BaseAbstractTest() { writerPlugin.writer.renderedContent("root/test/-a/index.html").let { javaClassContent -> val signatures = javaClassContent.signature().toList() - assertEquals(2, signatures.size, "Expected 2 signatures: class signature and property") + assertEquals( + 3, + signatures.size, + "Expected 3 signatures: class signature, default constructor and property" + ) - val property = signatures[1] + val property = signatures[2] property.match( "open val ", A("a"), ":", A("Int"), ignoreSpanWithTokenStyle = true @@ -156,9 +160,13 @@ class InheritedAccessorsSignatureTest : BaseAbstractTest() { writerPlugin.writer.renderedContent("root/test/-a/index.html").let { javaClassContent -> val signatures = javaClassContent.signature().toList() - assertEquals(2, signatures.size, "Expected 2 signatures: class signature and setter") + assertEquals( + 3, + signatures.size, + "Expected 3 signatures: class signature, default constructor and setter" + ) - val setterFunction = signatures[1] + val setterFunction = signatures[2] setterFunction.match( "open fun ", A("setA"), "(", Parameters( Parameter("a: ", A("Int")) @@ -241,9 +249,13 @@ class InheritedAccessorsSignatureTest : BaseAbstractTest() { renderingStage = { _, _ -> writerPlugin.writer.renderedContent("root/test/-java-class/index.html").let { kotlinClassContent -> val signatures = kotlinClassContent.signature().toList() - assertEquals(2, signatures.size, "Expected to find two signatures: class and property") + assertEquals( + 3, + signatures.size, + "Expected to find 3 signatures: class, default constructor and property" + ) - val property = signatures[1] + val property = signatures[2] property.match( "open var ", A("variable"), ": ", Span("String"), ignoreSpanWithTokenStyle = true @@ -290,15 +302,19 @@ class InheritedAccessorsSignatureTest : BaseAbstractTest() { // test added to control changes writerPlugin.writer.renderedContent("root/test/-java-class/index.html").let { javaClassContent -> val signatures = javaClassContent.signature().toList() - assertEquals(3, signatures.size, "Expected to find 3 signatures: class and two accessors") + assertEquals( + 4, + signatures.size, + "Expected to find 4 signatures: class, default constructor and two accessors" + ) - val getter = signatures[1] + val getter = signatures[2] getter.match( "fun ", A("getVariable"), "(): ", Span("String"), ignoreSpanWithTokenStyle = true ) - val setter = signatures[2] + val setter = signatures[3] setter.match( "fun ", A("setVariable"), "(", Parameters( Parameter("value: ", Span("String")) @@ -367,9 +383,13 @@ class InheritedAccessorsSignatureTest : BaseAbstractTest() { writerPlugin.writer.renderedContent("root/test/-java-class/index.html").let { javaClassContent -> val signatures = javaClassContent.signature().toList() - assertEquals(2, signatures.size, "Expected 2 signatures: class signature and property") + assertEquals( + 3, + signatures.size, + "Expected 2 signatures: class signature, default constructor and property" + ) - val property = signatures[1] + val property = signatures[2] property.match( "protected open var ", A("protectedGetterAndProtectedSetter"), ":", A("Int"), ignoreSpanWithTokenStyle = true diff --git a/plugins/base/src/test/kotlin/signatures/SignatureTest.kt b/plugins/base/src/test/kotlin/signatures/SignatureTest.kt index 77c92d9cba..3b2c253585 100644 --- a/plugins/base/src/test/kotlin/signatures/SignatureTest.kt +++ b/plugins/base/src/test/kotlin/signatures/SignatureTest.kt @@ -976,9 +976,13 @@ class SignatureTest : BaseAbstractTest() { writerPlugin.writer.renderedContent("root/test/-java-class/index.html").let { kotlinClassContent -> val signatures = kotlinClassContent.signature().toList() - assertEquals(2, signatures.size, "Expected 2 signatures: class signature and property") + assertEquals( + 3, + signatures.size, + "Expected 2 signatures: class signature, default constructor and property" + ) - val property = signatures[1] + val property = signatures[2] property.match( "open var ", A("property"), ":", A("Int"), ignoreSpanWithTokenStyle = true diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/search/JavadocIndexSearchTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/search/JavadocIndexSearchTest.kt index 276e9fc800..590fa186e4 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/search/JavadocIndexSearchTest.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/search/JavadocIndexSearchTest.kt @@ -1,9 +1,9 @@ package org.jetbrains.dokka.javadoc.search import org.jetbrains.dokka.javadoc.AbstractJavadocTemplateMapTest +import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import utils.TestOutputWriterPlugin -import org.junit.jupiter.api.Assertions.* internal class JavadocIndexSearchTest : AbstractJavadocTemplateMapTest() { @Test @@ -47,9 +47,12 @@ internal class JavadocIndexSearchTest : AbstractJavadocTemplateMapTest() { pluginsOverride = listOf(writerPlugin) ) { val contents = writerPlugin.writer.contents - val expectedPackageJson = """var packageSearchIndex = [{"l":"package0","url":"package0/package-summary.html"}, {"l":"All packages","url":"index.html"}]""" - val expectedClassesJson = """var typeSearchIndex = [{"p":"package0","l":"ClassA","url":"package0/ClassA.html"}, {"l":"All classes","url":"allclasses.html"}]""" - val expectedMembersJson = """var memberSearchIndex = [{"p":"package0","c":"ClassA","l":"sampleFunction()","url":"package0/ClassA.html#sampleFunction()"}, {"p":"package0","c":"ClassA","l":"propertyOfClassA","url":"package0/ClassA.html#propertyOfClassA"}]""" + val expectedPackageJson = + """var packageSearchIndex = [{"l":"package0","url":"package0/package-summary.html"}, {"l":"All packages","url":"index.html"}]""" + val expectedClassesJson = + """var typeSearchIndex = [{"p":"package0","l":"ClassA","url":"package0/ClassA.html"}, {"l":"All classes","url":"allclasses.html"}]""" + val expectedMembersJson = + """var memberSearchIndex = [{"p":"package0","c":"ClassA","l":"ClassA()","url":"package0/ClassA.html#ClassA()"}, {"p":"package0","c":"ClassA","l":"sampleFunction()","url":"package0/ClassA.html#sampleFunction()"}, {"p":"package0","c":"ClassA","l":"propertyOfClassA","url":"package0/ClassA.html#propertyOfClassA"}]""" assertEquals(expectedPackageJson, contents["package-search-index.js"]) assertEquals(expectedClassesJson, contents["type-search-index.js"]) diff --git a/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt b/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt index b43cea05ae..24c58e8e6a 100644 --- a/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt +++ b/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt @@ -13,8 +13,13 @@ import org.jetbrains.kotlin.utils.addToStdlib.cast import org.junit.Assert import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -import signatures.* -import utils.* +import signatures.Parameter +import signatures.Parameters +import signatures.firstSignature +import signatures.renderedContent +import utils.A +import utils.TestOutputWriterPlugin +import utils.match import kotlin.test.assertEquals class KotlinAsJavaPluginTest : BaseAbstractTest() { @@ -134,9 +139,18 @@ class KotlinAsJavaPluginTest : BaseAbstractTest() { } classes["TestJ"].let { - it?.children.orEmpty().assertCount(1, "(Java) TestJ members: ") - it!!.children.first() - .let { assert(it.name == "testF") { "(Java) Expected method name: testF, got: ${it.name}" } } + it?.children.orEmpty().assertCount(2, "(Java) TestJ members: ") // constructor + method + it!!.children.map { it.name } + .let { + assert( + it.containsAll( + setOf( + "testF", + "TestJ" + ) + ) + ) { "(Java) Expected method name: testF, got: $it" } + } } } } @@ -220,8 +234,9 @@ class KotlinAsJavaPluginTest : BaseAbstractTest() { val testClass = root.dfs { it.name == "TestJ" } as? ClasslikePageNode assert(testClass != null) (testClass!!.content as ContentGroup).children.last().assertNode { + skipAllNotMatching() group { - header(2){ + header(2) { +"Properties" } table { From a34eeedb59a3d529fec9de663d6b0bf877297a58 Mon Sep 17 00:00:00 2001 From: "Andrei.Tyrin" Date: Mon, 9 Jan 2023 18:30:39 +0100 Subject: [PATCH 3/4] fix feedback --- .../psi/DefaultPsiToDocumentableTranslator.kt | 40 ++++++++++++++----- .../InheritedAccessorsSignatureTest.kt | 2 +- .../test/kotlin/signatures/SignatureTest.kt | 2 +- .../DefaultPsiToDocumentableTranslatorTest.kt | 11 +++-- 4 files changed, 36 insertions(+), 19 deletions(-) diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt index 645b2e1920..321448b741 100644 --- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt @@ -267,7 +267,7 @@ class DefaultPsiToDocumentableTranslator( } parsedFields + parsedSuperFields } - val source = PsiDocumentableSource(this).toSourceSetDependent() + val source = parseSources() val classlikes = async { innerClasses.asIterable().parallelMap { parseClasslike(it, dri) } } val visibility = getVisibility().toSourceSetDependent() val ancestors = (listOfNotNull(ancestry.superclass?.let { @@ -287,9 +287,6 @@ class DefaultPsiToDocumentableTranslator( val implementedInterfacesExtra = ImplementedInterfaces(ancestry.allImplementedInterfaces().toSourceSetDependent()) - fun Array.defaultCtorIfEmpty() = - if (isEmpty()) arrayOf(psi.createDefaultConstructor()) else this - when { isAnnotationType -> DAnnotation( @@ -303,7 +300,7 @@ class DefaultPsiToDocumentableTranslator( classlikes = classlikes.await(), visibility = visibility, companion = null, - constructors = constructors.map { parseFunction(it, true) }, + constructors = parseConstructors(), generics = mapTypeParameters(dri), sourceSets = setOf(sourceSetData), isExpectActual = false, @@ -338,11 +335,12 @@ class DefaultPsiToDocumentableTranslator( expectPresentInSet = null, sources = source, functions = allFunctions.await(), - properties = fields.filter { it !is PsiEnumConstant }.map { parseField(it, accessors[it].orEmpty()) }, + properties = fields.filter { it !is PsiEnumConstant } + .map { parseField(it, accessors[it].orEmpty()) }, classlikes = classlikes.await(), visibility = visibility, companion = null, - constructors = constructors.map { parseFunction(it, true) }, + constructors = parseConstructors(), supertypes = ancestors, sourceSets = setOf(sourceSetData), isExpectActual = false, @@ -378,7 +376,7 @@ class DefaultPsiToDocumentableTranslator( else -> DClass( dri = dri, name = name.orEmpty(), - constructors = constructors.defaultCtorIfEmpty().map { parseFunction(it, true) }, + constructors = parseConstructors(), functions = allFunctions.await(), properties = allFields.await(), classlikes = classlikes.await(), @@ -403,6 +401,15 @@ class DefaultPsiToDocumentableTranslator( } } + private fun PsiClass.parseConstructors(): List { + val constructors = when { + isAnnotationType || isInterface -> emptyArray() + isEnum -> this.constructors + else -> this.constructors.takeIf { it.isNotEmpty() } ?: arrayOf(createDefaultConstructor()) + } + return constructors.map { parseFunction(it, true) } + } + /** * PSI doesn't return a default constructor if class doesn't contain an explicit one. * This method create synthetic constructor @@ -455,8 +462,7 @@ class DefaultPsiToDocumentableTranslator( }, documentation = docs.toSourceSetDependent(), expectPresentInSet = null, - // isPhysical detects the virtual methods without real sources. Here it used for default constructor - sources = if (psi.isPhysical) PsiDocumentableSource(psi).toSourceSetDependent() else emptyMap(), + sources = psi.parseSources(), visibility = psi.getVisibility().toSourceSetDependent(), type = psi.returnType?.let { getBound(type = it) } ?: Void, generics = psi.mapTypeParameters(dri), @@ -479,6 +485,18 @@ class DefaultPsiToDocumentableTranslator( ) } + /** + * `isPhysical` detects the virtual declarations without real sources. + * Otherwise, [PsiDocumentableSource] initialization will fail: non-physical declarations doesn't have `virtualFile`. + * The check is required since in [parseConstructors] we create a virtual method for default constructor. + */ + private fun PsiNamedElement.parseSources(): SourceSetDependent { + return when { + isPhysical -> PsiDocumentableSource(this).toSourceSetDependent() + else -> emptyMap() + } + } + private fun PsiMethod.getDocumentation(): DocumentationNode = this.takeIf { it is SyntheticElement }?.let { syntheticDocProvider.getDocumentation(it) } ?: javadocParser.parseDocumentation(this) @@ -686,7 +704,7 @@ class DefaultPsiToDocumentableTranslator( name = psi.name, documentation = javadocParser.parseDocumentation(psi).toSourceSetDependent(), expectPresentInSet = null, - sources = PsiDocumentableSource(psi).toSourceSetDependent(), + sources = psi.parseSources(), visibility = psi.getVisibility(getter).toSourceSetDependent(), type = getBound(psi.type), receiver = null, diff --git a/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt b/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt index 6a77a39912..49a70f1c53 100644 --- a/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt +++ b/plugins/base/src/test/kotlin/signatures/InheritedAccessorsSignatureTest.kt @@ -386,7 +386,7 @@ class InheritedAccessorsSignatureTest : BaseAbstractTest() { assertEquals( 3, signatures.size, - "Expected 2 signatures: class signature, default constructor and property" + "Expected 3 signatures: class signature, default constructor and property" ) val property = signatures[2] diff --git a/plugins/base/src/test/kotlin/signatures/SignatureTest.kt b/plugins/base/src/test/kotlin/signatures/SignatureTest.kt index 3b2c253585..f017c815a7 100644 --- a/plugins/base/src/test/kotlin/signatures/SignatureTest.kt +++ b/plugins/base/src/test/kotlin/signatures/SignatureTest.kt @@ -979,7 +979,7 @@ class SignatureTest : BaseAbstractTest() { assertEquals( 3, signatures.size, - "Expected 2 signatures: class signature, default constructor and property" + "Expected 3 signatures: class signature, default constructor and property" ) val property = signatures[2] diff --git a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt index 04fadfdaf1..537a4bfc23 100644 --- a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt +++ b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt @@ -1,6 +1,5 @@ package translators -import com.jetbrains.rd.util.first import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest @@ -761,7 +760,7 @@ class DefaultPsiToDocumentableTranslatorTest : BaseAbstractTest() { testedClass.constructors.first().parameters.isEmpty(), "Expect default constructor doesn't have params" ) - assertEquals(JavaVisibility.Public, testedClass.constructors.first().constructorVisibility()) + assertEquals(JavaVisibility.Public, testedClass.constructors.first().visibility()) } } } @@ -781,7 +780,7 @@ class DefaultPsiToDocumentableTranslatorTest : BaseAbstractTest() { val testedClass = module.findClasslike(packageName = "test", "A") as DClass assertEquals(1, testedClass.constructors.size, "Expect 1 default constructor") - assertEquals(JavaVisibility.Default, testedClass.constructors.first().constructorVisibility()) + assertEquals(JavaVisibility.Default, testedClass.constructors.first().visibility()) } } } @@ -803,7 +802,7 @@ class DefaultPsiToDocumentableTranslatorTest : BaseAbstractTest() { val testedClass = parentClass.classlikes.single { it.name == "PrivateNested" } as DClass assertEquals(1, testedClass.constructors.size, "Expect 1 default constructor") - assertEquals(JavaVisibility.Private, testedClass.constructors.first().constructorVisibility()) + assertEquals(JavaVisibility.Private, testedClass.constructors.first().visibility()) } } } @@ -824,10 +823,10 @@ class DefaultPsiToDocumentableTranslatorTest : BaseAbstractTest() { val testedClass = module.findClasslike(packageName = "test", "A") as DClass assertEquals(1, testedClass.constructors.size, "Expect 1 declared constructor") - assertEquals(JavaVisibility.Private, testedClass.constructors.first().constructorVisibility()) + assertEquals(JavaVisibility.Private, testedClass.constructors.first().visibility()) } } } } -private fun DFunction.constructorVisibility() = visibility.first().value +private fun DFunction.visibility() = visibility.values.first() From 93d1695c2a2ae00a15326e5cd7bd5b4815c0dc10 Mon Sep 17 00:00:00 2001 From: "Andrei.Tyrin" Date: Thu, 12 Jan 2023 19:37:52 +0100 Subject: [PATCH 4/4] move kdoc to comment, remove specific usage description --- .../translators/psi/DefaultPsiToDocumentableTranslator.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt index 321448b741..f67ee15c7d 100644 --- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt @@ -485,13 +485,11 @@ class DefaultPsiToDocumentableTranslator( ) } - /** - * `isPhysical` detects the virtual declarations without real sources. - * Otherwise, [PsiDocumentableSource] initialization will fail: non-physical declarations doesn't have `virtualFile`. - * The check is required since in [parseConstructors] we create a virtual method for default constructor. - */ private fun PsiNamedElement.parseSources(): SourceSetDependent { return when { + // `isPhysical` detects the virtual declarations without real sources. + // Otherwise, `PsiDocumentableSource` initialization will fail: non-physical declarations doesn't have `virtualFile`. + // This check protects from accidentally requesting sources for synthetic / virtual declarations. isPhysical -> PsiDocumentableSource(this).toSourceSetDependent() else -> emptyMap() }