6
6
package org.jetbrains.kotlin
7
7
8
8
import com.intellij.lang.jvm.JvmModifier
9
- import com.intellij.openapi.vfs.StandardFileSystems
10
- import com.intellij.openapi.vfs.VirtualFileManager
11
- import com.intellij.openapi.vfs.VirtualFileSystem
12
9
import com.intellij.psi.*
13
- import com.intellij.psi.util.childrenOfType
14
- import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
15
- import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
16
- import org.jetbrains.kotlin.config.CompilerConfiguration
17
10
import org.jetbrains.kotlin.lexer.KtTokens
18
11
import org.jetbrains.kotlin.name.FqName
19
12
import org.jetbrains.kotlin.psi.*
20
- import org.jetbrains.kotlin.psi.psiUtil.allChildren
21
- import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
22
13
import org.jetbrains.kotlin.psi.psiUtil.isPublic
23
- import org.jetbrains.kotlin.test.KotlinTestUtils
24
- import org.jetbrains.kotlin.test.testFramework.KtUsefulTestCase
25
- import org.jetbrains.kotlin.test.util.KtTestUtil
26
- import java.io.File
27
14
28
15
/* *
29
16
* This test was introduced to automatically check that every public API from some module
30
17
* is documented (i.e., has a KDoc attached).
31
18
*
32
19
* The test iterates through all the source directories [sourceDirectories] and
33
- * for each directory [DocumentationLocations .sourcePaths] builds a separate resulting file
20
+ * for each directory [SourceDirectory .sourcePaths] builds a separate resulting file
34
21
* containing all the undocumented public declarations along with fully qualified names and parameter types.
35
22
*
36
23
* Then the test compares the contents of the resulting file
37
- * and the master file [DocumentationLocations .outputFilePath]
24
+ * and the master file [SourceDirectory.ForDumpFileComparison .outputFilePath]
38
25
*
39
26
* The test is intended to prevent developers from writing undocumented APIs.
40
27
* If the lack of documentation for some declaration is intentional,
41
28
* the developer has to manually add this declaration to the master file.
42
29
*/
43
- abstract class AbstractKDocCoverageTest : KtUsefulTestCase () {
44
- @OptIn(K1Deprecation ::class )
45
- protected fun doTest () {
46
- val environment = KotlinCoreEnvironment .createForParallelTests(
47
- testRootDisposable,
48
- CompilerConfiguration (),
49
- EnvironmentConfigFiles .JVM_CONFIG_FILES
50
- )
51
- val psiManager = PsiManager .getInstance(environment.project)
52
- val fileSystem = VirtualFileManager .getInstance().getFileSystem(StandardFileSystems .FILE_PROTOCOL )
53
- val homeDir = KtTestUtil .getHomeDirectory()
54
-
55
- sourceDirectories.forEach { (sourceCodeDirectoryPaths, outputFilePath) ->
56
- val roots = sourceCodeDirectoryPaths.map { File (homeDir, it) }
57
-
58
- val actualText = buildList {
59
- for (root in roots) {
60
- for (file in root.walkTopDown()) {
61
- if (file.isDirectory) continue
62
- if (file.extension != " kt" && file.extension != " java" ) continue
63
-
64
- val relativePath = file.relativeTo(root).invariantSeparatorsPath
65
-
66
- try {
67
- val psiFile = createPsiFile(file.path, psiManager, fileSystem) ? : continue
68
- when (psiFile) {
69
- is KtFile if psiFile.packageFqName !in ignoredPackages -> addAll(
70
- getUndocumentedDeclarationsByFile(psiFile)
71
- )
72
- is PsiJavaFile if psiFile.packageName !in ignoredPackages.map { it.toString() } -> addAll(
73
- getUndocumentedDeclarationsByFile(psiFile)
74
- )
75
- }
76
- } catch (e: Exception ) {
77
- throw IllegalStateException (relativePath, e)
78
- }
79
- }
80
- }
81
- }.sorted().joinToString(" \n " )
82
-
83
- val expectedFile = File (homeDir, outputFilePath)
84
- val errorMessage = """
85
- The list of public undocumented declarations in `$roots ` does not match the expected list in `$outputFilePath `.
86
- If you added new undocumented declarations, please document them or add them to the exclusion list.
87
- Otherwise, update the exclusion list accordingly.
88
- """ .trimIndent()
89
- KotlinTestUtils .assertEqualsToFile(errorMessage, expectedFile, actualText)
30
+ abstract class AbstractKDocCoverageTest : AbstractAnalysisApiCodebaseDumpFileComparisonTest () {
31
+ override fun PsiFile.processFile (): List <String > = buildList {
32
+ when (this @processFile) {
33
+ is KtFile if packageFqName !in ignoredPackages -> addAll(
34
+ getUndocumentedDeclarationsByFile(this @processFile)
35
+ )
36
+ is PsiJavaFile if packageName !in ignoredPackages.map { it.toString() } -> addAll(
37
+ getUndocumentedDeclarationsByFile(this @processFile)
38
+ )
90
39
}
91
40
}
92
41
42
+ override fun SourceDirectory.ForDumpFileComparison.getErrorMessage (): String =
43
+ """
44
+ The list of public undocumented declarations in `${getRoots()} ` does not match the expected list in `${outputFilePath} `.
45
+ If you added new undocumented declarations, please document them.
46
+ Otherwise, update the exclusion list accordingly.
47
+ """ .trimIndent()
48
+
93
49
private fun getUndocumentedDeclarationsByFile (file : KtFile ): List <String > =
94
50
file.collectPublicDeclarations()
95
51
.filter { it.shouldBeRendered() }
96
- .map { renderDeclaration(it ) }
52
+ .map { it. renderDeclaration() }
97
53
98
54
private fun getUndocumentedDeclarationsByFile (file : PsiJavaFile ): List <String > =
99
55
file.collectPublicDeclarations()
100
56
.filter { it.shouldBeRendered() }
101
- .map { renderDeclaration(it) }
102
-
103
- private fun renderDeclaration (element : PsiElement ): String =
104
- element.getQualifiedName()?.let { fqn ->
105
- val parameterList = when (element) {
106
- is KtDeclaration -> getKtParameterList(element)
107
- else -> getParameterList(element)
108
- }
109
- fqn + parameterList
110
- } ? : " RENDERING ERROR"
111
-
112
- private fun PsiElement.getQualifiedName (): String? {
113
- return when (this ) {
114
- is KtConstructor <* > -> this .containingClassOrObject?.getQualifiedName().let { classFqName ->
115
- " $classFqName :constructor"
116
- }
117
- is KtNamedDeclaration -> this .fqName?.asString()
118
- is PsiQualifiedNamedElement -> this .qualifiedName
119
- is PsiJvmMember -> this .containingClass?.qualifiedName?.let { classFqName ->
120
- val isConstructor = (this as ? PsiMethod )?.isConstructor == true
121
- classFqName + " :" + if (isConstructor) " constructor" else (this .name ? : " " )
122
- }
123
- else -> null
124
- }
125
- }
126
-
127
- private fun getParameterList (element : PsiElement ): String {
128
- val parameterList =
129
- element.childrenOfType<PsiParameterList >().singleOrNull()
130
- ?.allChildren
131
- ?.filterIsInstance<PsiParameter >()?.joinToString(" , " ) {
132
- it.type.presentableText
133
- }?.let {
134
- " ($it )"
135
- } ? : " "
136
-
137
- return parameterList
138
- }
57
+ .map { it.renderDeclaration() }
139
58
140
- private fun getKtParameterList (declaration : KtDeclaration ): String {
141
- val parameterList =
142
- declaration.childrenOfType<KtParameterList >().singleOrNull()
143
- ?.allChildren
144
- ?.filterIsInstance<KtParameter >()?.joinToString(" , " ) {
145
- it.typeReference?.typeElement?.text ? : " "
146
- }?.let {
147
- " ($it )"
148
- } ? : " "
149
-
150
- return parameterList
151
- }
152
59
153
60
private fun KtFile.collectPublicDeclarations (): List <KtDeclaration > = buildList {
154
61
this @collectPublicDeclarations.declarations.forEach { ktDeclaration ->
@@ -198,22 +105,9 @@ abstract class AbstractKDocCoverageTest : KtUsefulTestCase() {
198
105
else -> (this as ? PsiDocCommentOwner )?.docComment == null
199
106
}
200
107
201
- abstract val sourceDirectories: List <DocumentationLocations >
202
-
203
108
protected open val ignoredPropertyNames: List <String > = listOf ()
204
109
205
110
protected open val ignoredFunctionNames: List <String > = listOf ()
206
111
207
112
protected open val ignoredPackages: List <FqName > = listOf ()
208
-
209
- data class DocumentationLocations (
210
- val sourcePaths : List <String >,
211
- val outputFilePath : String ,
212
- )
213
-
214
- private fun createPsiFile (fileName : String , psiManager : PsiManager , fileSystem : VirtualFileSystem ): PsiFile ? {
215
- val file = fileSystem.findFileByPath(fileName) ? : error(" File not found: $fileName " )
216
- val psiFile = psiManager.findFile(file)
217
- return psiFile
218
- }
219
113
}
0 commit comments