From c065412fdc7ee94c0be64b48eedb9819965df307 Mon Sep 17 00:00:00 2001 From: Vadim Mishenev Date: Thu, 13 Oct 2022 18:35:25 +0300 Subject: Ignore BuiltIns for StdLib (#2658) --- .../dokka-samples-transformer-plugin/build.gradle | 19 -- .../kotlinlang/KotlinWebsiteSamplesTransformer.kt | 196 --------------- .../dokka/kotlinlang/SamplesTransformerPlugin.kt | 15 -- .../org.jetbrains.dokka.plugability.DokkaPlugin | 1 - .../dokka-samples-transformer-plugin/build.gradle | 20 ++ .../kotlinlang/KotlinWebsiteSamplesTransformer.kt | 196 +++++++++++++++ .../dokka/kotlinlang/SamplesTransformerPlugin.kt | 15 ++ .../org.jetbrains.dokka.plugability.DokkaPlugin | 1 + .../dokka-stdlib-configuration-plugin/build.gradle | 20 ++ .../dokka/kotlinlang/StdLibConfigurationPlugin.kt | 22 ++ .../org.jetbrains.dokka.plugability.DokkaPlugin | 1 + .../gradle/projects/stdlib/stdlib.diff | 10 +- .../gradle/kotlin/StdlibGradleIntegrationTest.kt | 8 +- .../dokka/analysis/AnalysisEnvironment.kt | 16 +- .../dokka/analysis/EnvironmentAndFacade.kt | 7 +- .../org/jetbrains/dokka/analysis/KotlinAnalysis.kt | 13 +- plugins/base/src/main/kotlin/DokkaBase.kt | 8 +- .../base/src/test/kotlin/model/InheritorsTest.kt | 271 +++++++++++++++++++++ 18 files changed, 590 insertions(+), 249 deletions(-) delete mode 100644 integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/build.gradle delete mode 100644 integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/KotlinWebsiteSamplesTransformer.kt delete mode 100644 integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/SamplesTransformerPlugin.kt delete mode 100644 integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin create mode 100644 integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/build.gradle create mode 100644 integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/KotlinWebsiteSamplesTransformer.kt create mode 100644 integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/SamplesTransformerPlugin.kt create mode 100644 integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin create mode 100644 integration-tests/gradle/projects/stdlib/plugins/dokka-stdlib-configuration-plugin/build.gradle create mode 100644 integration-tests/gradle/projects/stdlib/plugins/dokka-stdlib-configuration-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/StdLibConfigurationPlugin.kt create mode 100644 integration-tests/gradle/projects/stdlib/plugins/dokka-stdlib-configuration-plugin/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin diff --git a/integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/build.gradle b/integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/build.gradle deleted file mode 100644 index 3ab3e09b..00000000 --- a/integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' -} -description "Dokka Plugin to transform the samples from stdlib" - -repositories { - mavenLocal() - mavenCentral() -} - -dependencies { - implementation "org.jetbrains.dokka:dokka-base:${dokka_it_kotlin_version}" - compileOnly "org.jetbrains.dokka:dokka-core:${dokka_it_kotlin_version}" -} - -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) { - kotlinOptions.jvmTarget = "1.8" -} - diff --git a/integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/KotlinWebsiteSamplesTransformer.kt b/integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/KotlinWebsiteSamplesTransformer.kt deleted file mode 100644 index 9d5115cd..00000000 --- a/integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/KotlinWebsiteSamplesTransformer.kt +++ /dev/null @@ -1,196 +0,0 @@ -package org.jetbrains.dokka.kotlinlang - -import com.intellij.psi.PsiDocumentManager -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiElementVisitor -import com.intellij.psi.PsiWhiteSpace -import com.intellij.psi.impl.source.tree.LeafPsiElement -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.dokka.base.transformers.pages.samples.SamplesTransformer -import org.jetbrains.dokka.plugability.DokkaContext -import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.allChildren -import org.jetbrains.kotlin.psi.psiUtil.prevLeaf -import org.jetbrains.kotlin.psi.psiUtil.startOffset -import org.jetbrains.kotlin.resolve.ImportPath -import org.jetbrains.kotlin.utils.addToStdlib.safeAs -import java.io.PrintWriter -import java.io.StringWriter - -class KotlinWebsiteSamplesTransformer(context: DokkaContext): SamplesTransformer(context) { - - private class SampleBuilder : KtTreeVisitorVoid() { - val builder = StringBuilder() - val text: String - get() = builder.toString() - - val errors = mutableListOf() - - data class ConvertError(val e: Exception, val text: String, val loc: String) - - fun KtValueArgument.extractStringArgumentValue() = - (getArgumentExpression() as KtStringTemplateExpression) - .entries.joinToString("") { it.text } - - - fun convertAssertPrints(expression: KtCallExpression) { - val (argument, commentArgument) = expression.valueArguments - builder.apply { - append("println(") - append(argument.text) - append(") // ") - append(commentArgument.extractStringArgumentValue()) - } - } - - fun convertAssertTrueFalse(expression: KtCallExpression, expectedResult: Boolean) { - val (argument) = expression.valueArguments - builder.apply { - expression.valueArguments.getOrNull(1)?.let { - append("// ${it.extractStringArgumentValue()}") - val ws = expression.prevLeaf { it is PsiWhiteSpace } - append(ws?.text ?: "\n") - } - append("println(\"") - append(argument.text) - append(" is \${") - append(argument.text) - append("}\") // $expectedResult") - } - } - - fun convertAssertFails(expression: KtCallExpression) { - val valueArguments = expression.valueArguments - - val funcArgument: KtValueArgument - val message: KtValueArgument? - - if (valueArguments.size == 1) { - message = null - funcArgument = valueArguments.first() - } else { - message = valueArguments.first() - funcArgument = valueArguments.last() - } - - builder.apply { - val argument = funcArgument.extractFunctionalArgumentText() - append(argument.lines().joinToString(separator = "\n") { "// $it" }) - append(" // ") - if (message != null) { - append(message.extractStringArgumentValue()) - } - append(" will fail") - } - } - - private fun KtValueArgument.extractFunctionalArgumentText(): String { - return if (getArgumentExpression() is KtLambdaExpression) - PsiTreeUtil.findChildOfType(this, KtBlockExpression::class.java)?.text ?: "" - else - text - } - - fun convertAssertFailsWith(expression: KtCallExpression) { - val (funcArgument) = expression.valueArguments - val (exceptionType) = expression.typeArguments - builder.apply { - val argument = funcArgument.extractFunctionalArgumentText() - append(argument.lines().joinToString(separator = "\n") { "// $it" }) - append(" // will fail with ") - append(exceptionType.text) - } - } - - override fun visitCallExpression(expression: KtCallExpression) { - when (expression.calleeExpression?.text) { - "assertPrints" -> convertAssertPrints(expression) - "assertTrue" -> convertAssertTrueFalse(expression, expectedResult = true) - "assertFalse" -> convertAssertTrueFalse(expression, expectedResult = false) - "assertFails" -> convertAssertFails(expression) - "assertFailsWith" -> convertAssertFailsWith(expression) - else -> super.visitCallExpression(expression) - } - } - - private fun reportProblemConvertingElement(element: PsiElement, e: Exception) { - val text = element.text - val document = PsiDocumentManager.getInstance(element.project).getDocument(element.containingFile) - - val lineInfo = if (document != null) { - val lineNumber = document.getLineNumber(element.startOffset) - "$lineNumber, ${element.startOffset - document.getLineStartOffset(lineNumber)}" - } else { - "offset: ${element.startOffset}" - } - errors += ConvertError(e, text, lineInfo) - } - - override fun visitElement(element: PsiElement) { - if (element is LeafPsiElement) - builder.append(element.text) - - element.acceptChildren(object : PsiElementVisitor() { - override fun visitElement(element: PsiElement) { - try { - element.accept(this@SampleBuilder) - } catch (e: Exception) { - try { - reportProblemConvertingElement(element, e) - } finally { - builder.append(element.text) //recover - } - } - } - }) - } - - } - - private fun PsiElement.buildSampleText(): String { - val sampleBuilder = SampleBuilder() - this.accept(sampleBuilder) - - sampleBuilder.errors.forEach { - val sw = StringWriter() - val pw = PrintWriter(sw) - it.e.printStackTrace(pw) - - this@KotlinWebsiteSamplesTransformer.context.logger.error("${containingFile.name}: (${it.loc}): Exception thrown while converting \n```\n${it.text}\n```\n$sw") - } - return sampleBuilder.text - } - - val importsToIgnore = arrayOf("samples.*", "samples.Sample").map { ImportPath.fromString(it) } - - override fun processImports(psiElement: PsiElement): String { - val psiFile = psiElement.containingFile - return when(val text = psiFile.safeAs()?.importList) { - is KtImportList -> text.let { - it.allChildren.filter { - it !is KtImportDirective || it.importPath !in importsToIgnore - }.joinToString(separator = "\n") { it.text } - } - else -> "" - } - } - - override fun processBody(psiElement: PsiElement): String { - val text = processSampleBody(psiElement).trim { it == '\n' || it == '\r' }.trimEnd() - val lines = text.split("\n") - val indent = lines.filter(String::isNotBlank).map { it.takeWhile(Char::isWhitespace).count() }.minOrNull() ?: 0 - return lines.joinToString("\n") { it.drop(indent) } - } - - private fun processSampleBody(psiElement: PsiElement) = when (psiElement) { - is KtDeclarationWithBody -> { - val bodyExpression = psiElement.bodyExpression - val bodyExpressionText = bodyExpression!!.buildSampleText() - when (bodyExpression) { - is KtBlockExpression -> bodyExpressionText.removeSurrounding("{", "}") - else -> bodyExpressionText - } - } - else -> psiElement.buildSampleText() - } -} \ No newline at end of file diff --git a/integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/SamplesTransformerPlugin.kt b/integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/SamplesTransformerPlugin.kt deleted file mode 100644 index e39a3cda..00000000 --- a/integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/SamplesTransformerPlugin.kt +++ /dev/null @@ -1,15 +0,0 @@ -package org.jetbrains.dokka.kotlinlang - -import org.jetbrains.dokka.CoreExtensions -import org.jetbrains.dokka.base.DokkaBase -import org.jetbrains.dokka.plugability.DokkaPlugin - -class SamplesTransformerPlugin : DokkaPlugin() { - private val dokkaBase by lazy { plugin() } - - val kotlinWebsiteSamplesTransformer by extending { - CoreExtensions.pageTransformer providing ::KotlinWebsiteSamplesTransformer override dokkaBase.defaultSamplesTransformer order { - before(dokkaBase.pageMerger) - } - } -} \ No newline at end of file diff --git a/integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin deleted file mode 100644 index 2ac2cd5f..00000000 --- a/integration-tests/gradle/projects/stdlib/dokka-samples-transformer-plugin/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin +++ /dev/null @@ -1 +0,0 @@ -org.jetbrains.dokka.kotlinlang.SamplesTransformerPlugin \ No newline at end of file diff --git a/integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/build.gradle b/integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/build.gradle new file mode 100644 index 00000000..fae54400 --- /dev/null +++ b/integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/build.gradle @@ -0,0 +1,20 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' +} +description "Dokka Plugin to transform the samples from stdlib" + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + implementation "org.jetbrains.dokka:dokka-base:${dokka_it_version}" + compileOnly "org.jetbrains.dokka:dokka-core:${dokka_it_version}" + compileOnly "org.jetbrains.dokka:dokka-analysis:${dokka_it_version}" +} + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) { + kotlinOptions.jvmTarget = "1.8" +} + diff --git a/integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/KotlinWebsiteSamplesTransformer.kt b/integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/KotlinWebsiteSamplesTransformer.kt new file mode 100644 index 00000000..9d5115cd --- /dev/null +++ b/integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/KotlinWebsiteSamplesTransformer.kt @@ -0,0 +1,196 @@ +package org.jetbrains.dokka.kotlinlang + +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.impl.source.tree.LeafPsiElement +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.dokka.base.transformers.pages.samples.SamplesTransformer +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.allChildren +import org.jetbrains.kotlin.psi.psiUtil.prevLeaf +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.resolve.ImportPath +import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import java.io.PrintWriter +import java.io.StringWriter + +class KotlinWebsiteSamplesTransformer(context: DokkaContext): SamplesTransformer(context) { + + private class SampleBuilder : KtTreeVisitorVoid() { + val builder = StringBuilder() + val text: String + get() = builder.toString() + + val errors = mutableListOf() + + data class ConvertError(val e: Exception, val text: String, val loc: String) + + fun KtValueArgument.extractStringArgumentValue() = + (getArgumentExpression() as KtStringTemplateExpression) + .entries.joinToString("") { it.text } + + + fun convertAssertPrints(expression: KtCallExpression) { + val (argument, commentArgument) = expression.valueArguments + builder.apply { + append("println(") + append(argument.text) + append(") // ") + append(commentArgument.extractStringArgumentValue()) + } + } + + fun convertAssertTrueFalse(expression: KtCallExpression, expectedResult: Boolean) { + val (argument) = expression.valueArguments + builder.apply { + expression.valueArguments.getOrNull(1)?.let { + append("// ${it.extractStringArgumentValue()}") + val ws = expression.prevLeaf { it is PsiWhiteSpace } + append(ws?.text ?: "\n") + } + append("println(\"") + append(argument.text) + append(" is \${") + append(argument.text) + append("}\") // $expectedResult") + } + } + + fun convertAssertFails(expression: KtCallExpression) { + val valueArguments = expression.valueArguments + + val funcArgument: KtValueArgument + val message: KtValueArgument? + + if (valueArguments.size == 1) { + message = null + funcArgument = valueArguments.first() + } else { + message = valueArguments.first() + funcArgument = valueArguments.last() + } + + builder.apply { + val argument = funcArgument.extractFunctionalArgumentText() + append(argument.lines().joinToString(separator = "\n") { "// $it" }) + append(" // ") + if (message != null) { + append(message.extractStringArgumentValue()) + } + append(" will fail") + } + } + + private fun KtValueArgument.extractFunctionalArgumentText(): String { + return if (getArgumentExpression() is KtLambdaExpression) + PsiTreeUtil.findChildOfType(this, KtBlockExpression::class.java)?.text ?: "" + else + text + } + + fun convertAssertFailsWith(expression: KtCallExpression) { + val (funcArgument) = expression.valueArguments + val (exceptionType) = expression.typeArguments + builder.apply { + val argument = funcArgument.extractFunctionalArgumentText() + append(argument.lines().joinToString(separator = "\n") { "// $it" }) + append(" // will fail with ") + append(exceptionType.text) + } + } + + override fun visitCallExpression(expression: KtCallExpression) { + when (expression.calleeExpression?.text) { + "assertPrints" -> convertAssertPrints(expression) + "assertTrue" -> convertAssertTrueFalse(expression, expectedResult = true) + "assertFalse" -> convertAssertTrueFalse(expression, expectedResult = false) + "assertFails" -> convertAssertFails(expression) + "assertFailsWith" -> convertAssertFailsWith(expression) + else -> super.visitCallExpression(expression) + } + } + + private fun reportProblemConvertingElement(element: PsiElement, e: Exception) { + val text = element.text + val document = PsiDocumentManager.getInstance(element.project).getDocument(element.containingFile) + + val lineInfo = if (document != null) { + val lineNumber = document.getLineNumber(element.startOffset) + "$lineNumber, ${element.startOffset - document.getLineStartOffset(lineNumber)}" + } else { + "offset: ${element.startOffset}" + } + errors += ConvertError(e, text, lineInfo) + } + + override fun visitElement(element: PsiElement) { + if (element is LeafPsiElement) + builder.append(element.text) + + element.acceptChildren(object : PsiElementVisitor() { + override fun visitElement(element: PsiElement) { + try { + element.accept(this@SampleBuilder) + } catch (e: Exception) { + try { + reportProblemConvertingElement(element, e) + } finally { + builder.append(element.text) //recover + } + } + } + }) + } + + } + + private fun PsiElement.buildSampleText(): String { + val sampleBuilder = SampleBuilder() + this.accept(sampleBuilder) + + sampleBuilder.errors.forEach { + val sw = StringWriter() + val pw = PrintWriter(sw) + it.e.printStackTrace(pw) + + this@KotlinWebsiteSamplesTransformer.context.logger.error("${containingFile.name}: (${it.loc}): Exception thrown while converting \n```\n${it.text}\n```\n$sw") + } + return sampleBuilder.text + } + + val importsToIgnore = arrayOf("samples.*", "samples.Sample").map { ImportPath.fromString(it) } + + override fun processImports(psiElement: PsiElement): String { + val psiFile = psiElement.containingFile + return when(val text = psiFile.safeAs()?.importList) { + is KtImportList -> text.let { + it.allChildren.filter { + it !is KtImportDirective || it.importPath !in importsToIgnore + }.joinToString(separator = "\n") { it.text } + } + else -> "" + } + } + + override fun processBody(psiElement: PsiElement): String { + val text = processSampleBody(psiElement).trim { it == '\n' || it == '\r' }.trimEnd() + val lines = text.split("\n") + val indent = lines.filter(String::isNotBlank).map { it.takeWhile(Char::isWhitespace).count() }.minOrNull() ?: 0 + return lines.joinToString("\n") { it.drop(indent) } + } + + private fun processSampleBody(psiElement: PsiElement) = when (psiElement) { + is KtDeclarationWithBody -> { + val bodyExpression = psiElement.bodyExpression + val bodyExpressionText = bodyExpression!!.buildSampleText() + when (bodyExpression) { + is KtBlockExpression -> bodyExpressionText.removeSurrounding("{", "}") + else -> bodyExpressionText + } + } + else -> psiElement.buildSampleText() + } +} \ No newline at end of file diff --git a/integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/SamplesTransformerPlugin.kt b/integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/SamplesTransformerPlugin.kt new file mode 100644 index 00000000..e39a3cda --- /dev/null +++ b/integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/SamplesTransformerPlugin.kt @@ -0,0 +1,15 @@ +package org.jetbrains.dokka.kotlinlang + +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.plugability.DokkaPlugin + +class SamplesTransformerPlugin : DokkaPlugin() { + private val dokkaBase by lazy { plugin() } + + val kotlinWebsiteSamplesTransformer by extending { + CoreExtensions.pageTransformer providing ::KotlinWebsiteSamplesTransformer override dokkaBase.defaultSamplesTransformer order { + before(dokkaBase.pageMerger) + } + } +} \ No newline at end of file diff --git a/integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..2ac2cd5f --- /dev/null +++ b/integration-tests/gradle/projects/stdlib/plugins/dokka-samples-transformer-plugin/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.kotlinlang.SamplesTransformerPlugin \ No newline at end of file diff --git a/integration-tests/gradle/projects/stdlib/plugins/dokka-stdlib-configuration-plugin/build.gradle b/integration-tests/gradle/projects/stdlib/plugins/dokka-stdlib-configuration-plugin/build.gradle new file mode 100644 index 00000000..ddf84a2f --- /dev/null +++ b/integration-tests/gradle/projects/stdlib/plugins/dokka-stdlib-configuration-plugin/build.gradle @@ -0,0 +1,20 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' +} +description "Dokka Plugin to configure Dokka for stdlib" + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + implementation "org.jetbrains.dokka:dokka-base:${dokka_it_version}" + compileOnly "org.jetbrains.dokka:dokka-core:${dokka_it_version}" + compileOnly "org.jetbrains.dokka:dokka-analysis:${dokka_it_version}" +} + +tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) { + kotlinOptions.jvmTarget = "1.8" +} + diff --git a/integration-tests/gradle/projects/stdlib/plugins/dokka-stdlib-configuration-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/StdLibConfigurationPlugin.kt b/integration-tests/gradle/projects/stdlib/plugins/dokka-stdlib-configuration-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/StdLibConfigurationPlugin.kt new file mode 100644 index 00000000..9f450b64 --- /dev/null +++ b/integration-tests/gradle/projects/stdlib/plugins/dokka-stdlib-configuration-plugin/src/main/kotlin/org/jetbrains/dokka/kotlinlang/StdLibConfigurationPlugin.kt @@ -0,0 +1,22 @@ +package org.jetbrains.dokka.kotlinlang + +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.plugability.DokkaPlugin +//import org.jetbrains.dokka.analysis.DokkaAnalysisConfiguration +import org.jetbrains.dokka.analysis.KotlinAnalysis + +class StdLibConfigurationPlugin : DokkaPlugin() { + private val dokkaBase by lazy { plugin() } + /** + * Uncomment after updating of StdLib in tests + */ + /*val stdLibKotlinAnalysis by extending { + dokkaBase.kotlinAnalysis providing { ctx -> + KotlinAnalysis( + sourceSets = ctx.configuration.sourceSets, + logger = ctx.logger, + analysisConfiguration = DokkaAnalysisConfiguration(ignoreCommonBuiltIns = true) + ) + } override dokkaBase.defaultKotlinAnalysis + }*/ +} \ No newline at end of file diff --git a/integration-tests/gradle/projects/stdlib/plugins/dokka-stdlib-configuration-plugin/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/integration-tests/gradle/projects/stdlib/plugins/dokka-stdlib-configuration-plugin/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..98793eca --- /dev/null +++ b/integration-tests/gradle/projects/stdlib/plugins/dokka-stdlib-configuration-plugin/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.kotlinlang.StdLibConfigurationPlugin \ No newline at end of file diff --git a/integration-tests/gradle/projects/stdlib/stdlib.diff b/integration-tests/gradle/projects/stdlib/stdlib.diff index 20404297..dcd5bb70 100644 --- a/integration-tests/gradle/projects/stdlib/stdlib.diff +++ b/integration-tests/gradle/projects/stdlib/stdlib.diff @@ -37,7 +37,7 @@ diff --git a/build.gradle b/build.gradle index aa8f21b..dd6a2ae 100644 --- a/build.gradle +++ b/build.gradle -@@ -1,80 +1,444 @@ +@@ -1,80 +1,445 @@ +import org.jetbrains.dokka.Platform + plugins { @@ -76,7 +76,8 @@ index aa8f21b..dd6a2ae 100644 dependencies { - dokka "org.jetbrains.dokka:dokka-fatjar:$dokka_version" -+ dokkaPlugin project(":dokka-samples-transformer-plugin") ++ dokkaPlugin project(":plugins:dokka-samples-transformer-plugin") ++ dokkaPlugin project(":plugins:dokka-stdlib-configuration-plugin") } -final File dokkaHome = new File(buildDir, "dokka-home") @@ -559,7 +560,7 @@ diff --git a/settings.gradle b/settings.gradle index 5209245..bd38b18 100644 --- a/settings.gradle +++ b/settings.gradle -@@ -1,5 +1,19 @@ +@@ -1,5 +1,20 @@ +pluginManagement { + resolutionStrategy { + eachPlugin { @@ -579,4 +580,5 @@ index 5209245..bd38b18 100644 include 'kotlin_native' include 'kotlin_big' -include 'ant' -+include 'dokka-samples-transformer-plugin' \ No newline at end of file ++include 'plugins:dokka-samples-transformer-plugin' ++include 'plugins:dokka-stdlib-configuration-plugin' \ No newline at end of file diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/kotlin/StdlibGradleIntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/kotlin/StdlibGradleIntegrationTest.kt index 22da0754..fc3ed66f 100644 --- a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/kotlin/StdlibGradleIntegrationTest.kt +++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/kotlin/StdlibGradleIntegrationTest.kt @@ -24,18 +24,20 @@ class StdlibGradleIntegrationTest(override val versions: BuildVersions) : Abstra override val projectOutputLocation: File by lazy { File(projectDir, "build/dokka/kotlin-stdlib") } + private val currentDokkaVersion: String = "1.5.0"//checkNotNull(System.getenv("DOKKA_VERSION")) // uncomment after updating of StdLib + @BeforeTest fun prepareProjectFiles() { val templateProjectDir = File("projects", "stdlib/kotlin-dokka-stdlib") templateProjectDir.listFiles().orEmpty() .forEach { topLevelFile -> topLevelFile.copyRecursively(File(projectDir, topLevelFile.name)) } - val pluginDir = File("projects", "stdlib/dokka-samples-transformer-plugin") + val pluginDir = File("projects", "stdlib/plugins") pluginDir.listFiles().orEmpty() .forEach { topLevelFile -> topLevelFile.copyRecursively( File( - projectDir.resolve("dokka-samples-transformer-plugin").also { it.mkdir() }, topLevelFile.name + projectDir.resolve("plugins").also { it.mkdir() }, topLevelFile.name ) ) } @@ -45,7 +47,7 @@ class StdlibGradleIntegrationTest(override val versions: BuildVersions) : Abstra @Test fun execute() { - val result = createGradleRunner("callDokka", "-i", "-s").buildRelaxed() + val result = createGradleRunner("callDokka", "-Pdokka_it_version=$currentDokkaVersion", "-i", "-s").buildRelaxed() assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":callDokka")).outcome) diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt index 044a4f07..0cae4e89 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt @@ -19,10 +19,7 @@ import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.dokka.Platform import org.jetbrains.dokka.analysis.resolve.* import org.jetbrains.kotlin.analyzer.* -import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters -import org.jetbrains.kotlin.analyzer.common.CommonDependenciesContainer -import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices -import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory +import org.jetbrains.kotlin.analyzer.common.* import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns @@ -179,7 +176,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl ) } - fun createResolutionFacade(environment: KotlinCoreEnvironment): Pair { + fun createResolutionFacade(environment: KotlinCoreEnvironment, ignoreCommonBuiltIns: Boolean = false): Pair { val projectContext = ProjectContext(environment.project, "Dokka") val sourceFiles = environment.getSourceFiles() @@ -218,6 +215,15 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl override val platform: TargetPlatform = targetPlatform override fun dependencies(): List = listOf(this, library) + extraModuleDependencies + + /** + * Only for common platform ignore BuiltIns for StdLib since it can cause a conflict + * between BuiltIns from a compiler and ones from source code. + */ + override fun dependencyOnBuiltIns(): ModuleInfo.DependencyOnBuiltIns { + return if (analysisPlatform == Platform.common && ignoreCommonBuiltIns) ModuleInfo.DependencyOnBuiltIns.NONE + else super.dependencyOnBuiltIns() + } } val sourcesScope = createSourceModuleSearchScope(environment.project, sourceFiles) diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt index 60ad3315..b946c5bd 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt @@ -8,12 +8,12 @@ import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSourceLocation import org.jetbrains.kotlin.cli.common.messages.MessageCollector import org.jetbrains.kotlin.cli.common.messages.MessageRenderer import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.utils.PathUtil internal fun createEnvironmentAndFacade( logger: DokkaLogger, sourceSets: List, - sourceSet: DokkaConfiguration.DokkaSourceSet + sourceSet: DokkaConfiguration.DokkaSourceSet, + analysisConfiguration: DokkaAnalysisConfiguration ): EnvironmentAndFacade = AnalysisEnvironment(DokkaMessageCollector(logger), sourceSet.analysisPlatform).run { if (analysisPlatform == Platform.jvm) { @@ -28,7 +28,8 @@ internal fun createEnvironmentAndFacade( loadLanguageVersionSettings(sourceSet.languageVersion, sourceSet.apiVersion) val environment = createCoreEnvironment() - val (facade, _) = createResolutionFacade(environment) + + val (facade, _) = createResolutionFacade(environment, analysisConfiguration.ignoreCommonBuiltIns) EnvironmentAndFacade(environment, facade) } diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinAnalysis.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinAnalysis.kt index e7c26c25..a188e3f9 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinAnalysis.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinAnalysis.kt @@ -8,18 +8,27 @@ import org.jetbrains.dokka.model.SourceSetDependent import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.dokka.utilities.DokkaLogger -fun KotlinAnalysis(sourceSets: List, logger: DokkaLogger): KotlinAnalysis { +fun KotlinAnalysis(sourceSets: List, logger: DokkaLogger, analysisConfiguration: DokkaAnalysisConfiguration = DokkaAnalysisConfiguration()): KotlinAnalysis { val environments = sourceSets.associateWith { sourceSet -> createEnvironmentAndFacade( logger = logger, sourceSets = sourceSets, - sourceSet = sourceSet + sourceSet = sourceSet, + analysisConfiguration = analysisConfiguration ) } return KotlinAnalysisImpl(environments) } +class DokkaAnalysisConfiguration( + /** + * Only for common platform ignore BuiltIns for StdLib since it can cause a conflict + * between BuiltIns from a compiler and ones from source code. + */ + val ignoreCommonBuiltIns: Boolean = false +) + @Deprecated(message = "Construct using list of DokkaSourceSets and logger", replaceWith = ReplaceWith("KotlinAnalysis(context.configuration.sourceSets, context.logger)") ) diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt index 377470d1..86160b4a 100644 --- a/plugins/base/src/main/kotlin/DokkaBase.kt +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -36,6 +36,7 @@ import org.jetbrains.dokka.base.translators.descriptors.ExternalClasslikesTransl import org.jetbrains.dokka.base.translators.descriptors.ExternalDocumentablesProvider import org.jetbrains.dokka.base.utils.NoopIntellijLoggerFactory import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.configuration import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer import org.jetbrains.dokka.transformers.pages.PageTransformer @@ -186,7 +187,12 @@ class DokkaBase : DokkaPlugin() { } val defaultKotlinAnalysis by extending { - kotlinAnalysis providing { ctx -> KotlinAnalysis(ctx.configuration.sourceSets, ctx.logger) } + kotlinAnalysis providing { ctx -> + KotlinAnalysis( + sourceSets = ctx.configuration.sourceSets, + logger = ctx.logger + ) + } } val locationProvider by extending { diff --git a/plugins/base/src/test/kotlin/model/InheritorsTest.kt b/plugins/base/src/test/kotlin/model/InheritorsTest.kt index 2b0aff62..151783a9 100644 --- a/plugins/base/src/test/kotlin/model/InheritorsTest.kt +++ b/plugins/base/src/test/kotlin/model/InheritorsTest.kt @@ -1,9 +1,16 @@ package model import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.analysis.DokkaAnalysisConfiguration +import org.jetbrains.dokka.analysis.KotlinAnalysis +import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.transformers.documentables.InheritorsInfo import org.jetbrains.dokka.model.DClass +import org.jetbrains.dokka.model.DFunction import org.jetbrains.dokka.model.DInterface +import org.jetbrains.dokka.model.doc.P +import org.jetbrains.dokka.model.doc.Text +import org.jetbrains.dokka.plugability.DokkaPlugin import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import utils.AbstractModelTest @@ -99,4 +106,268 @@ class InheritorsTest : AbstractModelTest("/src/main/kotlin/inheritors/Test.kt", } } } + interface A { + val a: E + } + + open class C + class B() : C(), A { + override val a: E + get() = TODO("Not yet implemented") + } + + @Test + fun `should inherit docs`() { + val expectedDoc = listOf(P(listOf(Text("some text")))) + inlineModelTest( + """|interface A { + | /** + | * some text + | */ + | val a: Int + | + | /** + | * some text + | */ + | fun b(): E + |} + |open class C + |class B() : C(), A { + | val a = 0 + | override fun b(): E {} + |} + """.trimMargin(), + platform = Platform.common.toString() + ) { + with((this / "inheritors" / "A").cast()) { + with(this / "a") { + val propDoc = this?.documentation?.values?.single()?.children?.first()?.children + propDoc equals expectedDoc + } + with(this / "b") { + val funDoc = this?.documentation?.values?.single()?.children?.first()?.children + funDoc equals expectedDoc + } + + } + + with((this / "inheritors" / "B").cast()) { + with(this / "a") { + val propDoc = this?.documentation?.values?.single()?.children?.first()?.children + propDoc equals expectedDoc + } + } + } + } + + class IgnoreCommonBuiltInsPlugin : DokkaPlugin() { + private val dokkaBase by lazy { plugin() } + @Suppress("unused") + val stdLibKotlinAnalysis by extending { + dokkaBase.kotlinAnalysis providing { ctx -> + KotlinAnalysis( + sourceSets = ctx.configuration.sourceSets, + logger = ctx.logger, + analysisConfiguration = DokkaAnalysisConfiguration(ignoreCommonBuiltIns = true) + ) + } override dokkaBase.defaultKotlinAnalysis + } + } + @Test + fun `should inherit docs for stdLib #2638`() { + val testConfiguration = dokkaConfiguration { + suppressObviousFunctions = false + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + analysisPlatform = "common" + languageVersion = "1.4" + } + } + } + + inlineModelTest( + """ + package kotlin.collections + + import kotlin.internal.PlatformDependent + + /** + * Classes that inherit from this interface can be represented as a sequence of elements that can + * be iterated over. + * @param T the type of element being iterated over. The iterator is covariant in its element type. + */ + public interface Iterable { + /** + * Returns an iterator over the elements of this object. + */ + public operator fun iterator(): Iterator + } + + /** + * Classes that inherit from this interface can be represented as a sequence of elements that can + * be iterated over and that supports removing elements during iteration. + * @param T the type of element being iterated over. The mutable iterator is invariant in its element type. + */ + public interface MutableIterable : Iterable { + /** + * Returns an iterator over the elements of this sequence that supports removing elements during iteration. + */ + override fun iterator(): MutableIterator + } + + /** + * A generic collection of elements. Methods in this interface support only read-only access to the collection; + * read/write access is supported through the [MutableCollection] interface. + * @param E the type of elements contained in the collection. The collection is covariant in its element type. + */ + public interface Collection : Iterable { + // Query Operations + /** + * Returns the size of the collection. + */ + public val size: Int + + /** + * Returns `true` if the collection is empty (contains no elements), `false` otherwise. + */ + public fun isEmpty(): Boolean + + /** + * Checks if the specified element is contained in this collection. + */ + public operator fun contains(element: @UnsafeVariance E): Boolean + + override fun iterator(): Iterator + + // Bulk Operations + /** + * Checks if all elements in the specified collection are contained in this collection. + */ + public fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean + } + + /** + * A generic collection of elements that supports adding and removing elements. + * + * @param E the type of elements contained in the collection. The mutable collection is invariant in its element type. + */ + public interface MutableCollection : Collection, MutableIterable { + // Query Operations + override fun iterator(): MutableIterator + + // Modification Operations + /** + * Adds the specified element to the collection. + * + * @return `true` if the element has been added, `false` if the collection does not support duplicates + * and the element is already contained in the collection. + */ + public fun add(element: E): Boolean + + /** + * Removes a single instance of the specified element from this + * collection, if it is present. + * + * @return `true` if the element has been successfully removed; `false` if it was not present in the collection. + */ + public fun remove(element: E): Boolean + + // Bulk Modification Operations + /** + * Adds all of the elements of the specified collection to this collection. + * + * @return `true` if any of the specified elements was added to the collection, `false` if the collection was not modified. + */ + public fun addAll(elements: Collection): Boolean + + /** + * Removes all of this collection's elements that are also contained in the specified collection. + * + * @return `true` if any of the specified elements was removed from the collection, `false` if the collection was not modified. + */ + public fun removeAll(elements: Collection): Boolean + + /** + * Retains only the elements in this collection that are contained in the specified collection. + * + * @return `true` if any element was removed from the collection, `false` if the collection was not modified. + */ + public fun retainAll(elements: Collection): Boolean + + /** + * Removes all elements from this collection. + */ + public fun clear(): Unit + } + + /** + * A generic ordered collection of elements. Methods in this interface support only read-only access to the list; + * read/write access is supported through the [MutableList] interface. + * @param E the type of elements contained in the list. The list is covariant in its element type. + */ + public interface List : Collection { + // Query Operations + + override val size: Int + override fun isEmpty(): Boolean + override fun contains(element: @UnsafeVariance E): Boolean + override fun iterator(): Iterator + + // Bulk Operations + override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean + + // Positional Access Operations + /** + * Returns the element at the specified index in the list. + */ + public operator fun get(index: Int): E + + // Search Operations + /** + * Returns the index of the first occurrence of the specified element in the list, or -1 if the specified + * element is not contained in the list. + */ + public fun indexOf(element: @UnsafeVariance E): Int + + /** + * Returns the index of the last occurrence of the specified element in the list, or -1 if the specified + * element is not contained in the list. + */ + public fun lastIndexOf(element: @UnsafeVariance E): Int + + // List Iterators + /** + * Returns a list iterator over the elements in this list (in proper sequence). + */ + public fun listIterator(): ListIterator + + /** + * Returns a list iterator over the elements in this list (in proper sequence), starting at the specified [index]. + */ + public fun listIterator(index: Int): ListIterator + + // View + /** + * Returns a view of the portion of this list between the specified [fromIndex] (inclusive) and [toIndex] (exclusive). + * The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa. + * + * Structural changes in the base list make the behavior of the view undefined. + */ + public fun subList(fromIndex: Int, toIndex: Int): List + } + + // etc + """.trimMargin(), + platform = Platform.common.toString(), + configuration = testConfiguration, + prependPackage = false, + pluginsOverrides = listOf(IgnoreCommonBuiltInsPlugin()) + ) { + with((this / "kotlin.collections" / "List" / "contains").cast()) { + documentation.size equals 1 + + } + } + } } -- cgit