Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
73b65f8
Update to Kotlin 2.2.20
nicolas-guichard Jul 3, 2026
46ae31c
scip-kotlinc: Populate SymbolInformation.Kind
nicolas-guichard Jul 3, 2026
03c9dad
scip-kotlinc: Rework getters and setters to be property children
nicolas-guichard Jul 3, 2026
9d26915
scip-kotlinc: Add enclosing_symbol field
nicolas-guichard Jul 3, 2026
a517885
scip-kotlinc: Ignore FirFileSymbols without warning
nicolas-guichard Jul 3, 2026
0f0231d
scip-kotlinc: Clear AnalyzerCheckers.visitors after consuming them
nicolas-guichard Jul 3, 2026
65a6e99
Update to Kotlin 2.3.10
rvandermeulen Jul 3, 2026
1e8c06a
scip-kotlinc: Fix compiler warnings
rvandermeulen Jul 3, 2026
9118d4c
scip-kotlinc: Fix PostAnalysisExtension to use the compilation's mess…
rvandermeulen Jul 3, 2026
a0a7088
scip-kotlinc: Add test for lambda parameters
rvandermeulen Jul 3, 2026
430a582
scip-kotlinc: Add tests for local functions and user-defined class re…
rvandermeulen Jul 3, 2026
f360ab6
scip-kotlinc: Fix displayName() for type aliases and type parameters
rvandermeulen Jul 3, 2026
89128a0
scip-kotlinc: Fix extension receiver, enum entry, and is/as type occu…
rvandermeulen Jul 3, 2026
358dea9
scip-kotlinc: Add tests for multiple supertypes and overload disambig…
rvandermeulen Jul 3, 2026
4d6bb1e
scip-kotlinc: Fix misleading LineMap docstrings
rvandermeulen Jul 3, 2026
fec6989
scip-kotlinc: Extend enclosing_range test coverage
rvandermeulen Jul 3, 2026
6e94365
Update to Kotlin 2.3.20
rvandermeulen Jul 3, 2026
d778a65
Update to Kotlin 2.4.0
nicolas-guichard Jul 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
clikt = "5.1.0"
gradle-api = "8.11.1"
junit-jupiter = "5.11.4"
kctfork = "0.7.1"
kctfork = "0.13.0"
kotest = "6.2.1"
kotlin = "2.2.0"
kotlin = "2.4.0"
kotlinx-serialization = "1.11.0"
lombok = "1.18.46"
maven-plugin-annotations = "3.15.2"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
plugins {
id 'java'
id 'org.jetbrains.kotlin.jvm' version '2.2.0'
id 'org.jetbrains.kotlin.jvm' version '2.4.0'
}
java {
toolchain {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
id 'org.jetbrains.kotlin.jvm' version '2.2.0'
id 'org.jetbrains.kotlin.jvm' version '2.4.0'
}
kotlin {
jvmToolchain(17)
Expand Down
4 changes: 0 additions & 4 deletions scip-kotlinc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ dependencies {
testImplementation(libs.kctfork.core)
}

tasks.withType<KotlinCompile>().configureEach {
compilerOptions.freeCompilerArgs.add("-Xcontext-parameters")
}

tasks.named<Test>("test") {
maxHeapSize = "2g"
}
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ val KEY_SOURCES = CompilerConfigurationKey<Path>(VAL_SOURCES)
const val VAL_TARGET = "targetroot"
val KEY_TARGET = CompilerConfigurationKey<Path>(VAL_TARGET)

const val PLUGIN_ID = "scip-kotlinc"

@OptIn(ExperimentalCompilerApi::class)
class AnalyzerCommandLineProcessor : CommandLineProcessor {
override val pluginId: String = "scip-kotlinc"
override val pluginId: String = PLUGIN_ID
override val pluginOptions: Collection<AbstractCliOption> =
listOf(
CliOption(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ class AnalyzerRegistrar(private val callback: (Document) -> Unit = {}) : Compile
FirExtensionRegistrarAdapter.registerExtension(AnalyzerFirExtensionRegistrar(options))
IrGenerationExtension.registerExtension(
PostAnalysisExtension(
configuration = configuration,
sourceRoot = options.sourceroot,
targetRoot = options.targetroot,
callback = callback,
)
)
}

override val pluginId = PLUGIN_ID

override val supportsK2: Boolean
get() = true
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ class LineMap(private val file: FirFile) {
private fun offsetToLineAndCol(offset: Int): Pair<Int, Int>? =
file.sourceFileLinesMapping?.getLineAndColumnByOffset(offset)

/** Returns the non-0-based line number for a given offset */
/** Returns the 1-based line number for a given offset (subtract 1 for protobuf). */
fun lineNumberForOffset(offset: Int): Int =
file.sourceFileLinesMapping?.getLineByOffset(offset)?.let { it + 1 } ?: 0

/** Returns the non-0-based column number for a given offset */
/** Returns the 0-based column number for a given offset. */
fun columnForOffset(offset: Int): Int = offsetToLineAndCol(offset)?.second ?: 0

/** Returns the non-0-based start character */
/** Returns the 0-based start character. */
fun startCharacter(element: KtSourceElement): Int =
offsetToLineAndCol(element.startOffset)?.second ?: 0

/** Returns the non-0-based end character */
/** Returns the 0-based end character. */
fun endCharacter(element: KtSourceElement): Int =
startCharacter(element) + nameForOffset(element).length

/** Returns the non-0-based line number */
/** Returns the 1-based line number (subtract 1 for protobuf). */
fun lineNumber(element: KtSourceElement): Int =
file.sourceFileLinesMapping?.getLineByOffset(element.startOffset)?.let { it + 1 } ?: 0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import org.scip_code.scip_java.shared.ScipShardWriter
* outside the source root are skipped with a stderr warning.
*/
class PostAnalysisExtension(
private val configuration: CompilerConfiguration,
private val sourceRoot: Path,
private val targetRoot: Path,
private val callback: (Document) -> Unit,
Expand All @@ -45,6 +46,7 @@ class PostAnalysisExtension(
} catch (e: Exception) {
handleException(e)
}
AnalyzerCheckers.visitors.clear()
}

private fun scipShardPathForFile(file: KtSourceFile): Path? {
Expand All @@ -60,11 +62,10 @@ class PostAnalysisExtension(
}

private val messageCollector =
CompilerConfiguration()
.get(
CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY,
PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false),
)
configuration.get(
CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY,
PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false),
)

private fun handleException(e: Exception) {
val writer =
Expand All @@ -73,7 +74,7 @@ class PostAnalysisExtension(
val buf = StringBuffer()

override fun close() =
messageCollector.report(CompilerMessageSeverity.EXCEPTION, buf.toString())
messageCollector.report(CompilerMessageSeverity.WARNING, buf.toString())

override fun flush() = Unit

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import java.nio.file.Paths
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.KtSourceFile
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirPackageDirective
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.directOverriddenSymbolsSafe
import org.jetbrains.kotlin.fir.analysis.checkers.toClassLikeSymbol
import org.jetbrains.kotlin.fir.analysis.getChild
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.isInterface
import org.jetbrains.kotlin.fir.renderer.*
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
Expand All @@ -19,6 +22,7 @@ import org.jetbrains.kotlin.text
import org.scip_code.scip.Document
import org.scip_code.scip.Occurrence
import org.scip_code.scip.SymbolInformation
import org.scip_code.scip.SymbolInformation.Kind
import org.scip_code.scip.SymbolRole
import org.scip_code.scip.relationship
import org.scip_code.scip.signature
Expand All @@ -39,26 +43,26 @@ class ScipTextDocumentBuilder(

fun build(): Document = documentBuilder.build("kotlin", relativePath(), fileText)

context(context: CheckerContext)
fun emitScipData(
firBasedSymbol: FirBasedSymbol<*>?,
symbol: Symbol,
element: KtSourceElement,
isDefinition: Boolean,
context: CheckerContext,
enclosingSource: KtSourceElement? = null,
) {
documentBuilder.addOccurrence(occurrence(symbol, element, isDefinition, enclosingSource))
if (isDefinition) {
documentBuilder.addSymbol(symbolInformation(firBasedSymbol, symbol, element, context))
documentBuilder.addSymbol(symbolInformation(firBasedSymbol, symbol, element))
}
}

@OptIn(SymbolInternals::class)
context(context: CheckerContext)
private fun symbolInformation(
firBasedSymbol: FirBasedSymbol<*>?,
symbol: Symbol,
element: KtSourceElement,
context: CheckerContext,
): SymbolInformation {
val supers =
when (firBasedSymbol) {
Expand All @@ -68,7 +72,7 @@ class ScipTextDocumentBuilder(
.mapNotNull { it.toClassLikeSymbol(firBasedSymbol.moduleData.session) }
.flatMap { cache[it] }
is FirFunctionSymbol<*> ->
firBasedSymbol.directOverriddenSymbolsSafe(context).flatMap { cache[it] }
firBasedSymbol.directOverriddenSymbolsSafe().flatMap { cache[it] }
else -> emptyList()
}
return symbolInformation {
Expand All @@ -84,6 +88,10 @@ class ScipTextDocumentBuilder(
}
docComment(firBasedSymbol.fir)?.let { documentation += it }
}
this.kind = scipKind(firBasedSymbol?.fir)
this.enclosingSymbol =
context.containingDeclarations.lastOrNull()?.let { cache[it].last().toString() }
?: ""
for (parent in supers) {
relationships += relationship {
this.symbol = parent.toString()
Expand Down Expand Up @@ -146,7 +154,7 @@ class ScipTextDocumentBuilder(
bodyRenderer = null,
propertyAccessorRenderer = null,
callArgumentsRenderer = FirCallNoArgumentsRenderer(),
modifierRenderer = FirAllModifierRenderer(),
modifierRenderer = FirAllModifierRenderer(FirModifierRenderer.StaticPolicy.Default),
callableSignatureRenderer = FirCallableSignatureRendererForReadability(),
declarationRenderer = FirDeclarationRenderer("local "),
)
Expand All @@ -159,6 +167,23 @@ class ScipTextDocumentBuilder(
return stripKdoc(kdoc).ifEmpty { null }
}

private fun scipKind(element: FirElement?): Kind =
when (element) {
is FirClass if element.isInterface -> Kind.Interface
is FirTypeAlias -> Kind.TypeAlias
is FirClassLikeDeclaration -> Kind.Class
is FirConstructor -> Kind.Constructor
is FirTypeParameter -> Kind.TypeParameter
is FirValueParameter -> Kind.Parameter
is FirField -> Kind.Field
is FirProperty -> Kind.Property
is FirEnumEntry -> Kind.EnumMember
is FirVariable -> Kind.Variable
is FirCallableDeclaration -> Kind.Method
is FirPackageDirective -> Kind.Package
else -> Kind.UNRECOGNIZED
}

/** Strips the `/**`, leading `*`s, and `*/` from a kdoc block, returning just the body text. */
private fun stripKdoc(kdoc: String): String {
if (kdoc.isEmpty()) return kdoc
Expand All @@ -184,14 +209,16 @@ class ScipTextDocumentBuilder(
}

companion object {
@OptIn(SymbolInternals::class)
@OptIn(SymbolInternals::class, RenderingInternals::class)
private fun displayName(firBasedSymbol: FirBasedSymbol<*>): String =
when (firBasedSymbol) {
is FirClassSymbol -> firBasedSymbol.classId.shortClassName.asString()
is FirClassLikeSymbol -> firBasedSymbol.classId.shortClassName.asString()
is FirPropertyAccessorSymbol -> firBasedSymbol.fir.propertySymbol.name.asString()
is FirFunctionSymbol -> firBasedSymbol.callableId.callableName.asString()
is FirPropertySymbol -> firBasedSymbol.callableId.callableName.asString()
is FirPropertySymbol ->
firBasedSymbol.callableIdForRendering.callableName.asString()
is FirVariableSymbol -> firBasedSymbol.name.asString()
is FirTypeParameterSymbol -> firBasedSymbol.name.asString()
else -> firBasedSymbol.toString()
}
}
Expand Down
Loading
Loading