Skip to content

Commit f495cf6

Browse files
Kotlin: attribute file-level KDoc to the compilation unit (K1/PSI)
A KDoc written before a file's `package` directive documents the file itself and has no declaration owner: the PSI extractor's `comment.owner` is null for it. The extractor previously emitted a "Couldn't get owner of KDoc" diagnostic and recorded no owner in that case. The K2/FIR extractor, by contrast, already attributes such a comment to the compilation unit, because the KDOC node is a direct child of the FILE node in the FIR lighter AST and the enclosing `IrFile` is discovered as its owner. The two frontends therefore disagreed on file-level KDoc ownership. Align the PSI extractor with the FIR one: when a KDoc has no declaration owner but is a direct child of the `KtFile`, attribute it to the file (`getLabel(file)` yields the compilation-unit label, the same target the FIR extractor reaches). The now-spurious "Couldn't get owner" diagnostic is suppressed for file-level KDoc (it remains for any genuinely ownerless KDoc elsewhere). This removes the last owner-attribution divergence for file-level KDoc. The `comments` and `dataflow/func` test sources, which previously carried frontend-specific text and a `// Diagnostic Matches` line asserting the old warning, are unified with their K2 counterparts; the K1 expectations lose the diagnostic and gain the compilation-unit owner row, matching K2. Remaining `comments` divergence (owners of enum entries, init blocks and an anonymous function) stems from those IR nodes carrying no FIR source metadata under K2 and is addressed separately. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent aeb5cab commit f495cf6

4 files changed

Lines changed: 22 additions & 14 deletions

File tree

java/kotlin-extractor/src/main/kotlin/comments/CommentExtractorPSI.kt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ir.declarations.*
1212
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
1313
import org.jetbrains.kotlin.lexer.KtTokens
1414
import org.jetbrains.kotlin.psi.KtVisitor
15+
import org.jetbrains.kotlin.psi.KtFile
1516
import org.jetbrains.kotlin.psi.psiUtil.endOffset
1617
import org.jetbrains.kotlin.psi.psiUtil.startOffset
1718

@@ -95,8 +96,21 @@ class CommentExtractorPSI(
9596
}
9697
}
9798

98-
// Only storing the owner of doc comments:
99-
val ownerPsi = getKDocOwner(comment) ?: return
99+
val ownerPsi = getKDocOwner(comment)
100+
if (ownerPsi == null) {
101+
// A file-level KDoc (one written before the file's package
102+
// directive) has no declaration owner. The K2/FIR extractor
103+
// attributes such a comment to the compilation unit (the KDOC
104+
// is a direct child of the FILE node there), so we do the same
105+
// here to keep the two frontends in agreement.
106+
if (comment.parent is KtFile) {
107+
val fileOwnerLabel = getLabel(file)
108+
if (fileOwnerLabel != null) {
109+
tw.writeKtCommentOwners(commentLabel, fileOwnerLabel)
110+
}
111+
}
112+
return
113+
}
100114

