Skip to content

Commit f0eabd4

Browse files
authored
Merge pull request github#10759 from igfoo/igfoo/numlines2
kotlin: Populate numlines
2 parents 05f70e9 + 9dc933c commit f0eabd4

File tree

14 files changed

+190
-8
lines changed

14 files changed

+190
-8
lines changed

java/kotlin-extractor/src/main/kotlin/ExternalDeclExtractor.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: Stri
8484
// file information if needed:
8585
val ftw = tw.makeFileTrapWriter(binaryPath, irDecl is IrClass)
8686

87-
val fileExtractor = KotlinFileExtractor(logger, ftw, binaryPath, manager, this, primitiveTypeMapping, pluginContext, KotlinFileExtractor.DeclarationStack(), globalExtensionState)
87+
val fileExtractor = KotlinFileExtractor(logger, ftw, null, binaryPath, manager, this, primitiveTypeMapping, pluginContext, KotlinFileExtractor.DeclarationStack(), globalExtensionState)
8888

8989
if (irDecl is IrClass) {
9090
// Populate a location and compilation-unit package for the file. This is similar to

java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,8 @@ private fun doFile(
324324
// file information
325325
val sftw = tw.makeSourceFileTrapWriter(srcFile, true)
326326
val externalDeclExtractor = ExternalDeclExtractor(logger, invocationTrapFile, srcFilePath, primitiveTypeMapping, pluginContext, globalExtensionState, fileTrapWriter)
327-
val fileExtractor = KotlinFileExtractor(logger, sftw, srcFilePath, null, externalDeclExtractor, primitiveTypeMapping, pluginContext, KotlinFileExtractor.DeclarationStack(), globalExtensionState)
327+
val linesOfCode = LinesOfCode(logger, sftw, srcFile)
328+
val fileExtractor = KotlinFileExtractor(logger, sftw, linesOfCode, srcFilePath, null, externalDeclExtractor, primitiveTypeMapping, pluginContext, KotlinFileExtractor.DeclarationStack(), globalExtensionState)
328329

329330
fileExtractor.extractFileContents(srcFile, sftw.fileId)
330331
externalDeclExtractor.extractExternalClasses()

java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import kotlin.collections.ArrayList
3636
open class KotlinFileExtractor(
3737
override val logger: FileLogger,
3838
override val tw: FileTrapWriter,
39+
val linesOfCode: LinesOfCode?,
3940
val filePath: String,
4041
dependencyCollector: OdasaOutput.TrapFileManager?,
4142
externalClassExtractor: ExternalDeclExtractor,
@@ -91,6 +92,8 @@ open class KotlinFileExtractor(
9192
if (!declarationStack.isEmpty()) {
9293
logger.errorElement("Declaration stack is not empty after processing the file", file)
9394
}
95+
96+
linesOfCode?.linesOfCodeInFile(id)
9497
}
9598
}
9699

@@ -472,6 +475,8 @@ open class KotlinFileExtractor(
472475
extractClassModifiers(c, id)
473476
extractClassSupertypes(c, id, inReceiverContext = true) // inReceiverContext = true is specified to force extraction of member prototypes of base types
474477

478+
linesOfCode?.linesOfCodeInDeclaration(c, id)
479+
475480
return id
476481
}
477482
}
@@ -1148,6 +1153,8 @@ open class KotlinFileExtractor(
11481153
addModifiers(id, "suspend")
11491154
}
11501155

1156+
linesOfCode?.linesOfCodeInDeclaration(f, id)
1157+
11511158
return id
11521159
}
11531160
}

java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,13 @@ open class KotlinUsesExtractor(
138138
val newTrapWriter = tw.makeFileTrapWriter(filePath, true)
139139
val newLoggerTrapWriter = logger.tw.makeFileTrapWriter(filePath, false)
140140
val newLogger = FileLogger(logger.loggerBase, newLoggerTrapWriter)
141-
return KotlinFileExtractor(newLogger, newTrapWriter, filePath, dependencyCollector, externalClassExtractor, primitiveTypeMapping, pluginContext, newDeclarationStack, globalExtensionState)
141+
return KotlinFileExtractor(newLogger, newTrapWriter, null, filePath, dependencyCollector, externalClassExtractor, primitiveTypeMapping, pluginContext, newDeclarationStack, globalExtensionState)
142142
}
143143

144144
val newTrapWriter = tw.makeSourceFileTrapWriter(clsFile, true)
145145
val newLoggerTrapWriter = logger.tw.makeSourceFileTrapWriter(clsFile, false)
146146
val newLogger = FileLogger(logger.loggerBase, newLoggerTrapWriter)
147-
return KotlinFileExtractor(newLogger, newTrapWriter, clsFile.path, dependencyCollector, externalClassExtractor, primitiveTypeMapping, pluginContext, newDeclarationStack, globalExtensionState)
147+
return KotlinFileExtractor(newLogger, newTrapWriter, null, clsFile.path, dependencyCollector, externalClassExtractor, primitiveTypeMapping, pluginContext, newDeclarationStack, globalExtensionState)
148148
}
149149

150150
// The Kotlin compiler internal representation of Outer<T>.Inner<S>.InnerInner<R> is InnerInner<R, S, T>. This function returns just `R`.
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package com.github.codeql
2+
3+
import com.github.codeql.utils.versions.Psi2Ir
4+
import com.intellij.psi.PsiComment
5+
import com.intellij.psi.PsiElement
6+
import com.intellij.psi.PsiWhiteSpace
7+
import org.jetbrains.kotlin.ir.IrElement
8+
import org.jetbrains.kotlin.ir.declarations.*
9+
import org.jetbrains.kotlin.kdoc.psi.api.KDocElement
10+
import org.jetbrains.kotlin.psi.KtCodeFragment
11+
import org.jetbrains.kotlin.psi.KtVisitor
12+
13+
class LinesOfCode(
14+
val logger: FileLogger,
15+
val tw: FileTrapWriter,
16+
val file: IrFile
17+
) {
18+
val psi2Ir = Psi2Ir(logger)
19+
20+
fun linesOfCodeInFile(id: Label<DbFile>) {
21+
val ktFile = psi2Ir.getKtFile(file)
22+
if (ktFile == null) {
23+
return
24+
}
25+
linesOfCodeInPsi(id, ktFile, file)
26+
}
27+
28+
fun linesOfCodeInDeclaration(d: IrDeclaration, id: Label<out DbSourceline>) {
29+
val p = psi2Ir.findPsiElement(d, file)
30+
if (p == null) {
31+
return
32+
}
33+
linesOfCodeInPsi(id, p, d)
34+
}
35+
36+
private fun linesOfCodeInPsi(id: Label<out DbSourceline>, root: PsiElement, e: IrElement) {
37+
val document = root.getContainingFile().getViewProvider().getDocument()
38+
if (document == null) {
39+
logger.errorElement("Cannot find document for PSI", e)
40+
tw.writeNumlines(id, 0, 0, 0)
41+
return
42+
}
43+
44+
val rootRange = root.getTextRange()
45+
val rootFirstLine = document.getLineNumber(rootRange.getStartOffset())
46+
val rootLastLine = document.getLineNumber(rootRange.getEndOffset())
47+
if (rootLastLine < rootFirstLine) {
48+
logger.errorElement("PSI ends before it starts", e)
49+
tw.writeNumlines(id, 0, 0, 0)
50+
return
51+
}
52+
val numLines = 1 + rootLastLine - rootFirstLine
53+
val lineContents = Array(numLines) { LineContent() }
54+
55+
val visitor =
56+
object : KtVisitor<Unit, Unit>() {
57+
override fun visitElement(element: PsiElement) {
58+
val isComment = element is PsiComment
59+
// Comments may include nodes that aren't PsiComments,
60+
// so we don't want to visit them or we'll think they
61+
// are code.
62+
if (!isComment) {
63+
element.acceptChildren(this)
64+
}
65+
66+
if (element is PsiWhiteSpace) {
67+
return
68+
}
69+
// Leaf nodes are assumed to be tokens, and
70+
// therefore we count any lines that they are on.
71+
// For comments, we actually need to look at the
72+
// outermost node, as the leaves of KDocs don't
73+
// necessarily cover all lines.
74+
if (isComment || element.getChildren().size == 0) {
75+
val range = element.getTextRange()
76+
val startOffset = range.getStartOffset()
77+
val endOffset = range.getEndOffset()
78+
// The PSI doesn't seem to have anything like
79+
// the IR's UNDEFINED_OFFSET and SYNTHETIC_OFFSET,
80+
// but < 0 still seem to represent bad/unknown
81+
// locations.
82+
if (startOffset < 0 || endOffset < 0) {
83+
logger.errorElement("PSI has negative offset", e)
84+
return
85+
}
86+
if (startOffset > endOffset) {
87+
return
88+
}
89+
// We might get e.g. an import list for a file
90+
// with no imports, which claims to have start
91+
// and end offsets of 0. Anything of 0 width
92+
// we therefore just skip.
93+
if (startOffset == endOffset) {
94+
return
95+
}
96+
val firstLine = document.getLineNumber(startOffset)
97+
val lastLine = document.getLineNumber(endOffset)
98+
if (firstLine < rootFirstLine) {
99+
logger.errorElement("PSI element starts before root", e)
100+
return
101+
} else if (lastLine > rootLastLine) {
102+
logger.errorElement("PSI element ends after root", e)
103+
return
104+
}
105+
for (line in firstLine..lastLine) {
106+
val lineContent = lineContents[line - rootFirstLine]
107+
if (isComment) {
108+
lineContent.containsComment = true
109+
} else {
110+
lineContent.containsCode = true
111+
}
112+
}
113+
}
114+
}
115+
}
116+
root.accept(visitor)
117+
val total = lineContents.size
118+
val code = lineContents.count { it.containsCode }
119+
val comment = lineContents.count { it.containsComment }
120+
tw.writeNumlines(id, total, code, comment)
121+
}
122+
123+
private class LineContent {
124+
var containsComment = false
125+
var containsCode = false
126+
}
127+
}