101115
val owners = mutableListOf<IrElement>()
102116
file.accept(IrVisitorLookup(psi2Ir, ownerPsi, file), owners)
@@ -111,7 +125,7 @@ class CommentExtractorPSI(
111125

112126
private fun getKDocOwner(comment: KDoc): PsiElement? {
113127
val owner = comment.owner
114-
if (owner == null) {
128+
if (owner == null && comment.parent !is KtFile) {
115129
logger.warn(
116130
"Couldn't get owner of KDoc. The comment is extracted without an owner."
117131
)

java/ql/test-kotlin1/library-tests/comments/comments.expected

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
comments
2-
| comments.kt:1:1:1:25 | /** Kdoc with no owner */ | /** Kdoc with no owner */ |
2+
| comments.kt:1:1:1:36 | /** Kdoc owned by CompilationUnit */ | /** Kdoc owned by CompilationUnit */ |
33
| comments.kt:4:1:11:3 | /**\n * A group of *members*.\n *\n * This class has no useful logic; it's just a documentation example.\n *\n * @property name the name of this group.\n * @constructor Creates an empty group.\n */ | /**\n * A group of *members*.\n *\n * This class has no useful logic; it's just a documentation example.\n *\n * @property name the name of this group.\n * @constructor Creates an empty group.\n */ |
44
| comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | /**\n * Members of this group.\n */ |
55
| comments.kt:19:5:22:7 | /**\n * Adds a [member] to this group.\n * @return the new size of the group.\n */ | /**\n * Adds a [member] to this group.\n * @return the new size of the group.\n */ |
@@ -15,8 +15,8 @@ comments
1515
| comments.kt:71:9:73:11 | /**\n * An anonymous function comment\n */ | /**\n * An anonymous function comment\n */ |
1616
| comments.kt:79:9:81:11 | /**\n * A local function comment\n */ | /**\n * A local function comment\n */ |
1717
| comments.kt:88:10:90:11 | /**\n * An anonymous object comment\n */ | /**\n * An anonymous object comment\n */ |
18-
| comments.kt:95:1:95:163 | // Diagnostic Matches: % Couldn't get owner of KDoc. The comment is extracted without an owner. ...while extracting a file (comments.kt) at %comments.kt:1:1:96:0% | // Diagnostic Matches: % Couldn't get owner of KDoc. The comment is extracted without an owner. ...while extracting a file (comments.kt) at %comments.kt:1:1:96:0% |
1918
commentOwners
19+
| comments.kt:1:1:1:36 | /** Kdoc owned by CompilationUnit */ | comments.kt:0:0:0:0 | comments |
2020
| comments.kt:4:1:11:3 | /**\n * A group of *members*.\n *\n * This class has no useful logic; it's just a documentation example.\n *\n * @property name the name of this group.\n * @constructor Creates an empty group.\n */ | comments.kt:12:1:31:1 | Group |
2121
| comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | comments.kt:17:13:17:23 | getMembers$private |
2222
| comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | comments.kt:17:13:17:46 | members |
@@ -35,13 +35,11 @@ commentOwners
3535
| comments.kt:88:10:90:11 | /**\n * An anonymous object comment\n */ | comments.kt:87:15:92:5 | |
3636
| comments.kt:88:10:90:11 | /**\n * An anonymous object comment\n */ | comments.kt:87:15:92:5 | new X(...) { ... } |
3737
commentNoOwners
38-
| comments.kt:1:1:1:25 | /** Kdoc with no owner */ |
3938
| comments.kt:24:9:24:25 | // A line comment |
4039
| comments.kt:28:5:30:6 | /*\n A block comment\n */ |
4140
| comments.kt:42:5:44:7 | /**\n * A variable.\n */ |
42-
| comments.kt:95:1:95:163 | // Diagnostic Matches: % Couldn't get owner of KDoc. The comment is extracted without an owner. ...while extracting a file (comments.kt) at %comments.kt:1:1:96:0% |
4341
commentSections
44-
| comments.kt:1:1:1:25 | /** Kdoc with no owner */ | Kdoc with no owner |
42+
| comments.kt:1:1:1:36 | /** Kdoc owned by CompilationUnit */ | Kdoc owned by CompilationUnit |
4543
| comments.kt:4:1:11:3 | /**\n * A group of *members*.\n *\n * This class has no useful logic; it's just a documentation example.\n *\n * @property name the name of this group.\n * @constructor Creates an empty group.\n */ | A group of *members*.\n\nThis class has no useful logic; it's just a documentation example.\n\n |
4644
| comments.kt:4:1:11:3 | /**\n * A group of *members*.\n *\n * This class has no useful logic; it's just a documentation example.\n *\n * @property name the name of this group.\n * @constructor Creates an empty group.\n */ | Creates an empty group. |
4745
| comments.kt:4:1:11:3 | /**\n * A group of *members*.\n *\n * This class has no useful logic; it's just a documentation example.\n *\n * @property name the name of this group.\n * @constructor Creates an empty group.\n */ | the name of this group. |
@@ -69,7 +67,7 @@ commentSectionContents
6967
| An anonymous object comment | An anonymous object comment |
7068
| An init block comment | An init block comment |
7169
| Creates an empty group. | Creates an empty group. |
72-
| Kdoc with no owner | Kdoc with no owner |
70+
| Kdoc owned by CompilationUnit | Kdoc owned by CompilationUnit |
7371
| Medium is in the middle | Medium is in the middle |
7472
| Members of this group. | Members of this group. |
7573
| This is high | This is high |

java/ql/test-kotlin1/library-tests/comments/comments.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/** Kdoc with no owner */
1+
/** Kdoc owned by CompilationUnit */
22
package foo.bar
33

44
/**
@@ -91,5 +91,3 @@ class XX {
9191
X() {
9292
}
9393
}
94-
95-
// Diagnostic Matches: % Couldn't get owner of KDoc. The comment is extracted without an owner. ...while extracting a file (comments.kt) at %comments.kt:1:1:96:0%

java/ql/test-kotlin1/library-tests/dataflow/func/kotlinx_coroutines_stubs.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,3 @@ public fun <T> CoroutineScope.async(
3131
): Deferred<T> {
3232
return null!!
3333
}
34-
35-
// Diagnostic Matches: % Couldn't get owner of KDoc. The comment is extracted without an owner. ...while extracting a file (kotlinx_coroutines_stubs.kt) at %kotlinx_coroutines_stubs.kt:1:1:36:0%

0 commit comments

Comments
 (0)