java/ql/test/kotlin/library-tests/inherited-callee/Test.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ open class TestKt {
44

55
}
66

7-
interface ParentIf {
7+
interface ParentIfK {
88

99
fun inheritedInterfaceMethodK()
1010

1111
}
1212

13-
interface ChildIf : ParentIf {
13+
interface ChildIfK : ParentIfK {
1414

1515

1616
}
@@ -24,7 +24,7 @@ class ChildKt : TestKt() {
2424
c.equals(c)
2525
c.hashCode()
2626
c.inheritMe()
27-
val c2: ParentIf? = null
27+
val c2: ParentIfK? = null
2828
c2?.inheritedInterfaceMethodK()
2929

3030
}

java/ql/test/kotlin/library-tests/inherited-callee/test.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77
| Test.kt:24:7:24:15 | equals(...) | equals | Object |
88
| Test.kt:25:7:25:16 | hashCode(...) | hashCode | Object |
99
| Test.kt:26:7:26:17 | inheritMe(...) | inheritMe | TestKt |
10-
| Test.kt:28:9:28:35 | inheritedInterfaceMethodK(...) | inheritedInterfaceMethodK | ParentIf |
10+
| Test.kt:28:9:28:35 | inheritedInterfaceMethodK(...) | inheritedInterfaceMethodK | ParentIfK |
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
| test.kt:2:1:4:1 | foo | 3 | 3 | 0 |
2+
| test.kt:8:1:8:9 | getX | 1 | 1 | 0 |
3+
| test.kt:18:1:18:17 | getY | 5 | 1 | 4 |
4+
| test.kt:20:1:26:1 | Foo | 7 | 6 | 1 |
5+
| test.kt:21:5:24:5 | bar | 4 | 3 | 1 |
6+
| test.kt:25:5:25:21 | getSomeField | 1 | 1 | 0 |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import java
2+
3+
from Callable c
4+
select c, c.getTotalNumberOfLines(), c.getNumberOfLinesOfCode(), c.getNumberOfCommentLines()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| test.kt:20:1:26:1 | Foo | 7 | 6 | 1 |

0 commit comments

Comments
 (0)