From 9d695e1b8cc2bb10f23ba57caf33bac0f07d6c04 Mon Sep 17 00:00:00 2001 From: jupblb Date: Thu, 2 Jul 2026 10:25:26 +0200 Subject: [PATCH 01/33] Add scip-kotlin-analysis: Analysis API based Kotlin indexer --- gradle/libs.versions.toml | 19 ++ scip-kotlin-analysis/build.gradle.kts | 32 +++ .../kotlin_analysis/KotlinAnalysisIndexer.kt | 231 ++++++++++++++++ .../scip_java/kotlin_analysis/ScipSymbols.kt | 73 +++++ .../scip_java/kotlin_analysis/SymbolsCache.kt | 260 ++++++++++++++++++ .../KotlinAnalysisIndexerTest.kt | 108 ++++++++ settings.gradle.kts | 5 + 7 files changed, 728 insertions(+) create mode 100644 scip-kotlin-analysis/build.gradle.kts create mode 100644 scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt create mode 100644 scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipSymbols.kt create mode 100644 scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt create mode 100644 scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d5196d3f0..8096faaa1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,6 +5,14 @@ junit-jupiter = "5.11.4" kctfork = "0.14.0" kotest = "6.2.6" kotlin = "2.4.21" +# Kotlin Analysis API engine bundled by scip-kotlin-analysis. Independent from +# the `kotlin` toolchain version: `-for-ide` artifacts are only published as +# IDE-aligned builds (see https://redirector.kotlinlang.org/maven/kotlin-ide-plugin-dependencies). +kotlin-analysis-api = "2.2.0-ij251-98" +kotlinx-collections-immutable = "0.3.8" +# Matches the coroutines dependency declared by the kotlin-compiler POM at the +# kotlin-analysis-api version above. +kotlinx-coroutines = "1.8.0" kotlinx-serialization = "1.12.0" lombok = "1.18.48" maven-plugin-annotations = "3.16.0" @@ -18,11 +26,20 @@ spotless = "8.10.4" vanniktech-maven-publish = "0.37.0" [libraries] +caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version = "2.9.3" } clikt-jvm = { module = "com.github.ajalt.clikt:clikt-jvm", version.ref = "clikt" } gradle-api = { module = "dev.gradleplugins:gradle-api", version.ref = "gradle-api" } gradle-test-kit = { module = "dev.gradleplugins:gradle-test-kit", version.ref = "gradle-api" } kctfork-core = { module = "dev.zacsweers.kctfork:core", version.ref = "kctfork" } kotest-assertions-core = { module = "io.kotest:kotest-assertions-core-jvm", version.ref = "kotest" } +kotlin-analysis-api-api = { module = "org.jetbrains.kotlin:analysis-api-for-ide", version.ref = "kotlin-analysis-api" } +kotlin-analysis-api-impl-base = { module = "org.jetbrains.kotlin:analysis-api-impl-base-for-ide", version.ref = "kotlin-analysis-api" } +kotlin-analysis-api-k2 = { module = "org.jetbrains.kotlin:analysis-api-k2-for-ide", version.ref = "kotlin-analysis-api" } +kotlin-analysis-api-platform-interface = { module = "org.jetbrains.kotlin:analysis-api-platform-interface-for-ide", version.ref = "kotlin-analysis-api" } +kotlin-analysis-api-standalone = { module = "org.jetbrains.kotlin:analysis-api-standalone-for-ide", version.ref = "kotlin-analysis-api" } +kotlin-analysis-compiler = { module = "org.jetbrains.kotlin:kotlin-compiler", version.ref = "kotlin-analysis-api" } +kotlin-analysis-low-level-api-fir = { module = "org.jetbrains.kotlin:low-level-api-fir-for-ide", version.ref = "kotlin-analysis-api" } +kotlin-analysis-symbol-light-classes = { module = "org.jetbrains.kotlin:symbol-light-classes-for-ide", version.ref = "kotlin-analysis-api" } kotlin-compiler-embeddable = { module = "org.jetbrains.kotlin:kotlin-compiler-embeddable", version.ref = "kotlin" } kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } kotlin-scripting-common = { module = "org.jetbrains.kotlin:kotlin-scripting-common", version.ref = "kotlin" } @@ -32,6 +49,8 @@ kotlin-scripting-jvm = { module = "org.jetbrains.kotlin:kotlin-scripting-jvm", v kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlin-test-junit5 = { module = "org.jetbrains.kotlin:kotlin-test-junit5", version.ref = "kotlin" } +kotlinx-collections-immutable-jvm = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm", version.ref = "kotlinx-collections-immutable" } +kotlinx-coroutines-core-jvm = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm", version.ref = "kotlinx-coroutines" } kotlinx-serialization-json-jvm = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json-jvm", version.ref = "kotlinx-serialization" } lombok = { module = "org.projectlombok:lombok", version.ref = "lombok" } maven-plugin-annotations = { module = "org.apache.maven.plugin-tools:maven-plugin-annotations", version.ref = "maven-plugin-annotations" } diff --git a/scip-kotlin-analysis/build.gradle.kts b/scip-kotlin-analysis/build.gradle.kts new file mode 100644 index 000000000..59adaf598 --- /dev/null +++ b/scip-kotlin-analysis/build.gradle.kts @@ -0,0 +1,32 @@ +plugins { + id("scip.java-library") + id("scip.kotlin-jvm") +} + +description = "Standalone Kotlin SCIP indexer built on the Kotlin Analysis API" + +// The Analysis API `*-for-ide` artifacts have unusable POM metadata (they drag in +// IDE-only dependencies), so every artifact is pinned non-transitively — the same +// approach used by Dokka's analysis-kotlin-symbols and KSP2. `kotlin-compiler` +// (the non-embeddable jar) provides the bundled IntelliJ core classes at runtime. +dependencies { + implementation(project(":scip-shared")) + implementation(libs.scip.kotlin.bindings) + + implementation(libs.kotlin.analysis.compiler) { isTransitive = false } + implementation(libs.kotlin.analysis.api.api) { isTransitive = false } + implementation(libs.kotlin.analysis.api.standalone) { isTransitive = false } + + runtimeOnly(libs.kotlin.analysis.api.impl.base) { isTransitive = false } + runtimeOnly(libs.kotlin.analysis.api.k2) { isTransitive = false } + runtimeOnly(libs.kotlin.analysis.low.level.api.fir) { isTransitive = false } + runtimeOnly(libs.kotlin.analysis.api.platform.`interface`) { isTransitive = false } + runtimeOnly(libs.kotlin.analysis.symbol.light.classes) { isTransitive = false } + runtimeOnly(libs.caffeine) + runtimeOnly(libs.kotlinx.coroutines.core.jvm) + runtimeOnly(libs.kotlinx.collections.immutable.jvm) + runtimeOnly(libs.kotlinx.serialization.json.jvm) + + testImplementation(libs.kotlin.test) + testImplementation(libs.kotlin.test.junit5) +} diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt new file mode 100644 index 000000000..86d62e6ee --- /dev/null +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -0,0 +1,231 @@ +package org.scip_code.scip_java.kotlin_analysis + +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.TextRange +import java.nio.file.Path +import java.nio.file.Paths +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.analyze +import org.jetbrains.kotlin.analysis.api.standalone.buildStandaloneAnalysisAPISession +import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtLibraryModule +import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSdkModule +import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSourceModule +import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.platform.jvm.JvmPlatforms +import org.jetbrains.kotlin.psi.KtConstructor +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtImportDirective +import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtPrimaryConstructor +import org.jetbrains.kotlin.psi.KtPropertyAccessor +import org.jetbrains.kotlin.psi.KtSecondaryConstructor +import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.KtTreeVisitorVoid +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.parents +import org.scip_code.scip.Document +import org.scip_code.scip.Occurrence +import org.scip_code.scip.SymbolRole +import org.scip_code.scip.symbolInformation +import org.scip_code.scip_java.shared.ScipDocumentBuilder +import org.scip_code.scip_java.shared.ScipRange +import org.scip_code.scip_java.shared.ScipShardPaths +import org.scip_code.scip_java.shared.ScipShardWriter + +/** + * Indexes Kotlin sources into SCIP documents using the Kotlin Analysis API in standalone mode. + * + * Unlike scip-kotlinc, this indexer does not run inside the user's kotlinc process: it bundles its + * own analysis engine, so the Kotlin version of the indexed project only matters as a + * language-version setting, never as a binary compatibility constraint. + * + * One SCIP shard is written per source file under + * `/META-INF/scip/.scip`, matching the layout produced by the + * scip-javac and scip-kotlinc compiler plugins. + */ +class KotlinAnalysisIndexer( + private val sourceroot: Path, + private val targetroot: Path, + private val sourceRoots: List, + private val classpath: List = emptyList(), + private val jdkHome: Path? = Paths.get(System.getProperty("java.home")), + private val callback: (Document) -> Unit = {}, +) { + fun run(): List { + val disposable = Disposer.newDisposable("scip-kotlin-analysis") + try { + val session = + buildStandaloneAnalysisAPISession(projectDisposable = disposable) { + buildKtModuleProvider { + platform = JvmPlatforms.defaultJvmPlatform + val dependencies = buildList { + jdkHome?.let { home -> + add( + buildKtSdkModule { + platform = JvmPlatforms.defaultJvmPlatform + addBinaryRootsFromJdkHome(home, isJre = false) + libraryName = "JDK" + } + ) + } + if (classpath.isNotEmpty()) { + add( + buildKtLibraryModule { + platform = JvmPlatforms.defaultJvmPlatform + addBinaryRoots(classpath) + libraryName = "classpath" + } + ) + } + } + addModule( + buildKtSourceModule { + platform = JvmPlatforms.defaultJvmPlatform + moduleName = "scip-kotlin-analysis" + addSourceRoots(sourceRoots) + dependencies.forEach { addRegularDependency(it) } + } + ) + } + } + + val documents = mutableListOf() + val ktFiles = session.modulesWithFiles.values.flatten().filterIsInstance() + for (ktFile in ktFiles) { + val document = indexFile(ktFile) + writeShard(ktFile, document) + callback(document) + documents.add(document) + } + return documents + } finally { + Disposer.dispose(disposable) + } + } + + private fun indexFile(ktFile: KtFile): Document { + val cache = SymbolsCache() + val text = ktFile.text + val lines = LineIndex(text) + val builder = ScipDocumentBuilder() + analyze(ktFile) { ktFile.accept(IndexingVisitor(this, cache, lines, builder)) } + val relativePath = + ScipShardPaths.relativePath(sourceroot, Paths.get(ktFile.virtualFilePath)) + return builder.build("kotlin", relativePath, text) + } + + private fun writeShard(ktFile: KtFile, document: Document) { + val absolutePath = Paths.get(ktFile.virtualFilePath).normalize() + val shardPath = ScipShardPaths.shardPath(targetroot, sourceroot, absolutePath) + if (shardPath.isPresent) { + ScipShardWriter.writeShard(shardPath.get(), document) + } else { + System.err.println( + "given file is not under the sourceroot.\n\tSourceroot: $sourceroot\n\tFile path: $absolutePath" + ) + } + } + + private class IndexingVisitor( + private val session: KaSession, + private val cache: SymbolsCache, + private val lines: LineIndex, + private val out: ScipDocumentBuilder, + ) : KtTreeVisitorVoid() { + + override fun visitDeclaration(dcl: KtDeclaration) { + super.visitDeclaration(dcl) + // Parameters that don't belong to a declaration (function types, catch + // clauses, for loops) are not emitted as definitions; references to the + // latter two still produce local symbols on first use. + if (dcl is KtParameter && !cache.isDeclarationParameter(dcl)) return + val range = definitionRange(dcl) ?: return + val symbol = cache.symbolForDeclaration(dcl) + if (symbol == Symbol.NONE) return + addOccurrence(symbol, range, definition = true) + out.addSymbol( + symbolInformation { + this.symbol = symbol.toString() + this.displayName = displayName(dcl) + } + ) + } + + override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) { + super.visitSimpleNameExpression(expression) + if (expression.parents.any { it is KtImportDirective }) return + val target = with(session) { expression.mainReference.resolveToSymbol() } ?: return + val symbol = cache.symbolForReference(target) + if (symbol == Symbol.NONE) return + addOccurrence( + symbol, + expression.getReferencedNameElement().textRange, + definition = false, + ) + } + + private fun definitionRange(declaration: KtDeclaration): TextRange? = + when (declaration) { + // A primary constructor without the `constructor` keyword is linked + // to the class identifier, matching the FIR indexer. + is KtPrimaryConstructor -> + declaration.getConstructorKeyword()?.textRange + ?: declaration.containingClassOrObject?.nameIdentifier?.textRange + is KtSecondaryConstructor -> declaration.getConstructorKeyword().textRange + is KtPropertyAccessor -> declaration.namePlaceholder.textRange + is KtNamedDeclaration -> declaration.nameIdentifier?.textRange + else -> null + } + + private fun displayName(declaration: KtDeclaration): String = + when (declaration) { + is KtPropertyAccessor -> declaration.property.name.orEmpty() + is KtConstructor<*> -> "" + is KtNamedDeclaration -> declaration.name.orEmpty() + else -> "" + } + + private fun addOccurrence(symbol: Symbol, range: TextRange, definition: Boolean) { + val scipRange = lines.scipRange(range) ?: return + val builder = Occurrence.newBuilder().setSymbol(symbol.toString()) + if (definition) { + builder.setSymbolRoles(SymbolRole.Definition.number) + } + if (scipRange.isSingleLine) { + builder.setSingleLineRange(scipRange.toSingleLineRange()) + } else { + builder.setMultiLineRange(scipRange.toMultiLineRange()) + } + out.addOccurrence(builder.build()) + } + } +} + +/** Maps text offsets to 0-based line/character positions. */ +internal class LineIndex(private val text: String) { + private val lineStartOffsets: IntArray + + init { + val offsets = ArrayList() + offsets.add(0) + for (index in text.indices) { + if (text[index] == '\n') offsets.add(index + 1) + } + lineStartOffsets = offsets.toIntArray() + } + + fun scipRange(range: TextRange): ScipRange? { + if (range.startOffset > text.length || range.endOffset > text.length) return null + val (startLine, startCharacter) = position(range.startOffset) + val (endLine, endCharacter) = position(range.endOffset) + return ScipRange.range(startLine, startCharacter, endLine, endCharacter) + } + + private fun position(offset: Int): Pair { + var line = java.util.Arrays.binarySearch(lineStartOffsets, offset) + if (line < 0) line = -line - 2 + return line to (offset - lineStartOffsets[line]) + } +} diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipSymbols.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipSymbols.kt new file mode 100644 index 000000000..afe9c19a9 --- /dev/null +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipSymbols.kt @@ -0,0 +1,73 @@ +package org.scip_code.scip_java.kotlin_analysis + +import org.scip_code.scip_java.shared.ScipSymbols as SharedSymbols + +// Ported from scip-kotlinc's ScipSymbols.kt so that the Analysis-API-based indexer +// produces byte-identical SCIP symbol strings. scip-kotlinc is deleted once this +// indexer reaches parity, at which point this file is the only copy. +@JvmInline +value class Symbol(private val symbol: String) { + companion object { + val NONE = Symbol(SharedSymbols.NONE) + val ROOT_PACKAGE = Symbol(SharedSymbols.ROOT_PACKAGE) + + // Note: this intentionally diverges from `SharedSymbols.global` when + // `desc == NONE` — Java returns `NONE`, Kotlin returns the owner. + // SymbolsCache relies on this behavior; do not delegate without first + // updating those call sites. + fun createGlobal(owner: Symbol, desc: ScipSymbolDescriptor): Symbol = + when { + desc == ScipSymbolDescriptor.NONE -> owner + owner != ROOT_PACKAGE -> Symbol(owner.symbol + desc.encode().symbol) + else -> desc.encode() + } + + fun createLocal(i: Int) = Symbol(SharedSymbols.local(i)) + } + + fun isGlobal() = SharedSymbols.isGlobal(symbol) + + fun isLocal() = SharedSymbols.isLocal(symbol) + + override fun toString(): String = symbol +} + +fun String.symbol(): Symbol = Symbol(this) + +data class ScipSymbolDescriptor( + val kind: Kind, + val name: String, + // Default differs from `SharedSymbols.Descriptor` (which is "") because + // Kotlin call sites — getters/setters in particular — rely on the no-arg + // overload producing `name().` rather than `name.` for METHOD kinds. + val disambiguator: String = "()", +) { + companion object { + val NONE = ScipSymbolDescriptor(Kind.NONE, "") + } + + enum class Kind { + NONE, + TERM, + METHOD, + TYPE, + PACKAGE, + PARAMETER, + TYPE_PARAMETER; + + internal fun toSharedKind(): SharedSymbols.Descriptor.Kind = + when (this) { + NONE -> SharedSymbols.Descriptor.Kind.None + TERM -> SharedSymbols.Descriptor.Kind.Term + METHOD -> SharedSymbols.Descriptor.Kind.Method + TYPE -> SharedSymbols.Descriptor.Kind.Type + PACKAGE -> SharedSymbols.Descriptor.Kind.Package + PARAMETER -> SharedSymbols.Descriptor.Kind.Parameter + TYPE_PARAMETER -> SharedSymbols.Descriptor.Kind.TypeParameter + } + } + + fun encode(): Symbol = + if (kind == Kind.NONE) Symbol(SharedSymbols.NONE) + else Symbol(SharedSymbols.Descriptor(kind.toSharedKind(), name, disambiguator).encode()) +} diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt new file mode 100644 index 000000000..cc5a42de8 --- /dev/null +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt @@ -0,0 +1,260 @@ +package org.scip_code.scip_java.kotlin_analysis + +import com.intellij.psi.PsiElement +import java.lang.System.err +import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaClassLikeSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaConstructorSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaPackageSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtClassBody +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtConstructor +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtEnumEntry +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtFunctionLiteral +import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtObjectDeclaration +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtParameterList +import org.jetbrains.kotlin.psi.KtPropertyAccessor +import org.jetbrains.kotlin.psi.KtSecondaryConstructor +import org.jetbrains.kotlin.psi.KtTypeParameter +import org.jetbrains.kotlin.psi.KtTypeParameterListOwner +import org.jetbrains.kotlin.psi.KtVariableDeclaration +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.parents +import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly +import org.scip_code.scip_java.kotlin_analysis.ScipSymbolDescriptor.Kind +import org.scip_code.scip_java.shared.LocalSymbolsCache as SharedLocalSymbolsCache + +/** + * Computes SCIP symbol strings for Kotlin declarations and resolved references. + * + * This is a PSI-driven port of the FIR-based `SymbolsCache` in scip-kotlinc. Declarations with + * Kotlin source are classified from their PSI structure, which keeps the version-sensitive Analysis + * API surface minimal. References that resolve to declarations without Kotlin source (classpath or + * JDK symbols) fall back to `ClassId`/`CallableId`-derived symbols. + * + * Instantiate one cache per indexed file: local symbols are numbered per SCIP document. + */ +class SymbolsCache { + private val globals = HashMap() + private val packages = HashMap() + private val locals = + SharedLocalSymbolsCache(HashMap()) { Symbol.createLocal(it) } + + fun packageSymbol(fqName: FqName): Symbol { + if (fqName.isRoot) return Symbol.ROOT_PACKAGE + return packages.getOrPut(fqName) { + Symbol.createGlobal( + packageSymbol(fqName.parent()), + ScipSymbolDescriptor(Kind.PACKAGE, fqName.shortName().asString()), + ) + } + } + + /** The SCIP symbol for a declaration in Kotlin source. */ + fun symbolForDeclaration(declaration: KtDeclaration): Symbol { + globals[declaration]?.let { + return it + } + locals.get(declaration)?.let { + return it + } + val symbol = uncachedSymbol(declaration) + if (symbol.isGlobal()) globals[declaration] = symbol + return symbol + } + + /** The SCIP symbol for the target of a resolved reference. */ + fun symbolForReference(target: KaSymbol): Symbol { + val psi = target.psi + if (psi is KtDeclaration) { + // A constructor symbol whose PSI is the class itself is an implicit + // primary constructor. + if (target is KaConstructorSymbol && psi is KtClassOrObject) { + return Symbol.createGlobal( + symbolForDeclaration(psi), + ScipSymbolDescriptor(Kind.METHOD, ""), + ) + } + return symbolForDeclaration(psi) + } + return when (target) { + is KaPackageSymbol -> packageSymbol(target.fqName) + is KaConstructorSymbol -> + target.containingClassId?.let { classId -> + Symbol.createGlobal( + classIdSymbol(classId), + ScipSymbolDescriptor(Kind.METHOD, ""), + ) + } ?: Symbol.NONE + is KaClassLikeSymbol -> target.classId?.let(::classIdSymbol) ?: Symbol.NONE + is KaCallableSymbol -> externalCallableSymbol(target) + else -> Symbol.NONE + } + } + + private fun uncachedSymbol(declaration: KtDeclaration): Symbol { + // Anonymous functions and lambdas have no symbol of their own, mirroring + // the FIR indexer which returned NONE for FirAnonymousFunctionSymbol. + if (declaration is KtFunctionLiteral) return Symbol.NONE + if (declaration is KtNamedFunction && declaration.name == null) return Symbol.NONE + + if (isLocalMember(declaration)) return locals.put(declaration) + + val owner = ownerSymbol(declaration) + if (owner.isLocal() || owner == Symbol.NONE) return locals.put(declaration) + + return Symbol.createGlobal(owner, scipDescriptor(declaration)) + } + + // Port of FIR's `isLocalMember`: only functions, variables and classes can be + // local members; everything else is classified through its owner. + private fun isLocalMember(declaration: KtDeclaration): Boolean = + when (declaration) { + is KtNamedFunction, + is KtVariableDeclaration, + is KtClassOrObject -> + declaration.parents + .takeWhile { it !is KtFile } + .any { it !is KtClassBody && it !is KtClassOrObject } + else -> false + } + + private fun ownerSymbol(declaration: KtDeclaration): Symbol = + when (declaration) { + is KtTypeParameter -> + declaration.getStrictParentOfType()?.let { + symbolForDeclaration(it) + } ?: Symbol.NONE + is KtParameter -> + ownerDeclarationOfParameter(declaration)?.let { symbolForDeclaration(it) } + ?: Symbol.NONE + is KtPropertyAccessor -> + declaration.property.containingClassOrObject?.let { symbolForDeclaration(it) } + ?: packageSymbol(declaration.containingKtFile.packageFqName) + else -> + declaration.containingClassOrObject?.let { symbolForDeclaration(it) } + ?: packageSymbol(declaration.containingKtFile.packageFqName) + } + + private fun scipDescriptor(declaration: KtDeclaration): ScipSymbolDescriptor = + when { + declaration is KtObjectDeclaration && declaration.isObjectLiteral() -> + ScipSymbolDescriptor( + Kind.TYPE, + "", + ) + declaration is KtEnumEntry -> + ScipSymbolDescriptor(Kind.TERM, declaration.name.orEmpty()) + declaration is KtObjectDeclaration && declaration.isCompanion() -> + ScipSymbolDescriptor(Kind.TYPE, declaration.name ?: "Companion") + declaration is KtClassOrObject -> + ScipSymbolDescriptor(Kind.TYPE, declaration.name.orEmpty()) + declaration is KtPropertyAccessor && declaration.isSetter -> + ScipSymbolDescriptor( + Kind.METHOD, + "set" + declaration.property.name.orEmpty().capitalizeAsciiOnly(), + ) + declaration is KtPropertyAccessor -> + ScipSymbolDescriptor( + Kind.METHOD, + "get" + declaration.property.name.orEmpty().capitalizeAsciiOnly(), + ) + declaration is KtConstructor<*> -> + ScipSymbolDescriptor(Kind.METHOD, "", constructorDisambiguator(declaration)) + declaration is KtNamedFunction -> + ScipSymbolDescriptor( + Kind.METHOD, + declaration.name.orEmpty(), + methodDisambiguator(declaration), + ) + declaration is KtTypeParameter -> + ScipSymbolDescriptor(Kind.TYPE_PARAMETER, declaration.name.orEmpty()) + declaration is KtParameter -> + ScipSymbolDescriptor(Kind.PARAMETER, declaration.name.orEmpty()) + declaration is KtVariableDeclaration -> + ScipSymbolDescriptor(Kind.TERM, declaration.name.orEmpty()) + else -> { + err.println("unknown declaration kind ${declaration.javaClass.simpleName}") + ScipSymbolDescriptor.NONE + } + } + + /** + * Port of FIR's `methodDisambiguator`: the N-th preceding declaration with the same name in the + * containing scope yields `(+N)`, the first one yields `()`. + */ + private fun methodDisambiguator(function: KtNamedFunction): String { + val name = function.name ?: return "()" + var count = 0 + var found = false + for (sibling in siblingDeclarations(function)) { + if (sibling == function) { + found = true + break + } + if (declaredName(sibling) == name) count++ + } + if (count == 0 || !found) return "()" + return "(+$count)" + } + + private fun constructorDisambiguator(constructor: KtConstructor<*>): String { + val containingClass = constructor.containingClassOrObject ?: return "()" + val constructors = + listOfNotNull(containingClass.primaryConstructor) + + containingClass.declarations.filterIsInstance() + val index = constructors.indexOf(constructor) + return if (index <= 0) "()" else "(+$index)" + } + + private fun siblingDeclarations(declaration: KtDeclaration): List = + when (val containingClass = declaration.containingClassOrObject) { + null -> declaration.containingKtFile.declarations + else -> listOfNotNull(containingClass.primaryConstructor) + containingClass.declarations + } + + private fun declaredName(declaration: KtDeclaration): String? = + when (declaration) { + is KtConstructor<*> -> "" + is KtNamedDeclaration -> declaration.name + else -> null + } + + private fun ownerDeclarationOfParameter(parameter: KtParameter): KtDeclaration? = + (parameter.parent as? KtParameterList)?.parent as? KtDeclaration + + internal fun isDeclarationParameter(parameter: KtParameter): Boolean = + ownerDeclarationOfParameter(parameter) != null + + private fun externalCallableSymbol(target: KaCallableSymbol): Symbol { + val callableId = target.callableId ?: return Symbol.NONE + val owner = + callableId.classId?.let(::classIdSymbol) ?: packageSymbol(callableId.packageName) + val name = callableId.callableName.asString() + val descriptor = + when (target) { + is KaFunctionSymbol -> ScipSymbolDescriptor(Kind.METHOD, name) + else -> ScipSymbolDescriptor(Kind.TERM, name) + } + return Symbol.createGlobal(owner, descriptor) + } + + private fun classIdSymbol(classId: ClassId): Symbol { + var symbol = packageSymbol(classId.packageFqName) + for (segment in classId.relativeClassName.pathSegments()) { + symbol = + Symbol.createGlobal(symbol, ScipSymbolDescriptor(Kind.TYPE, segment.asString())) + } + return symbol + } +} diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt new file mode 100644 index 000000000..6391232ae --- /dev/null +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -0,0 +1,108 @@ +package org.scip_code.scip_java.kotlin_analysis + +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.test.Test +import kotlin.test.assertContains +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import org.scip_code.scip.Document +import org.scip_code.scip.SymbolRole + +class KotlinAnalysisIndexerTest { + + @Test + fun `indexes definitions, references and locals across files`() { + val sourceroot = Files.createTempDirectory("scip-kotlin-analysis").toRealPath() + val src = Files.createDirectories(sourceroot.resolve("src")) + Files.writeString( + src.resolve("Greeter.kt"), + """ + |package snapshots + | + |class Greeter(val name: String) { + | fun greet(): String { + | val message = "Hello, " + name + | return message + | } + | + | fun greet(punctuation: String): String = greet() + punctuation + |} + """ + .trimMargin(), + ) + Files.writeString( + src.resolve("Main.kt"), + """ + |package snapshots + | + |fun useGreeter(): String { + | val greeter = Greeter("world") + | return greeter.greet("!") + |} + """ + .trimMargin(), + ) + val targetroot = sourceroot.resolve("scip-targetroot") + + val documents = + KotlinAnalysisIndexer( + sourceroot = sourceroot, + targetroot = targetroot, + sourceRoots = listOf(src), + classpath = listOf(kotlinStdlibJar()), + ) + .run() + .sortedBy { it.relativePath } + + assertEquals(listOf("src/Greeter.kt", "src/Main.kt"), documents.map { it.relativePath }) + val greeter = documents[0] + val main = documents[1] + + val greeterDefinitions = definitions(greeter) + assertContains(greeterDefinitions, "snapshots/Greeter#") + assertContains(greeterDefinitions, "snapshots/Greeter#``().(name)") + assertContains(greeterDefinitions, "snapshots/Greeter#greet().") + assertContains(greeterDefinitions, "snapshots/Greeter#greet(+1).") + assertContains(greeterDefinitions, "snapshots/Greeter#greet(+1).(punctuation)") + // `val message` inside the function body is the first local of the file. + assertContains(greeterDefinitions, "local 0") + // The overload delegating to `greet()` references it. + assertContains(references(greeter), "snapshots/Greeter#greet().") + // The stdlib type reference resolves to a classpath symbol. + assertContains(references(greeter), "kotlin/String#") + + val mainDefinitions = definitions(main) + assertContains(mainDefinitions, "snapshots/useGreeter().") + assertContains(mainDefinitions, "local 0") + val mainReferences = references(main) + // Cross-file references: constructor call, method call on the overload, + // and the local variable usage. + assertContains(mainReferences, "snapshots/Greeter#``().") + assertContains(mainReferences, "snapshots/Greeter#greet(+1).") + assertContains(mainReferences, "local 0") + + assertTrue( + Files.exists(targetroot.resolve("META-INF/scip/src/Greeter.kt.scip")), + "expected SCIP shard for Greeter.kt", + ) + assertTrue( + Files.exists(targetroot.resolve("META-INF/scip/src/Main.kt.scip")), + "expected SCIP shard for Main.kt", + ) + } + + private fun definitions(document: Document): List = + document.occurrencesList + .filter { it.symbolRoles and SymbolRole.Definition.number != 0 } + .map { it.symbol } + + private fun references(document: Document): List = + document.occurrencesList + .filter { it.symbolRoles and SymbolRole.Definition.number == 0 } + .map { it.symbol } + + private fun kotlinStdlibJar(): Path = + Paths.get(Unit::class.java.protectionDomain.codeSource.location.toURI()) +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 7a3395b07..6b12f3a5b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -11,6 +11,10 @@ dependencyResolutionManagement { repositories { mavenCentral() gradlePluginPortal() + // Kotlin Analysis API (`*-for-ide`) artifacts used by scip-kotlin-analysis. + maven("https://redirector.kotlinlang.org/maven/kotlin-ide-plugin-dependencies") { + content { includeGroupAndSubgroups("org.jetbrains.kotlin") } + } } } @@ -20,6 +24,7 @@ include( "scip-shared", "scip-javac", "scip-kotlinc", + "scip-kotlin-analysis", "scip-aggregator", "scip-maven-plugin", "scip-gradle-plugin", From 311890baad01bb6246cc1e454cf94efded53efe6 Mon Sep 17 00:00:00 2001 From: jupblb Date: Thu, 2 Jul 2026 10:33:48 +0200 Subject: [PATCH 02/33] Index Kotlin via Analysis API in ScipBuildTool --- scip-java/build.gradle.kts | 6 +- .../scip_java/buildtools/ScipBuildTool.kt | 114 ++++-------------- 2 files changed, 22 insertions(+), 98 deletions(-) diff --git a/scip-java/build.gradle.kts b/scip-java/build.gradle.kts index 835e24adc..ef86756b0 100644 --- a/scip-java/build.gradle.kts +++ b/scip-java/build.gradle.kts @@ -17,13 +17,9 @@ val kotlincShadowJar = shadowJarArtifact(":scip-kotlinc", "kotlincShadowJar") dependencies { implementation(project(":scip-aggregator")) + implementation(project(":scip-kotlin-analysis")) implementation(libs.clikt.jvm) implementation(libs.kotlin.stdlib) - implementation(libs.kotlin.compiler.embeddable) - implementation(libs.kotlin.scripting.common) - implementation(libs.kotlin.scripting.jvm) - implementation(libs.kotlin.scripting.dependencies) - implementation(libs.kotlin.scripting.dependencies.maven) implementation(libs.kotlinx.serialization.json.jvm) testImplementation(libs.kotlin.test) diff --git a/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/ScipBuildTool.kt b/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/ScipBuildTool.kt index 2c06f9d41..5b8844cde 100644 --- a/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/ScipBuildTool.kt +++ b/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/ScipBuildTool.kt @@ -17,16 +17,9 @@ import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.boolean import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.jsonArray -import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments -import org.jetbrains.kotlin.cli.common.arguments.parseCommandLineArguments -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity -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.K2JVMCompiler -import org.jetbrains.kotlin.config.Services import org.scip_code.scip_java.Embedded import org.scip_code.scip_java.commands.IndexCommand +import org.scip_code.scip_java.kotlin_analysis.KotlinAnalysisIndexer /** * A custom build tool that is specifically made for scip-java. @@ -130,7 +123,7 @@ class ScipBuildTool(index: IndexCommand) : BuildTool("SCIP", index) { val errors = mutableListOf() compileJavaFiles(tmp, config, javaFiles)?.let { errors += it } - compileKotlinFiles(config, kotlinFiles, tmp)?.let { errors += it } + indexKotlinFiles(config, kotlinFiles)?.let { errors += it } if (index.cleanup) { tmp.toFile().deleteRecursively() @@ -154,93 +147,28 @@ class ScipBuildTool(index: IndexCommand) : BuildTool("SCIP", index) { } } - private fun compileKotlinFiles( - config: Config, - allKotlinFiles: List, - tmp: Path, - ): Throwable? { + private fun indexKotlinFiles(config: Config, allKotlinFiles: List): Throwable? { if (allKotlinFiles.isEmpty()) return null val sourceroot = index.workingDirectory - val filesPaths = allKotlinFiles.map { it.toString() } - - // The scip-kotlinc compiler plugin is built and shipped together - // with the scip-java CLI as an embedded resource (see Embedded.kt and - // the :scip-java Gradle resources wiring). - val plugin = Embedded.scipKotlincJar(tmp) - - val classpath = - config.classpath.joinToString(File.pathSeparator) { - index.workingDirectory.resolve(it).toString() - } - - val kargs = K2JVMCompilerArguments() - val args = - mutableListOf( - "-nowarn", - "-no-reflect", - "-no-stdlib", - "-Xmulti-platform", - "-Xno-check-actual", - "-verbose:class", - "-opt-in=kotlin.RequiresOptIn", - "-opt-in=kotlin.ExperimentalUnsignedTypes", - "-opt-in=kotlin.ExperimentalStdlibApi", - "-opt-in=kotlin.ExperimentalMultiplatform", - "-opt-in=kotlin.contracts.ExperimentalContracts", - "-Xallow-kotlin-package", - "-Xplugin=$plugin", - "-P", - "plugin:scip-kotlinc:sourceroot=$sourceroot", - "-P", - "plugin:scip-kotlinc:targetroot=$targetroot", - "-classpath", - classpath, - ) - args += filesPaths - - parseCommandLineArguments(args, kargs) - - val exit = - K2JVMCompiler() - .exec( - object : MessageCollector { - private var sawError = false - - override fun clear() { - sawError = false - } - - override fun hasErrors(): Boolean = sawError - - override fun report( - severity: CompilerMessageSeverity, - message: String, - location: CompilerMessageSourceLocation?, - ) { - if ( - message.endsWith("without a body must be abstract") || - message.endsWith("must have a body") - ) { - // We get these when indexing the stdlib; - // no other solution found yet. - return - } - val rendered = - MessageRenderer.PLAIN_FULL_PATHS.render(severity, message, location) - index.app.reporter.debug(rendered) - // Only treat ERROR / EXCEPTION as failures. - // Kotlin 2.2.0's K2JVMCompiler emits LOGGING/INFO/WARNING - // messages during normal compilation; pushing those onto - // `errors` would cause hasErrors to return true. - if (severity.isError) { - sawError = true - } - } - }, - Services.EMPTY, - kargs, + val classpath = config.classpath.map { sourceroot.resolve(it).normalize() } + val jdkHome = + config.javaHome?.let { Paths.get(it) } ?: Paths.get(System.getProperty("java.home")) + // Unlike the old scip-kotlinc compiler plugin, the Analysis API indexer does + // not compile the sources: unresolved code degrades individual occurrences + // instead of failing the build, so only indexer crashes surface as errors. + return try { + KotlinAnalysisIndexer( + sourceroot = sourceroot, + targetroot = targetroot, + sourceRoots = allKotlinFiles, + classpath = classpath, + jdkHome = jdkHome, ) - return if (exit.code == 0) null else Exception(exit.toString()) + .run() + null + } catch (e: Exception) { + e + } } private fun compileJavaFiles(tmp: Path, config: Config, allJavaFiles: List): Throwable? { From fbd7a654bf901ec5d9671d54cb09358f7d259149 Mon Sep 17 00:00:00 2001 From: jupblb Date: Thu, 2 Jul 2026 11:16:11 +0200 Subject: [PATCH 03/33] Bring scip-kotlin-analysis to output parity with scip-kotlinc --- .../org/scip_code/scip_java/ScipJava.kt | 3 + .../kotlin_analysis/KotlinAnalysisIndexer.kt | 540 ++++++++++++++++-- .../scip_java/kotlin_analysis/Main.kt | 51 ++ .../kotlin_analysis/SignatureRenderer.kt | 220 +++++++ .../scip_java/kotlin_analysis/SymbolsCache.kt | 191 ++++++- .../cases/kotlin/common/build.gradle.kts | 43 ++ 6 files changed, 975 insertions(+), 73 deletions(-) create mode 100644 scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/Main.kt create mode 100644 scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt diff --git a/scip-java/src/main/kotlin/org/scip_code/scip_java/ScipJava.kt b/scip-java/src/main/kotlin/org/scip_code/scip_java/ScipJava.kt index a15ba62a4..47b535700 100644 --- a/scip-java/src/main/kotlin/org/scip_code/scip_java/ScipJava.kt +++ b/scip-java/src/main/kotlin/org/scip_code/scip_java/ScipJava.kt @@ -17,6 +17,9 @@ object ScipJava { @JvmStatic fun main(args: Array) { app.runAndExitIfNonZero(args.toList()) + // The Kotlin Analysis API indexer leaves non-daemon threads behind; exit + // explicitly instead of waiting on them. + kotlin.system.exitProcess(0) } fun printHelp(out: PrintStream) { diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index 86d62e6ee..3455bc0a7 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -7,27 +7,47 @@ import java.nio.file.Paths import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.analyze import org.jetbrains.kotlin.analysis.api.standalone.buildStandaloneAnalysisAPISession +import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaConstructorSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaPropertySymbol +import org.jetbrains.kotlin.analysis.api.types.KaClassType import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtLibraryModule import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSdkModule import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSourceModule import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.jvm.JvmPlatforms +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtConstructor +import org.jetbrains.kotlin.psi.KtConstructorCalleeExpression import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry +import org.jetbrains.kotlin.psi.KtEnumEntry import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi.KtImportDirective -import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.KtFunctionLiteral +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtPrimaryConstructor +import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPropertyAccessor import org.jetbrains.kotlin.psi.KtSecondaryConstructor import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtTreeVisitorVoid +import org.jetbrains.kotlin.psi.KtTypeParameter +import org.jetbrains.kotlin.psi.KtVariableDeclaration import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.parents import org.scip_code.scip.Document import org.scip_code.scip.Occurrence import org.scip_code.scip.SymbolRole +import org.scip_code.scip.relationship +import org.scip_code.scip.signature import org.scip_code.scip.symbolInformation import org.scip_code.scip_java.shared.ScipDocumentBuilder import org.scip_code.scip_java.shared.ScipRange @@ -134,73 +154,475 @@ class KotlinAnalysisIndexer( private val lines: LineIndex, private val out: ScipDocumentBuilder, ) : KtTreeVisitorVoid() { + private val signatures = SignatureRenderer(session) override fun visitDeclaration(dcl: KtDeclaration) { super.visitDeclaration(dcl) - // Parameters that don't belong to a declaration (function types, catch - // clauses, for loops) are not emitted as definitions; references to the - // latter two still produce local symbols on first use. - if (dcl is KtParameter && !cache.isDeclarationParameter(dcl)) return - val range = definitionRange(dcl) ?: return - val symbol = cache.symbolForDeclaration(dcl) - if (symbol == Symbol.NONE) return - addOccurrence(symbol, range, definition = true) - out.addSymbol( - symbolInformation { - this.symbol = symbol.toString() - this.displayName = displayName(dcl) - } - ) + when (dcl) { + is KtEnumEntry -> emitEnumEntry(dcl) + is KtClassOrObject -> emitClassLike(dcl) + is KtConstructor<*> -> emitConstructor(dcl) + is KtNamedFunction -> emitFunction(dcl) + is KtProperty -> emitProperty(dcl) + is KtParameter -> emitParameter(dcl) + is KtPropertyAccessor -> emitExplicitAccessor(dcl) + is KtTypeParameter -> emitTypeParameter(dcl) + is KtDestructuringDeclarationEntry -> emitLocalVariable(dcl) + else -> {} + } } override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) { super.visitSimpleNameExpression(expression) - if (expression.parents.any { it is KtImportDirective }) return + val referencedName = expression.getReferencedName() + if (referencedName == "this" || referencedName == "super") return val target = with(session) { expression.mainReference.resolveToSymbol() } ?: return - val symbol = cache.symbolForReference(target) - if (symbol == Symbol.NONE) return - addOccurrence( - symbol, - expression.getReferencedNameElement().textRange, - definition = false, + val range = expression.getReferencedNameElement().textRange + + // The implicit `it` lambda parameter has no declaration PSI; its + // definition is anchored at the whole lambda literal, emitted at the + // first reference. + val targetPsi = target.psi + if ( + targetPsi is KtFunctionLiteral && + target is KaCallableSymbol && + target !is KaFunctionSymbol + ) { + emitImplicitItDefinition(targetPsi, target) + addReference(cache.implicitItSymbol(targetPsi), range) + return + } + + // Super-type entries and annotations reference the class, not the + // constructor (`class Seagull : Bird()` references `Bird#`). + if ( + target is KaConstructorSymbol && + expression.parents + .takeWhile { it !is KtFile } + .any { it is KtConstructorCalleeExpression } + ) { + addReference(cache.constructorOwnerSymbol(session, target), range) + return + } + + // Property references fan out to the property and its accessors, + // e.g. `banana` yields `Class#banana.`, `Class#getBanana().` and + // `Class#setBanana().`. + if (target is KaPropertySymbol) { + propertyReferenceSymbols(target).forEach { addReference(it, range) } + return + } + + addReference(cache.symbolForReference(session, target), range) + } + + // --------------------------------------------------------------- + // Declarations + // --------------------------------------------------------------- + + private fun emitClassLike(declaration: KtClassOrObject) { + val isAnonymous = declaration is KtObjectDeclaration && declaration.isObjectLiteral() + val range = + when { + declaration.nameIdentifier != null -> declaration.nameIdentifier!!.textRange + isAnonymous -> + (declaration as KtObjectDeclaration).getObjectKeyword()?.textRange + ?: declaration.textRange + else -> declaration.textRange // unnamed companion object + } + val displayName = if (isAnonymous) "" else declaration.name ?: "Companion" + val symbol = cache.symbolForDeclaration(declaration) + emitDefinition( + symbol = symbol, + range = range, + enclosing = declaration.textRange, + displayName = displayName, + signatureText = signatures.classSignature(declaration), + documentation = docComment(declaration), + relationships = superTypeRelationships(declaration), + ) + // Classes and objects (but not interfaces) declare an implicit primary + // constructor when no explicit one is present in source. + val isInterface = declaration is KtClass && declaration.isInterface() + if (!isInterface && declaration.primaryConstructor == null) { + emitDefinition( + symbol = cache.implicitConstructorSymbol(declaration), + range = range, + enclosing = declaration.textRange, + displayName = displayName, + signatureText = signatures.implicitConstructorSignature(declaration), + documentation = docComment(declaration), + ) + } + } + + private fun emitEnumEntry(declaration: KtEnumEntry) { + val identifier = declaration.nameIdentifier ?: return + emitDefinition( + symbol = cache.symbolForDeclaration(declaration), + range = identifier.textRange, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + signatureText = signatures.propertySignature(declaration), + documentation = docComment(declaration), + ) + } + + private fun emitConstructor(declaration: KtConstructor<*>) { + val range = + when (declaration) { + is KtPrimaryConstructor -> + declaration.getConstructorKeyword()?.textRange + ?: declaration.containingClassOrObject?.nameIdentifier?.textRange + ?: return + is KtSecondaryConstructor -> declaration.textRange + else -> return + } + emitDefinition( + symbol = cache.symbolForDeclaration(declaration), + range = range, + enclosing = declaration.textRange, + displayName = declaration.containingClassOrObject?.name ?: "", + signatureText = signatures.constructorSignature(declaration), + documentation = docComment(declaration), ) } - private fun definitionRange(declaration: KtDeclaration): TextRange? = - when (declaration) { - // A primary constructor without the `constructor` keyword is linked - // to the class identifier, matching the FIR indexer. - is KtPrimaryConstructor -> - declaration.getConstructorKeyword()?.textRange - ?: declaration.containingClassOrObject?.nameIdentifier?.textRange - is KtSecondaryConstructor -> declaration.getConstructorKeyword().textRange - is KtPropertyAccessor -> declaration.namePlaceholder.textRange - is KtNamedDeclaration -> declaration.nameIdentifier?.textRange - else -> null + private fun emitFunction(declaration: KtNamedFunction) { + val identifier = declaration.nameIdentifier ?: return + val relationships = + if (declaration.hasModifier(org.jetbrains.kotlin.lexer.KtTokens.OVERRIDE_KEYWORD)) { + overriddenSymbols(declaration) + } else { + emptyList() + } + emitDefinition( + symbol = cache.symbolForDeclaration(declaration), + range = identifier.textRange, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + signatureText = signatures.functionSignature(declaration), + documentation = docComment(declaration), + relationships = relationships, + ) + } + + private fun emitProperty(declaration: KtProperty) { + val identifier = declaration.nameIdentifier ?: return + val symbol = cache.symbolForDeclaration(declaration) + if (symbol.isLocal()) { + emitDefinition( + symbol = symbol, + range = identifier.textRange, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + signatureText = signatures.localVariableSignature(declaration), + documentation = docComment(declaration), + ) + return } + emitDefinition( + symbol = symbol, + range = identifier.textRange, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + signatureText = signatures.propertySignature(declaration), + documentation = docComment(declaration), + ) + emitSyntheticAccessors( + property = declaration, + nameRange = identifier.textRange, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + hasExplicitGetter = declaration.getter != null, + hasExplicitSetter = declaration.setter != null, + isVar = declaration.isVar, + ) + } - private fun displayName(declaration: KtDeclaration): String = - when (declaration) { - is KtPropertyAccessor -> declaration.property.name.orEmpty() - is KtConstructor<*> -> "" - is KtNamedDeclaration -> declaration.name.orEmpty() - else -> "" + private fun emitParameter(declaration: KtParameter) { + // Parameters of function types, catch clauses and for loops are not + // declarations of their own. + if (!cache.isDeclarationParameter(declaration)) return + val identifier = declaration.nameIdentifier ?: return + val symbol = cache.symbolForDeclaration(declaration) + emitDefinition( + symbol = symbol, + range = identifier.textRange, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + signatureText = signatures.parameterSignature(declaration), + documentation = null, + ) + // A `val`/`var` constructor parameter also declares a property: the + // property initializer references the parameter at the same range, and + // the property plus its synthetic accessors are all anchored there. + if (declaration.hasValOrVar()) { + emitDefinition( + symbol = cache.parameterPropertySymbol(declaration), + range = identifier.textRange, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + signatureText = signatures.propertySignature(declaration), + documentation = null, + ) + addReference(symbol, identifier.textRange) + emitSyntheticAccessors( + property = declaration, + nameRange = identifier.textRange, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + hasExplicitGetter = false, + hasExplicitSetter = false, + isVar = declaration.isMutable, + ) } + } - private fun addOccurrence(symbol: Symbol, range: TextRange, definition: Boolean) { - val scipRange = lines.scipRange(range) ?: return - val builder = Occurrence.newBuilder().setSymbol(symbol.toString()) - if (definition) { - builder.setSymbolRoles(SymbolRole.Definition.number) + private fun emitSyntheticAccessors( + property: KtDeclaration, + nameRange: TextRange, + enclosing: TextRange, + displayName: String, + hasExplicitGetter: Boolean, + hasExplicitSetter: Boolean, + isVar: Boolean, + ) { + if (!hasExplicitGetter) { + emitDefinition( + symbol = cache.syntheticAccessorSymbol(property, setter = false), + range = nameRange, + enclosing = enclosing, + displayName = displayName, + signatureText = signatures.syntheticAccessorSignature(property, setter = false), + documentation = null, + ) } - if (scipRange.isSingleLine) { - builder.setSingleLineRange(scipRange.toSingleLineRange()) - } else { - builder.setMultiLineRange(scipRange.toMultiLineRange()) + if (isVar && !hasExplicitSetter) { + emitDefinition( + symbol = cache.syntheticAccessorSymbol(property, setter = true), + range = nameRange, + enclosing = enclosing, + displayName = displayName, + signatureText = signatures.syntheticAccessorSignature(property, setter = true), + documentation = null, + ) + emitDefinition( + symbol = cache.syntheticSetterValueSymbol(property), + range = nameRange, + enclosing = enclosing, + displayName = "value", + signatureText = signatures.setterValueSignature(property), + documentation = null, + ) } + } + + private fun emitExplicitAccessor(declaration: KtPropertyAccessor) { + emitDefinition( + symbol = cache.symbolForDeclaration(declaration), + range = declaration.namePlaceholder.textRange, + enclosing = declaration.textRange, + displayName = declaration.property.name.orEmpty(), + signatureText = signatures.accessorSignature(declaration), + documentation = docComment(declaration), + ) + } + + private fun emitTypeParameter(declaration: KtTypeParameter) { + val identifier = declaration.nameIdentifier ?: return + emitDefinition( + symbol = cache.symbolForDeclaration(declaration), + range = identifier.textRange, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + signatureText = declaration.text, + documentation = null, + ) + } + + private fun emitLocalVariable(declaration: KtVariableDeclaration) { + val identifier = declaration.nameIdentifier ?: return + emitDefinition( + symbol = cache.symbolForDeclaration(declaration), + range = identifier.textRange, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + signatureText = signatures.localVariableSignature(declaration), + documentation = null, + ) + } + + private val emittedItParameters = HashSet() + + /** + * A lambda with a single implicit `it` parameter declares it without any PSI: the FIR + * indexer anchored its definition at the whole lambda literal. + */ + private fun emitImplicitItDefinition(literal: KtFunctionLiteral, target: KaCallableSymbol) { + if (!emittedItParameters.add(literal)) return + emitDefinition( + symbol = cache.implicitItSymbol(literal), + range = literal.textRange, + enclosing = literal.textRange, + displayName = "it", + signatureText = signatures.implicitItSignature(target), + documentation = null, + ) + } + + // --------------------------------------------------------------- + // Symbol helpers + // --------------------------------------------------------------- + + private fun propertyReferenceSymbols(target: KaPropertySymbol): List { + val psi = target.psi + return when { + psi is KtProperty -> { + val property = cache.symbolForDeclaration(psi) + if (property.isLocal()) return listOf(property) + buildList { + add(property) + psi.getter?.let { add(cache.symbolForDeclaration(it)) } + ?: add(cache.syntheticAccessorSymbol(psi, setter = false)) + if (psi.isVar) { + psi.setter?.let { add(cache.symbolForDeclaration(it)) } + ?: add(cache.syntheticAccessorSymbol(psi, setter = true)) + } + } + } + psi is KtParameter -> + buildList { + add(cache.parameterPropertySymbol(psi)) + add(cache.syntheticAccessorSymbol(psi, setter = false)) + if (psi.isMutable) { + add(cache.syntheticAccessorSymbol(psi, setter = true)) + } + } + else -> + buildList { + add(cache.symbolForReference(session, target)) + add(cache.externalAccessorSymbol(session, target, setter = false)) + if (!target.isVal) { + add(cache.externalAccessorSymbol(session, target, setter = true)) + } + } + }.filter { it != Symbol.NONE } + } + + private fun superTypeRelationships(declaration: KtClassOrObject): List = + with(session) { + val classSymbol = declaration.symbol as? KaClassSymbol ?: return@with emptyList() + classSymbol.superTypes + .mapNotNull { (it as? KaClassType)?.classId } + .filterNot { it == ANY_CLASS_ID } + .map { cache.classSymbol(it) } + .filter { it != Symbol.NONE } + } + + private fun overriddenSymbols(declaration: KtNamedFunction): List = + with(session) { + val symbol = declaration.symbol as? KaCallableSymbol ?: return@with emptyList() + symbol.directlyOverriddenSymbols + .map { cache.symbolForReference(session, it) } + .filter { it != Symbol.NONE } + .toList() + } + + // --------------------------------------------------------------- + // Emission + // --------------------------------------------------------------- + + private fun emitDefinition( + symbol: Symbol, + range: TextRange, + enclosing: TextRange?, + displayName: String, + signatureText: String?, + documentation: String?, + relationships: List = emptyList(), + ) { + if (symbol == Symbol.NONE) return + val scipRange = lines.anchorRange(range) ?: return + val builder = + Occurrence.newBuilder() + .setSymbol(symbol.toString()) + .setSymbolRoles(SymbolRole.Definition.number) + builder.setSingleLineRange(scipRange.toSingleLineRange()) + enclosing + ?.let { lines.scipRange(it) } + ?.let { enclosingRange -> + if (enclosingRange.isSingleLine) { + builder.setSingleLineEnclosingRange(enclosingRange.toSingleLineRange()) + } else { + builder.setMultiLineEnclosingRange(enclosingRange.toMultiLineRange()) + } + } out.addOccurrence(builder.build()) + out.addSymbol( + symbolInformation { + this.symbol = symbol.toString() + this.displayName = displayName + if (signatureText != null) { + this.signatureDocumentation = signature { + language = "kotlin" + text = signatureText + } + } + if (documentation != null) { + this.documentation += documentation + } + for (parent in relationships) { + this.relationships += relationship { + this.symbol = parent.toString() + this.isImplementation = true + } + } + } + ) + } + + private fun addReference(symbol: Symbol, range: TextRange) { + if (symbol == Symbol.NONE) return + val scipRange = lines.anchorRange(range) ?: return + val builder = Occurrence.newBuilder().setSymbol(symbol.toString()) + builder.setSingleLineRange(scipRange.toSingleLineRange()) + out.addOccurrence(builder.build()) + } + + private fun docComment(declaration: KtDeclaration): String? { + val kdoc = declaration.docComment?.text ?: return null + return stripKdoc(kdoc).ifEmpty { null } } + + companion object { + private val ANY_CLASS_ID = ClassId(FqName("kotlin"), Name.identifier("Any")) + } + } +} + +/** Strips the `/**`, leading `*`s, and `*/` from a kdoc block, returning just the body text. */ +internal fun stripKdoc(kdoc: String): String { + if (kdoc.isEmpty()) return kdoc + val out = StringBuilder() + var first = true + kdoc.lineSequence().forEach { line -> + if (line.isEmpty()) return@forEach + var start = 0 + while (start < line.length && line[start].isWhitespace()) start++ + if (start < line.length && line[start] == '/') start++ + while (start < line.length && line[start] == '*') start++ + var end = line.length - 1 + if (end > start && line[end] == '/') end-- + while (end > start && line[end] == '*') end-- + while (end > start && line[end].isWhitespace()) end-- + start = minOf(start, line.length - 1) + if (end > start) end++ + if (!first) out.append('\n') + out.append(line, start, end) + first = false } + return out.toString().trim() } /** Maps text offsets to 0-based line/character positions. */ @@ -223,6 +645,26 @@ internal class LineIndex(private val text: String) { return ScipRange.range(startLine, startCharacter, endLine, endCharacter) } + /** + * Occurrence anchor ranges are always single-line in the scip-kotlinc output: multiline + * declarations (unnamed companion objects, multiline secondary constructors) were collapsed + * onto their start line with the end character measured from that line's start. The true extent + * lives in the enclosing range. Reproduced here so anchors stay byte-identical and renderable + * by `scip snapshot`. + */ + fun anchorRange(range: TextRange): ScipRange? { + if (range.startOffset > text.length || range.endOffset > text.length) return null + val (startLine, startCharacter) = position(range.startOffset) + val (endLine, endCharacter) = position(range.endOffset) + if (startLine == endLine) { + return ScipRange.singleLine(startLine, startCharacter, endCharacter) + } + val flattenedEnd = range.endOffset - lineStart(startLine) + return ScipRange.singleLine(startLine, startCharacter, flattenedEnd) + } + + private fun lineStart(line: Int): Int = lineStartOffsets[line] + private fun position(offset: Int): Pair { var line = java.util.Arrays.binarySearch(lineStartOffsets, offset) if (line < 0) line = -line - 2 diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/Main.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/Main.kt new file mode 100644 index 000000000..5e6f41170 --- /dev/null +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/Main.kt @@ -0,0 +1,51 @@ +package org.scip_code.scip_java.kotlin_analysis + +import java.io.File +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.system.exitProcess + +/** + * Command-line entry point, primarily used by the scip-snapshots harness. The scip-java CLI calls + * [KotlinAnalysisIndexer] in-process instead. + * + * Usage: `--sourceroot --targetroot [--classpath ] [--jdk-home + * ] ...` + */ +fun main(args: Array) { + var sourceroot: Path? = null + var targetroot: Path? = null + var classpath: List = emptyList() + var jdkHome: Path? = Paths.get(System.getProperty("java.home")) + val sourceRoots = mutableListOf() + var i = 0 + while (i < args.size) { + when (val arg = args[i]) { + "--sourceroot" -> sourceroot = Paths.get(args[++i]) + "--targetroot" -> targetroot = Paths.get(args[++i]) + "--classpath" -> + classpath = + args[++i] + .split(File.pathSeparator) + .filter { it.isNotEmpty() } + .map { Paths.get(it) } + "--jdk-home" -> jdkHome = Paths.get(args[++i]) + else -> sourceRoots.add(Paths.get(arg)) + } + i++ + } + require(sourceroot != null) { "missing required flag: --sourceroot" } + require(targetroot != null) { "missing required flag: --targetroot" } + require(sourceRoots.isNotEmpty()) { "missing source files or directories" } + KotlinAnalysisIndexer( + sourceroot = sourceroot, + targetroot = targetroot, + sourceRoots = sourceRoots, + classpath = classpath, + jdkHome = jdkHome, + ) + .run() + // The Analysis API environment leaves non-daemon threads behind; exit + // explicitly so callers are not left waiting on them. + exitProcess(0) +} diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt new file mode 100644 index 000000000..769130274 --- /dev/null +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt @@ -0,0 +1,220 @@ +package org.scip_code.scip_java.kotlin_analysis + +import org.jetbrains.kotlin.analysis.api.KaExperimentalApi +import org.jetbrains.kotlin.analysis.api.KaSession +import org.jetbrains.kotlin.analysis.api.renderer.types.impl.KaTypeRendererForSource +import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtConstructor +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.KtObjectDeclaration +import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtPropertyAccessor +import org.jetbrains.kotlin.psi.KtVariableDeclaration +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.types.Variance + +/** + * Renders hover signatures in the exact textual format that scip-kotlinc's FirRenderer setup + * produced (see the goldens under scip-snapshots/expected/kotlin), so that migrating the indexer + * does not churn signature documentation. Explicitly written type annotations are taken verbatim + * from source; inferred types are rendered with short names via the Analysis API. + */ +@OptIn(KaExperimentalApi::class) +internal class SignatureRenderer(private val session: KaSession) { + + fun classSignature(declaration: KtClassOrObject): String { + if (declaration is KtObjectDeclaration && declaration.isObjectLiteral()) { + return "object : ${superTypesText(declaration)}" + } + val keyword = + when { + declaration is KtObjectDeclaration && declaration.isCompanion() -> + "companion object" + declaration is KtObjectDeclaration -> "object" + declaration is KtClass && declaration.isInterface() -> "interface" + declaration is KtClass && declaration.isEnum() -> "enum class" + else -> "class" + } + val name = declaration.name ?: "Companion" + return "${visibility(declaration)} ${classModality(declaration)} $keyword $name : " + + superTypesText(declaration) + } + + fun constructorSignature(constructor: KtConstructor<*>): String { + val owner = constructor.containingClassOrObject + val visibility = explicitVisibility(constructor) ?: "public" + return "$visibility constructor(${parametersText(constructor.valueParameters)}): " + + ownerName(owner) + } + + fun implicitConstructorSignature(declaration: KtClassOrObject): String { + val visibility = if (declaration is KtObjectDeclaration) "private" else "public" + return "$visibility constructor(): ${ownerName(declaration)}" + } + + fun functionSignature(declaration: KtNamedFunction): String { + val returnType = + declaration.typeReference?.text ?: renderedReturnType(declaration) ?: "Unit" + val signature = + "${visibility(declaration)} ${memberModality(declaration)}fun " + + "${declaration.name.orEmpty()}(${parametersText(declaration.valueParameters)}): " + + returnType + // FirRenderer emitted a trailing newline for functions without a body + // (see the Animal#sound() golden). + return if (declaration.hasBody()) signature else signature + "\n" + } + + fun propertySignature(declaration: KtDeclaration): String { + val name = (declaration as? org.jetbrains.kotlin.psi.KtNamedDeclaration)?.name.orEmpty() + val valOrVar = if (isVar(declaration)) "var" else "val" + val type = explicitPropertyType(declaration) ?: renderedReturnType(declaration) ?: "Any" + return "${visibility(declaration)} ${memberModality(declaration)}$valOrVar $name: $type" + } + + fun localVariableSignature(declaration: KtVariableDeclaration): String { + val valOrVar = if (declaration.isVar) "var" else "val" + val type = declaration.typeReference?.text ?: renderedReturnType(declaration) ?: "Any" + return "local $valOrVar ${declaration.name.orEmpty()}: $type" + } + + fun accessorSignature(accessor: KtPropertyAccessor): String { + val property = accessor.property + val visibility = explicitVisibility(accessor) ?: visibility(property) + val type = explicitPropertyType(property) ?: renderedReturnType(property) ?: "Any" + return if (accessor.isSetter) { + val parameterName = accessor.parameter?.name ?: "value" + "$visibility set($parameterName: $type): Unit" + } else { + "$visibility get(): $type" + } + } + + fun syntheticAccessorSignature(property: KtDeclaration, setter: Boolean): String { + val visibility = visibility(property) + val type = explicitPropertyType(property) ?: renderedReturnType(property) ?: "Any" + return if (setter) "$visibility set(value: $type): Unit" else "$visibility get(): $type" + } + + fun setterValueSignature(property: KtDeclaration): String { + val type = explicitPropertyType(property) ?: renderedReturnType(property) ?: "Any" + return "value: $type" + } + + fun parameterSignature(parameter: KtParameter): String { + val vararg = if (parameter.hasModifier(KtTokens.VARARG_KEYWORD)) "vararg " else "" + val type = parameter.typeReference?.text ?: renderedReturnType(parameter) ?: "Any" + val default = if (parameter.hasDefaultValue()) " = ..." else "" + return "$vararg${parameter.name.orEmpty()}: $type$default" + } + + fun implicitItSignature(target: KaCallableSymbol): String { + val type = + with(session) { + target.returnType.render( + KaTypeRendererForSource.WITH_SHORT_NAMES, + position = Variance.INVARIANT, + ) + } + return "it: $type" + } + + private fun ownerName(declaration: KtClassOrObject?): String { + if (declaration == null) return "" + if (declaration is KtObjectDeclaration && declaration.isObjectLiteral()) { + return "" + } + val names = mutableListOf() + var current: KtClassOrObject? = declaration + while (current != null) { + names.add(current.name ?: "Companion") + current = current.containingClassOrObject + } + return names.reversed().joinToString(".") + } + + private fun superTypesText(declaration: KtClassOrObject): String { + val entries = declaration.superTypeListEntries.mapNotNull { it.typeReference?.text } + return if (entries.isEmpty()) "Any" else entries.joinToString(", ") + } + + private fun parametersText(parameters: List): String = + parameters.joinToString(", ") { parameter -> + val type = parameter.typeReference?.text ?: "Any" + val default = if (parameter.hasDefaultValue()) " = ..." else "" + "${parameter.name.orEmpty()}: $type$default" + } + + private fun explicitPropertyType(declaration: KtDeclaration): String? = + when (declaration) { + is KtProperty -> declaration.typeReference?.text + is KtParameter -> declaration.typeReference?.text + else -> null + } + + private fun renderedReturnType(declaration: KtDeclaration): String? = + with(session) { + (declaration.symbol as? KaCallableSymbol) + ?.returnType + ?.render(KaTypeRendererForSource.WITH_SHORT_NAMES, position = Variance.INVARIANT) + } + + private fun isVar(declaration: KtDeclaration): Boolean = + when (declaration) { + is KtProperty -> declaration.isVar + is KtParameter -> declaration.isMutable + else -> false + } + + private fun visibility(declaration: KtDeclaration): String = + explicitVisibility(declaration) ?: "public" + + private fun explicitVisibility(declaration: KtDeclaration): String? = + when { + declaration.hasModifier(KtTokens.PRIVATE_KEYWORD) -> "private" + declaration.hasModifier(KtTokens.PROTECTED_KEYWORD) -> "protected" + declaration.hasModifier(KtTokens.INTERNAL_KEYWORD) -> "internal" + else -> null + } + + private fun classModality(declaration: KtClassOrObject): String = + when { + declaration is KtClass && declaration.isInterface() -> "abstract" + declaration.hasModifier(KtTokens.SEALED_KEYWORD) -> "sealed" + declaration.hasModifier(KtTokens.ABSTRACT_KEYWORD) -> "abstract" + declaration.hasModifier(KtTokens.OPEN_KEYWORD) -> "open" + else -> "final" + } + + /** Modality + `override`, with a trailing space; empty for abstract-less contexts. */ + private fun memberModality(declaration: KtDeclaration): String { + val containingInterface = + (declaration.containingClassOrObject as? KtClass)?.isInterface() == true + val isAbstract = + declaration.hasModifier(KtTokens.ABSTRACT_KEYWORD) || + (containingInterface && !hasImplementation(declaration)) + val isOverride = declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD) + val modality = + when { + isAbstract -> "abstract" + declaration.hasModifier(KtTokens.OPEN_KEYWORD) || isOverride -> "open" + containingInterface -> "open" + else -> "final" + } + return if (isOverride) "$modality override " else "$modality " + } + + private fun hasImplementation(declaration: KtDeclaration): Boolean = + when (declaration) { + is KtNamedFunction -> declaration.hasBody() + is KtProperty -> + declaration.hasInitializer() || + declaration.hasDelegate() || + declaration.accessors.any { it.hasBody() } + else -> true + } +} diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt index cc5a42de8..518849de9 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt @@ -2,26 +2,33 @@ package org.scip_code.scip_java.kotlin_analysis import com.intellij.psi.PsiElement import java.lang.System.err +import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaClassLikeSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaConstructorSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaPackageSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaTypeAliasSymbol +import org.jetbrains.kotlin.analysis.api.types.KaClassType import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtAnonymousInitializer +import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtClassBody import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtConstructor import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtEnumEntry import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtFunctionLiteral import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtParameterList +import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPropertyAccessor import org.jetbrains.kotlin.psi.KtSecondaryConstructor import org.jetbrains.kotlin.psi.KtTypeParameter @@ -73,17 +80,84 @@ class SymbolsCache { return symbol } + /** + * The SCIP symbol of the implicit primary constructor of [classOrObject], which has no PSI of + * its own. Explicit constructors go through [symbolForDeclaration]. + */ + fun implicitConstructorSymbol(classOrObject: KtClassOrObject): Symbol = + Symbol.createGlobal( + symbolForDeclaration(classOrObject), + ScipSymbolDescriptor(Kind.METHOD, ""), + ) + + /** + * The SCIP symbol of the synthetic getter/setter of a property (or `val`/`var` constructor + * parameter) that has no explicit accessor PSI. Accessors are owned by the property's container + * (`Class#getBanana().`), not by the constructor that declares the parameter. + */ + fun syntheticAccessorSymbol(property: KtDeclaration, setter: Boolean): Symbol { + val name = (property as? KtNamedDeclaration)?.name.orEmpty() + return Symbol.createGlobal(propertyOwnerSymbol(property), accessorDescriptor(name, setter)) + } + + /** The property symbol declared by a `val`/`var` constructor parameter (`Class#banana.`). */ + fun parameterPropertySymbol(parameter: KtParameter): Symbol = + Symbol.createGlobal( + propertyOwnerSymbol(parameter), + ScipSymbolDescriptor(Kind.TERM, parameter.name.orEmpty()), + ) + + /** The class symbol owning a constructor reference (`class Seagull : Bird()` → `Bird#`). */ + fun constructorOwnerSymbol(session: KaSession, target: KaConstructorSymbol): Symbol { + when (val psi = target.psi) { + is KtClassOrObject -> return symbolForDeclaration(psi) + is KtConstructor<*> -> + psi.containingClassOrObject?.let { + return symbolForDeclaration(it) + } + } + return target.containingClassId?.let(::classSymbol) ?: Symbol.NONE + } + + /** The SCIP symbol of a class referenced by its (expanded) [ClassId]. */ + fun classSymbol(classId: ClassId): Symbol = classIdSymbol(classId) + + private fun propertyOwnerSymbol(property: KtDeclaration): Symbol = + when (property) { + is KtParameter -> + (ownerDeclarationOfParameter(property) as? KtConstructor<*>) + ?.containingClassOrObject + ?.let { symbolForDeclaration(it) } ?: Symbol.NONE + else -> ownerSymbol(property) + } + + /** The `(value)` parameter symbol of a synthetic setter. */ + fun syntheticSetterValueSymbol(property: KtDeclaration): Symbol = + Symbol.createGlobal( + syntheticAccessorSymbol(property, setter = true), + ScipSymbolDescriptor(Kind.PARAMETER, "value"), + ) + + /** + * The SCIP symbol of the implicit `it` parameter of a lambda. The function literal PSI is used + * as the cache key; this cannot collide with the literal itself because anonymous functions + * never cache a symbol. + */ + fun implicitItSymbol(literal: KtFunctionLiteral): Symbol = + locals.get(literal) ?: locals.put(literal) + /** The SCIP symbol for the target of a resolved reference. */ - fun symbolForReference(target: KaSymbol): Symbol { + fun symbolForReference(session: KaSession, target: KaSymbol): Symbol { val psi = target.psi if (psi is KtDeclaration) { // A constructor symbol whose PSI is the class itself is an implicit // primary constructor. if (target is KaConstructorSymbol && psi is KtClassOrObject) { - return Symbol.createGlobal( - symbolForDeclaration(psi), - ScipSymbolDescriptor(Kind.METHOD, ""), - ) + return implicitConstructorSymbol(psi) + } + // The implicit `it` parameter's PSI is the enclosing function literal. + if (psi is KtFunctionLiteral && target !is KaFunctionSymbol) { + return implicitItSymbol(psi) } return symbolForDeclaration(psi) } @@ -96,18 +170,54 @@ class SymbolsCache { ScipSymbolDescriptor(Kind.METHOD, ""), ) } ?: Symbol.NONE + // Library type aliases are expanded to the aliased class, matching the + // FIR indexer (`AutoCloseable` → `java/lang/AutoCloseable#`) and + // keeping Kotlin and Java references to the same class unified. + is KaTypeAliasSymbol -> + with(session) { (target.expandedType as? KaClassType)?.classId } + ?.let(::classIdSymbol) ?: target.classId?.let(::classIdSymbol) ?: Symbol.NONE is KaClassLikeSymbol -> target.classId?.let(::classIdSymbol) ?: Symbol.NONE - is KaCallableSymbol -> externalCallableSymbol(target) + is KaCallableSymbol -> externalCallableSymbol(session, target) else -> Symbol.NONE } } + /** The getter/setter symbol of an external (classpath) property reference. */ + fun externalAccessorSymbol( + session: KaSession, + target: KaCallableSymbol, + setter: Boolean, + ): Symbol { + val callableId = target.callableId ?: return Symbol.NONE + val owner = + callableId.classId?.let(::classIdSymbol) ?: packageSymbol(callableId.packageName) + return Symbol.createGlobal( + owner, + accessorDescriptor(callableId.callableName.asString(), setter), + ) + } + + private fun accessorDescriptor(propertyName: String, setter: Boolean): ScipSymbolDescriptor = + ScipSymbolDescriptor( + Kind.METHOD, + (if (setter) "set" else "get") + propertyName.capitalizeAsciiOnly(), + ) + private fun uncachedSymbol(declaration: KtDeclaration): Symbol { // Anonymous functions and lambdas have no symbol of their own, mirroring // the FIR indexer which returned NONE for FirAnonymousFunctionSymbol. if (declaration is KtFunctionLiteral) return Symbol.NONE if (declaration is KtNamedFunction && declaration.name == null) return Symbol.NONE + // Anonymous objects are global symbols owned by the file's package, + // mirroring FIR where getContainingDeclaration returns null for them. + if (declaration is KtObjectDeclaration && declaration.isObjectLiteral()) { + return Symbol.createGlobal( + packageSymbol(declaration.containingKtFile.packageFqName), + scipDescriptor(declaration), + ) + } + if (isLocalMember(declaration)) return locals.put(declaration) val owner = ownerSymbol(declaration) @@ -116,18 +226,31 @@ class SymbolsCache { return Symbol.createGlobal(owner, scipDescriptor(declaration)) } - // Port of FIR's `isLocalMember`: only functions, variables and classes can be - // local members; everything else is classified through its owner. - private fun isLocalMember(declaration: KtDeclaration): Boolean = + /** + * Port of FIR's `isLocalMember`: functions, variables and classes declared in code bodies are + * local. Members of any class-like container — including anonymous objects — are not: locality + * is decided by the nearest scope owner, not by syntactic nesting. + */ + private fun isLocalMember(declaration: KtDeclaration): Boolean { when (declaration) { is KtNamedFunction, is KtVariableDeclaration, - is KtClassOrObject -> - declaration.parents - .takeWhile { it !is KtFile } - .any { it !is KtClassBody && it !is KtClassOrObject } - else -> false + is KtClassOrObject -> {} + else -> return false + } + for (parent in declaration.parents) { + when (parent) { + is KtClassBody, + is KtFile -> return false + is KtBlockExpression, + is KtFunction, + is KtPropertyAccessor, + is KtProperty, + is KtAnonymousInitializer -> return true + } } + return false + } private fun ownerSymbol(declaration: KtDeclaration): Symbol = when (declaration) { @@ -160,15 +283,9 @@ class SymbolsCache { declaration is KtClassOrObject -> ScipSymbolDescriptor(Kind.TYPE, declaration.name.orEmpty()) declaration is KtPropertyAccessor && declaration.isSetter -> - ScipSymbolDescriptor( - Kind.METHOD, - "set" + declaration.property.name.orEmpty().capitalizeAsciiOnly(), - ) + accessorDescriptor(declaration.property.name.orEmpty(), setter = true) declaration is KtPropertyAccessor -> - ScipSymbolDescriptor( - Kind.METHOD, - "get" + declaration.property.name.orEmpty().capitalizeAsciiOnly(), - ) + accessorDescriptor(declaration.property.name.orEmpty(), setter = false) declaration is KtConstructor<*> -> ScipSymbolDescriptor(Kind.METHOD, "", constructorDisambiguator(declaration)) declaration is KtNamedFunction -> @@ -236,19 +353,45 @@ class SymbolsCache { internal fun isDeclarationParameter(parameter: KtParameter): Boolean = ownerDeclarationOfParameter(parameter) != null - private fun externalCallableSymbol(target: KaCallableSymbol): Symbol { + /** + * External (classpath/JDK) callables, disambiguated by their position among the same-named + * callables of their container — mirroring FIR, which consulted the class's declared members or + * the top-level symbol provider (e.g. `kotlin/collections/forEachIndexed(+9).`). + */ + private fun externalCallableSymbol(session: KaSession, target: KaCallableSymbol): Symbol { val callableId = target.callableId ?: return Symbol.NONE val owner = callableId.classId?.let(::classIdSymbol) ?: packageSymbol(callableId.packageName) val name = callableId.callableName.asString() val descriptor = when (target) { - is KaFunctionSymbol -> ScipSymbolDescriptor(Kind.METHOD, name) + is KaFunctionSymbol -> + ScipSymbolDescriptor( + Kind.METHOD, + name, + externalMethodDisambiguator(session, target), + ) else -> ScipSymbolDescriptor(Kind.TERM, name) } return Symbol.createGlobal(owner, descriptor) } + private fun externalMethodDisambiguator(session: KaSession, target: KaCallableSymbol): String { + val callableId = target.callableId ?: return "()" + val overloads: List = + with(session) { + val classId = callableId.classId + if (classId == null) { + findTopLevelCallables(callableId.packageName, callableId.callableName).toList() + } else { + val classSymbol = findClass(classId) ?: return "()" + classSymbol.declaredMemberScope.callables(callableId.callableName).toList() + } + } + val index = overloads.indexOfFirst { it == target } + return if (index <= 0) "()" else "(+$index)" + } + private fun classIdSymbol(classId: ClassId): Symbol { var symbol = packageSymbol(classId.packageFqName) for (segment in classId.relativeClassName.pathSegments()) { diff --git a/scip-snapshots/cases/kotlin/common/build.gradle.kts b/scip-snapshots/cases/kotlin/common/build.gradle.kts index 0dc573c2d..be5226192 100644 --- a/scip-snapshots/cases/kotlin/common/build.gradle.kts +++ b/scip-snapshots/cases/kotlin/common/build.gradle.kts @@ -17,6 +17,16 @@ dependencies { implementation(libs.kotlin.stdlib) } +// Runtime classpath of the Analysis-API-based indexer, used by the +// scipIndexKotlinAnalysis comparison task below. +val scipKotlinAnalysis: Configuration by configurations.creating { + isCanBeConsumed = false +} + +dependencies { + scipKotlinAnalysis(project(":scip-kotlin-analysis")) +} + val scipTargetroot = layout.buildDirectory.dir("scip-targetroot") val sourceroot = rootProject.rootDir.absolutePath val targetroot = scipTargetroot.get().asFile.absolutePath @@ -35,3 +45,36 @@ tasks.named("compileJava") { } publishDirectoryArtifact("scipTargetrootElements", scipTargetroot, tasks.named("classes")) + +// Indexes the same Kotlin sources with the standalone Analysis API indexer into a +// separate targetroot, so its SCIP output can be compared against scip-kotlinc's. +val scipAnalysisTargetroot = layout.buildDirectory.dir("scip-targetroot-analysis") + +tasks.register("scipIndexKotlinAnalysis") { + classpath = scipKotlinAnalysis + mainClass.set("org.scip_code.scip_java.kotlin_analysis.MainKt") + val kotlinSources = layout.projectDirectory.dir("src/main/kotlin") + val compileClasspath = sourceSets.main.map { it.compileClasspath } + inputs.dir(kotlinSources) + inputs.files(compileClasspath) + outputs.dir(scipAnalysisTargetroot) + cleanDirectoryBeforeRunning(scipAnalysisTargetroot) + // Locals only: the argument provider must not capture the build script + // (configuration cache). + val sourcerootArg = sourceroot + val targetrootArg = scipAnalysisTargetroot + val kotlinSourcesArg = kotlinSources.asFile.absolutePath + argumentProviders.add( + CommandLineArgumentProvider { + listOf( + "--sourceroot", + sourcerootArg, + "--targetroot", + targetrootArg.get().asFile.absolutePath, + "--classpath", + compileClasspath.get().asPath, + kotlinSourcesArg, + ) + } + ) +} From f988e0d97ad873b4726a47337bd3cd0076611946 Mon Sep 17 00:00:00 2001 From: jupblb Date: Thu, 2 Jul 2026 12:03:17 +0200 Subject: [PATCH 04/33] Extend kotlin snapshots and close indexer parity gaps --- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 297 +++++++++++++++++- .../kotlin_analysis/SignatureRenderer.kt | 86 ++++- .../scip_java/kotlin_analysis/SymbolsCache.kt | 44 ++- .../src/main/kotlin/snapshots/Annotations.kt | 10 + .../main/kotlin/snapshots/Destructuring.kt | 10 + .../common/src/main/kotlin/snapshots/Enums.kt | 18 ++ .../src/main/kotlin/snapshots/Generics.kt | 16 + .../src/main/kotlin/snapshots/Annotations.kt | 66 ++++ .../main/kotlin/snapshots/Destructuring.kt | 182 +++++++++++ .../common/src/main/kotlin/snapshots/Enums.kt | 182 +++++++++++ .../src/main/kotlin/snapshots/Generics.kt | 148 +++++++++ 11 files changed, 1040 insertions(+), 19 deletions(-) create mode 100644 scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt create mode 100644 scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt create mode 100644 scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt create mode 100644 scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt create mode 100644 scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt create mode 100644 scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt create mode 100644 scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt create mode 100644 scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index 3455bc0a7..b76e45f86 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -33,12 +33,14 @@ import org.jetbrains.kotlin.psi.KtFunctionLiteral import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter +import org.jetbrains.kotlin.psi.KtParameterList import org.jetbrains.kotlin.psi.KtPrimaryConstructor import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPropertyAccessor import org.jetbrains.kotlin.psi.KtSecondaryConstructor import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtTreeVisitorVoid +import org.jetbrains.kotlin.psi.KtTypeAlias import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtVariableDeclaration import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject @@ -148,6 +150,7 @@ class KotlinAnalysisIndexer( } } + @OptIn(org.jetbrains.kotlin.analysis.api.KaExperimentalApi::class) private class IndexingVisitor( private val session: KaSession, private val cache: SymbolsCache, @@ -157,7 +160,9 @@ class KotlinAnalysisIndexer( private val signatures = SignatureRenderer(session) override fun visitDeclaration(dcl: KtDeclaration) { - super.visitDeclaration(dcl) + // Definitions are emitted before visiting children so that same-range + // occurrence order and local-symbol numbering match scip-kotlinc, + // which processed declarations before their bodies. when (dcl) { is KtEnumEntry -> emitEnumEntry(dcl) is KtClassOrObject -> emitClassLike(dcl) @@ -167,9 +172,11 @@ class KotlinAnalysisIndexer( is KtParameter -> emitParameter(dcl) is KtPropertyAccessor -> emitExplicitAccessor(dcl) is KtTypeParameter -> emitTypeParameter(dcl) - is KtDestructuringDeclarationEntry -> emitLocalVariable(dcl) + is KtTypeAlias -> emitTypeAlias(dcl) + is KtDestructuringDeclarationEntry -> emitDestructuringEntry(dcl) else -> {} } + super.visitDeclaration(dcl) } override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) { @@ -242,9 +249,13 @@ class KotlinAnalysisIndexer( relationships = superTypeRelationships(declaration), ) // Classes and objects (but not interfaces) declare an implicit primary - // constructor when no explicit one is present in source. + // constructor when no explicit one is present in source. A primary + // constructor without the `constructor` keyword also anchors at the class + // identifier and is emitted here so the class definition stays first at + // that range (matching scip-kotlinc's order); emitConstructor skips it. val isInterface = declaration is KtClass && declaration.isInterface() - if (!isInterface && declaration.primaryConstructor == null) { + val primaryConstructor = declaration.primaryConstructor + if (!isInterface && primaryConstructor == null) { emitDefinition( symbol = cache.implicitConstructorSymbol(declaration), range = range, @@ -253,9 +264,87 @@ class KotlinAnalysisIndexer( signatureText = signatures.implicitConstructorSignature(declaration), documentation = docComment(declaration), ) + } else if ( + primaryConstructor != null && primaryConstructor.getConstructorKeyword() == null + ) { + emitDefinition( + symbol = cache.symbolForDeclaration(primaryConstructor), + range = range, + enclosing = primaryConstructor.textRange, + displayName = displayName, + signatureText = signatures.constructorSignature(primaryConstructor), + documentation = docComment(primaryConstructor), + ) + } + if (declaration is KtClass && declaration.isEnum()) { + emitEnumSynthetics(declaration, range) + } + if (declaration is KtClass && declaration.isData()) { + declaration.primaryConstructor?.let { constructor -> + emitDefinition( + symbol = cache.memberMethodSymbol(declaration, "copy"), + range = constructor.textRange, + enclosing = constructor.textRange, + displayName = "copy", + signatureText = signatures.dataCopySignature(declaration), + documentation = null, + ) + } } } + /** + * Compiler-generated enum members: `values()`, `valueOf(value)` and `entries` (plus its + * getter), all anchored at the enum class identifier like in scip-kotlinc. Unlike + * scip-kotlinc, the entries getter is owned by the enum class instead of the package — the + * old symbol (`snapshots/getEntries().`) was a bug. + */ + private fun emitEnumSynthetics(declaration: KtClass, range: TextRange) { + val name = declaration.name.orEmpty() + val enclosing = declaration.textRange + emitDefinition( + symbol = cache.memberMethodSymbol(declaration, "values"), + range = range, + enclosing = enclosing, + displayName = "values", + signatureText = signatures.enumValuesSignature(name), + documentation = null, + ) + val valueOf = cache.memberMethodSymbol(declaration, "valueOf") + emitDefinition( + symbol = valueOf, + range = range, + enclosing = enclosing, + displayName = "valueOf", + signatureText = signatures.enumValueOfSignature(name), + documentation = null, + ) + emitDefinition( + symbol = cache.methodParameterSymbol(valueOf, "value"), + range = range, + enclosing = enclosing, + displayName = "value", + signatureText = "value: String", + documentation = null, + ) + emitDefinition( + symbol = cache.memberTermSymbol(declaration, "entries"), + range = range, + enclosing = enclosing, + displayName = "entries", + signatureText = signatures.enumEntriesSignature(name), + documentation = null, + ) + emitDefinition( + symbol = cache.memberMethodSymbol(declaration, "getEntries"), + range = range, + enclosing = enclosing, + displayName = "entries", + signatureText = signatures.enumGetEntriesSignature(name), + documentation = null, + ) + } + private fun emitEnumEntry(declaration: KtEnumEntry) { val identifier = declaration.nameIdentifier ?: return emitDefinition( @@ -271,10 +360,9 @@ class KotlinAnalysisIndexer( private fun emitConstructor(declaration: KtConstructor<*>) { val range = when (declaration) { + // Keyword-less primary constructors are emitted by emitClassLike. is KtPrimaryConstructor -> - declaration.getConstructorKeyword()?.textRange - ?: declaration.containingClassOrObject?.nameIdentifier?.textRange - ?: return + declaration.getConstructorKeyword()?.textRange ?: return is KtSecondaryConstructor -> declaration.textRange else -> return } @@ -341,8 +429,42 @@ class KotlinAnalysisIndexer( } private fun emitParameter(declaration: KtParameter) { - // Parameters of function types, catch clauses and for loops are not - // declarations of their own. + val loopOrCatchIdentifier = declaration.nameIdentifier + // `for` loop variables and `catch` parameters are local declarations, + // mirroring the FIR indexer (which also assigned their local numbers at + // the declaration site). + if (loopOrCatchIdentifier != null) { + if (declaration.parent is org.jetbrains.kotlin.psi.KtForExpression) { + val type = + declaration.typeReference?.text + ?: renderedParameterType(declaration) + ?: "Any" + emitDefinition( + symbol = cache.symbolForDeclaration(declaration), + range = loopOrCatchIdentifier.textRange, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + signatureText = "local val ${declaration.name.orEmpty()}: $type", + documentation = null, + ) + return + } + if ( + (declaration.parent as? KtParameterList)?.parent + is org.jetbrains.kotlin.psi.KtCatchClause + ) { + emitDefinition( + symbol = cache.symbolForDeclaration(declaration), + range = loopOrCatchIdentifier.textRange, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + signatureText = signatures.parameterSignature(declaration), + documentation = null, + ) + return + } + } + // Parameters of function types are not declarations of their own. if (!cache.isDeclarationParameter(declaration)) return val identifier = declaration.nameIdentifier ?: return val symbol = cache.symbolForDeclaration(declaration) @@ -376,9 +498,166 @@ class KotlinAnalysisIndexer( hasExplicitSetter = false, isVar = declaration.isMutable, ) + emitDataClassParameterSynthetics(declaration, identifier.textRange) + } + } + + /** + * A data-class constructor parameter additionally declares `componentN()` and a `copy()` + * parameter, and the generated `copy()` default value reads the property — all anchored at + * the parameter identifier, mirroring scip-kotlinc. + */ + private fun emitDataClassParameterSynthetics(declaration: KtParameter, range: TextRange) { + val constructor = declaration.parent?.parent as? KtPrimaryConstructor ?: return + val containingClass = constructor.containingClassOrObject as? KtClass ?: return + if (!containingClass.isData()) return + val index = constructor.valueParameters.indexOf(declaration) + if (index < 0) return + emitDefinition( + symbol = cache.memberMethodSymbol(containingClass, "component${index + 1}"), + range = range, + enclosing = declaration.textRange, + displayName = "component${index + 1}", + signatureText = signatures.dataComponentSignature(index + 1, declaration), + documentation = null, + ) + emitDefinition( + symbol = + cache.methodParameterSymbol( + cache.memberMethodSymbol(containingClass, "copy"), + declaration.name.orEmpty(), + ), + range = range, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + signatureText = signatures.dataCopyParameterSignature(declaration), + documentation = null, + ) + addReference(cache.parameterPropertySymbol(declaration), range) + addReference(cache.syntheticAccessorSymbol(declaration, setter = false), range) + if (declaration.isMutable) { + addReference(cache.syntheticAccessorSymbol(declaration, setter = true), range) + } + } + + private fun emitTypeAlias(declaration: KtTypeAlias) { + val identifier = declaration.nameIdentifier ?: return + emitDefinition( + symbol = cache.symbolForDeclaration(declaration), + range = identifier.textRange, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + signatureText = signatures.typeAliasSignature(declaration), + documentation = docComment(declaration), + ) + } + + /** + * `when (subject)` introduces a synthetic `` local in the FIR desugaring; + * scip-kotlinc emitted a definition for it at the subject expression. Emitted before + * visiting children so local numbering matches. + */ + override fun visitWhenExpression(expression: org.jetbrains.kotlin.psi.KtWhenExpression) { + val subject = expression.subjectExpression + if (subject != null && expression.subjectVariable == null) { + emitDefinition( + symbol = cache.syntheticLocalSymbol(expression), + range = subject.textRange, + enclosing = subject.textRange, + displayName = "", + signatureText = + signatures.whenSubjectSignature(renderedExpressionType(subject)), + documentation = null, + ) } + super.visitWhenExpression(expression) } + /** + * Destructuring introduces a synthetic `` local holding the destructured value; + * each entry references its `componentN()` function and the `` local. Emitted + * before visiting children so local numbering matches. + */ + override fun visitDestructuringDeclaration( + destructuringDeclaration: org.jetbrains.kotlin.psi.KtDestructuringDeclaration + ) { + val isParameter = destructuringDeclaration.parent is KtParameter + val type = + destructuringDeclaration.initializer?.let { renderedExpressionType(it) } + ?: (destructuringDeclaration.parent as? KtParameter)?.let { + renderedParameterType(it) + } + ?: "Any" + emitDefinition( + symbol = cache.syntheticLocalSymbol(destructuringDeclaration), + range = destructuringDeclaration.textRange, + enclosing = destructuringDeclaration.textRange, + displayName = "", + signatureText = signatures.destructSignature(type, isParameter), + documentation = null, + ) + super.visitDestructuringDeclaration(destructuringDeclaration) + } + + private fun emitDestructuringEntry(declaration: KtDestructuringDeclarationEntry) { + emitLocalVariable(declaration) + val range = declaration.nameIdentifier?.textRange ?: return + val destructuring = + declaration.parent as? org.jetbrains.kotlin.psi.KtDestructuringDeclaration ?: return + // Each entry desugars to a `componentN()` call on the destructured value. + // Standalone analysis does not resolve destructuring-entry references, so + // the symbol is derived from the destructured type and the entry position. + val index = destructuring.entries.indexOf(declaration) + destructuredClassId(destructuring)?.let { classId -> + addReference( + Symbol.createGlobal( + cache.classSymbol(classId), + ScipSymbolDescriptor( + ScipSymbolDescriptor.Kind.METHOD, + "component${index + 1}", + ), + ), + range, + ) + } + addReference(cache.syntheticLocalSymbol(destructuring), range) + } + + private fun destructuredClassId( + destructuring: org.jetbrains.kotlin.psi.KtDestructuringDeclaration + ): ClassId? = + with(session) { + val type = + destructuring.initializer?.expressionType + ?: (destructuring.parent as? KtParameter)?.let { + (it.symbol as? KaCallableSymbol)?.returnType + } + (type as? KaClassType)?.classId + } + + private fun renderedExpressionType( + expression: org.jetbrains.kotlin.psi.KtExpression + ): String = + with(session) { + expression.expressionType?.render( + org.jetbrains.kotlin.analysis.api.renderer.types.impl.KaTypeRendererForSource + .WITH_SHORT_NAMES, + position = org.jetbrains.kotlin.types.Variance.INVARIANT, + ) + } ?: "Any" + + private fun renderedParameterType(parameter: KtParameter): String? = + with(session) { + (parameter.symbol as? KaCallableSymbol) + ?.returnType + ?.render( + org.jetbrains.kotlin.analysis.api.renderer.types.impl + .KaTypeRendererForSource + .WITH_SHORT_NAMES, + position = org.jetbrains.kotlin.types.Variance.INVARIANT, + ) + } + private fun emitSyntheticAccessors( property: KtDeclaration, nameRange: TextRange, diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt index 769130274..7469d45c3 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt @@ -14,6 +14,7 @@ import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPropertyAccessor +import org.jetbrains.kotlin.psi.KtTypeAlias import org.jetbrains.kotlin.psi.KtVariableDeclaration import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.types.Variance @@ -38,30 +39,36 @@ internal class SignatureRenderer(private val session: KaSession) { declaration is KtObjectDeclaration -> "object" declaration is KtClass && declaration.isInterface() -> "interface" declaration is KtClass && declaration.isEnum() -> "enum class" + declaration is KtClass && declaration.isAnnotation() -> "annotation class" + declaration is KtClass && declaration.isData() -> "data class" else -> "class" } val name = declaration.name ?: "Companion" - return "${visibility(declaration)} ${classModality(declaration)} $keyword $name : " + + val typeParameters = (declaration as? KtClass)?.typeParameterList?.text.orEmpty() + return "${annotationsPrefix(declaration)}${visibility(declaration)} " + + "${classModality(declaration)} $keyword $name$typeParameters : " + superTypesText(declaration) } fun constructorSignature(constructor: KtConstructor<*>): String { val owner = constructor.containingClassOrObject - val visibility = explicitVisibility(constructor) ?: "public" - return "$visibility constructor(${parametersText(constructor.valueParameters)}): " + - ownerName(owner) + val visibility = explicitVisibility(constructor) ?: constructorVisibility(owner) + return "$visibility constructor${classTypeParameters(owner)}" + + "(${parametersText(constructor.valueParameters)}): " + + constructorReturnType(owner) } fun implicitConstructorSignature(declaration: KtClassOrObject): String { - val visibility = if (declaration is KtObjectDeclaration) "private" else "public" - return "$visibility constructor(): ${ownerName(declaration)}" + return "${constructorVisibility(declaration)} constructor(): ${ownerName(declaration)}" } fun functionSignature(declaration: KtNamedFunction): String { val returnType = declaration.typeReference?.text ?: renderedReturnType(declaration) ?: "Unit" + val typeParameters = declaration.typeParameterList?.text?.let { "$it " }.orEmpty() val signature = - "${visibility(declaration)} ${memberModality(declaration)}fun " + + "${annotationsPrefix(declaration)}${visibility(declaration)} " + + "${memberModality(declaration)}fun $typeParameters" + "${declaration.name.orEmpty()}(${parametersText(declaration.valueParameters)}): " + returnType // FirRenderer emitted a trailing newline for functions without a body @@ -69,6 +76,64 @@ internal class SignatureRenderer(private val session: KaSession) { return if (declaration.hasBody()) signature else signature + "\n" } + fun typeAliasSignature(declaration: KtTypeAlias): String = + "public final typealias ${declaration.name.orEmpty()} = " + + declaration.getTypeReference()?.text.orEmpty() + + "\n" + + /** `values()` / `valueOf(value: String)` / `entries` signatures of an enum class. */ + fun enumValuesSignature(name: String): String = "public final static fun values(): Array<$name>" + + fun enumValueOfSignature(name: String): String = + "public final static fun valueOf(value: String): $name" + + fun enumEntriesSignature(name: String): String = + "public final static val entries: EnumEntries<$name>" + + fun enumGetEntriesSignature(name: String): String = "public get(): EnumEntries<$name>" + + fun dataCopySignature(declaration: KtClass): String { + val parameters = + declaration.primaryConstructor?.valueParameters.orEmpty().joinToString(", ") { + "${it.name.orEmpty()}: ${it.typeReference?.text ?: "Any"} = ..." + } + return "public final fun copy($parameters): ${declaration.name.orEmpty()}\n" + } + + fun dataComponentSignature(index: Int, parameter: KtParameter): String = + "public final operator fun component$index(): ${parameter.typeReference?.text ?: "Any"}\n" + + fun dataCopyParameterSignature(parameter: KtParameter): String = + "${parameter.name.orEmpty()}: ${parameter.typeReference?.text ?: "Any"} = ..." + + fun whenSubjectSignature(type: String): String = "local val : $type" + + fun destructSignature(type: String, isParameter: Boolean): String = + if (isParameter) ": $type" else "local val : $type" + + private fun constructorVisibility(declaration: KtClassOrObject?): String = + when { + declaration is KtObjectDeclaration -> "private" + declaration is KtClass && declaration.isEnum() -> "private" + else -> "public" + } + + private fun classTypeParameters(declaration: KtClassOrObject?): String = + (declaration as? KtClass)?.typeParameterList?.text.orEmpty() + + private fun constructorReturnType(declaration: KtClassOrObject?): String { + val name = ownerName(declaration) + val typeArguments = + (declaration as? KtClass)?.typeParameters?.mapNotNull { it.name }.orEmpty() + return if (typeArguments.isEmpty()) name else "$name<${typeArguments.joinToString(", ")}>" + } + + private fun annotationsPrefix(declaration: KtDeclaration): String = + declaration.annotationEntries.joinToString("") { entry -> + val name = entry.shortName?.asString().orEmpty() + if (entry.valueArgumentList != null) "@$name(...) " else "@$name " + } + fun propertySignature(declaration: KtDeclaration): String { val name = (declaration as? org.jetbrains.kotlin.psi.KtNamedDeclaration)?.name.orEmpty() val valOrVar = if (isVar(declaration)) "var" else "val" @@ -139,7 +204,12 @@ internal class SignatureRenderer(private val session: KaSession) { private fun superTypesText(declaration: KtClassOrObject): String { val entries = declaration.superTypeListEntries.mapNotNull { it.typeReference?.text } - return if (entries.isEmpty()) "Any" else entries.joinToString(", ") + if (entries.isNotEmpty()) return entries.joinToString(", ") + return when { + declaration is KtClass && declaration.isEnum() -> "Enum<${declaration.name.orEmpty()}>" + declaration is KtClass && declaration.isAnnotation() -> "Annotation" + else -> "Any" + } } private fun parametersText(parameters: List): String = diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt index 518849de9..3f260a000 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.psi.KtParameterList import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtPropertyAccessor import org.jetbrains.kotlin.psi.KtSecondaryConstructor +import org.jetbrains.kotlin.psi.KtTypeAlias import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtTypeParameterListOwner import org.jetbrains.kotlin.psi.KtVariableDeclaration @@ -122,6 +123,24 @@ class SymbolsCache { /** The SCIP symbol of a class referenced by its (expanded) [ClassId]. */ fun classSymbol(classId: ClassId): Symbol = classIdSymbol(classId) + /** Symbol of a compiler-generated member method (enum `values()`, data-class `copy()`, …). */ + fun memberMethodSymbol(owner: KtClassOrObject, name: String): Symbol = + Symbol.createGlobal(symbolForDeclaration(owner), ScipSymbolDescriptor(Kind.METHOD, name)) + + /** Symbol of a compiler-generated member property (enum `entries`). */ + fun memberTermSymbol(owner: KtClassOrObject, name: String): Symbol = + Symbol.createGlobal(symbolForDeclaration(owner), ScipSymbolDescriptor(Kind.TERM, name)) + + /** Symbol of a parameter of a compiler-generated method. */ + fun methodParameterSymbol(method: Symbol, name: String): Symbol = + Symbol.createGlobal(method, ScipSymbolDescriptor(Kind.PARAMETER, name)) + + /** + * A per-document local symbol for a compiler-generated declaration without dedicated PSI + * (implicit `it`, ``, ``), keyed by the surrounding element. + */ + fun syntheticLocalSymbol(key: PsiElement): Symbol = locals.get(key) ?: locals.put(key) + private fun propertyOwnerSymbol(property: KtDeclaration): Symbol = when (property) { is KtParameter -> @@ -143,8 +162,7 @@ class SymbolsCache { * as the cache key; this cannot collide with the literal itself because anonymous functions * never cache a symbol. */ - fun implicitItSymbol(literal: KtFunctionLiteral): Symbol = - locals.get(literal) ?: locals.put(literal) + fun implicitItSymbol(literal: KtFunctionLiteral): Symbol = syntheticLocalSymbol(literal) /** The SCIP symbol for the target of a resolved reference. */ fun symbolForReference(session: KaSession, target: KaSymbol): Symbol { @@ -159,6 +177,26 @@ class SymbolsCache { if (psi is KtFunctionLiteral && target !is KaFunctionSymbol) { return implicitItSymbol(psi) } + // Source type aliases are expanded like library ones, matching scip-kotlinc. + if (psi is KtTypeAlias && target is KaTypeAliasSymbol) { + return with(session) { (target.expandedType as? KaClassType)?.classId } + ?.let(::classIdSymbol) ?: symbolForDeclaration(psi) + } + // Compiler-generated callables (data-class componentN/copy, enum + // `entries`, …) resolve to PSI of the class or parameter they derive + // from; their symbol comes from the callable id instead. + if ( + target is KaFunctionSymbol && + target !is KaConstructorSymbol && + psi !is KtNamedFunction && + psi !is KtPropertyAccessor && + psi !is KtFunctionLiteral + ) { + return externalCallableSymbol(session, target) + } + if (target is KaCallableSymbol && psi is KtClassOrObject) { + return externalCallableSymbol(session, target) + } return symbolForDeclaration(psi) } return when (target) { @@ -294,6 +332,8 @@ class SymbolsCache { declaration.name.orEmpty(), methodDisambiguator(declaration), ) + declaration is KtTypeAlias -> + ScipSymbolDescriptor(Kind.TYPE, declaration.name.orEmpty()) declaration is KtTypeParameter -> ScipSymbolDescriptor(Kind.TYPE_PARAMETER, declaration.name.orEmpty()) declaration is KtParameter -> diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt new file mode 100644 index 000000000..72c2d7dce --- /dev/null +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt @@ -0,0 +1,10 @@ +package snapshots + +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) +annotation class Tagged(val tag: String) + +@Tagged("service") +class AnnotatedService { + @Tagged("run") + fun run(): String = "running" +} diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt new file mode 100644 index 000000000..ca2646890 --- /dev/null +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt @@ -0,0 +1,10 @@ +package snapshots + +data class Point(val x: Int, val y: Int) + +fun destructure(): Int { + val (x, y) = Point(1, 2) + val labeled = listOf(Point(3, 4) to "label") + val total = labeled.sumOf { (point, label) -> point.x + label.length } + return x + y + total +} diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt new file mode 100644 index 000000000..32366c6d5 --- /dev/null +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt @@ -0,0 +1,18 @@ +package snapshots + +enum class Suit(val symbol: Char) { + HEARTS('h'), + SPADES('s'); + + fun isRed(): Boolean = symbol == 'h' + + companion object { + fun fromSymbol(symbol: Char): Suit? = entries.find { it.symbol == symbol } + } +} + +fun describe(suit: Suit): String = + when (suit) { + Suit.HEARTS -> "red" + Suit.SPADES -> "black" + } diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt new file mode 100644 index 000000000..d917e3c04 --- /dev/null +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt @@ -0,0 +1,16 @@ +package snapshots + +class Container>(private val items: MutableList) { + fun add(item: T): Container { + items.add(item) + return this + } + + fun mapItems(transform: (T) -> R?): List = items.mapNotNull(transform) +} + +fun firstOrSelf(items: List, fallback: T): T = items.firstOrNull() ?: fallback + +typealias StringContainer = Container + +fun useContainer(container: StringContainer): StringContainer = container.add("hello") diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt new file mode 100644 index 000000000..a458ba724 --- /dev/null +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt @@ -0,0 +1,66 @@ + package snapshots +// ^^^^^^^^^ reference scip-java maven . . snapshots/ + +//⌄ enclosing_range_start scip-java maven . . snapshots/Tagged# + @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) +// ^^^^^ reference scip-java maven . . kotlin/annotation/AnnotationTarget#CLASS. +// ^^^^^^^^ reference scip-java maven . . kotlin/annotation/AnnotationTarget#FUNCTION. +// ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#``().(tag) +// ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#tag. +// ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#getTag(). + annotation class Tagged(val tag: String) +// ^^^^^^ definition scip-java maven . . snapshots/Tagged# +// display_name Tagged +// signature_documentation +// > @Target(...) public final annotation class Tagged : Annotation +// relationship scip-java maven . . kotlin/Annotation# implementation +// ^^^^^^ definition scip-java maven . . snapshots/Tagged#``(). +// display_name Tagged +// signature_documentation +// > public constructor(tag: String): Tagged +// ^^^ definition scip-java maven . . snapshots/Tagged#``().(tag) +// display_name tag +// signature_documentation +// > tag: String +// ^^^ definition scip-java maven . . snapshots/Tagged#tag. +// display_name tag +// signature_documentation +// > public final val tag: String +// ^^^ reference scip-java maven . . snapshots/Tagged#``().(tag) +// ^^^ definition scip-java maven . . snapshots/Tagged#getTag(). +// display_name tag +// signature_documentation +// > public get(): String +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged#``().(tag) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged#tag. +// ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged#getTag(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged#``(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/AnnotatedService# +//⌄ enclosing_range_start scip-java maven . . snapshots/AnnotatedService#``(). + @Tagged("service") + class AnnotatedService { +// ^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/AnnotatedService# +// display_name AnnotatedService +// signature_documentation +// > @Tagged(...) public final class AnnotatedService : Any +// ^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/AnnotatedService#``(). +// display_name AnnotatedService +// signature_documentation +// > public constructor(): AnnotatedService +// ⌄ enclosing_range_start scip-java maven . . snapshots/AnnotatedService#run(). + @Tagged("run") + fun run(): String = "running" +// ^^^ definition scip-java maven . . snapshots/AnnotatedService#run(). +// display_name run +// signature_documentation +// > @Tagged(...) public final fun run(): String +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ⌃ enclosing_range_end scip-java maven . . snapshots/AnnotatedService#run(). + } +//⌃ enclosing_range_end scip-java maven . . snapshots/AnnotatedService# +//⌃ enclosing_range_end scip-java maven . . snapshots/AnnotatedService#``(). + diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt new file mode 100644 index 000000000..56bc9c232 --- /dev/null +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt @@ -0,0 +1,182 @@ + package snapshots +// ^^^^^^^^^ reference scip-java maven . . snapshots/ + +//⌄ enclosing_range_start scip-java maven . . snapshots/Point# +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#copy(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#``().(x) +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#x. +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#getX(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#component1(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#copy().(x) +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#``().(y) +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#y. +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#getY(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#component2(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#copy().(y) + data class Point(val x: Int, val y: Int) +// ^^^^^ definition scip-java maven . . snapshots/Point# +// display_name Point +// signature_documentation +// > public final data class Point : Any +// ^^^^^ definition scip-java maven . . snapshots/Point#``(). +// display_name Point +// signature_documentation +// > public constructor(x: Int, y: Int): Point +// ^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Point#copy(). +// display_name copy +// signature_documentation +// > public final fun copy(x: Int = ..., y: Int = ...): Point +// > +// ^ definition scip-java maven . . snapshots/Point#``().(x) +// display_name x +// signature_documentation +// > x: Int +// ^ definition scip-java maven . . snapshots/Point#x. +// display_name x +// signature_documentation +// > public final val x: Int +// ^ reference scip-java maven . . snapshots/Point#``().(x) +// ^ definition scip-java maven . . snapshots/Point#getX(). +// display_name x +// signature_documentation +// > public get(): Int +// ^ definition scip-java maven . . snapshots/Point#component1(). +// display_name component1 +// signature_documentation +// > public final operator fun component1(): Int +// > +// ^ definition scip-java maven . . snapshots/Point#copy().(x) +// display_name x +// signature_documentation +// > x: Int = ... +// ^ reference scip-java maven . . snapshots/Point#x. +// ^ reference scip-java maven . . snapshots/Point#getX(). +// ^^^ reference scip-java maven . . kotlin/Int# +// ^ definition scip-java maven . . snapshots/Point#``().(y) +// display_name y +// signature_documentation +// > y: Int +// ^ definition scip-java maven . . snapshots/Point#y. +// display_name y +// signature_documentation +// > public final val y: Int +// ^ reference scip-java maven . . snapshots/Point#``().(y) +// ^ definition scip-java maven . . snapshots/Point#getY(). +// display_name y +// signature_documentation +// > public get(): Int +// ^ definition scip-java maven . . snapshots/Point#component2(). +// display_name component2 +// signature_documentation +// > public final operator fun component2(): Int +// > +// ^ definition scip-java maven . . snapshots/Point#copy().(y) +// display_name y +// signature_documentation +// > y: Int = ... +// ^ reference scip-java maven . . snapshots/Point#y. +// ^ reference scip-java maven . . snapshots/Point#getY(). +// ^^^ reference scip-java maven . . kotlin/Int# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#``().(x) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#x. +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#getX(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#component1(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#copy().(x) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#``().(y) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#y. +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#getY(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#component2(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#copy().(y) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#``(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#copy(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/destructure(). + fun destructure(): Int { +// ^^^^^^^^^^^ definition scip-java maven . . snapshots/destructure(). +// display_name destructure +// signature_documentation +// > public final fun destructure(): Int +// ^^^ reference scip-java maven . . kotlin/Int# +// ⌄ enclosing_range_start local 0 +// ⌄ enclosing_range_start local 1 +// ⌄ enclosing_range_start local 2 + val (x, y) = Point(1, 2) +// ^^^^^^^^^^^^^^^^^^^^^^^^ definition local 0 +// display_name +// signature_documentation +// > local val : Point +// ^ definition local 1 +// display_name x +// signature_documentation +// > local val x: Int +// ^ reference scip-java maven . . snapshots/Point#component1(). +// ^ reference local 0 +// ^ definition local 2 +// display_name y +// signature_documentation +// > local val y: Int +// ^ reference scip-java maven . . snapshots/Point#component2(). +// ^ reference local 0 +// ^^^^^ reference scip-java maven . . snapshots/Point#``(). +// ⌃ enclosing_range_end local 1 +// ⌃ enclosing_range_end local 2 +// ⌃ enclosing_range_end local 0 +// ⌄ enclosing_range_start local 3 + val labeled = listOf(Point(3, 4) to "label") +// ^^^^^^^ definition local 3 +// display_name labeled +// signature_documentation +// > local val labeled: List> +// ^^^^^^ reference scip-java maven . . kotlin/collections/listOf(). +// ^^^^^ reference scip-java maven . . snapshots/Point#``(). +// ^^ reference scip-java maven . . kotlin/to(). +// ⌃ enclosing_range_end local 3 +// ⌄ enclosing_range_start local 4 +// ⌄ enclosing_range_start local 5 +// ⌄ enclosing_range_start local 6 +// ⌄ enclosing_range_start local 7 + val total = labeled.sumOf { (point, label) -> point.x + label.length } +// ^^^^^ definition local 4 +// display_name total +// signature_documentation +// > local val total: Int +// ^^^^^^^ reference local 3 +// ^^^^^ reference scip-java maven . . kotlin/collections/sumOf(+66). +// ^^^^^^^^^^^^^^ definition local 5 +// display_name +// signature_documentation +// > : Pair +// ^^^^^ definition local 6 +// display_name point +// signature_documentation +// > local val point: Point +// ^^^^^ reference scip-java maven . . kotlin/Pair#component1(). +// ^^^^^ reference local 5 +// ^^^^^ definition local 7 +// display_name label +// signature_documentation +// > local val label: String +// ^^^^^ reference scip-java maven . . kotlin/Pair#component2(). +// ^^^^^ reference local 5 +// ^^^^^ reference local 6 +// ^ reference scip-java maven . . snapshots/Point#x. +// ^ reference scip-java maven . . snapshots/Point#getX(). +// ^ reference scip-java maven . . kotlin/Int#plus(+2). +// ^^^^^ reference local 7 +// ^^^^^^ reference scip-java maven . . kotlin/String#length. +// ^^^^^^ reference scip-java maven . . kotlin/String#getLength(). +// ⌃ enclosing_range_end local 6 +// ⌃ enclosing_range_end local 7 +// ⌃ enclosing_range_end local 5 +// ⌃ enclosing_range_end local 4 + return x + y + total +// ^ reference local 1 +// ^ reference scip-java maven . . kotlin/Int#plus(+2). +// ^ reference local 2 +// ^ reference scip-java maven . . kotlin/Int#plus(+2). +// ^^^^^ reference local 4 + } +//⌃ enclosing_range_end scip-java maven . . snapshots/destructure(). + diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt new file mode 100644 index 000000000..3361608b0 --- /dev/null +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt @@ -0,0 +1,182 @@ + package snapshots +// ^^^^^^^^^ reference scip-java maven . . snapshots/ + +//⌄ enclosing_range_start scip-java maven . . snapshots/Suit# +//⌄ enclosing_range_start scip-java maven . . snapshots/Suit#values(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Suit#valueOf(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Suit#valueOf().(value) +//⌄ enclosing_range_start scip-java maven . . snapshots/Suit#entries. +//⌄ enclosing_range_start scip-java maven . . snapshots/getEntries(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#``().(symbol) +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#symbol. +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#getSymbol(). + enum class Suit(val symbol: Char) { +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reference scip-java maven . . kotlin/Enum# +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reference scip-java maven . . snapshots/Suit# +// ^^^^ definition scip-java maven . . snapshots/Suit# +// display_name Suit +// signature_documentation +// > public final enum class Suit : Enum +// relationship scip-java maven . . kotlin/Enum# implementation +// ^^^^ definition scip-java maven . . snapshots/Suit#``(). +// display_name Suit +// signature_documentation +// > private constructor(symbol: Char): Suit +// ^^^^ definition scip-java maven . . snapshots/Suit#values(). +// display_name values +// signature_documentation +// > public final static fun values(): Array +// ^^^^ definition scip-java maven . . snapshots/Suit#valueOf(). +// display_name valueOf +// signature_documentation +// > public final static fun valueOf(value: String): Suit +// ^^^^ definition scip-java maven . . snapshots/Suit#valueOf().(value) +// display_name value +// signature_documentation +// > value: String +// ^^^^ definition scip-java maven . . snapshots/Suit#entries. +// display_name entries +// signature_documentation +// > public final static val entries: EnumEntries +// ^^^^ definition scip-java maven . . snapshots/getEntries(). +// display_name entries +// signature_documentation +// > public get(): EnumEntries +// ^^^^^^ definition scip-java maven . . snapshots/Suit#``().(symbol) +// display_name symbol +// signature_documentation +// > symbol: Char +// ^^^^^^ definition scip-java maven . . snapshots/Suit#symbol. +// display_name symbol +// signature_documentation +// > public final val symbol: Char +// ^^^^^^ reference scip-java maven . . snapshots/Suit#``().(symbol) +// ^^^^^^ definition scip-java maven . . snapshots/Suit#getSymbol(). +// display_name symbol +// signature_documentation +// > public get(): Char +// ^^^^ reference scip-java maven . . kotlin/Char# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#``().(symbol) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#symbol. +// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#getSymbol(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/``# +// ⌄ enclosing_range_start scip-java maven . . snapshots/``#``(). + HEARTS('h'), +// ^^^^^^ definition scip-java maven . . snapshots/``# +// display_name +// signature_documentation +// > object : Suit +// relationship scip-java maven . . snapshots/Suit# implementation +// ^^^^^^ definition scip-java maven . . snapshots/``#``(). +// display_name HEARTS +// signature_documentation +// > private constructor(): +// ⌃ enclosing_range_end scip-java maven . . snapshots/``# +// ⌃ enclosing_range_end scip-java maven . . snapshots/``#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/``# +// ⌄ enclosing_range_start scip-java maven . . snapshots/``#``(). + SPADES('s'); +// ^^^^^^ definition scip-java maven . . snapshots/``# +// display_name +// signature_documentation +// > object : Suit +// relationship scip-java maven . . snapshots/Suit# implementation +// ^^^^^^ definition scip-java maven . . snapshots/``#``(). +// display_name SPADES +// signature_documentation +// > private constructor(): +// ⌃ enclosing_range_end scip-java maven . . snapshots/``# +// ⌃ enclosing_range_end scip-java maven . . snapshots/``#``(). + +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#isRed(). + fun isRed(): Boolean = symbol == 'h' +// ^^^^^ definition scip-java maven . . snapshots/Suit#isRed(). +// display_name isRed +// signature_documentation +// > public final fun isRed(): Boolean +// ^^^^^^^ reference scip-java maven . . kotlin/Boolean# +// ^^^^^^ reference scip-java maven . . snapshots/Suit#symbol. +// ^^^^^^ reference scip-java maven . . snapshots/Suit#getSymbol(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#isRed(). + +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion# +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion#``(). + companion object { +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion# +// display_name Companion +// signature_documentation +// > public final companion object Companion : Any +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion#``(). +// display_name Companion +// signature_documentation +// > private constructor(): Suit.Companion +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion#fromSymbol(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion#fromSymbol().(symbol) +// ⌄ enclosing_range_start local 0 + fun fromSymbol(symbol: Char): Suit? = entries.find { it.symbol == symbol } +// ^^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion#fromSymbol(). +// display_name fromSymbol +// signature_documentation +// > public final fun fromSymbol(symbol: Char): Suit? +// ^^^^^^ definition scip-java maven . . snapshots/Suit#Companion#fromSymbol().(symbol) +// display_name symbol +// signature_documentation +// > symbol: Char +// ^^^^ reference scip-java maven . . kotlin/Char# +// ^^^^^ reference scip-java maven . . snapshots/Suit# +// ^^^^^^^ reference scip-java maven . . snapshots/Suit#entries. +// ^^^^^^^ reference scip-java maven . . snapshots/getEntries(). +// ^^^^ reference scip-java maven . . kotlin/collections/find(+9). +// ^^^^^^^^^^^^^^^^^^^^^^^ definition local 0 +// display_name it +// signature_documentation +// > it: Suit +// ^^ reference local 0 +// ^^^^^^ reference scip-java maven . . snapshots/Suit#symbol. +// ^^^^^^ reference scip-java maven . . snapshots/Suit#getSymbol(). +// ^^^^^^ reference scip-java maven . . snapshots/Suit#Companion#fromSymbol().(symbol) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#Companion#fromSymbol().(symbol) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#Companion#fromSymbol(). +// ⌃ enclosing_range_end local 0 + } +// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#Companion# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#Companion#``(). + } +//⌃ enclosing_range_end scip-java maven . . snapshots/Suit# +//⌃ enclosing_range_end scip-java maven . . snapshots/Suit#values(). +//⌃ enclosing_range_end scip-java maven . . snapshots/Suit#valueOf(). +//⌃ enclosing_range_end scip-java maven . . snapshots/Suit#valueOf().(value) +//⌃ enclosing_range_end scip-java maven . . snapshots/Suit#entries. +//⌃ enclosing_range_end scip-java maven . . snapshots/getEntries(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/describe(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/describe().(suit) + fun describe(suit: Suit): String = +// ^^^^^^^^ definition scip-java maven . . snapshots/describe(). +// display_name describe +// signature_documentation +// > public final fun describe(suit: Suit): String +// ^^^^ definition scip-java maven . . snapshots/describe().(suit) +// display_name suit +// signature_documentation +// > suit: Suit +// ^^^^ reference scip-java maven . . snapshots/Suit# +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ⌃ enclosing_range_end scip-java maven . . snapshots/describe().(suit) +// ⌄ enclosing_range_start local 1 + when (suit) { +// ^^^^ definition local 1 +// display_name +// signature_documentation +// > local val : Suit +// ^^^^ reference scip-java maven . . snapshots/describe().(suit) +// ⌃ enclosing_range_end local 1 + Suit.HEARTS -> "red" +// ^^^^^^ reference scip-java maven . . snapshots/Suit#HEARTS. + Suit.SPADES -> "black" +// ^^^^^^ reference scip-java maven . . snapshots/Suit#SPADES. + } +// ⌃ enclosing_range_end scip-java maven . . snapshots/describe(). + diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt new file mode 100644 index 000000000..f2731f886 --- /dev/null +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt @@ -0,0 +1,148 @@ + package snapshots +// ^^^^^^^^^ reference scip-java maven . . snapshots/ + +//⌄ enclosing_range_start scip-java maven . . snapshots/Container# +// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#[T] +// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#``().(items) +// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#items. +// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#getItems(). + class Container>(private val items: MutableList) { +// ^^^^^^^^^ definition scip-java maven . . snapshots/Container# +// display_name Container +// signature_documentation +// > public final class Container> : Any +// ^^^^^^^^^ definition scip-java maven . . snapshots/Container#``(). +// display_name Container +// signature_documentation +// > public constructor>(items: MutableList): Container +// ^ definition scip-java maven . . snapshots/Container#[T] +// display_name FirTypeParameterSymbol T +// signature_documentation +// > T : Comparable +// ^^^^^ definition scip-java maven . . snapshots/Container#``().(items) +// display_name items +// signature_documentation +// > items: MutableList +// ^^^^^ definition scip-java maven . . snapshots/Container#items. +// display_name items +// signature_documentation +// > private final val items: MutableList +// ^^^^^ reference scip-java maven . . snapshots/Container#``().(items) +// ^^^^^ definition scip-java maven . . snapshots/Container#getItems(). +// display_name items +// signature_documentation +// > private get(): MutableList +// ^^^^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/MutableList# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Container#[T] +// ⌃ enclosing_range_end scip-java maven . . snapshots/Container#``().(items) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Container#items. +// ⌃ enclosing_range_end scip-java maven . . snapshots/Container#getItems(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Container#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#add(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#add().(item) + fun add(item: T): Container { +// ^^^ definition scip-java maven . . snapshots/Container#add(). +// display_name add +// signature_documentation +// > public final fun add(item: T): Container +// ^^^^ definition scip-java maven . . snapshots/Container#add().(item) +// display_name item +// signature_documentation +// > item: T +// ^^^^^^^^^^^^ reference scip-java maven . . snapshots/Container# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Container#add().(item) + items.add(item) +// ^^^^^ reference scip-java maven . . snapshots/Container#items. +// ^^^^^ reference scip-java maven . . snapshots/Container#getItems(). +// ^^^ reference scip-java maven . . kotlin/collections/MutableList#add(). +// ^^^^ reference scip-java maven . . snapshots/Container#add().(item) + return this + } +// ⌃ enclosing_range_end scip-java maven . . snapshots/Container#add(). + +// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#mapItems(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#mapItems().[R] +// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#mapItems().(transform) + fun mapItems(transform: (T) -> R?): List = items.mapNotNull(transform) +// ^ definition scip-java maven . . snapshots/Container#mapItems().[R] +// display_name FirTypeParameterSymbol R +// signature_documentation +// > R : Any +// ^^^^^^^^ definition scip-java maven . . snapshots/Container#mapItems(). +// display_name mapItems +// signature_documentation +// > public final fun mapItems(transform: (T) -> R?): List +// ^^^^^^^^^ definition scip-java maven . . snapshots/Container#mapItems().(transform) +// display_name transform +// signature_documentation +// > transform: (T) -> R? +// ^^^^^^^^^ reference scip-java maven . . kotlin/Function1# +// ^^^^^^^ reference scip-java maven . . kotlin/collections/List# +// ^^^^^ reference scip-java maven . . snapshots/Container#items. +// ^^^^^ reference scip-java maven . . snapshots/Container#getItems(). +// ^^^^^^^^^^ reference scip-java maven . . kotlin/collections/mapNotNull(+1). +// ^^^^^^^^^ reference scip-java maven . . snapshots/Container#mapItems().(transform) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Container#mapItems().[R] +// ⌃ enclosing_range_end scip-java maven . . snapshots/Container#mapItems().(transform) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Container#mapItems(). + } +//⌃ enclosing_range_end scip-java maven . . snapshots/Container# + +//⌄ enclosing_range_start scip-java maven . . snapshots/firstOrSelf(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/firstOrSelf().[T] +// ⌄ enclosing_range_start scip-java maven . . snapshots/firstOrSelf().(items) +// ⌄ enclosing_range_start scip-java maven . . snapshots/firstOrSelf().(fallback) + fun firstOrSelf(items: List, fallback: T): T = items.firstOrNull() ?: fallback +// ^ definition scip-java maven . . snapshots/firstOrSelf().[T] +// display_name FirTypeParameterSymbol T +// signature_documentation +// > T +// ^^^^^^^^^^^ definition scip-java maven . . snapshots/firstOrSelf(). +// display_name firstOrSelf +// signature_documentation +// > public final fun firstOrSelf(items: List, fallback: T): T +// ^^^^^ definition scip-java maven . . snapshots/firstOrSelf().(items) +// display_name items +// signature_documentation +// > items: List +// ^^^^^^^ reference scip-java maven . . kotlin/collections/List# +// ^^^^^^^^ definition scip-java maven . . snapshots/firstOrSelf().(fallback) +// display_name fallback +// signature_documentation +// > fallback: T +// ^^^^^ reference scip-java maven . . snapshots/firstOrSelf().(items) +// ^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/firstOrNull(+19). +// ^^^^^^^^ reference scip-java maven . . snapshots/firstOrSelf().(fallback) +// ⌃ enclosing_range_end scip-java maven . . snapshots/firstOrSelf().[T] +// ⌃ enclosing_range_end scip-java maven . . snapshots/firstOrSelf().(items) +// ⌃ enclosing_range_end scip-java maven . . snapshots/firstOrSelf().(fallback) +// ⌃ enclosing_range_end scip-java maven . . snapshots/firstOrSelf(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/StringContainer# + typealias StringContainer = Container +// ^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/StringContainer# +// display_name FirTypeAliasSymbol snapshots/StringContainer +// signature_documentation +// > public final typealias StringContainer = Container +// > +// ⌃ enclosing_range_end scip-java maven . . snapshots/StringContainer# + +//⌄ enclosing_range_start scip-java maven . . snapshots/useContainer(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/useContainer().(container) + fun useContainer(container: StringContainer): StringContainer = container.add("hello") +// ^^^^^^^^^^^^ definition scip-java maven . . snapshots/useContainer(). +// display_name useContainer +// signature_documentation +// > public final fun useContainer(container: {snapshots/StringContainer=} Container): {snapshots/StringContainer=} Container +// ^^^^^^^^^ definition scip-java maven . . snapshots/useContainer().(container) +// display_name container +// signature_documentation +// > container: {snapshots/StringContainer=} Container +// ^^^^^^^^^^^^^^^ reference scip-java maven . . snapshots/Container# +// ^^^^^^^^^^^^^^^ reference scip-java maven . . snapshots/Container# +// ^^^^^^^^^ reference scip-java maven . . snapshots/useContainer().(container) +// ^^^ reference scip-java maven . . snapshots/Container#add(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/useContainer().(container) +// ⌃ enclosing_range_end scip-java maven . . snapshots/useContainer(). + From a65513c70ccfb1260adaba31e89558298e3c64db Mon Sep 17 00:00:00 2001 From: jupblb Date: Thu, 2 Jul 2026 12:17:24 +0200 Subject: [PATCH 05/33] Fix indexer divergences found in large-corpus A/B comparison --- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 37 ++++++++++++++----- .../kotlin_analysis/SignatureRenderer.kt | 13 +++++-- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index b76e45f86..66f2f335b 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -609,16 +609,33 @@ class KotlinAnalysisIndexer( // the symbol is derived from the destructured type and the entry position. val index = destructuring.entries.indexOf(declaration) destructuredClassId(destructuring)?.let { classId -> - addReference( - Symbol.createGlobal( - cache.classSymbol(classId), - ScipSymbolDescriptor( - ScipSymbolDescriptor.Kind.METHOD, - "component${index + 1}", - ), - ), - range, - ) + val name = "component${index + 1}" + // `componentN` may be a member (data classes, Pair) or an extension + // (e.g. Map.Entry's componentN lives in kotlin.collections). + val isMember = + with(session) { + findClass(classId) + ?.declaredMemberScope + ?.callables(Name.identifier(name)) + ?.any() == true + } + val symbol = + if (isMember) { + Symbol.createGlobal( + cache.classSymbol(classId), + ScipSymbolDescriptor(ScipSymbolDescriptor.Kind.METHOD, name), + ) + } else { + with(session) { + findTopLevelCallables(classId.packageFqName, Name.identifier(name)) + .firstOrNull { extension -> + (extension.receiverParameter?.returnType as? KaClassType) + ?.classId == classId + } + ?.let { cache.symbolForReference(session, it) } + } ?: Symbol.NONE + } + addReference(symbol, range) } addReference(cache.syntheticLocalSymbol(destructuring), range) } diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt index 7469d45c3..947e10cfa 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt @@ -41,6 +41,7 @@ internal class SignatureRenderer(private val session: KaSession) { declaration is KtClass && declaration.isEnum() -> "enum class" declaration is KtClass && declaration.isAnnotation() -> "annotation class" declaration is KtClass && declaration.isData() -> "data class" + declaration is KtClass && declaration.isInner() -> "inner class" else -> "class" } val name = declaration.name ?: "Companion" @@ -97,7 +98,7 @@ internal class SignatureRenderer(private val session: KaSession) { declaration.primaryConstructor?.valueParameters.orEmpty().joinToString(", ") { "${it.name.orEmpty()}: ${it.typeReference?.text ?: "Any"} = ..." } - return "public final fun copy($parameters): ${declaration.name.orEmpty()}\n" + return "public final fun copy($parameters): ${ownerName(declaration)}\n" } fun dataComponentSignature(index: Int, parameter: KtParameter): String = @@ -131,20 +132,24 @@ internal class SignatureRenderer(private val session: KaSession) { private fun annotationsPrefix(declaration: KtDeclaration): String = declaration.annotationEntries.joinToString("") { entry -> val name = entry.shortName?.asString().orEmpty() - if (entry.valueArgumentList != null) "@$name(...) " else "@$name " + val useSite = entry.useSiteTarget?.let { "${it.text}:" }.orEmpty() + val arguments = if (entry.valueArguments.isEmpty()) "()" else "(...)" + "$useSite@$name$arguments " } fun propertySignature(declaration: KtDeclaration): String { val name = (declaration as? org.jetbrains.kotlin.psi.KtNamedDeclaration)?.name.orEmpty() + val const = if (declaration.hasModifier(KtTokens.CONST_KEYWORD)) "const " else "" + val lateinit = if (declaration.hasModifier(KtTokens.LATEINIT_KEYWORD)) "lateinit " else "" val valOrVar = if (isVar(declaration)) "var" else "val" val type = explicitPropertyType(declaration) ?: renderedReturnType(declaration) ?: "Any" - return "${visibility(declaration)} ${memberModality(declaration)}$valOrVar $name: $type" + return "${visibility(declaration)} ${memberModality(declaration)}$const$lateinit$valOrVar $name: $type" } fun localVariableSignature(declaration: KtVariableDeclaration): String { val valOrVar = if (declaration.isVar) "var" else "val" val type = declaration.typeReference?.text ?: renderedReturnType(declaration) ?: "Any" - return "local $valOrVar ${declaration.name.orEmpty()}: $type" + return "${annotationsPrefix(declaration)}local $valOrVar ${declaration.name.orEmpty()}: $type" } fun accessorSignature(accessor: KtPropertyAccessor): String { From 19a2cb270e784f1ada8932bf86f9e0edaf8298c4 Mon Sep 17 00:00:00 2001 From: jupblb Date: Thu, 2 Jul 2026 12:44:49 +0200 Subject: [PATCH 06/33] Replace scip-kotlinc with scip-kotlin-analysis everywhere --- .github/workflows/ci.yaml | 10 +- CONTRIBUTING.md | 2 +- .../scip_java/buildlogic/ScipCompile.kt | 24 - gradle/libs.versions.toml | 10 - .../scip_java/gradle/ScipGradlePlugin.java | 89 +- scip-java/build.gradle.kts | 4 - .../org/scip_code/scip_java/Embedded.kt | 2 - .../scip_java/buildtools/GradleBuildTool.kt | 51 +- scip-kotlinc/build.gradle.kts | 47 - .../scip_java/kotlinc/AnalyzerCheckers.kt | 467 --- .../kotlinc/AnalyzerCommandLineProcessor.kt | 49 - .../kotlinc/AnalyzerFirExtensionRegistrar.kt | 11 - .../kotlinc/AnalyzerParamsProvider.kt | 22 - .../scip_java/kotlinc/AnalyzerRegistrar.kt | 34 - .../scip_code/scip_java/kotlinc/LineMap.kt | 39 - .../kotlinc/PostAnalysisExtension.kt | 96 - .../scip_java/kotlinc/ScipSymbols.kt | 70 - .../kotlinc/ScipTextDocumentBuilder.kt | 225 -- .../scip_java/kotlinc/ScipVisitor.kt | 191 -- .../scip_java/kotlinc/SymbolsCache.kt | 271 -- ...otlin.compiler.plugin.CommandLineProcessor | 1 - ...in.compiler.plugin.CompilerPluginRegistrar | 1 - .../scip_java/kotlinc/test/AnalyzerTest.kt | 3040 ----------------- .../scip_java/kotlinc/test/ScipBuilders.kt | 125 - .../scip_java/kotlinc/test/ScipSymbolsTest.kt | 798 ----- .../scip_code/scip_java/kotlinc/test/Utils.kt | 209 -- scip-snapshots/README.md | 2 +- .../cases/kotlin/common/build.gradle.kts | 50 +- .../src/main/kotlin/snapshots/Annotations.kt | 5 + .../common/src/main/kotlin/snapshots/Class.kt | 67 +- .../main/kotlin/snapshots/CompanionOwner.kt | 16 +- .../src/main/kotlin/snapshots/Docstrings.kt | 5 - .../common/src/main/kotlin/snapshots/Enums.kt | 45 +- .../src/main/kotlin/snapshots/Functions.kt | 2 - .../src/main/kotlin/snapshots/Generics.kt | 35 +- .../main/kotlin/snapshots/Implementations.kt | 37 +- .../src/main/kotlin/snapshots/Lambdas.kt | 36 +- .../src/main/kotlin/snapshots/ObjectKt.kt | 6 +- settings.gradle.kts | 1 - 39 files changed, 234 insertions(+), 5961 deletions(-) delete mode 100644 scip-kotlinc/build.gradle.kts delete mode 100644 scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerCheckers.kt delete mode 100644 scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerCommandLineProcessor.kt delete mode 100644 scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerFirExtensionRegistrar.kt delete mode 100644 scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerParamsProvider.kt delete mode 100644 scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerRegistrar.kt delete mode 100644 scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/LineMap.kt delete mode 100644 scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/PostAnalysisExtension.kt delete mode 100644 scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/ScipSymbols.kt delete mode 100644 scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/ScipTextDocumentBuilder.kt delete mode 100644 scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/ScipVisitor.kt delete mode 100644 scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/SymbolsCache.kt delete mode 100644 scip-kotlinc/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor delete mode 100644 scip-kotlinc/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar delete mode 100644 scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/AnalyzerTest.kt delete mode 100644 scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/ScipBuilders.kt delete mode 100644 scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/ScipSymbolsTest.kt delete mode 100644 scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/Utils.kt diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 536988c8b..e09d2b5ac 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -22,9 +22,9 @@ jobs: - run: gradle spotlessCheck --no-daemon - kotlin_plugin: + kotlin_indexer: runs-on: ubuntu-latest - name: scip-kotlinc + name: scip-kotlin-analysis steps: - uses: actions/checkout@v7 @@ -37,8 +37,8 @@ jobs: with: gradle-version: 9.8.2 - - name: scip-kotlinc tests - run: gradle :scip-kotlinc:test --no-daemon + - name: scip-kotlin-analysis tests + run: gradle :scip-kotlin-analysis:test --no-daemon docker_test: runs-on: ubuntu-latest @@ -69,7 +69,7 @@ jobs: ci-pass: if: always() - needs: [format, kotlin_plugin, docker_test] + needs: [format, kotlin_indexer, docker_test] runs-on: ubuntu-latest name: CI pass steps: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 85c7f09c1..ba814e94d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,7 +54,7 @@ These are the main components of the project. | ------------------------------------------------------- | -------- | ----------------------------------------------------------------------- | | `gradle test --no-daemon` | terminal | Run all Gradle tests. | | `gradle :scip-java:test --no-daemon` | terminal | Run CLI build-tool integration tests (Gradle, Maven, SCIP config). | -| `gradle :scip-kotlinc:test --no-daemon` | terminal | Run Kotlin compiler-plugin tests. | +| `gradle :scip-kotlin-analysis:test --no-daemon` | terminal | Run Kotlin indexer tests. | | `gradle :scip-snapshots:test --no-daemon` | terminal | Compare Java and Kotlin snapshot goldens. | | `gradle :scip-snapshots:saveSnapshots --no-daemon` | terminal | Regenerate Java and Kotlin snapshot goldens. | | `gradle :scip-java:installDist --no-daemon` | terminal | Build a local `scip-java` distribution under `scip-java/build/install/`. | diff --git a/build-logic/src/main/kotlin/org/scip_code/scip_java/buildlogic/ScipCompile.kt b/build-logic/src/main/kotlin/org/scip_code/scip_java/buildlogic/ScipCompile.kt index 079e9b877..7d766e266 100644 --- a/build-logic/src/main/kotlin/org/scip_code/scip_java/buildlogic/ScipCompile.kt +++ b/build-logic/src/main/kotlin/org/scip_code/scip_java/buildlogic/ScipCompile.kt @@ -4,7 +4,6 @@ import java.io.File import org.gradle.api.Task import org.gradle.api.artifacts.Configuration import org.gradle.api.file.Directory -import org.gradle.api.file.FileSystemLocation import org.gradle.api.provider.Provider import org.gradle.api.tasks.compile.JavaCompile @@ -27,29 +26,6 @@ fun JavaCompile.useScipJavac( (options.forkOptions.jvmArgs ?: emptyList()) + JavacInternals.jvmOptions(rootDir) } -/** - * Builds the `kotlinc` arguments that load the scip-kotlinc compiler plugin from [pluginClasspath] - * (the resolved shaded jar) and point it at [sourceroot]/[targetroot]. - * - * The mapping lives here, in compiled build logic, rather than in a build script: a `.map {}` - * lambda declared in a `.gradle.kts` file captures a hidden reference to the script object, which - * the configuration cache cannot serialize. - */ -fun scipKotlincPluginArgs( - pluginClasspath: Provider>, - sourceroot: String, - targetroot: String, -): Provider> = - pluginClasspath.map { locations -> - listOf( - "-Xplugin=${locations.single().asFile.absolutePath}", - "-P", - "plugin:scip-kotlinc:sourceroot=$sourceroot", - "-P", - "plugin:scip-kotlinc:targetroot=$targetroot", - ) - } - /** * Registers a `doFirst` action that empties [dir] (deletes then recreates it) before the task runs. * diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8096faaa1..0fc209663 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,8 +2,6 @@ clikt = "5.1.0" gradle-api = "8.11.1" junit-jupiter = "5.11.4" -kctfork = "0.14.0" -kotest = "6.2.6" kotlin = "2.4.21" # Kotlin Analysis API engine bundled by scip-kotlin-analysis. Independent from # the `kotlin` toolchain version: `-for-ide` artifacts are only published as @@ -30,8 +28,6 @@ caffeine = { module = "com.github.ben-manes.caffeine:caffeine", version = "2.9.3 clikt-jvm = { module = "com.github.ajalt.clikt:clikt-jvm", version.ref = "clikt" } gradle-api = { module = "dev.gradleplugins:gradle-api", version.ref = "gradle-api" } gradle-test-kit = { module = "dev.gradleplugins:gradle-test-kit", version.ref = "gradle-api" } -kctfork-core = { module = "dev.zacsweers.kctfork:core", version.ref = "kctfork" } -kotest-assertions-core = { module = "io.kotest:kotest-assertions-core-jvm", version.ref = "kotest" } kotlin-analysis-api-api = { module = "org.jetbrains.kotlin:analysis-api-for-ide", version.ref = "kotlin-analysis-api" } kotlin-analysis-api-impl-base = { module = "org.jetbrains.kotlin:analysis-api-impl-base-for-ide", version.ref = "kotlin-analysis-api" } kotlin-analysis-api-k2 = { module = "org.jetbrains.kotlin:analysis-api-k2-for-ide", version.ref = "kotlin-analysis-api" } @@ -40,12 +36,6 @@ kotlin-analysis-api-standalone = { module = "org.jetbrains.kotlin:analysis-api-s kotlin-analysis-compiler = { module = "org.jetbrains.kotlin:kotlin-compiler", version.ref = "kotlin-analysis-api" } kotlin-analysis-low-level-api-fir = { module = "org.jetbrains.kotlin:low-level-api-fir-for-ide", version.ref = "kotlin-analysis-api" } kotlin-analysis-symbol-light-classes = { module = "org.jetbrains.kotlin:symbol-light-classes-for-ide", version.ref = "kotlin-analysis-api" } -kotlin-compiler-embeddable = { module = "org.jetbrains.kotlin:kotlin-compiler-embeddable", version.ref = "kotlin" } -kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } -kotlin-scripting-common = { module = "org.jetbrains.kotlin:kotlin-scripting-common", version.ref = "kotlin" } -kotlin-scripting-dependencies = { module = "org.jetbrains.kotlin:kotlin-scripting-dependencies", version.ref = "kotlin" } -kotlin-scripting-dependencies-maven = { module = "org.jetbrains.kotlin:kotlin-scripting-dependencies-maven", version.ref = "kotlin" } -kotlin-scripting-jvm = { module = "org.jetbrains.kotlin:kotlin-scripting-jvm", version.ref = "kotlin" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlin-test-junit5 = { module = "org.jetbrains.kotlin:kotlin-test-junit5", version.ref = "kotlin" } diff --git a/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java b/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java index 8ad6ddfa7..1351a8b09 100644 --- a/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java +++ b/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java @@ -111,12 +111,9 @@ private void configureProject(Project project) { triggers.add("compileTestKotlin"); } - // The CLI's init script provides the path of the embedded scip-kotlinc jar. - Object scipKotlinc = requiredExtra(extraProperties, "scipKotlincJar"); project .getTasks() - .configureEach( - task -> configureKotlinCompileTask(task, scipKotlinc, sourceRoot, targetRoot)); + .configureEach(task -> configureKotlinCompileTask(task, sourceRoot, targetRoot)); } project.getTasks().create("scipCompileAll").dependsOn(triggers); @@ -172,39 +169,65 @@ private static boolean tryAddJavacPlugin(Project project, Object javacPluginDep) } } - private static void configureKotlinCompileTask( - Task task, Object scipKotlinc, String sourceRoot, String targetRoot) { + /** + * Kotlin sources are no longer indexed inside kotlinc: after each Kotlin compile task runs, its + * sources and classpath are dumped to {@code /kotlin-configs/.txt}, and the + * scip-java CLI indexes them with the standalone Analysis API indexer after the build. This + * removes any binary-compatibility coupling with the Kotlin compiler version of the build being + * indexed. + */ + private static void configureKotlinCompileTask(Task task, String sourceRoot, String targetRoot) { if (!task.getClass().getSimpleName().contains("KotlinCompile")) { return; } - // Referring to KotlinCompile directly here triggers NoClassDefFoundError - - // the plugin classpath is murky and we deliberately don't bundle the Kotlin - // Gradle plugin. So we commit the sins of reflection for our limited needs. - try { - Object kotlinOptions = task.getClass().getMethod("getKotlinOptions").invoke(task); - - @SuppressWarnings("unchecked") - List freeCompilerArgs = - (List) - kotlinOptions.getClass().getMethod("getFreeCompilerArgs").invoke(kotlinOptions); - - List newArgs = new ArrayList<>(freeCompilerArgs.size() + 5); - newArgs.addAll(freeCompilerArgs); - newArgs.add("-Xplugin=" + scipKotlinc); - newArgs.add("-P"); - newArgs.add("plugin:scip-kotlinc:sourceroot=" + sourceRoot); - newArgs.add("-P"); - newArgs.add("plugin:scip-kotlinc:targetroot=" + targetRoot); - - kotlinOptions - .getClass() - .getMethod("setFreeCompilerArgs", List.class) - .invoke(kotlinOptions, newArgs); - } catch (ReflectiveOperationException exc) { - throw new RuntimeException( - "scip-java: failed to configure Kotlin compile task '" + task.getName() + "'", exc); - } + task.doLast( + ignored -> { + try { + List lines = new ArrayList<>(); + lines.add("sourceroot " + sourceRoot); + + // Referring to KotlinCompile directly triggers NoClassDefFoundError - + // the plugin classpath is murky and we deliberately don't bundle the + // Kotlin Gradle plugin. So we commit the sins of reflection. + org.gradle.api.file.FileCollection libraries; + try { + libraries = + (org.gradle.api.file.FileCollection) + task.getClass().getMethod("getLibraries").invoke(task); + } catch (ReflectiveOperationException exc) { + libraries = + (org.gradle.api.file.FileCollection) + task.getClass().getMethod("getClasspath").invoke(task); + } + for (java.io.File entry : libraries.getFiles()) { + lines.add("classpath " + entry.getAbsolutePath()); + } + + int sources = 0; + for (java.io.File file : task.getInputs().getSourceFiles().getFiles()) { + if (file.getName().endsWith(".kt") || file.getName().endsWith(".kts")) { + lines.add("source " + file.getAbsolutePath()); + sources++; + } + } + if (sources == 0) { + return; + } + + java.nio.file.Path configDir = java.nio.file.Paths.get(targetRoot, "kotlin-configs"); + java.nio.file.Files.createDirectories(configDir); + String fileName = task.getPath().replaceAll("[^A-Za-z0-9]", "-") + ".txt"; + java.nio.file.Files.write(configDir.resolve(fileName), lines); + } catch (Exception exc) { + throw new RuntimeException( + "scip-java: failed to extract Kotlin compile configuration for task '" + + task.getName() + + "': " + + exc, + exc); + } + }); } private static Object requiredExtra(Map extraProperties, String name) { diff --git a/scip-java/build.gradle.kts b/scip-java/build.gradle.kts index ef86756b0..26111ee58 100644 --- a/scip-java/build.gradle.kts +++ b/scip-java/build.gradle.kts @@ -13,7 +13,6 @@ description = "Java and Kotlin indexer for SCIP" val javacShadowJar = shadowJarArtifact(":scip-javac", "javacShadowJar") val gradlePluginShadowJar = shadowJarArtifact(":scip-gradle-plugin", "gradlePluginShadowJar") -val kotlincShadowJar = shadowJarArtifact(":scip-kotlinc", "kotlincShadowJar") dependencies { implementation(project(":scip-aggregator")) @@ -42,9 +41,6 @@ val generateEmbeddedResources = tasks.register("generateEmbeddedResources" from(gradlePluginShadowJar) { rename { "gradle-plugin.jar" } } - from(kotlincShadowJar) { - rename { "scip-kotlinc.jar" } - } into(layout.buildDirectory.dir("generated/resources/embedded")) } diff --git a/scip-java/src/main/kotlin/org/scip_code/scip_java/Embedded.kt b/scip-java/src/main/kotlin/org/scip_code/scip_java/Embedded.kt index caf9b4f50..2d86b8074 100644 --- a/scip-java/src/main/kotlin/org/scip_code/scip_java/Embedded.kt +++ b/scip-java/src/main/kotlin/org/scip_code/scip_java/Embedded.kt @@ -25,8 +25,6 @@ object Embedded { fun gradlePluginJar(tmpDir: Path): Path = copyFile(tmpDir, "gradle-plugin.jar") - fun scipKotlincJar(tmpDir: Path): Path = copyFile(tmpDir, "scip-kotlinc.jar") - private fun javacErrorpath(tmp: Path): Path = tmp.resolve("errorpath.txt") data class CustomJavac(val executable: Path, val environment: Map) diff --git a/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/GradleBuildTool.kt b/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/GradleBuildTool.kt index 900287442..fee71085b 100644 --- a/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/GradleBuildTool.kt +++ b/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/GradleBuildTool.kt @@ -6,6 +6,7 @@ import java.nio.file.Path import java.nio.file.Paths import org.scip_code.scip_java.Embedded import org.scip_code.scip_java.commands.IndexCommand +import org.scip_code.scip_java.kotlin_analysis.KotlinAnalysisIndexer class GradleBuildTool(index: IndexCommand) : BuildTool("Gradle", index) { @@ -89,19 +90,64 @@ This means our SCIP compiler plugin was not attached to one or more JavaCompile cmd += "--no-daemon" cmd += "--init-script" cmd += script - cmd += "-Pkotlin.compiler.execution.strategy=in-process" cmd += "-Dscip.targetroot=${targetroot()}" cmd += index.finalBuildCommand(listOf("clean", "scipPrintDependencies", "scipCompileAll")) targetroot().toFile().deleteRecursively() val result = index.app.runProcess(cmd, env = mapOf("TERM" to "dumb")) + indexKotlinConfigs() return Embedded.reportUnexpectedJavacErrors(index.app.reporter, tmp) ?: result } + /** + * The Gradle plugin dumps each Kotlin compile task's sources and classpath under + * `/kotlin-configs/`; index them with the standalone Analysis API indexer. The + * Kotlin version of the indexed build is irrelevant here — the indexer bundles its own analysis + * engine. + */ + private fun indexKotlinConfigs() { + val configsDir = targetroot().resolve("kotlin-configs") + if (!Files.isDirectory(configsDir)) return + Files.list(configsDir).use { stream -> + for (config in stream.filter { it.fileName.toString().endsWith(".txt") }) { + try { + indexKotlinConfig(config) + } catch (e: Exception) { + index.app.reporter.error( + RuntimeException("failed to index Kotlin sources from $config", e) + ) + } + } + } + } + + private fun indexKotlinConfig(config: Path) { + var sourceroot: Path = index.workingDirectory + val classpath = mutableListOf() + val sources = mutableListOf() + for (line in Files.readAllLines(config)) { + val separator = line.indexOf(' ') + if (separator < 0) continue + val value = line.substring(separator + 1) + when (line.substring(0, separator)) { + "sourceroot" -> sourceroot = Paths.get(value) + "classpath" -> classpath.add(Paths.get(value)) + "source" -> sources.add(Paths.get(value)) + } + } + if (sources.isEmpty()) return + KotlinAnalysisIndexer( + sourceroot = sourceroot, + targetroot = targetroot(), + sourceRoots = sources, + classpath = classpath, + ) + .run() + } + private fun initScript(tmp: Path): Path { val pluginpath = Embedded.scipJar(tmp) val gradlePluginPath = Embedded.gradlePluginJar(tmp) - val scipKotlincPath = Embedded.scipKotlincJar(tmp) val dependenciesPath = targetroot().resolve("dependencies.txt") Files.deleteIfExists(dependenciesPath) @@ -119,7 +165,6 @@ This means our SCIP compiler plugin was not attached to one or more JavaCompile project.ext["scipTarget"] = "${targetroot()}" project.ext["javacPluginJar"] = "$pluginpath" project.ext["dependenciesOut"] = "$dependenciesPath" - project.ext["scipKotlincJar"] = "$scipKotlincPath" apply plugin: ScipGradlePlugin } """ diff --git a/scip-kotlinc/build.gradle.kts b/scip-kotlinc/build.gradle.kts deleted file mode 100644 index 0a10173c5..000000000 --- a/scip-kotlinc/build.gradle.kts +++ /dev/null @@ -1,47 +0,0 @@ -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - -plugins { - id("scip.java-library") - id("scip.kotlin-jvm") - id("scip.shadow-producer") - id("scip.maven-publish") -} - -description = "A kotlinc plugin to emit SCIP information" - -dependencies { - implementation(project(":scip-shared")) - implementation(libs.scip.kotlin.bindings) - compileOnly(libs.kotlin.stdlib) - compileOnly(libs.kotlin.compiler.embeddable) - - testImplementation(libs.kotlin.compiler.embeddable) - testImplementation(libs.kotlin.test) - testImplementation(libs.kotlin.test.junit5) - testImplementation(libs.kotlin.reflect) - testImplementation(libs.kotest.assertions.core) - testImplementation(libs.kctfork.core) -} - -tasks.named("test") { - maxHeapSize = "2g" - dependsOn("testKotlin240") -} - -// The plugin runs inside the indexed project's compiler, which can predate our build compiler. -val kotlin240TestRuntime = configurations.create("kotlin240TestRuntime") { - extendsFrom(configurations.testRuntimeClasspath.get()) - resolutionStrategy.force("org.jetbrains.kotlin:kotlin-compiler-embeddable:2.4.21") -} - -tasks.register("testKotlin240") { - useJUnitPlatform() - testClassesDirs = sourceSets.test.get().output.classesDirs - classpath = sourceSets.test.get().output + sourceSets.main.get().output + kotlin240TestRuntime - maxHeapSize = "2g" -} - -tasks.named("shadowJar") { - mergeServiceFiles() -} diff --git a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerCheckers.kt b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerCheckers.kt deleted file mode 100644 index 366752b21..000000000 --- a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerCheckers.kt +++ /dev/null @@ -1,467 +0,0 @@ -package org.scip_code.scip_java.kotlinc - -import java.nio.file.Path -import org.jetbrains.kotlin.* -import org.jetbrains.kotlin.com.intellij.lang.LighterASTNode -import org.jetbrains.kotlin.com.intellij.util.diff.FlyweightCapableTreeStructure -import org.jetbrains.kotlin.diagnostics.* -import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind -import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext -import org.jetbrains.kotlin.fir.analysis.checkers.declaration.* -import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers -import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessExpressionChecker -import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirResolvedQualifierChecker -import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirTypeOperatorCallChecker -import org.jetbrains.kotlin.fir.analysis.checkers.toClassLikeSymbol -import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension -import org.jetbrains.kotlin.fir.declarations.* -import org.jetbrains.kotlin.fir.declarations.utils.isCompanion -import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression -import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier -import org.jetbrains.kotlin.fir.expressions.FirTypeOperatorCall -import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference -import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticFunctionSymbol -import org.jetbrains.kotlin.fir.resolve.getContainingClassSymbol -import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider -import org.jetbrains.kotlin.fir.resolve.toClassLikeSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol -import org.jetbrains.kotlin.fir.types.FirTypeRef -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName - -open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtension(session) { - companion object { - val visitors: MutableMap = mutableMapOf() - - private fun getIdentifier(element: KtSourceElement): KtSourceElement = - element.treeStructure - .findChildByType(element.lighterASTNode, KtTokens.IDENTIFIER) - ?.toKtLightSourceElement(element.treeStructure) ?: element - - context(context: CheckerContext) - private fun ScipVisitor.emitTypeRef(typeRef: FirTypeRef) { - val klass = typeRef.toClassLikeSymbol(context.session) - val source = typeRef.source - if (klass != null && source != null && source.kind !is KtFakeSourceElementKind) { - visitClassReference(klass, getIdentifier(source)) - } - } - } - - override val declarationCheckers: DeclarationCheckers - get() = AnalyzerDeclarationCheckers(session.analyzerParamsProvider.sourceroot) - - override val expressionCheckers: ExpressionCheckers - get() = - object : ExpressionCheckers() { - override val qualifiedAccessExpressionCheckers: - Set = - setOf(SemanticQualifiedAccessExpressionChecker()) - - override val resolvedQualifierCheckers: Set = - setOf(SemanticResolvedQualifierChecker()) - - override val typeOperatorCallCheckers: Set = - setOf(SemanticClassReferenceExpressionChecker()) - } - - open class AnalyzerDeclarationCheckers(sourceroot: Path) : DeclarationCheckers() { - override val fileCheckers: Set = - setOf(SemanticFileChecker(sourceroot), SemanticImportsChecker()) - override val classLikeCheckers: Set = setOf(SemanticClassLikeChecker()) - override val constructorCheckers: Set = - setOf(SemanticConstructorChecker()) - // The specialized checker property was renamed in Kotlin 2.4.20. - override val functionCheckers: Set = - setOf(SemanticSimpleFunctionChecker()) - override val anonymousFunctionCheckers: Set = - setOf(SemanticAnonymousFunctionChecker()) - override val propertyCheckers: Set = setOf(SemanticPropertyChecker()) - override val valueParameterCheckers: Set = - setOf(SemanticValueParameterChecker()) - override val typeParameterCheckers: Set = - setOf(SemanticTypeParameterChecker()) - override val typeAliasCheckers: Set = setOf(SemanticTypeAliasChecker()) - override val propertyAccessorCheckers: Set = - setOf(SemanticPropertyAccessorChecker()) - override val enumEntryCheckers: Set = setOf(SemanticEnumEntryChecker()) - } - - private class SemanticFileChecker(private val sourceroot: Path) : - FirFileChecker(MppCheckerKind.Common) { - companion object { - val globals = GlobalSymbolsCache() - } - - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirFile) { - val ktFile = declaration.sourceFile ?: return - val lineMap = LineMap(declaration) - val visitor = ScipVisitor(sourceroot, ktFile, lineMap, globals) - visitors[ktFile] = visitor - } - } - - class SemanticImportsChecker : FirFileChecker(MppCheckerKind.Common) { - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirFile) { - val ktFile = declaration.sourceFile ?: return - val visitor = visitors[ktFile] - - val eachFqNameElement = - { - fqName: FqName, - tree: FlyweightCapableTreeStructure, - names: LighterASTNode, - callback: (FqName, KtLightSourceElement) -> Unit -> - val nameList = - if (names.tokenType == KtNodeTypes.REFERENCE_EXPRESSION) listOf(names) - else tree.collectDescendantsOfType(names, KtNodeTypes.REFERENCE_EXPRESSION) - - var ancestor = fqName - var depth = 0 - - while (ancestor != FqName.ROOT) { - val nameNode = nameList[nameList.lastIndex - depth] - val nameSource = nameNode.toKtLightSourceElement(tree) - - callback(ancestor, nameSource) - - ancestor = ancestor.parent() - depth++ - } - } - - val packageDirective = declaration.packageDirective - val fqName = packageDirective.packageFqName - val source = packageDirective.source - if (source != null) { - val names = - source.treeStructure.findChildByType( - source.lighterASTNode, - KtNodeTypes.DOT_QUALIFIED_EXPRESSION, - ) - ?: source.treeStructure.findChildByType( - source.lighterASTNode, - KtNodeTypes.REFERENCE_EXPRESSION, - ) - - if (names != null) { - eachFqNameElement(fqName, source.treeStructure, names) { fqName, name -> - visitor?.visitPackage(fqName, name) - } - } - } - - declaration.imports.forEach { import -> - val source = import.source ?: return@forEach - val fqName = import.importedFqName ?: return@forEach - - val names = - source.treeStructure.findDescendantByType( - source.lighterASTNode, - KtNodeTypes.DOT_QUALIFIED_EXPRESSION, - ) - if (names != null) { - eachFqNameElement(fqName, source.treeStructure, names) { fqName, name -> - val symbolProvider = context.session.symbolProvider - - val klass = - symbolProvider.getClassLikeSymbolByClassId(ClassId.topLevel(fqName)) - val callables = - symbolProvider.getTopLevelCallableSymbols( - fqName.parent(), - fqName.shortName(), - ) - - if (klass != null) { - visitor?.visitClassReference(klass, name) - } else if (callables.isNotEmpty()) { - for (callable in callables) { - visitor?.visitCallableReference(callable, name) - } - } else { - visitor?.visitPackage(fqName, name) - } - } - } - } - } - } - - private class SemanticClassLikeChecker : FirClassLikeChecker(MppCheckerKind.Common) { - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirClassLikeDeclaration) { - val source = declaration.source ?: return - val ktFile = context.containingFileSymbol?.sourceFile ?: return - val visitor = visitors[ktFile] - val objectKeyword = - if (declaration is FirAnonymousObject) { - source.treeStructure - .findChildByType(source.lighterASTNode, KtTokens.OBJECT_KEYWORD) - ?.toKtLightSourceElement(source.treeStructure) - } else { - null - } - val identifierSource = getIdentifier(source) - // For unnamed companion objects, getIdentifier() falls back to source (no IDENTIFIER - // token). Use the 'companion' keyword as the range instead. The COMPANION_KEYWORD is - // inside a MODIFIER_LIST child, so we use findDescendantByType instead of - // findChildByType. - val companionKeyword = - if ( - identifierSource === source && - declaration is FirRegularClass && - declaration.isCompanion - ) { - source.treeStructure - .findDescendantByType(source.lighterASTNode, KtTokens.COMPANION_KEYWORD) - ?.toKtLightSourceElement(source.treeStructure) - } else { - null - } - visitor?.visitClassOrObject( - declaration, - objectKeyword ?: companionKeyword ?: identifierSource, - enclosingSource = source, - ) - - if (declaration is FirClass) { - for (superType in declaration.superTypeRefs) { - visitor?.emitTypeRef(superType) - } - } - } - } - - private class SemanticConstructorChecker : FirConstructorChecker(MppCheckerKind.Common) { - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirConstructor) { - val source = declaration.source ?: return - val ktFile = context.containingFileSymbol?.sourceFile ?: return - val visitor = visitors[ktFile] - - if (declaration.isPrimary) { - // if the constructor is not denoted by the 'constructor' keyword, we want to link - // it to the - // class identifier - val klass = declaration.symbol.getContainingClassSymbol() - val klassSource = klass?.source ?: source - val constructorKeyboard = - source.treeStructure - .findChildByType(source.lighterASTNode, KtTokens.CONSTRUCTOR_KEYWORD) - ?.toKtLightSourceElement(source.treeStructure) - - val objectKeyword = - if (klass is FirAnonymousObjectSymbol) { - source.treeStructure - .findChildByType(source.lighterASTNode, KtTokens.OBJECT_KEYWORD) - ?.toKtLightSourceElement(source.treeStructure) - } else { - null - } - - visitor?.visitPrimaryConstructor( - declaration, - constructorKeyboard ?: objectKeyword ?: getIdentifier(klassSource), - enclosingSource = source, - ) - } else { - visitor?.visitSecondaryConstructor( - declaration, - getIdentifier(source), - enclosingSource = source, - ) - } - } - } - - private class SemanticSimpleFunctionChecker : FirFunctionChecker(MppCheckerKind.Common) { - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirFunction) { - if (declaration !is FirNamedFunction) return - val source = declaration.source ?: return - val ktFile = context.containingFileSymbol?.sourceFile ?: return - val visitor = visitors[ktFile] - visitor?.visitNamedFunction( - declaration, - getIdentifier(source), - enclosingSource = source, - ) - visitor?.emitTypeRef(declaration.returnTypeRef) - declaration.receiverParameter?.typeRef?.let { visitor?.emitTypeRef(it) } - } - } - - private class SemanticAnonymousFunctionChecker : - FirAnonymousFunctionChecker(MppCheckerKind.Common) { - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirAnonymousFunction) { - val source = declaration.source ?: return - val ktFile = context.containingFileSymbol?.sourceFile ?: return - val visitor = visitors[ktFile] - visitor?.visitNamedFunction(declaration, source, enclosingSource = source) - } - } - - private class SemanticPropertyChecker : FirPropertyChecker(MppCheckerKind.Common) { - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirProperty) { - val source = declaration.source ?: return - val ktFile = context.containingFileSymbol?.sourceFile ?: return - val visitor = visitors[ktFile] - visitor?.visitProperty(declaration, getIdentifier(source), enclosingSource = source) - visitor?.emitTypeRef(declaration.returnTypeRef) - declaration.receiverParameter?.typeRef?.let { visitor?.emitTypeRef(it) } - } - } - - private class SemanticValueParameterChecker : FirValueParameterChecker(MppCheckerKind.Common) { - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirValueParameter) { - val source = declaration.source ?: return - val ktFile = context.containingFileSymbol?.sourceFile ?: return - val visitor = visitors[ktFile] - visitor?.visitParameter(declaration, getIdentifier(source), enclosingSource = source) - visitor?.emitTypeRef(declaration.returnTypeRef) - } - } - - private class SemanticTypeParameterChecker : FirTypeParameterChecker(MppCheckerKind.Common) { - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirTypeParameter) { - val source = declaration.source ?: return - val ktFile = context.containingFileSymbol?.sourceFile ?: return - val visitor = visitors[ktFile] - visitor?.visitTypeParameter( - declaration, - getIdentifier(source), - enclosingSource = source, - ) - } - } - - private class SemanticTypeAliasChecker : FirTypeAliasChecker(MppCheckerKind.Common) { - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirTypeAlias) { - val source = declaration.source ?: return - val ktFile = context.containingFileSymbol?.sourceFile ?: return - val visitor = visitors[ktFile] - visitor?.visitTypeAlias(declaration, getIdentifier(source), enclosingSource = source) - } - } - - private class SemanticPropertyAccessorChecker : - FirPropertyAccessorChecker(MppCheckerKind.Common) { - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirPropertyAccessor) { - val source = declaration.source ?: return - val ktFile = context.containingFileSymbol?.sourceFile ?: return - val visitor = visitors[ktFile] - val identifierSource = - if (declaration.isGetter) { - source.treeStructure - .findChildByType(source.lighterASTNode, KtTokens.GET_KEYWORD) - ?.toKtLightSourceElement(source.treeStructure) ?: getIdentifier(source) - } else if (declaration.isSetter) { - source.treeStructure - .findChildByType(source.lighterASTNode, KtTokens.SET_KEYWORD) - ?.toKtLightSourceElement(source.treeStructure) ?: getIdentifier(source) - } else { - getIdentifier(source) - } - - visitor?.visitPropertyAccessor(declaration, identifierSource, enclosingSource = source) - } - } - - private class SemanticEnumEntryChecker : FirEnumEntryChecker(MppCheckerKind.Common) { - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirEnumEntry) { - val source = declaration.source ?: return - val ktFile = context.containingFileSymbol?.sourceFile ?: return - val visitor = visitors[ktFile] - visitor?.visitEnumEntry(declaration, getIdentifier(source), enclosingSource = source) - } - } - - private class SemanticResolvedQualifierChecker : - FirResolvedQualifierChecker(MppCheckerKind.Common) { - companion object { - // Resolve once against the host compiler: symbol became qualifierSymbol in 2.4.20. - private val symbolGetter = - try { - FirResolvedQualifier::class.java.getMethod("getQualifierSymbol") - } catch (_: NoSuchMethodException) { - FirResolvedQualifier::class.java.getMethod("getSymbol") - } - } - - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(expression: FirResolvedQualifier) { - val symbol = symbolGetter.invoke(expression) as FirClassLikeSymbol<*>? ?: return - val source = expression.source ?: return - if (source.kind is KtFakeSourceElementKind) return - val ktFile = context.containingFileSymbol?.sourceFile ?: return - val visitor = visitors[ktFile] - visitor?.visitClassReference(symbol, getIdentifier(source)) - } - } - - private class SemanticQualifiedAccessExpressionChecker : - FirQualifiedAccessExpressionChecker(MppCheckerKind.Common) { - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(expression: FirQualifiedAccessExpression) { - val source = expression.source ?: return - val calleeReference = expression.calleeReference - if ((calleeReference as? FirResolvedNamedReference) == null) { - return - } - - val ktFile = context.containingFileSymbol?.sourceFile ?: return - val visitor = visitors[ktFile] - val identifierSource = getIdentifier(calleeReference.source ?: source) - visitor?.visitSimpleNameExpression(calleeReference, identifierSource) - - val resolvedSymbol = calleeReference.resolvedSymbol - if ( - resolvedSymbol.origin == FirDeclarationOrigin.SamConstructor && - resolvedSymbol is FirSyntheticFunctionSymbol - ) { - val referencedKlass = resolvedSymbol.resolvedReturnType.toClassLikeSymbol() - if (referencedKlass != null) { - visitor?.visitClassReference(referencedKlass, identifierSource) - } - } - - // When encountering a reference to a property symbol, emit both getter and setter - // symbols - if (resolvedSymbol is FirPropertySymbol) { - resolvedSymbol.getterSymbol?.let { - visitor?.visitCallableReference(it, identifierSource) - } - resolvedSymbol.setterSymbol?.let { - visitor?.visitCallableReference(it, identifierSource) - } - } - } - } - - private class SemanticClassReferenceExpressionChecker : - FirTypeOperatorCallChecker(MppCheckerKind.Common) { - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(expression: FirTypeOperatorCall) { - val typeRef = expression.conversionTypeRef - val source = typeRef.source ?: return - val classSymbol = typeRef.toClassLikeSymbol(context.session) ?: return - val ktFile = context.containingFileSymbol?.sourceFile ?: return - val visitor = visitors[ktFile] - - visitor?.visitClassReference(classSymbol, getIdentifier(source)) - } - } -} diff --git a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerCommandLineProcessor.kt b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerCommandLineProcessor.kt deleted file mode 100644 index 601511099..000000000 --- a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerCommandLineProcessor.kt +++ /dev/null @@ -1,49 +0,0 @@ -package org.scip_code.scip_java.kotlinc - -import java.nio.file.Path -import java.nio.file.Paths -import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption -import org.jetbrains.kotlin.compiler.plugin.CliOption -import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor -import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.config.CompilerConfigurationKey - -const val VAL_SOURCES = "sourceroot" -val KEY_SOURCES = CompilerConfigurationKey(VAL_SOURCES) - -const val VAL_TARGET = "targetroot" -val KEY_TARGET = CompilerConfigurationKey(VAL_TARGET) - -const val PLUGIN_ID = "scip-kotlinc" - -@OptIn(ExperimentalCompilerApi::class) -class AnalyzerCommandLineProcessor : CommandLineProcessor { - override val pluginId: String = PLUGIN_ID - override val pluginOptions: Collection = - listOf( - CliOption( - VAL_SOURCES, - "", - "the absolute path to the root of the Kotlin sources", - required = true, - ), - CliOption( - VAL_TARGET, - "", - "the absolute path to the directory where to generate SCIP files.", - required = true, - ), - ) - - override fun processOption( - option: AbstractCliOption, - value: String, - configuration: CompilerConfiguration, - ) { - when (option.optionName) { - VAL_SOURCES -> configuration.put(KEY_SOURCES, Paths.get(value)) - VAL_TARGET -> configuration.put(KEY_TARGET, Paths.get(value)) - } - } -} diff --git a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerFirExtensionRegistrar.kt b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerFirExtensionRegistrar.kt deleted file mode 100644 index e0d6271de..000000000 --- a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerFirExtensionRegistrar.kt +++ /dev/null @@ -1,11 +0,0 @@ -package org.scip_code.scip_java.kotlinc - -import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar -import org.scip_code.scip_java.shared.ScipOptions - -class AnalyzerFirExtensionRegistrar(private val options: ScipOptions) : FirExtensionRegistrar() { - override fun ExtensionRegistrarContext.configurePlugin() { - +AnalyzerParamsProvider.getFactory(options) - +::AnalyzerCheckers - } -} diff --git a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerParamsProvider.kt b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerParamsProvider.kt deleted file mode 100644 index 38f64ce2a..000000000 --- a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerParamsProvider.kt +++ /dev/null @@ -1,22 +0,0 @@ -package org.scip_code.scip_java.kotlinc - -import java.nio.file.Path -import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent -import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent.Factory -import org.scip_code.scip_java.shared.ScipOptions - -open class AnalyzerParamsProvider(session: FirSession, val options: ScipOptions) : - FirExtensionSessionComponent(session) { - val sourceroot: Path - get() = options.sourceroot - - companion object { - fun getFactory(options: ScipOptions): Factory { - return Factory { AnalyzerParamsProvider(it, options) } - } - } -} - -val FirSession.analyzerParamsProvider: AnalyzerParamsProvider by - FirSession.sessionComponentAccessor() diff --git a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerRegistrar.kt b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerRegistrar.kt deleted file mode 100644 index 5833d8883..000000000 --- a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/AnalyzerRegistrar.kt +++ /dev/null @@ -1,34 +0,0 @@ -package org.scip_code.scip_java.kotlinc - -import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension -import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar -import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter -import org.scip_code.scip.Document -import org.scip_code.scip_java.shared.ScipOptions - -@OptIn(ExperimentalCompilerApi::class) -class AnalyzerRegistrar(private val callback: (Document) -> Unit = {}) : CompilerPluginRegistrar() { - override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) { - val options = - ScipOptions().apply { - sourceroot = configuration[KEY_SOURCES]!! - targetroot = configuration[KEY_TARGET]!! - } - 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 -} diff --git a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/LineMap.kt b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/LineMap.kt deleted file mode 100644 index afd51bd0a..000000000 --- a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/LineMap.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.scip_code.scip_java.kotlinc - -import org.jetbrains.kotlin.KtSourceElement -import org.jetbrains.kotlin.com.intellij.navigation.NavigationItem -import org.jetbrains.kotlin.fir.declarations.FirFile -import org.jetbrains.kotlin.text - -/** Maps between an element and its identifier positions */ -class LineMap(private val file: FirFile) { - private fun offsetToLineAndCol(offset: Int): Pair? = - file.sourceFileLinesMapping?.getLineAndColumnByOffset(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 0-based column number for a given offset. */ - fun columnForOffset(offset: Int): Int = offsetToLineAndCol(offset)?.second ?: 0 - - /** Returns the 0-based start character. */ - fun startCharacter(element: KtSourceElement): Int = - offsetToLineAndCol(element.startOffset)?.second ?: 0 - - /** Returns the 0-based end character. */ - fun endCharacter(element: KtSourceElement): Int = - startCharacter(element) + nameForOffset(element).length - - /** Returns the 1-based line number (subtract 1 for protobuf). */ - fun lineNumber(element: KtSourceElement): Int = - file.sourceFileLinesMapping?.getLineByOffset(element.startOffset)?.let { it + 1 } ?: 0 - - companion object { - fun nameForOffset(element: KtSourceElement): String = - when (element) { - is NavigationItem -> element.name ?: element.text?.toString() ?: "" - else -> element.text?.toString() ?: "" - } - } -} diff --git a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/PostAnalysisExtension.kt b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/PostAnalysisExtension.kt deleted file mode 100644 index 211ca221e..000000000 --- a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/PostAnalysisExtension.kt +++ /dev/null @@ -1,96 +0,0 @@ -package org.scip_code.scip_java.kotlinc - -import java.io.PrintWriter -import java.io.Writer -import java.nio.file.Path -import java.nio.file.Paths -import org.jetbrains.kotlin.KtSourceFile -import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension -import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity -import org.jetbrains.kotlin.cli.common.messages.MessageRenderer -import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector -import org.jetbrains.kotlin.config.CommonConfigurationKeys -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.config.MessageCollectorAccess -import org.jetbrains.kotlin.ir.declarations.IrModuleFragment -import org.scip_code.scip.Document -import org.scip_code.scip_java.shared.ScipShardPaths -import org.scip_code.scip_java.shared.ScipShardWriter - -/** - * Writes per-source SCIP shards once the FIR checkers have finished and the IR phase begins. - * - *

For each source file [AnalyzerCheckers] registered a [ScipVisitor] for, this builds the file's - * [Document] and serializes it under `/META-INF/scip/.scip`. Files - * 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, -) : IrGenerationExtension { - override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) { - try { - for ((ktSourceFile, visitor) in AnalyzerCheckers.visitors) { - try { - val document = visitor.build() - scipShardPathForFile(ktSourceFile)?.let { outPath -> - ScipShardWriter.writeShard(outPath, document) - } - callback(document) - } catch (e: Exception) { - handleException(e) - } - } - } catch (e: Exception) { - handleException(e) - } - AnalyzerCheckers.visitors.clear() - } - - private fun scipShardPathForFile(file: KtSourceFile): Path? { - val normalizedPath = Paths.get(file.path).normalize() - val outPath = ScipShardPaths.shardPath(targetRoot, sourceRoot, normalizedPath) - if (outPath.isPresent) { - return outPath.get() - } - System.err.println( - "given file is not under the sourceroot.\n\tSourceroot: $sourceRoot\n\tFile path: ${file.path}\n\tNormalized file path: $normalizedPath" - ) - return null - } - - @OptIn(MessageCollectorAccess::class) - private val messageCollector = - configuration.get( - CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY, - PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false), - ) - - private fun handleException(e: Exception) { - val writer = - PrintWriter( - object : Writer() { - val buf = StringBuffer() - - override fun close() = - messageCollector.report(CompilerMessageSeverity.WARNING, buf.toString()) - - override fun flush() = Unit - - override fun write(data: CharArray, offset: Int, len: Int) { - buf.append(data, offset, len) - } - }, - false, - ) - writer.println("Exception in scip-kotlin compiler plugin:") - e.printStackTrace(writer) - writer.println( - "Please report a bug to https://github.com/scip-code/scip-java with the stack trace above." - ) - writer.close() - } -} diff --git a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/ScipSymbols.kt b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/ScipSymbols.kt deleted file mode 100644 index b3ffb4520..000000000 --- a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/ScipSymbols.kt +++ /dev/null @@ -1,70 +0,0 @@ -package org.scip_code.scip_java.kotlinc - -import org.scip_code.scip_java.shared.ScipSymbols as SharedSymbols - -@JvmInline -value class Symbol(private val symbol: String) { - companion object { - val NONE = Symbol(SharedSymbols.NONE) - val ROOT_PACKAGE = Symbol(SharedSymbols.ROOT_PACKAGE) - - // Note: this intentionally diverges from `SharedSymbols.global` when - // `desc == NONE` — Java returns `NONE`, Kotlin returns the owner. - // SymbolsCache relies on this behavior; do not delegate without first - // updating those call sites. - fun createGlobal(owner: Symbol, desc: ScipSymbolDescriptor): Symbol = - when { - desc == ScipSymbolDescriptor.NONE -> owner - owner != ROOT_PACKAGE -> Symbol(owner.symbol + desc.encode().symbol) - else -> desc.encode() - } - - fun createLocal(i: Int) = Symbol(SharedSymbols.local(i)) - } - - fun isGlobal() = SharedSymbols.isGlobal(symbol) - - fun isLocal() = SharedSymbols.isLocal(symbol) - - override fun toString(): String = symbol -} - -fun String.symbol(): Symbol = Symbol(this) - -data class ScipSymbolDescriptor( - val kind: Kind, - val name: String, - // Default differs from `SharedSymbols.Descriptor` (which is "") because - // Kotlin call sites — getters/setters in particular — rely on the no-arg - // overload producing `name().` rather than `name.` for METHOD kinds. - val disambiguator: String = "()", -) { - companion object { - val NONE = ScipSymbolDescriptor(Kind.NONE, "") - } - - enum class Kind { - NONE, - TERM, - METHOD, - TYPE, - PACKAGE, - PARAMETER, - TYPE_PARAMETER; - - internal fun toSharedKind(): SharedSymbols.Descriptor.Kind = - when (this) { - NONE -> SharedSymbols.Descriptor.Kind.None - TERM -> SharedSymbols.Descriptor.Kind.Term - METHOD -> SharedSymbols.Descriptor.Kind.Method - TYPE -> SharedSymbols.Descriptor.Kind.Type - PACKAGE -> SharedSymbols.Descriptor.Kind.Package - PARAMETER -> SharedSymbols.Descriptor.Kind.Parameter - TYPE_PARAMETER -> SharedSymbols.Descriptor.Kind.TypeParameter - } - } - - fun encode(): Symbol = - if (kind == Kind.NONE) Symbol(SharedSymbols.NONE) - else Symbol(SharedSymbols.Descriptor(kind.toSharedKind(), name, disambiguator).encode()) -} diff --git a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/ScipTextDocumentBuilder.kt b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/ScipTextDocumentBuilder.kt deleted file mode 100644 index 4e904b3ef..000000000 --- a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/ScipTextDocumentBuilder.kt +++ /dev/null @@ -1,225 +0,0 @@ -package org.scip_code.scip_java.kotlinc - -import java.nio.file.Path -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 -import org.jetbrains.kotlin.fir.symbols.impl.* -import org.jetbrains.kotlin.fir.types.impl.FirImplicitAnyTypeRef -import org.jetbrains.kotlin.lexer.KtTokens -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 -import org.scip_code.scip.symbolInformation -import org.scip_code.scip_java.shared.ScipDocumentBuilder -import org.scip_code.scip_java.shared.ScipRange -import org.scip_code.scip_java.shared.ScipShardPaths - -/** Builds a SCIP [Document] for a single Kotlin source file. */ -class ScipTextDocumentBuilder( - private val sourceroot: Path, - private val file: KtSourceFile, - private val lineMap: LineMap, - private val cache: SymbolsCache, -) { - private val documentBuilder = ScipDocumentBuilder() - private val fileText = file.getContentsAsStream().reader().readText() - - fun build(): Document = documentBuilder.build("kotlin", relativePath(), fileText) - - context(context: CheckerContext) - fun emitScipData( - firBasedSymbol: FirBasedSymbol<*>?, - symbol: Symbol, - element: KtSourceElement, - isDefinition: Boolean, - enclosingSource: KtSourceElement? = null, - ) { - documentBuilder.addOccurrence(occurrence(symbol, element, isDefinition, enclosingSource)) - if (isDefinition) { - documentBuilder.addSymbol(symbolInformation(firBasedSymbol, symbol, element)) - } - } - - @OptIn(SymbolInternals::class) - context(context: CheckerContext) - private fun symbolInformation( - firBasedSymbol: FirBasedSymbol<*>?, - symbol: Symbol, - element: KtSourceElement, - ): SymbolInformation { - val supers = - when (firBasedSymbol) { - is FirClassSymbol -> - firBasedSymbol.resolvedSuperTypeRefs - .filter { it !is FirImplicitAnyTypeRef } - .mapNotNull { it.toClassLikeSymbol(firBasedSymbol.moduleData.session) } - .flatMap { cache[it] } - is FirFunctionSymbol<*> -> - firBasedSymbol.directOverriddenSymbolsSafe().flatMap { cache[it] } - else -> emptyList() - } - return symbolInformation { - this.symbol = symbol.toString() - this.displayName = - if (firBasedSymbol != null) displayName(firBasedSymbol) else element.text.toString() - if (firBasedSymbol != null) { - renderSignature(firBasedSymbol.fir)?.let { rendered -> - signatureDocumentation = signature { - language = "kotlin" - text = rendered - } - } - 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() - isImplementation = true - } - } - } - } - - private fun occurrence( - symbol: Symbol, - element: KtSourceElement, - isDefinition: Boolean, - enclosingSource: KtSourceElement?, - ): Occurrence { - val builder = Occurrence.newBuilder().setSymbol(symbol.toString()) - if (isDefinition) builder.setSymbolRoles(SymbolRole.Definition.number) - val range = range(element) - if (range.isSingleLine) builder.singleLineRange = range.toSingleLineRange() - else builder.multiLineRange = range.toMultiLineRange() - if (enclosingSource != null) { - val enclosingRange = enclosingRange(enclosingSource) - if (enclosingRange.isSingleLine) { - builder.singleLineEnclosingRange = enclosingRange.toSingleLineRange() - } else { - builder.multiLineEnclosingRange = enclosingRange.toMultiLineRange() - } - } - return builder.build() - } - - private fun range(element: KtSourceElement): ScipRange { - val line = lineMap.lineNumber(element) - 1 - val startCol = lineMap.startCharacter(element) - val endCol = lineMap.endCharacter(element) - return ScipRange.singleLine(line, startCol, endCol) - } - - private fun enclosingRange(element: KtSourceElement): ScipRange { - val startLine = lineMap.lineNumber(element) - 1 - val startCol = lineMap.startCharacter(element) - val endLine = lineMap.lineNumberForOffset(element.endOffset) - 1 - val endCol = lineMap.columnForOffset(element.endOffset) - return ScipRange.range(startLine, startCol, endLine, endCol) - } - - private fun relativePath(): String = - ScipShardPaths.relativePath(sourceroot, Paths.get(file.path)) - - /** - * Renders [element] as a Kotlin signature using [FirRenderer]'s readability preset, with kdoc - * stripped (kdoc is exposed separately via [SymbolInformation.documentation]). - */ - private fun renderSignature(element: FirElement): String? { - val renderer = - FirRenderer( - typeRenderer = ConeTypeRenderer(), - idRenderer = ConeIdShortRenderer(), - classMemberRenderer = FirNoClassMemberRenderer(), - bodyRenderer = null, - propertyAccessorRenderer = null, - callArgumentsRenderer = FirCallNoArgumentsRenderer(), - modifierRenderer = FirAllModifierRenderer(FirModifierRenderer.StaticPolicy.Default), - callableSignatureRenderer = FirCallableSignatureRendererForReadability(), - declarationRenderer = FirDeclarationRenderer("local "), - ) - val rendered = renderer.renderElementAsString(element) - return if (rendered.isEmpty()) null else rendered - } - - private fun docComment(element: FirElement): String? { - val kdoc = element.source?.getChild(KtTokens.DOC_COMMENT)?.text?.toString() ?: return null - 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 - val out = StringBuilder() - var first = true - kdoc.lineSequence().forEach { line -> - if (line.isEmpty()) return@forEach - var start = 0 - while (start < line.length && line[start].isWhitespace()) start++ - if (start < line.length && line[start] == '/') start++ - while (start < line.length && line[start] == '*') start++ - var end = line.length - 1 - if (end > start && line[end] == '/') end-- - while (end > start && line[end] == '*') end-- - while (end > start && line[end].isWhitespace()) end-- - start = minOf(start, line.length - 1) - if (end > start) end++ - if (!first) out.append('\n') - out.append(line, start, end) - first = false - } - return out.toString().trim() - } - - companion object { - @OptIn(SymbolInternals::class, RenderingInternals::class) - private fun displayName(firBasedSymbol: FirBasedSymbol<*>): String = - when (firBasedSymbol) { - is FirClassLikeSymbol -> firBasedSymbol.classId.shortClassName.asString() - is FirPropertyAccessorSymbol -> firBasedSymbol.fir.propertySymbol.name.asString() - is FirFunctionSymbol -> firBasedSymbol.callableId.callableName.asString() - is FirPropertySymbol -> - firBasedSymbol.callableIdForRendering.callableName.asString() - is FirVariableSymbol -> firBasedSymbol.name.asString() - is FirTypeParameterSymbol -> firBasedSymbol.name.asString() - else -> firBasedSymbol.toString() - } - } -} diff --git a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/ScipVisitor.kt b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/ScipVisitor.kt deleted file mode 100644 index 5cfbe6c5a..000000000 --- a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/ScipVisitor.kt +++ /dev/null @@ -1,191 +0,0 @@ -package org.scip_code.scip_java.kotlinc - -import java.nio.file.Path -import org.jetbrains.kotlin.KtSourceElement -import org.jetbrains.kotlin.KtSourceFile -import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext -import org.jetbrains.kotlin.fir.declarations.* -import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference -import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol -import org.jetbrains.kotlin.name.FqName -import org.scip_code.scip.Document - -/** - * Per-file accumulator of SCIP occurrences and symbols. The FIR checkers in [AnalyzerCheckers] call - * into this and the resulting [Document] is written as a `.scip` shard at the end of compilation. - */ -class ScipVisitor( - sourceroot: Path, - file: KtSourceFile, - lineMap: LineMap, - globals: GlobalSymbolsCache, - locals: LocalSymbolsCache = LocalSymbolsCache(), -) { - private val cache = SymbolsCache(globals, locals) - private val documentBuilder = ScipTextDocumentBuilder(sourceroot, file, lineMap, cache) - - private data class SymbolDescriptorPair( - val firBasedSymbol: FirBasedSymbol<*>?, - val symbol: Symbol, - ) - - fun build(): Document = documentBuilder.build() - - context(context: CheckerContext) - private fun Sequence?.emitAll( - element: KtSourceElement, - isDefinition: Boolean, - enclosingSource: KtSourceElement? = null, - ): List? = - this?.onEach { (firBasedSymbol, symbol) -> - documentBuilder.emitScipData( - firBasedSymbol, - symbol, - element, - isDefinition, - enclosingSource, - ) - } - ?.map { it.symbol } - ?.toList() - - private fun Sequence.with(firBasedSymbol: FirBasedSymbol<*>?) = - this.map { SymbolDescriptorPair(firBasedSymbol, it) } - - context(context: CheckerContext) - fun visitPackage(pkg: FqName, element: KtSourceElement) { - cache[pkg].with(null).emitAll(element, isDefinition = false) - } - - context(context: CheckerContext) - fun visitClassReference(firClassSymbol: FirClassLikeSymbol<*>, element: KtSourceElement) { - cache[firClassSymbol].with(firClassSymbol).emitAll(element, isDefinition = false) - } - - context(context: CheckerContext) - fun visitCallableReference(firClassSymbol: FirCallableSymbol<*>, element: KtSourceElement) { - cache[firClassSymbol].with(firClassSymbol).emitAll(element, isDefinition = false) - } - - context(context: CheckerContext) - fun visitClassOrObject( - firClass: FirClassLikeDeclaration, - element: KtSourceElement, - enclosingSource: KtSourceElement? = null, - ) { - cache[firClass.symbol] - .with(firClass.symbol) - .emitAll(element, isDefinition = true, enclosingSource) - } - - context(context: CheckerContext) - fun visitPrimaryConstructor( - firConstructor: FirConstructor, - source: KtSourceElement, - enclosingSource: KtSourceElement? = null, - ) { - cache[firConstructor.symbol] - .with(firConstructor.symbol) - .emitAll(source, isDefinition = true, enclosingSource) - } - - context(context: CheckerContext) - fun visitSecondaryConstructor( - firConstructor: FirConstructor, - source: KtSourceElement, - enclosingSource: KtSourceElement? = null, - ) { - cache[firConstructor.symbol] - .with(firConstructor.symbol) - .emitAll(source, isDefinition = true, enclosingSource) - } - - context(context: CheckerContext) - fun visitNamedFunction( - firFunction: FirFunction, - source: KtSourceElement, - enclosingSource: KtSourceElement? = null, - ) { - cache[firFunction.symbol] - .with(firFunction.symbol) - .emitAll(source, isDefinition = true, enclosingSource) - } - - context(context: CheckerContext) - fun visitProperty( - firProperty: FirProperty, - source: KtSourceElement, - enclosingSource: KtSourceElement? = null, - ) { - cache[firProperty.symbol] - .with(firProperty.symbol) - .emitAll(source, isDefinition = true, enclosingSource) - } - - context(context: CheckerContext) - fun visitParameter( - firParameter: FirValueParameter, - source: KtSourceElement, - enclosingSource: KtSourceElement? = null, - ) { - cache[firParameter.symbol] - .with(firParameter.symbol) - .emitAll(source, isDefinition = true, enclosingSource) - } - - context(context: CheckerContext) - fun visitTypeParameter( - firTypeParameter: FirTypeParameter, - source: KtSourceElement, - enclosingSource: KtSourceElement? = null, - ) { - cache[firTypeParameter.symbol] - .with(firTypeParameter.symbol) - .emitAll(source, isDefinition = true, enclosingSource) - } - - context(context: CheckerContext) - fun visitTypeAlias( - firTypeAlias: FirTypeAlias, - source: KtSourceElement, - enclosingSource: KtSourceElement? = null, - ) { - cache[firTypeAlias.symbol] - .with(firTypeAlias.symbol) - .emitAll(source, isDefinition = true, enclosingSource) - } - - context(context: CheckerContext) - fun visitPropertyAccessor( - firPropertyAccessor: FirPropertyAccessor, - source: KtSourceElement, - enclosingSource: KtSourceElement? = null, - ) { - cache[firPropertyAccessor.symbol] - .with(firPropertyAccessor.symbol) - .emitAll(source, isDefinition = true, enclosingSource) - } - - context(context: CheckerContext) - fun visitEnumEntry( - firEnumEntry: FirEnumEntry, - source: KtSourceElement, - enclosingSource: KtSourceElement? = null, - ) { - cache[firEnumEntry.symbol] - .with(firEnumEntry.symbol) - .emitAll(source, isDefinition = true, enclosingSource) - } - - context(context: CheckerContext) - fun visitSimpleNameExpression( - firResolvedNamedReference: FirResolvedNamedReference, - source: KtSourceElement, - ) { - cache[firResolvedNamedReference.resolvedSymbol] - .with(firResolvedNamedReference.resolvedSymbol) - .emitAll(source, isDefinition = false) - } -} diff --git a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/SymbolsCache.kt b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/SymbolsCache.kt deleted file mode 100644 index 7ba63f961..000000000 --- a/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/SymbolsCache.kt +++ /dev/null @@ -1,271 +0,0 @@ -package org.scip_code.scip_java.kotlinc - -import java.lang.System.err -import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalDeclaredInBlock -import org.jetbrains.kotlin.fir.declarations.DirectDeclarationsAccess -import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin -import org.jetbrains.kotlin.fir.declarations.utils.memberDeclarationNameOrNull -import org.jetbrains.kotlin.fir.packageFqName -import org.jetbrains.kotlin.fir.resolve.getContainingDeclaration -import org.jetbrains.kotlin.fir.resolve.getContainingSymbol -import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider -import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol -import org.jetbrains.kotlin.fir.symbols.SymbolInternals -import org.jetbrains.kotlin.fir.symbols.impl.* -import org.jetbrains.kotlin.fir.types.classId -import org.jetbrains.kotlin.fir.types.coneType -import org.jetbrains.kotlin.name.FqName -import org.scip_code.scip_java.kotlinc.ScipSymbolDescriptor.Kind -import org.scip_code.scip_java.shared.LocalSymbolsCache as SharedLocalSymbolsCache - -class GlobalSymbolsCache(testing: Boolean = false) : Iterable { - private val globals = - if (testing) LinkedHashMap, Symbol>() - else HashMap, Symbol>() - private val packages = - if (testing) LinkedHashMap() else HashMap() - - operator fun get(symbol: FirBasedSymbol<*>, locals: LocalSymbolsCache): Sequence = - sequence { - emitSymbols(symbol, locals) - } - - operator fun get(symbol: FqName): Sequence = sequence { emitSymbols(symbol) } - - /** - * called whenever a new symbol should be yielded in the sequence e.g. for properties we also - * want to yield for every implicit getter/setter, but wouldn't want to yield for e.g. the - * package symbol parts that a class symbol is composed of. - */ - @OptIn(SymbolInternals::class) - private suspend fun SequenceScope.emitSymbols( - symbol: FirBasedSymbol<*>, - locals: LocalSymbolsCache, - ) { - yield(getSymbol(symbol, locals)) - if (symbol is FirPropertySymbol) { - if (symbol.fir.getter?.origin is FirDeclarationOrigin.Synthetic) - emitSymbols(symbol.fir.getter!!.symbol, locals) - if (symbol.fir.setter?.origin is FirDeclarationOrigin.Synthetic) - emitSymbols(symbol.fir.setter!!.symbol, locals) - } - } - - private suspend fun SequenceScope.emitSymbols(symbol: FqName) { - yield(getSymbol(symbol)) - } - - /** - * Entrypoint for building or looking-up a symbol without yielding a value in the sequence. - * Called recursively for every part of a symbol, unless a cached result short circuits. - */ - private fun getSymbol(symbol: FirBasedSymbol<*>, locals: LocalSymbolsCache): Symbol { - globals[symbol]?.let { - return it - } - locals[symbol]?.let { - return it - } - return uncachedSymbol(symbol, locals).also { if (it.isGlobal()) globals[symbol] = it } - } - - private fun getSymbol(symbol: FqName): Symbol { - packages[symbol]?.let { - return it - } - return uncachedSymbol(symbol).also { if (it.isGlobal()) packages[symbol] = it } - } - - @OptIn(SymbolInternals::class) - private fun uncachedSymbol(symbol: FirBasedSymbol<*>?, locals: LocalSymbolsCache): Symbol { - if (symbol == null || symbol is FirAnonymousFunctionSymbol) return Symbol.NONE - - if (symbol.fir.isLocalDeclaredInBlock) return locals + symbol - - val owner = getParentSymbol(symbol, locals) - - if (owner.isLocal() || owner == Symbol.NONE) return locals + symbol - - val scipDescriptor = scipDescriptor(symbol) - - return Symbol.createGlobal(owner, scipDescriptor) - } - - private fun uncachedSymbol(symbol: FqName): Symbol { - if (symbol.isRoot) return Symbol.ROOT_PACKAGE - - val owner = this.getSymbol(symbol.parent()) - return Symbol.createGlobal( - owner, - ScipSymbolDescriptor(Kind.PACKAGE, symbol.shortName().asString()), - ) - } - - /** - * Returns the parent DeclarationDescriptor for a given DeclarationDescriptor. For most - * descriptor types, this simply returns the 'containing' descriptor. For Module- or - * PackageFragmentDescriptors, it returns the descriptor for the parent fqName of the current - * descriptors fqName e.g. for the fqName `test.sample.main`, the parent fqName would be - * `test.sample`. - */ - @OptIn(SymbolInternals::class) - private fun getParentSymbol(symbol: FirBasedSymbol<*>, locals: LocalSymbolsCache): Symbol { - when (symbol) { - is FirTypeParameterSymbol -> - return getSymbol(symbol.containingDeclarationSymbol, locals) - is FirValueParameterSymbol -> - return getSymbol(symbol.containingDeclarationSymbol, locals) - is FirPropertyAccessorSymbol -> return getSymbol(symbol.propertySymbol, locals) - is FirCallableSymbol -> { - val session = symbol.fir.moduleData.session - val containingSymbol = symbol.getContainingSymbol(session) - // For top-level extension functions/properties (containingSymbol is file or null), - // use the receiver type as a synthetic parent within the package - // (e.g. sample/String#foo().). - if (containingSymbol == null || containingSymbol is FirFileSymbol) { - val receiverClassId = symbol.fir.receiverParameter?.typeRef?.coneType?.classId - if (receiverClassId != null) { - val packageSymbol = getSymbol(symbol.packageFqName()) - return Symbol.createGlobal( - packageSymbol, - ScipSymbolDescriptor( - Kind.TYPE, - receiverClassId.shortClassName.asString(), - ), - ) - } - } - containingSymbol?.let { - return getSymbol(it, locals) - } - return getSymbol(symbol.packageFqName()) - } - is FirClassLikeSymbol -> { - val session = symbol.fir.moduleData.session - return symbol.getContainingDeclaration(session)?.let { getSymbol(it, locals) } - ?: getSymbol(symbol.packageFqName()) - } - is FirFileSymbol -> { - return getSymbol(symbol.fir.packageFqName) - } - else -> return Symbol.NONE - } - } - - @OptIn(SymbolInternals::class) - private fun scipDescriptor(symbol: FirBasedSymbol<*>): ScipSymbolDescriptor { - return when { - symbol is FirAnonymousObjectSymbol -> - symbol.source?.let { source -> - ScipSymbolDescriptor(Kind.TYPE, "") - } ?: ScipSymbolDescriptor.NONE - symbol is FirClassLikeSymbol -> - ScipSymbolDescriptor(Kind.TYPE, symbol.classId.shortClassName.asString()) - symbol is FirPropertyAccessorSymbol && symbol.isSetter -> - ScipSymbolDescriptor(Kind.METHOD, "set") - symbol is FirPropertyAccessorSymbol && symbol.isGetter -> - ScipSymbolDescriptor(Kind.METHOD, "get") - symbol is FirConstructorSymbol -> - ScipSymbolDescriptor(Kind.METHOD, "", methodDisambiguator(symbol)) - symbol is FirFunctionSymbol -> - ScipSymbolDescriptor( - Kind.METHOD, - symbol.name.toString(), - methodDisambiguator(symbol), - ) - symbol is FirTypeParameterSymbol -> - ScipSymbolDescriptor(Kind.TYPE_PARAMETER, symbol.name.toString()) - symbol is FirValueParameterSymbol -> - ScipSymbolDescriptor(Kind.PARAMETER, symbol.name.toString()) - symbol is FirVariableSymbol -> ScipSymbolDescriptor(Kind.TERM, symbol.name.toString()) - symbol is FirFileSymbol -> ScipSymbolDescriptor.NONE - else -> { - err.println("unknown symbol kind ${symbol.javaClass.simpleName}") - ScipSymbolDescriptor.NONE - } - } - } - - @OptIn(SymbolInternals::class, DirectDeclarationsAccess::class) - private fun methodDisambiguator(symbol: FirFunctionSymbol<*>): String { - val session = symbol.moduleData.session - - val siblings = - when (val containingSymbol = symbol.getContainingSymbol(session)) { - is FirClassSymbol -> containingSymbol.fir.declarations.map { it.symbol } - is FirFileSymbol, - null -> { - // For top-level extension functions, siblings are the receiver class members - // (if in the same package) followed by other extension functions on the same - // receiver type in this package. This ensures consistent disambiguation - // when both a class member and an extension share the same parent namespace - // (e.g. sample/MyClass#foo(). vs sample/MyClass#foo(+1).). - val receiverClassId = symbol.fir.receiverParameter?.typeRef?.coneType?.classId - if (receiverClassId != null) { - val receiverClass = - session.symbolProvider.getClassLikeSymbolByClassId(receiverClassId) - as? FirClassSymbol<*> - val classMembers = - if (receiverClass?.packageFqName() == symbol.packageFqName()) { - receiverClass.fir.declarations.map { it.symbol } - } else { - emptyList() - } - val extensionFns = - session.symbolProvider - .getTopLevelCallableSymbols(symbol.packageFqName(), symbol.name) - .filter { - it is FirFunctionSymbol<*> && - it.fir.receiverParameter?.typeRef?.coneType?.classId == - receiverClassId - } - classMembers + extensionFns - } else if (containingSymbol is FirFileSymbol) { - containingSymbol.fir.declarations.map { it.symbol } - } else { - session.symbolProvider.getTopLevelCallableSymbols( - symbol.packageFqName(), - symbol.name, - ) - } - } - else -> return "()" - } - - var count = 0 - var found = false - for (decl in siblings) { - if (decl == symbol) { - found = true - break - } - - if (decl.memberDeclarationNameOrNull?.equals(symbol.name) == true) { - count++ - } - } - - if (!found) { - err.println("methodDisambiguator: ${symbol.callableId} not found in sibling list") - return "()" - } - if (count == 0) return "()" - return "(+${count})" - } - - override fun iterator(): Iterator = globals.values.iterator() -} - -typealias LocalSymbolsCache = SharedLocalSymbolsCache, Symbol> - -@Suppress("FunctionName") -fun LocalSymbolsCache(): LocalSymbolsCache = - SharedLocalSymbolsCache(HashMap()) { Symbol.createLocal(it) } - -operator fun LocalSymbolsCache.plus(symbol: FirBasedSymbol<*>): Symbol = put(symbol) - -class SymbolsCache(private val globals: GlobalSymbolsCache, private val locals: LocalSymbolsCache) { - operator fun get(symbol: FirBasedSymbol<*>) = globals[symbol, locals] - - operator fun get(symbol: FqName) = globals[symbol] -} diff --git a/scip-kotlinc/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor b/scip-kotlinc/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor deleted file mode 100644 index 13fc07284..000000000 --- a/scip-kotlinc/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor +++ /dev/null @@ -1 +0,0 @@ -org.scip_code.scip_java.kotlinc.AnalyzerCommandLineProcessor diff --git a/scip-kotlinc/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar b/scip-kotlinc/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar deleted file mode 100644 index 64d92f95c..000000000 --- a/scip-kotlinc/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar +++ /dev/null @@ -1 +0,0 @@ -org.scip_code.scip_java.kotlinc.AnalyzerRegistrar diff --git a/scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/AnalyzerTest.kt b/scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/AnalyzerTest.kt deleted file mode 100644 index 4f77e9a82..000000000 --- a/scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/AnalyzerTest.kt +++ /dev/null @@ -1,3040 +0,0 @@ -package org.scip_code.scip_java.kotlinc.test - -import com.tschuchort.compiletesting.KotlinCompilation -import com.tschuchort.compiletesting.PluginOption -import com.tschuchort.compiletesting.SourceFile -import io.kotest.assertions.fail -import io.kotest.matchers.collections.shouldContainAll -import io.kotest.matchers.shouldBe -import io.kotest.matchers.shouldNotBe -import io.kotest.matchers.string.shouldContain -import java.io.File -import java.nio.file.Path -import kotlin.test.Test -import kotlin.test.assertEquals -import org.intellij.lang.annotations.Language -import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi -import org.junit.jupiter.api.io.TempDir -import org.scip_code.scip.Document -import org.scip_code.scip.SymbolInformation.Kind -import org.scip_code.scip_java.kotlinc.* - -@OptIn(ExperimentalCompilerApi::class) -class AnalyzerTest { - fun compileScip(path: Path, @Language("kotlin") code: String): Document { - val buildPath = File(path.resolve("build").toString()).apply { mkdir() } - val source = SourceFile.testKt(code) - lateinit var document: Document - - val result = - KotlinCompilation() - .apply { - sources = listOf(source) - compilerPluginRegistrars = listOf(AnalyzerRegistrar { document = it }) - verbose = false - pluginOptions = - listOf( - PluginOption("scip-kotlinc", "sourceroot", path.toString()), - PluginOption("scip-kotlinc", "targetroot", buildPath.toString()), - ) - commandLineProcessors = listOf(AnalyzerCommandLineProcessor()) - workingDir = path.toFile() - } - .compile() - - result.exitCode shouldBe KotlinCompilation.ExitCode.OK - document shouldNotBe null - return document - } - - @Test - fun `basic test`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - class Banana { - fun foo() { } - }""", - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = REFERENCE - symbol = "sample/" - range { - startLine = 0 - startCharacter = 8 - endLine = 0 - endCharacter = 14 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Banana#" - range { - startLine = 1 - startCharacter = 6 - endLine = 1 - endCharacter = 12 - } - enclosingRange { - startLine = 1 - endLine = 3 - endCharacter = 1 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Banana#foo()." - range { - startLine = 2 - startCharacter = 8 - endLine = 2 - endCharacter = 11 - } - enclosingRange { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 17 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Banana#" - kind = Kind.Class - enclosingSymbol = "sample/" - displayName = "Banana" - signatureText = "public final class Banana : Any" - }, - scipSymbol { - symbol = "sample/Banana#foo()." - kind = Kind.Method - enclosingSymbol = "sample/Banana#" - displayName = "foo" - signatureText = "public final fun foo(): Unit" - }, - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun imports(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - import kotlin.Boolean - import kotlin.Int as KInt - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = REFERENCE - symbol = "sample/" - range { - startLine = 0 - startCharacter = 8 - endLine = 0 - endCharacter = 14 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/" - range { - startLine = 2 - startCharacter = 7 - endLine = 2 - endCharacter = 13 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Boolean#" - range { - startLine = 2 - startCharacter = 14 - endLine = 2 - endCharacter = 21 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/" - range { - startLine = 3 - startCharacter = 7 - endLine = 3 - endCharacter = 13 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Int#" - range { - startLine = 3 - startCharacter = 14 - endLine = 3 - endCharacter = 17 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - } - - @Test - fun `local classes`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun foo() { - class LocalClass { - fun localClassMethod() {} - } - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/foo()." - range { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 7 - } - enclosingRange { - startLine = 2 - endLine = 6 - endCharacter = 1 - } - }, - // LocalClass - scipOccurrence { - role = DEFINITION - symbol = "local 0" - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 18 - } - enclosingRange { - startLine = 3 - startCharacter = 2 - endLine = 5 - endCharacter = 3 - } - }, - // LocalClass constructor - scipOccurrence { - role = DEFINITION - symbol = "local 1" - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 18 - } - enclosingRange { - startLine = 3 - startCharacter = 2 - endLine = 5 - endCharacter = 3 - } - }, - // localClassMethod - scipOccurrence { - role = DEFINITION - symbol = "local 2" - range { - startLine = 4 - startCharacter = 8 - endLine = 4 - endCharacter = 24 - } - enclosingRange { - startLine = 4 - startCharacter = 4 - endLine = 4 - endCharacter = 29 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/foo()." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "foo" - signatureText = "public final fun foo(): Unit" - }, - scipSymbol { - symbol = "local 0" - kind = Kind.Class - enclosingSymbol = "sample/foo()." - displayName = "LocalClass" - signatureText = "local final class LocalClass : Any" - }, - scipSymbol { - symbol = "local 1" - kind = Kind.Constructor - enclosingSymbol = "local 0" - displayName = "LocalClass" - signatureText = "public constructor(): LocalClass" - }, - scipSymbol { - symbol = "local 2" - kind = Kind.Method - enclosingSymbol = "local 0" - displayName = "localClassMethod" - signatureText = "public final fun localClassMethod(): Unit" - }, - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `lambda parameters`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun use() { - val f = { n: Int -> n * 2 } - } - """, - ) - - val occurrences = - arrayOf( - // val f is a local variable — gets local0 via isLocalMember - scipOccurrence { - role = DEFINITION - symbol = "local 0" - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 9 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 31 - } - }, - // n is a lambda parameter — gets local1 via the owner == Symbol.NONE path - // (the containing FirAnonymousFunction is skipped, yielding Symbol.NONE as owner) - scipOccurrence { - role = DEFINITION - symbol = "local 1" - range { - startLine = 3 - startCharacter = 14 - endLine = 3 - endCharacter = 15 - } - enclosingRange { - startLine = 3 - startCharacter = 14 - endLine = 3 - endCharacter = 20 - } - }, - // explicit type annotation on n emits a class reference - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Int#" - range { - startLine = 3 - startCharacter = 17 - endLine = 3 - endCharacter = 20 - } - }, - // reference to n in the lambda body uses the same local symbol - scipOccurrence { - role = REFERENCE - symbol = "local 1" - range { - startLine = 3 - startCharacter = 24 - endLine = 3 - endCharacter = 25 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/use()." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "use" - signatureText = "public final fun use(): Unit" - }, - scipSymbol { - symbol = "local 0" - kind = Kind.Property - enclosingSymbol = "sample/use()." - displayName = "f" - signatureText = "local val f: (Int) -> Int" - }, - scipSymbol { - symbol = "local 1" - kind = Kind.Parameter - displayName = "n" - signatureText = "n: Int" - }, - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `local functions`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun outer() { - fun inner() {} - fun innerWithReturnType(): Int = 42 - inner() - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/outer()." - range { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 9 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 6 - endCharacter = 1 - } - }, - // inner() — local named function gets a local symbol - scipOccurrence { - role = DEFINITION - symbol = "local 0" - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 13 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 18 - } - }, - // innerWithReturnType() — local named function with explicit return type - scipOccurrence { - role = DEFINITION - symbol = "local 1" - range { - startLine = 4 - startCharacter = 8 - endLine = 4 - endCharacter = 27 - } - enclosingRange { - startLine = 4 - startCharacter = 4 - endLine = 4 - endCharacter = 39 - } - }, - // Int return-type reference - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Int#" - range { - startLine = 4 - startCharacter = 31 - endLine = 4 - endCharacter = 34 - } - }, - // call site inner() references the same local symbol - scipOccurrence { - role = REFERENCE - symbol = "local 0" - range { - startLine = 5 - startCharacter = 4 - endLine = 5 - endCharacter = 9 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "local 0" - kind = Kind.Method - enclosingSymbol = "sample/outer()." - displayName = "inner" - signatureText = "local final fun inner(): Unit" - }, - scipSymbol { - symbol = "local 1" - kind = Kind.Method - enclosingSymbol = "sample/outer()." - displayName = "innerWithReturnType" - signatureText = "local final fun innerWithReturnType(): Int" - }, - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `user-defined class as return type`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - class MyClass - - fun bar(): MyClass = MyClass() - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/bar()." - range { - startLine = 4 - startCharacter = 4 - endLine = 4 - endCharacter = 7 - } - enclosingRange { - startLine = 4 - startCharacter = 0 - endLine = 4 - endCharacter = 30 - } - }, - // MyClass in the return type position generates a class reference - scipOccurrence { - role = REFERENCE - symbol = "sample/MyClass#" - range { - startLine = 4 - startCharacter = 11 - endLine = 4 - endCharacter = 18 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/bar()." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "bar" - signatureText = "public final fun bar(): MyClass" - } - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `typealias`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - typealias MyAlias = Int - val x: MyAlias = 42 - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/MyAlias#" - range { - startLine = 2 - startCharacter = 10 - endLine = 2 - endCharacter = 17 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 2 - endCharacter = 23 - } - }, - // Note: val x: MyAlias does not emit a REFERENCE for sample/MyAlias# because the - // property checker resolves the type alias to its expansion (Int). - scipOccurrence { - role = DEFINITION - symbol = "sample/x." - range { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 5 - } - enclosingRange { - startLine = 3 - startCharacter = 0 - endLine = 3 - endCharacter = 19 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/MyAlias#" - kind = Kind.TypeAlias - enclosingSymbol = "sample/" - displayName = "MyAlias" - signatureText = "public final typealias MyAlias = Int\n" - } - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `type parameters`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun identity(x: T): T = x - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/identity()." - range { - startLine = 2 - startCharacter = 8 - endLine = 2 - endCharacter = 16 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 2 - endCharacter = 29 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/identity().[T]" - range { - startLine = 2 - startCharacter = 5 - endLine = 2 - endCharacter = 6 - } - enclosingRange { - startLine = 2 - startCharacter = 5 - endLine = 2 - endCharacter = 6 - } - }, - // Note: T in type-annotation positions (x: T and return type : T) does not produce - // REFERENCE occurrences. The checkers use toClassLikeSymbol() to detect type - // references, which returns null for type parameters, so those usages are not - // currently tracked. - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/identity().[T]" - kind = Kind.TypeParameter - enclosingSymbol = "sample/identity()." - displayName = "T" - signatureText = "T" - } - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `generic class declaration`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - class Box(val value: T) { - fun unwrap(): T = value - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/Box#" - range { - startLine = 2 - startCharacter = 6 - endLine = 2 - endCharacter = 9 - } - enclosingRange { - startLine = 2 - endLine = 4 - endCharacter = 1 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Box#[T]" - range { - startLine = 2 - startCharacter = 10 - endLine = 2 - endCharacter = 11 - } - enclosingRange { - startLine = 2 - startCharacter = 10 - endLine = 2 - endCharacter = 11 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Box#unwrap()." - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 14 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 27 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - } - - @Test - fun overrides(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - interface Interface { - fun foo() - } - - class Class : Interface { - override fun foo() {} - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = REFERENCE - symbol = "sample/" - range { - startLine = 0 - startCharacter = 8 - endLine = 0 - endCharacter = 14 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Interface#" - range { - startLine = 2 - startCharacter = 10 - endLine = 2 - endCharacter = 19 - } - enclosingRange { - startLine = 2 - endLine = 4 - endCharacter = 1 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Interface#foo()." - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 11 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 13 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Class#" - range { - startLine = 6 - startCharacter = 6 - endLine = 6 - endCharacter = 11 - } - enclosingRange { - startLine = 6 - endLine = 8 - endCharacter = 1 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Interface#" - range { - startLine = 6 - startCharacter = 14 - endLine = 6 - endCharacter = 23 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Class#foo()." - range { - startLine = 7 - startCharacter = 17 - endLine = 7 - endCharacter = 20 - } - enclosingRange { - startLine = 7 - startCharacter = 4 - endLine = 7 - endCharacter = 25 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Interface#" - kind = Kind.Interface - enclosingSymbol = "sample/" - displayName = "Interface" - signatureText = "public abstract interface Interface : Any" - }, - scipSymbol { - symbol = "sample/Interface#foo()." - kind = Kind.Method - enclosingSymbol = "sample/Interface#" - displayName = "foo" - signatureText = "public abstract fun foo(): Unit\n" - }, - scipSymbol { - symbol = "sample/Class#" - kind = Kind.Class - enclosingSymbol = "sample/" - displayName = "Class" - signatureText = "public final class Class : Interface" - addOverriddenSymbols("sample/Interface#") - }, - scipSymbol { - symbol = "sample/Class#foo()." - kind = Kind.Method - enclosingSymbol = "sample/Class#" - displayName = "foo" - signatureText = "public open override fun foo(): Unit" - addOverriddenSymbols("sample/Interface#foo().") - }, - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `anonymous object`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - interface Interface { - fun foo() - } - - fun main() { - val a = object : Interface { - override fun foo() {} - } - val b = object : Interface { - override fun foo() {} - } - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = REFERENCE - symbol = "sample/" - range { - startLine = 0 - startCharacter = 8 - endLine = 0 - endCharacter = 14 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Interface#" - range { - startLine = 2 - startCharacter = 10 - endLine = 2 - endCharacter = 19 - } - enclosingRange { - startLine = 2 - endLine = 4 - endCharacter = 1 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Interface#foo()." - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 11 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 13 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "local 1" - range { - startLine = 7 - startCharacter = 12 - endLine = 7 - endCharacter = 18 - } - enclosingRange { - startLine = 7 - startCharacter = 12 - endLine = 9 - endCharacter = 5 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "local 2" - range { - startLine = 7 - startCharacter = 12 - endLine = 7 - endCharacter = 18 - } - enclosingRange { - startLine = 7 - startCharacter = 12 - endLine = 9 - endCharacter = 5 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Interface#" - range { - startLine = 7 - startCharacter = 21 - endLine = 7 - endCharacter = 30 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "local 3" - range { - startLine = 8 - startCharacter = 21 - endLine = 8 - endCharacter = 24 - } - enclosingRange { - startLine = 8 - startCharacter = 8 - endLine = 8 - endCharacter = 29 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "local 5" - range { - startLine = 10 - startCharacter = 12 - endLine = 10 - endCharacter = 18 - } - enclosingRange { - startLine = 10 - startCharacter = 12 - endLine = 12 - endCharacter = 5 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "local 6" - range { - startLine = 10 - startCharacter = 12 - endLine = 10 - endCharacter = 18 - } - enclosingRange { - startLine = 10 - startCharacter = 12 - endLine = 12 - endCharacter = 5 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Interface#" - range { - startLine = 10 - startCharacter = 21 - endLine = 10 - endCharacter = 30 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "local 7" - range { - startLine = 11 - startCharacter = 21 - endLine = 11 - endCharacter = 24 - } - enclosingRange { - startLine = 11 - startCharacter = 8 - endLine = 11 - endCharacter = 29 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Interface#" - kind = Kind.Interface - enclosingSymbol = "sample/" - displayName = "Interface" - signatureText = "public abstract interface Interface : Any" - }, - scipSymbol { - symbol = "local 1" - kind = Kind.Class - enclosingSymbol = "local 0" - displayName = "" - signatureText = "object : Interface" - addOverriddenSymbols("sample/Interface#") - }, - scipSymbol { - symbol = "local 3" - kind = Kind.Method - enclosingSymbol = "local 1" - displayName = "foo" - signatureText = "public open override fun foo(): Unit" - addOverriddenSymbols("sample/Interface#foo().") - }, - scipSymbol { - symbol = "local 5" - kind = Kind.Class - enclosingSymbol = "local 4" - displayName = "" - signatureText = "object : Interface" - addOverriddenSymbols("sample/Interface#") - }, - scipSymbol { - symbol = "local 7" - kind = Kind.Method - enclosingSymbol = "local 5" - displayName = "foo" - signatureText = "public open override fun foo(): Unit" - addOverriddenSymbols("sample/Interface#foo().") - }, - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `function return type`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun foo(arg: Int): Boolean = true - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/foo()." - range { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 7 - } - enclosingRange { - startLine = 2 - endLine = 2 - endCharacter = 33 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/foo().(arg)" - range { - startLine = 2 - startCharacter = 8 - endLine = 2 - endCharacter = 11 - } - enclosingRange { - startLine = 2 - startCharacter = 8 - endLine = 2 - endCharacter = 16 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Int#" - range { - startLine = 2 - startCharacter = 13 - endLine = 2 - endCharacter = 16 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Boolean#" - range { - startLine = 2 - startCharacter = 19 - endLine = 2 - endCharacter = 26 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - } - - @Test - fun `type operators`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun foo(x: Any) { - when (x) { - is Int -> true - else -> x as Float - } - } - - class Wrapper - fun classify(x: Any) { - if (x is Wrapper) {} - val s = x as? String - val w = x as Wrapper - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Int#" - range { - startLine = 4 - startCharacter = 11 - endLine = 4 - endCharacter = 14 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Float#" - range { - startLine = 5 - startCharacter = 21 - endLine = 5 - endCharacter = 26 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Wrapper#" - range { - startLine = 11 - startCharacter = 13 - endLine = 11 - endCharacter = 20 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/String#" - range { - startLine = 12 - startCharacter = 18 - endLine = 12 - endCharacter = 24 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Wrapper#" - range { - startLine = 13 - startCharacter = 17 - endLine = 13 - endCharacter = 24 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - } - - @Test - fun `exception test`(@TempDir path: Path) { - val buildPath = File(path.resolve("build").toString()).apply { mkdir() } - val result = - KotlinCompilation() - .apply { - sources = listOf(SourceFile.testKt("")) - compilerPluginRegistrars = - listOf(AnalyzerRegistrar { throw Exception("sample text") }) - verbose = false - messageOutputStream = java.io.OutputStream.nullOutputStream() - pluginOptions = - listOf( - PluginOption("scip-kotlinc", "sourceroot", path.toString()), - PluginOption("scip-kotlinc", "targetroot", buildPath.toString()), - ) - commandLineProcessors = listOf(AnalyzerCommandLineProcessor()) - workingDir = path.toFile() - } - .compile() - - result.exitCode shouldBe KotlinCompilation.ExitCode.OK - result.messages shouldContain "Exception in scip-kotlin compiler plugin:" - result.messages shouldContain "sample text" - } - - @Test - // shamelessly stolen code snippet from https://learnxinyminutes.com/docs/kotlin/ - fun `learn x in y test`(@TempDir path: Path) { - val buildPath = File(path.resolve("build").toString()).apply { mkdir() } - - val source = - SourceFile.testKt( - """ - @file:Suppress("UNUSED_VARIABLE", "UNUSED_PARAMETER", "NAME_SHADOWING", "ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE", "UNUSED_VALUE") - package sample - - fun main(args: Array) { - val fooVal = 10 // we cannot later reassign fooVal to something else - var fooVar = 10 - fooVar = 20 // fooVar can be reassigned - - /* - In most cases, Kotlin can determine what the type of a variable is, - so we don't have to explicitly specify it every time. - We can explicitly declare the type of a variable like so: - */ - val foo: Int = 7 - - /* - Strings can be represented in a similar way as in Java. - Escaping is done with a backslash. - */ - val fooString = "My String Is Here!" - val barString = "Printing on a new line?\nNo Problem!" - val bazString = "Do you want to add a tab?\tNo Problem!" - println(fooString) - println(barString) - println(bazString) - - /* - Strings can contain template expressions. - A template expression starts with a dollar sign (${'$'}). - */ - val fooTemplateString = "$'fooString' has ${"fooString.length"} characters" - println(fooTemplateString) // => My String Is Here! has 18 characters - - /* - For a variable to hold null it must be explicitly specified as nullable. - A variable can be specified as nullable by appending a ? to its type. - We can access a nullable variable by using the ?. operator. - We can use the ?: operator to specify an alternative value to use - if a variable is null. - */ - var fooNullable: String? = "abc" - println(fooNullable?.length) // => 3 - println(fooNullable?.length ?: -1) // => 3 - fooNullable = null - println(fooNullable?.length) // => null - println(fooNullable?.length ?: -1) // => -1 - - /* - Functions can be declared using the "fun" keyword. - Function arguments are specified in brackets after the function name. - Function arguments can optionally have a default value. - The function return type, if required, is specified after the arguments. - */ - fun hello(name: String = "world"): String { - return "Hello, $'name'!" - } - println(hello("foo")) // => Hello, foo! - println(hello(name = "bar")) // => Hello, bar! - println(hello()) // => Hello, world! - - /* - A function parameter may be marked with the "vararg" keyword - to allow a variable number of arguments to be passed to the function. - */ - fun varargExample(vararg names: Int) { - println("Argument has ${"names.size"} elements") - } - varargExample() // => Argument has 0 elements - varargExample(1) // => Argument has 1 elements - varargExample(1, 2, 3) // => Argument has 3 elements - - /* - When a function consists of a single expression then the curly brackets can - be omitted. The body is specified after the = symbol. - */ - fun odd(x: Int): Boolean = x % 2 == 1 - println(odd(6)) // => false - println(odd(7)) // => true - - // If the return type can be inferred then we don't need to specify it. - fun even(x: Int) = x % 2 == 0 - println(even(6)) // => true - println(even(7)) // => false - - // Functions can take functions as arguments and return functions. - fun not(f: (Int) -> Boolean): (Int) -> Boolean { - return {n -> !f.invoke(n)} - } - // Named functions can be specified as arguments using the :: operator. - val notOdd = not(::odd) - val notEven = not(::even) - // Lambda expressions can be specified as arguments. - val notZero = not {n -> n == 0} - /* - If a lambda has only one parameter - then its declaration can be omitted (along with the ->). - The name of the single parameter will be "it". - */ - val notPositive = not {it > 0} - for (i in 0..4) { - println("${"notOdd(i)"} ${"notEven(i)"} ${"notZero(i)"} ${"notPositive(i)"}") - } - - // The "class" keyword is used to declare classes. - class ExampleClass(val x: Int) { - fun memberFunction(y: Int): Int { - return x + y - } - - infix fun infixMemberFunction(y: Int): Int { - return x * y - } - } - /* - To create a new instance we call the constructor. - Note that Kotlin does not have a "new" keyword. - */ - val fooExampleClass = ExampleClass(7) - // Member functions can be called using dot notation. - println(fooExampleClass.memberFunction(4)) // => 11 - /* - If a function has been marked with the "infix" keyword then it can be - called using infix notation. - */ - println(fooExampleClass infixMemberFunction 4) // => 28 - - /* - Data classes are a concise way to create classes that just hold data. - The "hashCode"/"equals" and "toString" methods are automatically generated. - */ - data class DataClassExample (val x: Int, val y: Int, val z: Int) - val fooData = DataClassExample(1, 2, 4) - println(fooData) // => DataClassExample(x=1, y=2, z=4) - - // Data classes have a "copy" function. - val fooCopy = fooData.copy(y = 100) - println(fooCopy) // => DataClassExample(x=1, y=100, z=4) - - // Objects can be destructured into multiple variables. - val (a, b, c) = fooCopy - println("$'a' $'b' $'c'") // => 1 100 4 - - // destructuring in "for" loop - for ((a, b, c) in listOf(fooData)) { - println("$'a' $'b' $'c'") // => 1 2 4 - } - - val mapData = mapOf("a" to 1, "b" to 2) - // Map.Entry is destructurable as well - for ((key, value) in mapData) { - println("$'key' -> $'value'") - } - - // The "with" function is similar to the JavaScript "with" statement. - data class MutableDataClassExample (var x: Int, var y: Int, var z: Int) - val fooMutableData = MutableDataClassExample(7, 4, 9) - with (fooMutableData) { - x -= 2 - y += 2 - z-- - } - println(fooMutableData) // => MutableDataClassExample(x=5, y=6, z=8) - - /* - We can create a list using the "listOf" function. - The list will be immutable - elements cannot be added or removed. - */ - val fooList = listOf("a", "b", "c") - println(fooList.size) // => 3 - println(fooList.first()) // => a - println(fooList.last()) // => c - // Elements of a list can be accessed by their index. - println(fooList[1]) // => b - - // A mutable list can be created using the "mutableListOf" function. - val fooMutableList = mutableListOf("a", "b", "c") - fooMutableList.add("d") - println(fooMutableList.last()) // => d - println(fooMutableList.size) // => 4 - - // We can create a set using the "setOf" function. - val fooSet = setOf("a", "b", "c") - println(fooSet.contains("a")) // => true - println(fooSet.contains("z")) // => false - - // We can create a map using the "mapOf" function. - val fooMap = mapOf("a" to 8, "b" to 7, "c" to 9) - // Map values can be accessed by their key. - println(fooMap["a"]) // => 8 - - /* - Sequences represent lazily-evaluated collections. - We can create a sequence using the "generateSequence" function. - */ - val fooSequence = generateSequence(1, { it + 1 }) - val x = fooSequence.take(10).toList() - println(x) // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - - // An example of using a sequence to generate Fibonacci numbers: - fun fibonacciSequence(): Sequence { - var a = 0L - var b = 1L - - fun next(): Long { - val result = a + b - a = b - b = result - return a - } - - return generateSequence(::next) - } - val y = fibonacciSequence().take(10).toList() - println(y) // => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] - - // Kotlin provides higher-order functions for working with collections. - val z = (1..9).map {it * 3} - .filter {it < 20} - .groupBy {it % 2 == 0} - .mapKeys {if (it.key) "even" else "odd"} - println(z) // => {odd=[3, 9, 15], even=[6, 12, 18]} - - // A "for" loop can be used with anything that provides an iterator. - for (c in "hello") { - println(c) - } - - // "while" loops work in the same way as other languages. - var ctr = 0 - while (ctr < 5) { - println(ctr) - ctr++ - } - do { - println(ctr) - ctr++ - } while (ctr < 10) - - /* - "if" can be used as an expression that returns a value. - For this reason the ternary ?: operator is not needed in Kotlin. - */ - val num = 5 - val message = if (num % 2 == 0) "even" else "odd" - println("$'num' is $'message'") // => 5 is odd - - // "when" can be used as an alternative to "if-else if" chains. - val i = 10 - when { - i < 7 -> println("first block") - fooString.startsWith("hello") -> println("second block") - else -> println("else block") - } - - // "when" can be used with an argument. - when (i) { - 0, 21 -> println("0 or 21") - in 1..20 -> println("in the range 1 to 20") - else -> println("none of the above") - } - - // "when" can be used as a function that returns a value. - var result = when (i) { - 0, 21 -> "0 or 21" - in 1..20 -> "in the range 1 to 20" - else -> "none of the above" - } - println(result) - - /* - We can check if an object is of a particular type by using the "is" operator. - If an object passes a type check then it can be used as that type without - explicitly casting it. - */ - fun smartCastExample(x: Any) : Boolean { - if (x is Boolean) { - // x is automatically cast to Boolean - return x - } else if (x is Int) { - // x is automatically cast to Int - return x > 0 - } else if (x is String) { - // x is automatically cast to String - return x.isNotEmpty() - } else { - return false - } - } - println(smartCastExample("Hello, world!")) // => true - println(smartCastExample("")) // => false - println(smartCastExample(5)) // => true - println(smartCastExample(0)) // => false - println(smartCastExample(true)) // => true - - // Smartcast also works with when block - fun smartCastWhenExample(x: Any) = when (x) { - is Boolean -> x - is Int -> x > 0 - is String -> x.isNotEmpty() - else -> false - } - - /* - Extensions are a way to add new functionality to a class. - This is similar to C# extension methods. - */ - fun String.remove(c: Char): String { - return this.filter {it != c} - } - println("Hello, world!".remove('l')) // => Heo, word! - } - - // Enum classes are similar to Java enum types. - enum class EnumExample { - A, B, C // Enum constants are separated with commas. - } - fun printEnum() = println(EnumExample.A) // => A - - // Since each enum is an instance of the enum class, they can be initialized as: - enum class EnumExample1(val value: Int) { - A(value = 1), - B(value = 2), - C(value = 3) - } - fun printProperty() = println(EnumExample1.A.value) // => 1 - - // Every enum has properties to obtain its name and ordinal(position) in the enum class declaration: - fun printName() = println(EnumExample1.A.name) // => A - fun printPosition() = println(EnumExample1.A.ordinal) // => 0 - - /* - The "object" keyword can be used to create singleton objects. - We cannot instantiate it but we can refer to its unique instance by its name. - This is similar to Scala singleton objects. - */ - object ObjectExample { - fun hello(): String { - return "hello" - } - - override fun toString(): String { - return "Hello, it's me, ${"ObjectExample::class.simpleName"}" - } - } - - - fun useSingletonObject() { - println(ObjectExample.hello()) // => hello - // In Kotlin, "Any" is the root of the class hierarchy, just like "Object" is in Java - val someRef: Any = ObjectExample - println(someRef) // => Hello, it's me, ObjectExample - } - - - /* The not-null assertion operator (!!) converts any value to a non-null type and - throws an exception if the value is null. - */ - var b: String? = "abc" - val l = b!!.length - - data class Counter(var value: Int) { - // overload Counter += Int - operator fun plusAssign(increment: Int) { - this.value += increment - } - - // overload Counter++ and ++Counter - operator fun inc() = Counter(value + 1) - - // overload Counter + Counter - operator fun plus(other: Counter) = Counter(this.value + other.value) - - // overload Counter * Counter - operator fun times(other: Counter) = Counter(this.value * other.value) - - // overload Counter * Int - operator fun times(value: Int) = Counter(this.value * value) - - // overload Counter in Counter - operator fun contains(other: Counter) = other.value == this.value - - // overload Counter[Int] = Int - operator fun set(index: Int, value: Int) { - this.value = index + value - } - - // overload Counter instance invocation - operator fun invoke() = println("The value of the counter is $'value'") - - } - /* You can also overload operators through extension methods */ - // overload -Counter - operator fun Counter.unaryMinus() = Counter(-this.value) - - fun operatorOverloadingDemo() { - var counter1 = Counter(0) - var counter2 = Counter(5) - counter1 += 7 - println(counter1) // => Counter(value=7) - println(counter1 + counter2) // => Counter(value=12) - println(counter1 * counter2) // => Counter(value=35) - println(counter2 * 2) // => Counter(value=10) - println(counter1 in Counter(5)) // => false - println(counter1 in Counter(7)) // => true - counter1[26] = 10 - println(counter1) // => Counter(value=36) - counter1() // => The value of the counter is 36 - println(-counter2) // => Counter(value=-5) - } - """ - ) - - val result = - KotlinCompilation() - .apply { - sources = listOf(source) - compilerPluginRegistrars = listOf(AnalyzerRegistrar()) - verbose = false - pluginOptions = - listOf( - PluginOption("scip-kotlinc", "sourceroot", path.toString()), - PluginOption("scip-kotlinc", "targetroot", buildPath.toString()), - ) - commandLineProcessors = listOf(AnalyzerCommandLineProcessor()) - workingDir = path.toFile() - } - .compile() - - result.exitCode shouldBe KotlinCompilation.ExitCode.OK - } - - @Test - fun `compound package name semicolon test`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package hello.sample; - class Apple - """ - .trimIndent(), - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = REFERENCE - symbol = "hello/" - range { - startLine = 0 - startCharacter = 8 - endLine = 0 - endCharacter = 13 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "hello/sample/" - range { - startLine = 0 - startCharacter = 14 - endLine = 0 - endCharacter = 20 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "hello/sample/Apple#" - range { - startLine = 1 - startCharacter = 6 - endLine = 1 - endCharacter = 11 - } - enclosingRange { - startLine = 1 - endLine = 1 - endCharacter = 11 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "hello/sample/Apple#``()." - range { - startLine = 1 - startCharacter = 6 - endLine = 1 - endCharacter = 11 - } - enclosingRange { - startLine = 1 - endLine = 1 - endCharacter = 11 - } - }, - ) - - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "hello/sample/Apple#" - kind = Kind.Class - enclosingSymbol = "hello/sample/" - displayName = "Apple" - signatureText = "public final class Apple : Any" - } - ) - - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `simple package name semicolon test`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample; - class Banana { - fun foo() { } - }""", - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = REFERENCE - symbol = "sample/" - range { - startLine = 0 - startCharacter = 8 - endLine = 0 - endCharacter = 14 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Banana#" - range { - startLine = 1 - startCharacter = 6 - endLine = 1 - endCharacter = 12 - } - enclosingRange { - startLine = 1 - endLine = 3 - endCharacter = 1 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Banana#foo()." - range { - startLine = 2 - startCharacter = 8 - endLine = 2 - endCharacter = 11 - } - enclosingRange { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 17 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Banana#" - range { - startLine = 1 - startCharacter = 6 - endLine = 1 - endCharacter = 12 - } - enclosingRange { - startLine = 1 - endLine = 3 - endCharacter = 1 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Banana#" - kind = Kind.Class - enclosingSymbol = "sample/" - displayName = "Banana" - signatureText = "public final class Banana : Any" - }, - scipSymbol { - symbol = "sample/Banana#foo()." - kind = Kind.Method - enclosingSymbol = "sample/Banana#" - displayName = "foo" - signatureText = "public final fun foo(): Unit" - }, - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun documentation(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - import java.io.Serializable - abstract class DocstringSuperclass - - /** Example class docstring */ - class Docstrings: DocstringSuperclass(), Serializable - - /** - * Example method docstring - * - **/ - fun docstrings(msg: String): Int { return msg.length } - """ - .trimIndent(), - ) - document.assertDocumentation("sample/Docstrings#", "Example class docstring") - document.assertDocumentation("sample/docstrings().", "Example method docstring") - } - - @Test - fun `extension receiver type reference`(@TempDir path: Path) { - // String is from the kotlin package; our extension in sample gets - // symbol sample/String#foo(). — distinct from kotlin/String#foo(). - // This means extensions on cross-package types never collide with - // the receiver class's own methods in the symbol table. - val document = - compileScip( - path, - """ - package sample - - fun String.foo(): Int = 42 - fun use(s: String) = s.foo() - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/String#foo()." - range { - startLine = 2 - startCharacter = 11 - endLine = 2 - endCharacter = 14 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 2 - endCharacter = 26 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/String#" - range { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 10 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Int#" - range { - startLine = 2 - startCharacter = 18 - endLine = 2 - endCharacter = 21 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/String#foo()." - range { - startLine = 3 - startCharacter = 23 - endLine = 3 - endCharacter = 26 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/String#foo()." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "foo" - signatureText = "public final fun String.foo(): Int" - } - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `extension property receiver type reference`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - val Int.asString: String get() = this.toString() - fun use() = 42.asString - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/Int#asString." - range { - startLine = 2 - startCharacter = 8 - endLine = 2 - endCharacter = 16 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 2 - endCharacter = 48 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Int#" - range { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 7 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/String#" - range { - startLine = 2 - startCharacter = 18 - endLine = 2 - endCharacter = 24 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Int#asString." - range { - startLine = 3 - startCharacter = 15 - endLine = 3 - endCharacter = 23 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Int#asString." - kind = Kind.Property - enclosingSymbol = "sample/" - displayName = "asString" - signatureText = "public final val Int.asString: String" - } - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `extension overload disambiguator`(@TempDir path: Path) { - // When a class already has a member named foo() and an extension also - // adds foo(), the extension is counted after the member in the combined - // sibling list (class members + same-package extensions on the same - // receiver type), so the extension gets the (+1) disambiguator and the - // two produce distinct symbols. - val document = - compileScip( - path, - """ - package sample - - class MyClass { - fun foo() {} - } - fun MyClass.foo(x: Int) {} - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/MyClass#foo()." - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 11 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 16 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/MyClass#foo(+1)." - range { - startLine = 5 - startCharacter = 12 - endLine = 5 - endCharacter = 15 - } - enclosingRange { - startLine = 5 - startCharacter = 0 - endLine = 5 - endCharacter = 26 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - } - - @Test - fun `enum entry definitions`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - enum class Color { RED, GREEN, BLUE } - - fun useEnum(): Color = Color.RED - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/Color#" - range { - startLine = 2 - startCharacter = 11 - endLine = 2 - endCharacter = 16 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 2 - endCharacter = 37 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Color#RED." - range { - startLine = 2 - startCharacter = 19 - endLine = 2 - endCharacter = 22 - } - enclosingRange { - startLine = 2 - startCharacter = 19 - endLine = 2 - endCharacter = 23 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Color#GREEN." - range { - startLine = 2 - startCharacter = 24 - endLine = 2 - endCharacter = 29 - } - enclosingRange { - startLine = 2 - startCharacter = 24 - endLine = 2 - endCharacter = 30 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Color#BLUE." - range { - startLine = 2 - startCharacter = 31 - endLine = 2 - endCharacter = 35 - } - enclosingRange { - startLine = 2 - startCharacter = 31 - endLine = 2 - endCharacter = 35 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Color#" - range { - startLine = 4 - startCharacter = 23 - endLine = 4 - endCharacter = 28 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Color#RED." - range { - startLine = 4 - startCharacter = 29 - endLine = 4 - endCharacter = 32 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Color#" - kind = Kind.Class - enclosingSymbol = "sample/" - displayName = "Color" - addOverriddenSymbols("kotlin/Enum#") - signatureText = "public final enum class Color : Enum" - }, - scipSymbol { - symbol = "sample/Color#RED." - kind = Kind.EnumMember - enclosingSymbol = "sample/Color#" - displayName = "RED" - signatureText = "public final static enum entry RED: Color" - }, - scipSymbol { - symbol = "sample/Color#GREEN." - kind = Kind.EnumMember - enclosingSymbol = "sample/Color#" - displayName = "GREEN" - signatureText = "public final static enum entry GREEN: Color" - }, - scipSymbol { - symbol = "sample/Color#BLUE." - kind = Kind.EnumMember - enclosingSymbol = "sample/Color#" - displayName = "BLUE" - signatureText = "public final static enum entry BLUE: Color" - }, - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `enum entry with body`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - enum class Op { - PLUS { - override fun apply(a: Int, b: Int) = a + b - }; - - abstract fun apply(a: Int, b: Int): Int - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/Op#PLUS." - range { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 8 - } - // Body-having enum entry: enclosing_range covers PLUS through the - // trailing `};` that terminates the entry list. - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 5 - endCharacter = 6 - } - }, - // An enum entry with a body is modeled as a synthetic anonymous subclass, - // so its overridden member is a local symbol (local2), not a global - // sample/Op#PLUS.apply(). It still gets an enclosing_range spanning the - // function declaration. - scipOccurrence { - role = DEFINITION - symbol = "local 2" - range { - startLine = 4 - startCharacter = 21 - endLine = 4 - endCharacter = 26 - } - enclosingRange { - startLine = 4 - startCharacter = 8 - endLine = 4 - endCharacter = 50 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - // The entry body becomes an anonymous class enclosed by the entry... - scipSymbol { - symbol = "local 0" - kind = Kind.Class - enclosingSymbol = "sample/Op#PLUS." - displayName = "" - addOverriddenSymbols("sample/Op#") - signatureText = "object : Op" - }, - // ...and the override is a method of that anonymous class. - scipSymbol { - symbol = "local 2" - kind = Kind.Method - enclosingSymbol = "local 0" - displayName = "apply" - addOverriddenSymbols("sample/Op#apply().") - signatureText = "public open override fun apply(a: Int, b: Int): Int" - }, - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `named object declarations`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - object MySingleton { - fun hello(): String = "hi" - } - fun use() = MySingleton.hello() - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/MySingleton#" - range { - startLine = 2 - startCharacter = 7 - endLine = 2 - endCharacter = 18 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 4 - endCharacter = 1 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/MySingleton#hello()." - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 13 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 30 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/MySingleton#" - range { - startLine = 5 - startCharacter = 12 - endLine = 5 - endCharacter = 23 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/MySingleton#hello()." - range { - startLine = 5 - startCharacter = 24 - endLine = 5 - endCharacter = 29 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/MySingleton#" - kind = Kind.Class - enclosingSymbol = "sample/" - displayName = "MySingleton" - signatureText = "public final object MySingleton : Any" - }, - scipSymbol { - symbol = "sample/MySingleton#hello()." - kind = Kind.Method - enclosingSymbol = "sample/MySingleton#" - displayName = "hello" - signatureText = "public final fun hello(): String" - }, - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `companion object`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - class Foo { - companion object Factory { - fun create(): Foo = Foo() - } - } - fun use() = Foo.Factory.create() - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/Foo#" - range { - startLine = 2 - startCharacter = 6 - endLine = 2 - endCharacter = 9 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 6 - endCharacter = 1 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Foo#Factory#" - range { - startLine = 3 - startCharacter = 21 - endLine = 3 - endCharacter = 28 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 5 - endCharacter = 5 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Foo#Factory#create()." - range { - startLine = 4 - startCharacter = 12 - endLine = 4 - endCharacter = 18 - } - enclosingRange { - startLine = 4 - startCharacter = 8 - endLine = 4 - endCharacter = 33 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Foo#" - range { - startLine = 7 - startCharacter = 12 - endLine = 7 - endCharacter = 15 - } - }, - // Foo.Factory is a FirResolvedQualifier spanning the full qualifier expression - scipOccurrence { - role = REFERENCE - symbol = "sample/Foo#Factory#" - range { - startLine = 7 - startCharacter = 12 - endLine = 7 - endCharacter = 23 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Foo#Factory#create()." - range { - startLine = 7 - startCharacter = 24 - endLine = 7 - endCharacter = 30 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Foo#" - kind = Kind.Class - enclosingSymbol = "sample/" - displayName = "Foo" - signatureText = "public final class Foo : Any" - }, - scipSymbol { - symbol = "sample/Foo#Factory#" - kind = Kind.Class - enclosingSymbol = "sample/Foo#" - displayName = "Factory" - signatureText = "public final companion object Factory : Any" - }, - scipSymbol { - symbol = "sample/Foo#Factory#create()." - kind = Kind.Method - enclosingSymbol = "sample/Foo#Factory#" - displayName = "create" - signatureText = "public final fun create(): Foo" - }, - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `unnamed companion object`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - class Bar { - companion object { - fun instance(): Bar = Bar() - } - } - fun use() = Bar.instance() - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/Bar#" - range { - startLine = 2 - startCharacter = 6 - endLine = 2 - endCharacter = 9 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 6 - endCharacter = 1 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Bar#Companion#instance()." - range { - startLine = 4 - startCharacter = 12 - endLine = 4 - endCharacter = 20 - } - enclosingRange { - startLine = 4 - startCharacter = 8 - endLine = 4 - endCharacter = 35 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Bar#" - range { - startLine = 7 - startCharacter = 12 - endLine = 7 - endCharacter = 15 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Bar#Companion#instance()." - range { - startLine = 7 - startCharacter = 16 - endLine = 7 - endCharacter = 24 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Bar#Companion#" - range { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 13 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 5 - endCharacter = 5 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Bar#" - kind = Kind.Class - enclosingSymbol = "sample/" - displayName = "Bar" - signatureText = "public final class Bar : Any" - }, - scipSymbol { - symbol = "sample/Bar#Companion#" - kind = Kind.Class - enclosingSymbol = "sample/Bar#" - displayName = "Companion" - signatureText = "public final companion object Companion : Any" - }, - scipSymbol { - symbol = "sample/Bar#Companion#instance()." - kind = Kind.Method - enclosingSymbol = "sample/Bar#Companion#" - displayName = "instance" - signatureText = "public final fun instance(): Bar" - }, - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `string template references`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun greet(name: String) = "Hello, ${'$'}name!" - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/greet()." - range { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 9 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 2 - endCharacter = 41 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/greet().(name)" - range { - startLine = 2 - startCharacter = 10 - endLine = 2 - endCharacter = 14 - } - enclosingRange { - startLine = 2 - startCharacter = 10 - endLine = 2 - endCharacter = 22 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/greet().(name)" - range { - startLine = 2 - startCharacter = 35 - endLine = 2 - endCharacter = 39 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/greet()." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "greet" - signatureText = "public final fun greet(name: String): String" - } - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `multiple supertype references`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - interface Named - interface Speakable - class Person : Named, Speakable - fun use(p: Person): Named = p - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/Person#" - range { - startLine = 4 - startCharacter = 6 - endLine = 4 - endCharacter = 12 - } - enclosingRange { - startLine = 4 - startCharacter = 0 - endLine = 4 - endCharacter = 31 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Named#" - range { - startLine = 4 - startCharacter = 15 - endLine = 4 - endCharacter = 20 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Speakable#" - range { - startLine = 4 - startCharacter = 22 - endLine = 4 - endCharacter = 31 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Named#" - range { - startLine = 5 - startCharacter = 20 - endLine = 5 - endCharacter = 25 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Person#" - kind = Kind.Class - enclosingSymbol = "sample/" - displayName = "Person" - addOverriddenSymbols("sample/Named#") - addOverriddenSymbols("sample/Speakable#") - signatureText = "public final class Person : Named, Speakable" - } - ) - document.symbolsList.shouldContainAll(*symbols) - } - - @Test - fun `three-way overload disambiguator`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun add(x: Int, y: Int): Int = x + y - fun add(x: Double, y: Double): Double = x + y - fun add(x: String, y: String): String = x + y - fun use() { - add(1, 2) - add(1.0, 2.0) - add("a", "b") - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/add()." - range { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 7 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 2 - endCharacter = 36 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/add(+1)." - range { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 7 - } - enclosingRange { - startLine = 3 - startCharacter = 0 - endLine = 3 - endCharacter = 45 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/add(+2)." - range { - startLine = 4 - startCharacter = 4 - endLine = 4 - endCharacter = 7 - } - enclosingRange { - startLine = 4 - startCharacter = 0 - endLine = 4 - endCharacter = 45 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/add()." - range { - startLine = 6 - startCharacter = 4 - endLine = 6 - endCharacter = 7 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/add(+1)." - range { - startLine = 7 - startCharacter = 4 - endLine = 7 - endCharacter = 7 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/add(+2)." - range { - startLine = 8 - startCharacter = 4 - endLine = 8 - endCharacter = 7 - } - }, - ) - document.occurrencesList.shouldContainAll(*occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/add()." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "add" - signatureText = "public final fun add(x: Int, y: Int): Int" - }, - scipSymbol { - symbol = "sample/add(+1)." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "add" - signatureText = "public final fun add(x: Double, y: Double): Double" - }, - scipSymbol { - symbol = "sample/add(+2)." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "add" - signatureText = "public final fun add(x: String, y: String): String" - }, - ) - document.symbolsList.shouldContainAll(*symbols) - } - - private fun Document.assertDocumentation(symbol: String, expectedDocumentation: String) { - val info = - this.symbolsList.find { it.symbol == symbol } - ?: fail("no scipSymbol for symbol $symbol") - val obtainedDocumentation = info.documentationList.joinToString("\n").trim() - assertEquals(expectedDocumentation, obtainedDocumentation) - } -} diff --git a/scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/ScipBuilders.kt b/scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/ScipBuilders.kt deleted file mode 100644 index fe8bdebc7..000000000 --- a/scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/ScipBuilders.kt +++ /dev/null @@ -1,125 +0,0 @@ -package org.scip_code.scip_java.kotlinc.test - -import org.scip_code.scip.Occurrence -import org.scip_code.scip.SymbolInformation -import org.scip_code.scip.SymbolRole -import org.scip_code.scip.relationship -import org.scip_code.scip.signature -import org.scip_code.scip.symbolInformation -import org.scip_code.scip_java.shared.ScipRange - -/** - * Tiny DSL for building SCIP [Occurrence] / [SymbolInformation] test fixtures with the same shape - * as the original SCIP-based one used by the Kotlin tests. - * - *

Example: - * ``` - * scipOccurrence { - * role = DEFINITION - * symbol = "sample/Banana#" - * range { startLine = 1; startCharacter = 6; endLine = 1; endCharacter = 12 } - * enclosingRange { startLine = 1; endLine = 3; endCharacter = 1 } - * } - * ``` - */ -internal val REFERENCE: Int = SymbolRole.UnspecifiedSymbolRole.number -internal val DEFINITION: Int = SymbolRole.Definition.number - -@DslMarker annotation class ScipBuilderDsl - -@ScipBuilderDsl -class ScipRangeBuilder { - var startLine: Int = 0 - var startCharacter: Int = 0 - /** - * Default sentinel: when [endLine] is left untouched, the produced range is single-line at - * [startLine]. - */ - var endLine: Int = -1 - var endCharacter: Int = 0 - - internal fun toScipRange(): ScipRange { - val line = if (endLine < 0) startLine else endLine - return ScipRange.range(startLine, startCharacter, line, endCharacter) - } -} - -@ScipBuilderDsl -class ScipOccurrenceBuilder { - var role: Int = REFERENCE - var symbol: String = "" - private var range: ScipRange? = null - private var enclosingRange: ScipRange? = null - - fun range(block: ScipRangeBuilder.() -> Unit) { - range = ScipRangeBuilder().apply(block).toScipRange() - } - - fun enclosingRange(block: ScipRangeBuilder.() -> Unit) { - enclosingRange = ScipRangeBuilder().apply(block).toScipRange() - } - - internal fun build(): Occurrence { - val builder = Occurrence.newBuilder().setSymbol(symbol).setSymbolRoles(role) - range?.let { - if (it.isSingleLine) builder.singleLineRange = it.toSingleLineRange() - else builder.multiLineRange = it.toMultiLineRange() - } - enclosingRange?.let { - if (it.isSingleLine) builder.singleLineEnclosingRange = it.toSingleLineRange() - else builder.multiLineEnclosingRange = it.toMultiLineRange() - } - return builder.build() - } -} - -@ScipBuilderDsl -class ScipSymbolInformationBuilder { - var symbol: String = "" - var kind: SymbolInformation.Kind = SymbolInformation.Kind.UnspecifiedKind - var enclosingSymbol: String = "" - var displayName: String = "" - var signatureText: String? = null - private val docs = mutableListOf() - private val overrides = mutableListOf() - - fun documentation(text: String) { - docs += text - } - - /** - * Appends an `is_implementation` [Relationship]. Mirrors the old SCIP-flavored - * `addOverriddenSymbols` so existing test fixtures port over with minimal diff. - */ - fun addOverriddenSymbols(vararg symbols: String) { - overrides.addAll(symbols) - } - - internal fun build(): SymbolInformation = symbolInformation { - symbol = this@ScipSymbolInformationBuilder.symbol - kind = this@ScipSymbolInformationBuilder.kind - enclosingSymbol = this@ScipSymbolInformationBuilder.enclosingSymbol - if (this@ScipSymbolInformationBuilder.displayName.isNotEmpty()) { - displayName = this@ScipSymbolInformationBuilder.displayName - } - signatureText?.let { sigText -> - signatureDocumentation = signature { - language = "kotlin" - text = sigText - } - } - for (d in docs) documentation += d - for (s in overrides) { - relationships += relationship { - symbol = s - isImplementation = true - } - } - } -} - -internal fun scipOccurrence(block: ScipOccurrenceBuilder.() -> Unit): Occurrence = - ScipOccurrenceBuilder().apply(block).build() - -internal fun scipSymbol(block: ScipSymbolInformationBuilder.() -> Unit): SymbolInformation = - ScipSymbolInformationBuilder().apply(block).build() diff --git a/scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/ScipSymbolsTest.kt b/scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/ScipSymbolsTest.kt deleted file mode 100644 index 0bc8349bf..000000000 --- a/scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/ScipSymbolsTest.kt +++ /dev/null @@ -1,798 +0,0 @@ -package org.scip_code.scip_java.kotlinc.test - -import com.tschuchort.compiletesting.SourceFile -import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi -import org.junit.jupiter.api.TestFactory -import org.scip_code.scip.SymbolInformation.Kind -import org.scip_code.scip_java.kotlinc.* -import org.scip_code.scip_java.kotlinc.test.ExpectedSymbols.ScipData -import org.scip_code.scip_java.kotlinc.test.ExpectedSymbols.SymbolCacheData - -@ExperimentalCompilerApi -class ScipSymbolsTest { - @TestFactory - fun `method disambiguator`() = - listOf( - ExpectedSymbols( - "Basic two methods", - SourceFile.testKt( - """ - |class Test { - | fun sample() {} - | fun sample(x: Int) {} - |} - |""" - .trimMargin() - ), - symbolsCacheData = - SymbolCacheData( - listOf("Test#sample().".symbol(), "Test#sample(+1).".symbol()) - ), - ), - ExpectedSymbols( - "Inline class constructor", - SourceFile.testKt( - """ - |class Test(val x: Int) - |""" - .trimMargin() - ), - symbolsCacheData = SymbolCacheData(listOf("Test#``().(x)".symbol())), - ), - ExpectedSymbols( - "Inline + secondary class constructors", - SourceFile.testKt( - """ - |class Test(val x: Int) { - | constructor(y: Long): this(y.toInt()) - | constructor(z: String): this(z.toInt()) - |} - |""" - .trimMargin() - ), - symbolsCacheData = - SymbolCacheData( - listOf( - "Test#``().(x)".symbol(), - "Test#``(+1).(y)".symbol(), - "Test#``(+2).(z)".symbol(), - ) - ), - ), - ExpectedSymbols( - "Disambiguator number is not affected by different named methods", - SourceFile.testKt( - """ - |class Test { - | fun sample() {} - | fun test() {} - | fun test(x: Int) {} - |} - |""" - .trimMargin() - ), - symbolsCacheData = - SymbolCacheData(listOf("Test#test().".symbol(), "Test#test(+1).".symbol())), - ), - ExpectedSymbols( - "Top level overloaded functions", - SourceFile.testKt( - """ - |fun test() {} - |fun test(x: Int) {} - |""" - .trimMargin() - ), - symbolsCacheData = - SymbolCacheData(listOf("test().".symbol(), "test(+1).(x)".symbol())), - ), - ExpectedSymbols( - "Annotations incl annotation type alias", - SourceFile.testKt( - """ - |import kotlin.contracts.ExperimentalContracts - |import kotlin.test.Test - | - |@ExperimentalContracts - |class Banaan { - | @Test - | fun test() {} - |} - |""" - .trimMargin() - ), - symbolsCacheData = - SymbolCacheData( - listOf( - "kotlin/contracts/ExperimentalContracts#".symbol(), - "kotlin/test/Test#".symbol(), - ) - ), - ), - // https://kotlinlang.slack.com/archives/C7L3JB43G/p1624995376114900 - /*ExpectedSymbols( - "Method call with type parameters", - SourceFile.testKt(""" - import org.junit.jupiter.api.io.TempDir - val burger = LinkedHashMap() - """), - symbolsCacheData = SymbolCacheData( - listOf("kotlin/collection/TypeAliasesKt#LinkedHashMap#``().".symbol()) - ) - )*/ - ) - .mapCheckExpectedSymbols() - - @TestFactory - fun `check package symbols`() = - listOf( - ExpectedSymbols( - "single component package name", - SourceFile.testKt( - """ - |package main - | - |class Test - |""" - .trimMargin() - ), - symbolsCacheData = SymbolCacheData(listOf("main/Test#".symbol()), 0), - ), - ExpectedSymbols( - "multi component package name", - SourceFile.testKt( - """ - |package test.sample.main - | - |class Test - |""" - .trimMargin() - ), - symbolsCacheData = SymbolCacheData(listOf("test/sample/main/Test#".symbol()), 0), - ), - ExpectedSymbols( - "no package name", - SourceFile.testKt( - """ - |class Test - |""" - .trimMargin() - ), - symbolsCacheData = SymbolCacheData(listOf("Test#".symbol()), 0), - ), - ) - .mapCheckExpectedSymbols() - - @TestFactory - fun `check locals counts`() = - listOf( - ExpectedSymbols( - "simple variables", - SourceFile.testKt( - """ - |fun test() { - | val x = "hello" - | println(x) - |} - |""" - .trimMargin() - ), - symbolsCacheData = SymbolCacheData(localsCount = 1), - ) - ) - .mapCheckExpectedSymbols() - - @TestFactory - fun `builtin symbols`() = - listOf( - ExpectedSymbols( - "types", - SourceFile.testKt( - """ - |var x: Int = 1 - |lateinit var y: Unit - |lateinit var z: Any - |lateinit var w: Nothing - |""" - .trimMargin() - ), - symbolsCacheData = - SymbolCacheData( - listOf( - "kotlin/Int#".symbol(), - "kotlin/Unit#".symbol(), - "kotlin/Any#".symbol(), - "kotlin/Nothing#".symbol(), - ) - ), - ), - ExpectedSymbols( - "functions", - SourceFile.testKt( - """ - |val x = mapOf() - |fun main() { - | println() - |} - |""" - .trimMargin() - ), - symbolsCacheData = - SymbolCacheData( - listOf( - "kotlin/collections/mapOf(+2).".symbol(), - "kotlin/io/println(+10).".symbol(), - ) - ), - ), - ) - .mapCheckExpectedSymbols() - - @TestFactory - fun `reference expressions`() = - listOf( - ExpectedSymbols( - "dot qualified expression", - SourceFile.testKt( - """ - |import java.lang.System - | - |fun main() { - | System.err - |} - |""" - .trimMargin() - ), - symbolsCacheData = SymbolCacheData(listOf("java/lang/System#err.".symbol())), - ) - ) - .mapCheckExpectedSymbols() - - @TestFactory - fun `properties with getters-setters`() = - listOf( - ExpectedSymbols( - "top level properties - implicit", - SourceFile.testKt( - """ - |var x: Int = 5 - |""" - .trimMargin() - ), - scip = - ScipData( - expectedOccurrences = - listOf( - scipOccurrence { - role = DEFINITION - symbol = "x." - range { - startLine = 0 - startCharacter = 4 - endLine = 0 - endCharacter = 5 - } - enclosingRange { endCharacter = 14 } - }, - scipOccurrence { - role = DEFINITION - symbol = "x.get()." - range { - startLine = 0 - startCharacter = 4 - endLine = 0 - endCharacter = 5 - } - enclosingRange { endCharacter = 14 } - }, - scipOccurrence { - role = DEFINITION - symbol = "x.set()." - range { - startLine = 0 - startCharacter = 4 - endLine = 0 - endCharacter = 5 - } - enclosingRange { endCharacter = 14 } - }, - ) - ), - ), - ExpectedSymbols( - "top level properties - explicit getter", - SourceFile.testKt( - """ - |var x: Int = 5 - | get() = field + 10 - |""" - .trimMargin() - ), - scip = - ScipData( - expectedOccurrences = - listOf( - scipOccurrence { - role = DEFINITION - symbol = "x." - range { - startLine = 0 - startCharacter = 4 - endLine = 0 - endCharacter = 5 - } - enclosingRange { - endLine = 1 - endCharacter = 22 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "x.set()." - range { - startLine = 0 - startCharacter = 4 - endLine = 0 - endCharacter = 5 - } - enclosingRange { - endLine = 1 - endCharacter = 22 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "x.get()." - range { - startLine = 1 - startCharacter = 4 - endLine = 1 - endCharacter = 7 - } - enclosingRange { - startLine = 1 - startCharacter = 4 - endLine = 1 - endCharacter = 22 - } - }, - ) - ), - ), - ExpectedSymbols( - "top level properties - explicit setter", - SourceFile.testKt( - """ - |var x: Int = 5 - | set(value) { field = value + 5 } - |""" - .trimMargin() - ), - scip = - ScipData( - expectedOccurrences = - listOf( - scipOccurrence { - role = DEFINITION - symbol = "x." - range { - startLine = 0 - startCharacter = 4 - endLine = 0 - endCharacter = 5 - } - enclosingRange { - endLine = 1 - endCharacter = 36 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "x.get()." - range { - startLine = 0 - startCharacter = 4 - endLine = 0 - endCharacter = 5 - } - enclosingRange { - endLine = 1 - endCharacter = 36 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "x.set()." - range { - startLine = 1 - startCharacter = 4 - endLine = 1 - endCharacter = 7 - } - enclosingRange { - startLine = 1 - startCharacter = 4 - endLine = 1 - endCharacter = 36 - } - }, - ) - ), - ), - ExpectedSymbols( - "top level properties - explicit getter & setter", - SourceFile.testKt( - """ - |var x: Int = 5 - | get() = field + 10 - | set(value) { field = value + 10 } - |""" - .trimMargin() - ), - scip = - ScipData( - expectedOccurrences = - listOf( - scipOccurrence { - role = DEFINITION - symbol = "x." - range { - startLine = 0 - startCharacter = 4 - endLine = 0 - endCharacter = 5 - } - enclosingRange { - endLine = 2 - endCharacter = 37 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "x.get()." - range { - startLine = 1 - startCharacter = 4 - endLine = 1 - endCharacter = 7 - } - enclosingRange { - startLine = 1 - startCharacter = 4 - endLine = 1 - endCharacter = 22 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "x.set()." - range { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 7 - } - enclosingRange { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 37 - } - }, - ) - ), - ), - ExpectedSymbols( - "class constructor properties", - SourceFile.testKt( - """ - |class Test(var sample: Int, text: String): Throwable(sample.toString()) { - | fun test() { - | println(sample) - | } - |} - |""" - .trimMargin() - ), - scip = - ScipData( - expectedOccurrences = - listOf( - scipOccurrence { - role = DEFINITION - symbol = "Test#``().(sample)" - range { - startLine = 0 - startCharacter = 15 - endLine = 0 - endCharacter = 21 - } - enclosingRange { - startCharacter = 11 - endCharacter = 26 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "Test#sample." - range { - startLine = 0 - startCharacter = 15 - endLine = 0 - endCharacter = 21 - } - enclosingRange { - startCharacter = 11 - endCharacter = 26 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "Test#sample.get()." - range { - startLine = 0 - startCharacter = 15 - endLine = 0 - endCharacter = 21 - } - enclosingRange { - startCharacter = 11 - endCharacter = 26 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "Test#sample.set()." - range { - startLine = 0 - startCharacter = 15 - endLine = 0 - endCharacter = 21 - } - enclosingRange { - startCharacter = 11 - endCharacter = 26 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "Test#``().(sample)" - range { - startLine = 0 - startCharacter = 53 - endLine = 0 - endCharacter = 59 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "Test#sample." - range { - startLine = 2 - startCharacter = 16 - endLine = 2 - endCharacter = 22 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "Test#sample.get()." - range { - startLine = 2 - startCharacter = 16 - endLine = 2 - endCharacter = 22 - } - }, - ) - ), - ), - ) - .mapCheckExpectedSymbols() - - @TestFactory - fun `class constructors`() = - listOf( - ExpectedSymbols( - "implicit primary constructor", - SourceFile.testKt( - """ - |class Banana - |""" - .trimMargin() - ), - scip = - ScipData( - expectedOccurrences = - listOf( - scipOccurrence { - role = DEFINITION - symbol = "Banana#" - range { - startLine = 0 - startCharacter = 6 - endLine = 0 - endCharacter = 12 - } - enclosingRange { endCharacter = 12 } - }, - scipOccurrence { - role = DEFINITION - symbol = "Banana#``()." - range { - startLine = 0 - startCharacter = 6 - endLine = 0 - endCharacter = 12 - } - enclosingRange { endCharacter = 12 } - }, - ) - ), - ), - ExpectedSymbols( - "explicit primary constructor without keyword", - SourceFile.testKt( - """ - |class Banana(size: Int) - |""" - .trimMargin() - ), - scip = - ScipData( - expectedOccurrences = - listOf( - scipOccurrence { - role = DEFINITION - symbol = "Banana#" - range { - startLine = 0 - startCharacter = 6 - endLine = 0 - endCharacter = 12 - } - enclosingRange { endCharacter = 23 } - }, - scipOccurrence { - role = DEFINITION - symbol = "Banana#``()." - range { - startLine = 0 - startCharacter = 6 - endLine = 0 - endCharacter = 12 - } - enclosingRange { - startCharacter = 12 - endCharacter = 23 - } - }, - ) - ), - ), - ExpectedSymbols( - "explicit primary constructor with keyword", - SourceFile.testKt( - """ - |class Banana constructor(size: Int) - |""" - .trimMargin() - ), - scip = - ScipData( - expectedOccurrences = - listOf( - scipOccurrence { - role = DEFINITION - symbol = "Banana#" - range { - startLine = 0 - startCharacter = 6 - endLine = 0 - endCharacter = 12 - } - enclosingRange { endCharacter = 35 } - }, - scipOccurrence { - role = DEFINITION - symbol = "Banana#``()." - range { - startLine = 0 - startCharacter = 13 - endLine = 0 - endCharacter = 24 - } - enclosingRange { - startCharacter = 13 - endCharacter = 35 - } - }, - ) - ), - ), - ) - .mapCheckExpectedSymbols() - - @TestFactory - fun `Single Abstract Method interface`() = - listOf( - ExpectedSymbols( - "basic java.lang.Runnable", - SourceFile.testKt( - """ - |val x = Runnable { }.run() - |""" - .trimMargin() - ), - scip = - ScipData( - expectedOccurrences = - listOf( - scipOccurrence { - role = REFERENCE - symbol = "java/lang/Runnable#" - range { - startLine = 0 - startCharacter = 8 - endLine = 0 - endCharacter = 16 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "java/lang/Runnable#run()." - range { - startLine = 0 - startCharacter = 21 - endLine = 0 - endCharacter = 24 - } - }, - ) - ), - ) - ) - .mapCheckExpectedSymbols() - - @TestFactory - fun kdoc() = - listOf( - ExpectedSymbols( - "empty kdoc line", - SourceFile.testKt( - """ - |/** - | - |hello world - |* test content - |*/ - |val x = "" - |""" - .trimMargin() - ), - scip = - ScipData( - expectedSymbols = - listOf( - scipSymbol { - symbol = "x." - kind = Kind.Property - enclosingSymbol = "_root_/" - displayName = "x" - signatureText = "public final val x: String" - documentation("hello world\n test content") - }, - scipSymbol { - symbol = "x.get()." - kind = Kind.Method - enclosingSymbol = "x." - displayName = "x" - signatureText = "public get(): String" - documentation("hello world\n test content") - }, - ) - ), - ) - ) - .mapCheckExpectedSymbols() -} diff --git a/scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/Utils.kt b/scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/Utils.kt deleted file mode 100644 index 5622fe594..000000000 --- a/scip-kotlinc/src/test/kotlin/org/scip_code/scip_java/kotlinc/test/Utils.kt +++ /dev/null @@ -1,209 +0,0 @@ -package org.scip_code.scip_java.kotlinc.test - -import com.tschuchort.compiletesting.KotlinCompilation -import com.tschuchort.compiletesting.SourceFile -import io.kotest.assertions.assertSoftly -import io.kotest.assertions.throwables.shouldNotThrowAny -import io.kotest.matchers.collections.shouldContainInOrder -import io.kotest.matchers.shouldBe -import java.nio.file.Path -import java.nio.file.Paths -import org.intellij.lang.annotations.Language -import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension -import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar -import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.diagnostics.DiagnosticReporter -import org.jetbrains.kotlin.fir.FirSession -import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind -import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext -import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers -import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFileChecker -import org.jetbrains.kotlin.fir.declarations.FirFile -import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar -import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter -import org.junit.jupiter.api.Assumptions.assumeFalse -import org.junit.jupiter.api.DynamicTest -import org.junit.jupiter.api.DynamicTest.dynamicTest -import org.scip_code.scip.Document -import org.scip_code.scip.Occurrence -import org.scip_code.scip.SymbolInformation -import org.scip_code.scip_java.kotlinc.* -import org.scip_code.scip_java.kotlinc.AnalyzerCheckers.Companion.visitors -import org.scip_code.scip_java.shared.ScipOptions - -data class ExpectedSymbols( - val testName: String, - val source: SourceFile, - val symbolsCacheData: SymbolCacheData? = null, - val scip: ScipData? = null, -) { - /** Subset of a SCIP [Document] that a single test wants to assert on. */ - data class ScipData( - val expectedOccurrences: List? = null, - val expectedSymbols: List? = null, - ) - - data class SymbolCacheData( - val expectedGlobals: List? = null, - val localsCount: Int? = null, - ) -} - -fun SourceFile.Companion.testKt(@Language("kotlin") contents: String): SourceFile = - kotlin("Test.kt", contents) - -@ExperimentalCompilerApi -fun List.mapCheckExpectedSymbols(): List = - this.flatMap { (testName, source, symbolsData, scipData) -> - val globals = GlobalSymbolsCache(testing = true) - val locals = LocalSymbolsCache() - lateinit var document: Document - val compilation = configureTestCompiler(source, globals, locals) { document = it } - listOf( - dynamicTest("$testName - compilation") { - val result = shouldNotThrowAny { compilation.compile() } - result.exitCode shouldBe KotlinCompilation.ExitCode.OK - }, - dynamicTest("$testName - symbols") { - symbolsData?.apply { - println( - "checking symbols: ${expectedGlobals?.size ?: 0} globals and presence of $localsCount locals" - ) - checkContainsExpectedSymbols(globals, locals, expectedGlobals, localsCount) - } ?: assumeFalse(true) - }, - dynamicTest("$testName - scip") { - scipData?.apply { - println( - "checking scip: ${expectedOccurrences?.size ?: 0} occurrences and ${expectedSymbols?.size ?: 0} symbols" - ) - checkContainsExpectedScip(document, expectedOccurrences, expectedSymbols) - } ?: assumeFalse(true) - }, - ) - } - -fun checkContainsExpectedSymbols( - globals: GlobalSymbolsCache, - locals: LocalSymbolsCache, - expectedGlobals: List?, - localsCount: Int? = null, -) { - assertSoftly(globals) { expectedGlobals?.let { this.shouldContainInOrder(it) } } - localsCount?.also { locals.size shouldBe it } -} - -fun checkContainsExpectedScip( - document: Document, - expectedOccurrences: List?, - expectedSymbols: List?, -) { - assertSoftly(document.occurrencesList) { - expectedOccurrences?.let { this.shouldContainInOrder(it) } - } - assertSoftly(document.symbolsList) { expectedSymbols?.let { this.shouldContainInOrder(it) } } -} - -@OptIn(ExperimentalCompilerApi::class) -private fun configureTestCompiler( - source: SourceFile, - globals: GlobalSymbolsCache, - locals: LocalSymbolsCache, - hook: (Document) -> Unit = {}, -): KotlinCompilation { - val compilation = - KotlinCompilation().apply { - sources = listOf(source) - inheritClassPath = true - verbose = false - } - - val analyzer = scipVisitorAnalyzer(globals, locals, compilation.workingDir.toPath(), hook) - compilation.apply { compilerPluginRegistrars = listOf(analyzer) } - return compilation -} - -private class TestAnalyzerDeclarationCheckers( - globals: GlobalSymbolsCache, - locals: LocalSymbolsCache, - sourceRoot: Path, -) : AnalyzerCheckers.AnalyzerDeclarationCheckers(sourceRoot) { - override val fileCheckers: Set = - setOf( - object : FirFileChecker(MppCheckerKind.Common) { - context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirFile) { - val ktFile = declaration.sourceFile ?: return - val lineMap = LineMap(declaration) - val visitor = ScipVisitor(sourceRoot, ktFile, lineMap, globals, locals) - visitors[ktFile] = visitor - } - }, - AnalyzerCheckers.SemanticImportsChecker(), - ) -} - -private class TestAnalyzerCheckers(session: FirSession) : AnalyzerCheckers(session) { - override val declarationCheckers: DeclarationCheckers - get() = - TestAnalyzerDeclarationCheckers( - session.testAnalyzerParamsProvider.globals, - session.testAnalyzerParamsProvider.locals, - session.testAnalyzerParamsProvider.sourceroot, - ) -} - -class TestAnalyzerParamsProvider( - session: FirSession, - var globals: GlobalSymbolsCache, - var locals: LocalSymbolsCache, - sourceroot: Path, -) : AnalyzerParamsProvider(session, ScipOptions().apply { this.sourceroot = sourceroot }) { - companion object { - fun getFactory( - globals: GlobalSymbolsCache, - locals: LocalSymbolsCache, - sourceroot: Path, - ): Factory { - return Factory { TestAnalyzerParamsProvider(it, globals, locals, sourceroot) } - } - } -} - -val FirSession.testAnalyzerParamsProvider: TestAnalyzerParamsProvider by - FirSession.sessionComponentAccessor() - -@OptIn(ExperimentalCompilerApi::class) -fun scipVisitorAnalyzer( - globals: GlobalSymbolsCache, - locals: LocalSymbolsCache, - sourceroot: Path, - hook: (Document) -> Unit = {}, -): CompilerPluginRegistrar { - return object : CompilerPluginRegistrar() { - override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) { - FirExtensionRegistrarAdapter.registerExtension( - object : FirExtensionRegistrar() { - override fun ExtensionRegistrarContext.configurePlugin() { - +TestAnalyzerParamsProvider.getFactory(globals, locals, sourceroot) - +::TestAnalyzerCheckers - } - } - ) - IrGenerationExtension.registerExtension( - PostAnalysisExtension( - configuration = configuration, - sourceRoot = sourceroot, - targetRoot = Paths.get(""), - callback = hook, - ) - ) - } - - override val pluginId = PLUGIN_ID - - override val supportsK2: Boolean - get() = true - } -} diff --git a/scip-snapshots/README.md b/scip-snapshots/README.md index ebd30fc3d..d51cece11 100644 --- a/scip-snapshots/README.md +++ b/scip-snapshots/README.md @@ -1,7 +1,7 @@ # SCIP snapshots This directory contains end-to-end snapshot fixtures for `scip-javac`, -`scip-kotlinc`, and future mixed Java/Kotlin cases. +`scip-kotlin-analysis`, and future mixed Java/Kotlin cases. ## Layout diff --git a/scip-snapshots/cases/kotlin/common/build.gradle.kts b/scip-snapshots/cases/kotlin/common/build.gradle.kts index be5226192..e3fc46602 100644 --- a/scip-snapshots/cases/kotlin/common/build.gradle.kts +++ b/scip-snapshots/cases/kotlin/common/build.gradle.kts @@ -1,9 +1,7 @@ import org.scip_code.scip_java.buildlogic.cleanDirectoryBeforeRunning import org.scip_code.scip_java.buildlogic.publishDirectoryArtifact -import org.scip_code.scip_java.buildlogic.scipKotlincPluginArgs import org.scip_code.scip_java.buildlogic.shadowJarArtifact import org.scip_code.scip_java.buildlogic.useScipJavac -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("scip.java-library") @@ -11,14 +9,12 @@ plugins { } val javacShadowJar = shadowJarArtifact(":scip-javac", "javacShadowJar") -val kotlincShadowJar = shadowJarArtifact(":scip-kotlinc", "kotlincShadowJar") dependencies { implementation(libs.kotlin.stdlib) } -// Runtime classpath of the Analysis-API-based indexer, used by the -// scipIndexKotlinAnalysis comparison task below. +// Runtime classpath of the Analysis-API-based Kotlin indexer. val scipKotlinAnalysis: Configuration by configurations.creating { isCanBeConsumed = false } @@ -31,38 +27,21 @@ val scipTargetroot = layout.buildDirectory.dir("scip-targetroot") val sourceroot = rootProject.rootDir.absolutePath val targetroot = scipTargetroot.get().asFile.absolutePath -tasks.named("compileKotlin") { - inputs.files(kotlincShadowJar) - outputs.dir(scipTargetroot) - compilerOptions.freeCompilerArgs.addAll(scipKotlincPluginArgs(kotlincShadowJar.elements, sourceroot, targetroot)) - cleanDirectoryBeforeRunning(scipTargetroot) -} - -tasks.named("compileJava") { - useScipJavac(rootDir, javacShadowJar, scipTargetroot) - options.annotationProcessorPath = javacShadowJar - options.compilerArgs.add("-Xplugin:scip -sourceroot:$sourceroot -targetroot:$targetroot") -} - -publishDirectoryArtifact("scipTargetrootElements", scipTargetroot, tasks.named("classes")) - -// Indexes the same Kotlin sources with the standalone Analysis API indexer into a -// separate targetroot, so its SCIP output can be compared against scip-kotlinc's. -val scipAnalysisTargetroot = layout.buildDirectory.dir("scip-targetroot-analysis") - -tasks.register("scipIndexKotlinAnalysis") { +// Kotlin sources are indexed by the standalone scip-kotlin-analysis indexer; +// compileKotlin only produces the classes that compileJava needs. +val scipIndexKotlin = tasks.register("scipIndexKotlin") { classpath = scipKotlinAnalysis mainClass.set("org.scip_code.scip_java.kotlin_analysis.MainKt") val kotlinSources = layout.projectDirectory.dir("src/main/kotlin") val compileClasspath = sourceSets.main.map { it.compileClasspath } inputs.dir(kotlinSources) inputs.files(compileClasspath) - outputs.dir(scipAnalysisTargetroot) - cleanDirectoryBeforeRunning(scipAnalysisTargetroot) + outputs.dir(scipTargetroot) + cleanDirectoryBeforeRunning(scipTargetroot) // Locals only: the argument provider must not capture the build script // (configuration cache). val sourcerootArg = sourceroot - val targetrootArg = scipAnalysisTargetroot + val targetrootArg = scipTargetroot val kotlinSourcesArg = kotlinSources.asFile.absolutePath argumentProviders.add( CommandLineArgumentProvider { @@ -78,3 +57,18 @@ tasks.register("scipIndexKotlinAnalysis") { } ) } + +tasks.named("compileJava") { + useScipJavac(rootDir, javacShadowJar, scipTargetroot) + options.annotationProcessorPath = javacShadowJar + options.compilerArgs.add("-Xplugin:scip -sourceroot:$sourceroot -targetroot:$targetroot") + // The Kotlin indexer cleans the targetroot before writing its shards; javac + // must add its shards afterwards. + mustRunAfter(scipIndexKotlin) +} + +tasks.named("classes") { + dependsOn(scipIndexKotlin) +} + +publishDirectoryArtifact("scipTargetrootElements", scipTargetroot, tasks.named("classes")) diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt index a458ba724..e86954de2 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt @@ -3,7 +3,10 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Tagged# @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) +// ^^^^^^ reference scip-java maven . . kotlin/annotation/Target# +// ^^^^^^^^^^^^^^^^ reference scip-java maven . . kotlin/annotation/AnnotationTarget# // ^^^^^ reference scip-java maven . . kotlin/annotation/AnnotationTarget#CLASS. +// ^^^^^^^^^^^^^^^^ reference scip-java maven . . kotlin/annotation/AnnotationTarget# // ^^^^^^^^ reference scip-java maven . . kotlin/annotation/AnnotationTarget#FUNCTION. // ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#``().(tag) @@ -42,6 +45,7 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/AnnotatedService# //⌄ enclosing_range_start scip-java maven . . snapshots/AnnotatedService#``(). @Tagged("service") +// ^^^^^^ reference scip-java maven . . snapshots/Tagged# class AnnotatedService { // ^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/AnnotatedService# // display_name AnnotatedService @@ -53,6 +57,7 @@ // > public constructor(): AnnotatedService // ⌄ enclosing_range_start scip-java maven . . snapshots/AnnotatedService#run(). @Tagged("run") +// ^^^^^^ reference scip-java maven . . snapshots/Tagged# fun run(): String = "running" // ^^^ definition scip-java maven . . snapshots/AnnotatedService#run(). // display_name run diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt index 288589564..12e88e573 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt @@ -5,60 +5,52 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``().(banana) // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#banana. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#banana.get(). -// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#banana.set(). -// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#banana.set().(value) +// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#getBanana(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#setBanana(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#setBanana().(value) // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``().(apple) class Class constructor(private var banana: Int, apple: String) : // ^^^^^ definition scip-java maven . . snapshots/Class# -// kind Class // display_name Class // signature_documentation // > public final class Class : Throwable // relationship scip-java maven . . kotlin/Throwable# implementation // ^^^^^^^^^^^ definition scip-java maven . . snapshots/Class#``(). -// kind Constructor // display_name Class // signature_documentation // > public constructor(banana: Int, apple: String): Class // ^^^^^^ definition scip-java maven . . snapshots/Class#``().(banana) -// kind Parameter // display_name banana // signature_documentation // > banana: Int // ^^^^^^ definition scip-java maven . . snapshots/Class#banana. -// kind Property // display_name banana // signature_documentation // > private final var banana: Int // ^^^^^^ reference scip-java maven . . snapshots/Class#``().(banana) -// ^^^^^^ definition scip-java maven . . snapshots/Class#banana.get(). -// kind Method +// ^^^^^^ definition scip-java maven . . snapshots/Class#getBanana(). // display_name banana // signature_documentation // > private get(): Int -// ^^^^^^ definition scip-java maven . . snapshots/Class#banana.set(). -// kind Method +// ^^^^^^ definition scip-java maven . . snapshots/Class#setBanana(). // display_name banana // signature_documentation // > private set(value: Int): Unit -// ^^^^^^ definition scip-java maven . . snapshots/Class#banana.set().(value) -// kind Parameter +// ^^^^^^ definition scip-java maven . . snapshots/Class#setBanana().(value) // display_name value // signature_documentation // > value: Int // ^^^ reference scip-java maven . . kotlin/Int# // ^^^^^ definition scip-java maven . . snapshots/Class#``().(apple) -// kind Parameter // display_name apple // signature_documentation // > apple: String // ^^^^^^ reference scip-java maven . . kotlin/String# // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#``().(banana) // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#banana. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#banana.get(). -// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#banana.set(). -// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#banana.set().(value) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#getBanana(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#setBanana(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#setBanana().(value) // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#``().(apple) // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#``(). Throwable(banana.toString() + apple) { @@ -73,50 +65,44 @@ } // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#asdf. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#asdf.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#getAsdf(). val asdf = // ^^^^ definition scip-java maven . . snapshots/Class#asdf. -// kind Property // display_name asdf // signature_documentation // > public final val asdf: Any -// ^^^^ definition scip-java maven . . snapshots/Class#asdf.get(). -// kind Method +// ^^^^ definition scip-java maven . . snapshots/Class#getAsdf(). // display_name asdf // signature_documentation // > public get(): Any -// ⌄ enclosing_range_start local 0 -// ⌄ enclosing_range_start local 1 +// ⌄ enclosing_range_start scip-java maven . . snapshots/``# +// ⌄ enclosing_range_start scip-java maven . . snapshots/``#``(). object { -// ^^^^^^ definition local 0 -// kind Class +// ^^^^^^ definition scip-java maven . . snapshots/``# // display_name // signature_documentation // > object : Any -// ^^^^^^ definition local 1 -// kind Constructor +// ^^^^^^ definition scip-java maven . . snapshots/``#``(). // display_name // signature_documentation // > private constructor(): -// ⌄ enclosing_range_start local 2 +// ⌄ enclosing_range_start scip-java maven . . snapshots/``#doStuff(). fun doStuff() = Unit -// ^^^^^^^ definition local 2 -// kind Method +// ^^^^^^^ definition scip-java maven . . snapshots/``#doStuff(). // display_name doStuff // signature_documentation // > public final fun doStuff(): Unit // ^^^^ reference scip-java maven . . kotlin/Unit# -// ⌃ enclosing_range_end local 2 +// ⌃ enclosing_range_end scip-java maven . . snapshots/``#doStuff(). } // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#asdf. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#asdf.get(). -// ⌃ enclosing_range_end local 0 -// ⌃ enclosing_range_end local 1 +// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#getAsdf(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/``# +// ⌃ enclosing_range_end scip-java maven . . snapshots/``#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``(+1). constructor() : this(1, "") // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Class#``(+1). -// kind Constructor // display_name Class // signature_documentation // > public constructor(): Class @@ -126,12 +112,10 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``(+2).(banana) constructor(banana: Int) : this(banana, "") // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Class#``(+2). -// kind Constructor // display_name Class // signature_documentation // > public constructor(banana: Int): Class // ^^^^^^ definition scip-java maven . . snapshots/Class#``(+2).(banana) -// kind Parameter // display_name banana // signature_documentation // > banana: Int @@ -143,7 +127,6 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#run(). fun run() { // ^^^ definition scip-java maven . . snapshots/Class#run(). -// kind Method // display_name run // signature_documentation // > public final fun run(): Unit @@ -153,12 +136,12 @@ println("I eat $banana for lunch") // ^^^^^^^ reference scip-java maven . . kotlin/io/println(). // ^^^^^^ reference scip-java maven . . snapshots/Class#banana. -// ^^^^^^ reference scip-java maven . . snapshots/Class#banana.get(). -// ^^^^^^ reference scip-java maven . . snapshots/Class#banana.set(). +// ^^^^^^ reference scip-java maven . . snapshots/Class#getBanana(). +// ^^^^^^ reference scip-java maven . . snapshots/Class#setBanana(). banana = 42 // ^^^^^^ reference scip-java maven . . snapshots/Class#banana. -// ^^^^^^ reference scip-java maven . . snapshots/Class#banana.get(). -// ^^^^^^ reference scip-java maven . . snapshots/Class#banana.set(). +// ^^^^^^ reference scip-java maven . . snapshots/Class#getBanana(). +// ^^^^^^ reference scip-java maven . . snapshots/Class#setBanana(). } // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#run(). } diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt index db2d168b9..37ace2dd4 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt @@ -5,32 +5,27 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#``(). class CompanionOwner { // ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner# -// kind Class // display_name CompanionOwner // signature_documentation // > public final class CompanionOwner : Any // ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#``(). -// kind Constructor // display_name CompanionOwner // signature_documentation // > public constructor(): CompanionOwner // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion# // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion#``(). companion object { -// ^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion# -// kind Class -// display_name Companion -// signature_documentation -// > public final companion object Companion : Any +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion# +// display_name Companion +// signature_documentation +// > public final companion object Companion : Any // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion#``(). -// kind Constructor // display_name Companion // signature_documentation // > private constructor(): CompanionOwner.Companion // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion#create(). fun create(): CompanionOwner = CompanionOwner() // ^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion#create(). -// kind Method // display_name create // signature_documentation // > public final fun create(): CompanionOwner @@ -43,12 +38,11 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#create(). fun create(): Int = CompanionOwner.create().hashCode() // ^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#create(). -// kind Method // display_name create // signature_documentation // > public final fun create(): Int // ^^^ reference scip-java maven . . kotlin/Int# -// ^^^^^^^^^^^^^^ reference scip-java maven . . snapshots/CompanionOwner# +// ^^^^^^^^^^^^^^ reference scip-java maven . . snapshots/CompanionOwner#Companion# // ^^^^^^ reference scip-java maven . . snapshots/CompanionOwner#Companion#create(). // ^^^^^^^^ reference scip-java maven . . kotlin/Any#hashCode(). // ⌃ enclosing_range_end scip-java maven . . snapshots/CompanionOwner#create(). diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Docstrings.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Docstrings.kt index 69c6333f2..b7a60c8a5 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Docstrings.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Docstrings.kt @@ -10,12 +10,10 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/DocstringSuperclass#``(). abstract class DocstringSuperclass // ^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/DocstringSuperclass# -// kind Class // display_name DocstringSuperclass // signature_documentation // > public abstract class DocstringSuperclass : Any // ^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/DocstringSuperclass#``(). -// kind Constructor // display_name DocstringSuperclass // signature_documentation // > public constructor(): DocstringSuperclass @@ -26,7 +24,6 @@ /** Example class docstring. */ class Docstrings : DocstringSuperclass(), Serializable { // ^^^^^^^^^^ definition scip-java maven . . snapshots/Docstrings# -// kind Class // display_name Docstrings // signature_documentation // > public final class Docstrings : DocstringSuperclass, Serializable @@ -35,7 +32,6 @@ // relationship scip-java maven . . snapshots/DocstringSuperclass# implementation // relationship scip-java maven jdk 17 java/io/Serializable# implementation // ^^^^^^^^^^ definition scip-java maven . . snapshots/Docstrings#``(). -// kind Constructor // display_name Docstrings // signature_documentation // > public constructor(): Docstrings @@ -51,7 +47,6 @@ /** Example method docstring. */ fun docstrings() { } // ^^^^^^^^^^ definition scip-java maven . . snapshots/docstrings(). -// kind Method // display_name docstrings // signature_documentation // > public final fun docstrings(): Unit diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt index 3361608b0..a87d36f11 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt @@ -6,13 +6,12 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Suit#valueOf(). //⌄ enclosing_range_start scip-java maven . . snapshots/Suit#valueOf().(value) //⌄ enclosing_range_start scip-java maven . . snapshots/Suit#entries. -//⌄ enclosing_range_start scip-java maven . . snapshots/getEntries(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Suit#getEntries(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#``().(symbol) // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#symbol. // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#getSymbol(). enum class Suit(val symbol: Char) { -//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reference scip-java maven . . kotlin/Enum# //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reference scip-java maven . . snapshots/Suit# // ^^^^ definition scip-java maven . . snapshots/Suit# // display_name Suit @@ -39,7 +38,7 @@ // display_name entries // signature_documentation // > public final static val entries: EnumEntries -// ^^^^ definition scip-java maven . . snapshots/getEntries(). +// ^^^^ definition scip-java maven . . snapshots/Suit#getEntries(). // display_name entries // signature_documentation // > public get(): EnumEntries @@ -61,34 +60,20 @@ // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#symbol. // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#getSymbol(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#``(). -// ⌄ enclosing_range_start scip-java maven . . snapshots/``# -// ⌄ enclosing_range_start scip-java maven . . snapshots/``#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#HEARTS. HEARTS('h'), -// ^^^^^^ definition scip-java maven . . snapshots/``# -// display_name -// signature_documentation -// > object : Suit -// relationship scip-java maven . . snapshots/Suit# implementation -// ^^^^^^ definition scip-java maven . . snapshots/``#``(). +// ^^^^^^ definition scip-java maven . . snapshots/Suit#HEARTS. // display_name HEARTS // signature_documentation -// > private constructor(): -// ⌃ enclosing_range_end scip-java maven . . snapshots/``# -// ⌃ enclosing_range_end scip-java maven . . snapshots/``#``(). -// ⌄ enclosing_range_start scip-java maven . . snapshots/``# -// ⌄ enclosing_range_start scip-java maven . . snapshots/``#``(). +// > public final val HEARTS: Suit +// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#HEARTS. +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#SPADES. SPADES('s'); -// ^^^^^^ definition scip-java maven . . snapshots/``# -// display_name -// signature_documentation -// > object : Suit -// relationship scip-java maven . . snapshots/Suit# implementation -// ^^^^^^ definition scip-java maven . . snapshots/``#``(). +// ^^^^^^ definition scip-java maven . . snapshots/Suit#SPADES. // display_name SPADES // signature_documentation -// > private constructor(): -// ⌃ enclosing_range_end scip-java maven . . snapshots/``# -// ⌃ enclosing_range_end scip-java maven . . snapshots/``#``(). +// > public final val SPADES: Suit +// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#SPADES. // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#isRed(). fun isRed(): Boolean = symbol == 'h' @@ -99,6 +84,7 @@ // ^^^^^^^ reference scip-java maven . . kotlin/Boolean# // ^^^^^^ reference scip-java maven . . snapshots/Suit#symbol. // ^^^^^^ reference scip-java maven . . snapshots/Suit#getSymbol(). +// ^^ reference scip-java maven . . kotlin/Char#equals(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#isRed(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion# @@ -125,9 +111,9 @@ // signature_documentation // > symbol: Char // ^^^^ reference scip-java maven . . kotlin/Char# -// ^^^^^ reference scip-java maven . . snapshots/Suit# +// ^^^^ reference scip-java maven . . snapshots/Suit# // ^^^^^^^ reference scip-java maven . . snapshots/Suit#entries. -// ^^^^^^^ reference scip-java maven . . snapshots/getEntries(). +// ^^^^^^^ reference scip-java maven . . snapshots/Suit#getEntries(). // ^^^^ reference scip-java maven . . kotlin/collections/find(+9). // ^^^^^^^^^^^^^^^^^^^^^^^ definition local 0 // display_name it @@ -136,6 +122,7 @@ // ^^ reference local 0 // ^^^^^^ reference scip-java maven . . snapshots/Suit#symbol. // ^^^^^^ reference scip-java maven . . snapshots/Suit#getSymbol(). +// ^^ reference scip-java maven . . kotlin/Char#equals(). // ^^^^^^ reference scip-java maven . . snapshots/Suit#Companion#fromSymbol().(symbol) // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#Companion#fromSymbol().(symbol) // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#Companion#fromSymbol(). @@ -149,7 +136,7 @@ //⌃ enclosing_range_end scip-java maven . . snapshots/Suit#valueOf(). //⌃ enclosing_range_end scip-java maven . . snapshots/Suit#valueOf().(value) //⌃ enclosing_range_end scip-java maven . . snapshots/Suit#entries. -//⌃ enclosing_range_end scip-java maven . . snapshots/getEntries(). +//⌃ enclosing_range_end scip-java maven . . snapshots/Suit#getEntries(). //⌄ enclosing_range_start scip-java maven . . snapshots/describe(). // ⌄ enclosing_range_start scip-java maven . . snapshots/describe().(suit) @@ -174,8 +161,10 @@ // ^^^^ reference scip-java maven . . snapshots/describe().(suit) // ⌃ enclosing_range_end local 1 Suit.HEARTS -> "red" +// ^^^^ reference scip-java maven . . snapshots/Suit# // ^^^^^^ reference scip-java maven . . snapshots/Suit#HEARTS. Suit.SPADES -> "black" +// ^^^^ reference scip-java maven . . snapshots/Suit# // ^^^^^^ reference scip-java maven . . snapshots/Suit#SPADES. } // ⌃ enclosing_range_end scip-java maven . . snapshots/describe(). diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt index 0ee9ed764..2b9224e80 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt @@ -5,12 +5,10 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/sampleText().(x) fun sampleText(x: String = "") { // ^^^^^^^^^^ definition scip-java maven . . snapshots/sampleText(). -// kind Method // display_name sampleText // signature_documentation // > public final fun sampleText(x: String = ...): Unit // ^ definition scip-java maven . . snapshots/sampleText().(x) -// kind Parameter // display_name x // signature_documentation // > x: String = ... diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt index f2731f886..2d37f628a 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt @@ -17,9 +17,11 @@ // signature_documentation // > public constructor>(items: MutableList): Container // ^ definition scip-java maven . . snapshots/Container#[T] -// display_name FirTypeParameterSymbol T +// display_name T // signature_documentation // > T : Comparable +// ^^^^^^^^^^ reference scip-java maven . . kotlin/Comparable# +// ^ reference scip-java maven . . snapshots/Container#[T] // ^^^^^ definition scip-java maven . . snapshots/Container#``().(items) // display_name items // signature_documentation @@ -33,7 +35,8 @@ // display_name items // signature_documentation // > private get(): MutableList -// ^^^^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/MutableList# +// ^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/MutableList# +// ^ reference scip-java maven . . snapshots/Container#[T] // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#[T] // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#``().(items) // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#items. @@ -50,7 +53,9 @@ // display_name item // signature_documentation // > item: T -// ^^^^^^^^^^^^ reference scip-java maven . . snapshots/Container# +// ^ reference scip-java maven . . snapshots/Container#[T] +// ^^^^^^^^^ reference scip-java maven . . snapshots/Container# +// ^ reference scip-java maven . . snapshots/Container#[T] // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#add().(item) items.add(item) // ^^^^^ reference scip-java maven . . snapshots/Container#items. @@ -66,9 +71,10 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#mapItems().(transform) fun mapItems(transform: (T) -> R?): List = items.mapNotNull(transform) // ^ definition scip-java maven . . snapshots/Container#mapItems().[R] -// display_name FirTypeParameterSymbol R +// display_name R // signature_documentation // > R : Any +// ^^^ reference scip-java maven . . kotlin/Any# // ^^^^^^^^ definition scip-java maven . . snapshots/Container#mapItems(). // display_name mapItems // signature_documentation @@ -77,8 +83,10 @@ // display_name transform // signature_documentation // > transform: (T) -> R? -// ^^^^^^^^^ reference scip-java maven . . kotlin/Function1# -// ^^^^^^^ reference scip-java maven . . kotlin/collections/List# +// ^ reference scip-java maven . . snapshots/Container#[T] +// ^ reference scip-java maven . . snapshots/Container#mapItems().[R] +// ^^^^ reference scip-java maven . . kotlin/collections/List# +// ^ reference scip-java maven . . snapshots/Container#mapItems().[R] // ^^^^^ reference scip-java maven . . snapshots/Container#items. // ^^^^^ reference scip-java maven . . snapshots/Container#getItems(). // ^^^^^^^^^^ reference scip-java maven . . kotlin/collections/mapNotNull(+1). @@ -95,7 +103,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/firstOrSelf().(fallback) fun firstOrSelf(items: List, fallback: T): T = items.firstOrNull() ?: fallback // ^ definition scip-java maven . . snapshots/firstOrSelf().[T] -// display_name FirTypeParameterSymbol T +// display_name T // signature_documentation // > T // ^^^^^^^^^^^ definition scip-java maven . . snapshots/firstOrSelf(). @@ -106,11 +114,14 @@ // display_name items // signature_documentation // > items: List -// ^^^^^^^ reference scip-java maven . . kotlin/collections/List# +// ^^^^ reference scip-java maven . . kotlin/collections/List# +// ^ reference scip-java maven . . snapshots/firstOrSelf().[T] // ^^^^^^^^ definition scip-java maven . . snapshots/firstOrSelf().(fallback) // display_name fallback // signature_documentation // > fallback: T +// ^ reference scip-java maven . . snapshots/firstOrSelf().[T] +// ^ reference scip-java maven . . snapshots/firstOrSelf().[T] // ^^^^^ reference scip-java maven . . snapshots/firstOrSelf().(items) // ^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/firstOrNull(+19). // ^^^^^^^^ reference scip-java maven . . snapshots/firstOrSelf().(fallback) @@ -122,10 +133,12 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/StringContainer# typealias StringContainer = Container // ^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/StringContainer# -// display_name FirTypeAliasSymbol snapshots/StringContainer +// display_name StringContainer // signature_documentation // > public final typealias StringContainer = Container // > +// ^^^^^^^^^ reference scip-java maven . . snapshots/Container# +// ^^^^^^ reference scip-java maven . . kotlin/String# // ⌃ enclosing_range_end scip-java maven . . snapshots/StringContainer# //⌄ enclosing_range_start scip-java maven . . snapshots/useContainer(). @@ -134,11 +147,11 @@ // ^^^^^^^^^^^^ definition scip-java maven . . snapshots/useContainer(). // display_name useContainer // signature_documentation -// > public final fun useContainer(container: {snapshots/StringContainer=} Container): {snapshots/StringContainer=} Container +// > public final fun useContainer(container: StringContainer): StringContainer // ^^^^^^^^^ definition scip-java maven . . snapshots/useContainer().(container) // display_name container // signature_documentation -// > container: {snapshots/StringContainer=} Container +// > container: StringContainer // ^^^^^^^^^^^^^^^ reference scip-java maven . . snapshots/Container# // ^^^^^^^^^^^^^^^ reference scip-java maven . . snapshots/Container# // ^^^^^^^^^ reference scip-java maven . . snapshots/useContainer().(container) diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt index 0041a9f5a..f0c4f2d03 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt @@ -5,13 +5,11 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Overrides#``(). class Overrides : AutoCloseable { // ^^^^^^^^^ definition scip-java maven . . snapshots/Overrides# -// kind Class // display_name Overrides // signature_documentation -// > public final class Overrides : {kotlin/AutoCloseable=} AutoCloseable +// > public final class Overrides : AutoCloseable // relationship scip-java maven jdk 17 java/lang/AutoCloseable# implementation // ^^^^^^^^^ definition scip-java maven . . snapshots/Overrides#``(). -// kind Constructor // display_name Overrides // signature_documentation // > public constructor(): Overrides @@ -19,7 +17,6 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Overrides#close(). override fun close() { // ^^^^^ definition scip-java maven . . snapshots/Overrides#close(). -// kind Method // display_name close // signature_documentation // > public open override fun close(): Unit @@ -35,30 +32,26 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Animal# interface Animal { // ^^^^^^ definition scip-java maven . . snapshots/Animal# -// kind Interface // display_name Animal // signature_documentation // > public abstract interface Animal : Any // ⌄ enclosing_range_start scip-java maven . . snapshots/Animal#favoriteNumber. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Animal#favoriteNumber.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Animal#getFavoriteNumber(). val favoriteNumber: Int // ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Animal#favoriteNumber. -// kind Property // display_name favoriteNumber // signature_documentation // > public abstract val favoriteNumber: Int -// ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Animal#favoriteNumber.get(). -// kind Method +// ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Animal#getFavoriteNumber(). // display_name favoriteNumber // signature_documentation // > public get(): Int // ^^^ reference scip-java maven . . kotlin/Int# // ⌃ enclosing_range_end scip-java maven . . snapshots/Animal#favoriteNumber. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Animal#favoriteNumber.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Animal#getFavoriteNumber(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Animal#sound(). fun sound(): String // ^^^^^ definition scip-java maven . . snapshots/Animal#sound(). -// kind Method // display_name sound // signature_documentation // > public abstract fun sound(): String @@ -71,13 +64,11 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Bird#``(). open class Bird : Animal { // ^^^^ definition scip-java maven . . snapshots/Bird# -// kind Class // display_name Bird // signature_documentation // > public open class Bird : Animal // relationship scip-java maven . . snapshots/Animal# implementation // ^^^^ definition scip-java maven . . snapshots/Bird#``(). -// kind Constructor // display_name Bird // signature_documentation // > public constructor(): Bird @@ -85,25 +76,22 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Bird#favoriteNumber. override val favoriteNumber: Int // ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Bird#favoriteNumber. -// kind Property // display_name favoriteNumber // signature_documentation // > public open override val favoriteNumber: Int // ^^^ reference scip-java maven . . kotlin/Int# -// ⌄ enclosing_range_start scip-java maven . . snapshots/Bird#favoriteNumber.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Bird#getFavoriteNumber(). get() = 42 -// ^^^ definition scip-java maven . . snapshots/Bird#favoriteNumber.get(). -// kind Method +// ^^^ definition scip-java maven . . snapshots/Bird#getFavoriteNumber(). // display_name favoriteNumber // signature_documentation // > public get(): Int // ⌃ enclosing_range_end scip-java maven . . snapshots/Bird#favoriteNumber. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Bird#favoriteNumber.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Bird#getFavoriteNumber(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Bird#sound(). override fun sound(): String { // ^^^^^ definition scip-java maven . . snapshots/Bird#sound(). -// kind Method // display_name sound // signature_documentation // > public open override fun sound(): String @@ -119,13 +107,11 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#``(). class Seagull : Bird() { // ^^^^^^^ definition scip-java maven . . snapshots/Seagull# -// kind Class // display_name Seagull // signature_documentation // > public final class Seagull : Bird // relationship scip-java maven . . snapshots/Bird# implementation // ^^^^^^^ definition scip-java maven . . snapshots/Seagull#``(). -// kind Constructor // display_name Seagull // signature_documentation // > public constructor(): Seagull @@ -133,24 +119,21 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#favoriteNumber. override val favoriteNumber: Int // ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Seagull#favoriteNumber. -// kind Property // display_name favoriteNumber // signature_documentation // > public open override val favoriteNumber: Int // ^^^ reference scip-java maven . . kotlin/Int# -// ⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#favoriteNumber.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#getFavoriteNumber(). get() = 1337 -// ^^^ definition scip-java maven . . snapshots/Seagull#favoriteNumber.get(). -// kind Method +// ^^^ definition scip-java maven . . snapshots/Seagull#getFavoriteNumber(). // display_name favoriteNumber // signature_documentation // > public get(): Int // ⌃ enclosing_range_end scip-java maven . . snapshots/Seagull#favoriteNumber. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Seagull#favoriteNumber.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Seagull#getFavoriteNumber(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#sound(). override fun sound(): String { // ^^^^^ definition scip-java maven . . snapshots/Seagull#sound(). -// kind Method // display_name sound // signature_documentation // > public open override fun sound(): String diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt index 1748c0b3b..5266b8d31 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt @@ -2,29 +2,26 @@ // ^^^^^^^^^ reference scip-java maven . . snapshots/ //⌄ enclosing_range_start scip-java maven . . snapshots/x. -//⌄ enclosing_range_start scip-java maven . . snapshots/x.get(). +//⌄ enclosing_range_start scip-java maven . . snapshots/getX(). // ⌄ enclosing_range_start local 0 // ⌄ enclosing_range_start local 1 val x = arrayListOf().forEachIndexed { i, s -> println("$i $s") } // ^ definition scip-java maven . . snapshots/x. -// kind Property // display_name x // signature_documentation // > public final val x: Unit -// ^ definition scip-java maven . . snapshots/x.get(). -// kind Method +// ^ definition scip-java maven . . snapshots/getX(). // display_name x // signature_documentation // > public get(): Unit // ^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/arrayListOf(). -// ^^^^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/Iterable#forEachIndexed(). +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ^^^^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/forEachIndexed(+9). // ^ definition local 0 -// kind Parameter // display_name i // signature_documentation // > i: Int // ^ definition local 1 -// kind Parameter // display_name s // signature_documentation // > s: String @@ -34,52 +31,47 @@ // ⌃ enclosing_range_end local 0 // ⌃ enclosing_range_end local 1 // ⌃ enclosing_range_end scip-java maven . . snapshots/x. -// ⌃ enclosing_range_end scip-java maven . . snapshots/x.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/getX(). //⌄ enclosing_range_start scip-java maven . . snapshots/y. -//⌄ enclosing_range_start scip-java maven . . snapshots/y.get(). +//⌄ enclosing_range_start scip-java maven . . snapshots/getY(). val y = "fdsa".run { this.toByteArray() } // ^ definition scip-java maven . . snapshots/y. -// kind Property // display_name y // signature_documentation // > public final val y: ByteArray -// ^ definition scip-java maven . . snapshots/y.get(). -// kind Method +// ^ definition scip-java maven . . snapshots/getY(). // display_name y // signature_documentation // > public get(): ByteArray // ^^^ reference scip-java maven . . kotlin/run(+1). -// ^^^^^^^^^^^ reference scip-java maven . . kotlin/text/String#toByteArray(). +// ^^^^^^^^^^^ reference scip-java maven . . kotlin/text/toByteArray(). // ⌃ enclosing_range_end scip-java maven . . snapshots/y. -// ⌃ enclosing_range_end scip-java maven . . snapshots/y.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/getY(). //⌄ enclosing_range_start scip-java maven . . snapshots/z. -//⌄ enclosing_range_start scip-java maven . . snapshots/z.get(). +//⌄ enclosing_range_start scip-java maven . . snapshots/getZ(). // ⌄ enclosing_range_start local 2 val z = y.let { it.size } // ^ definition scip-java maven . . snapshots/z. -// kind Property // display_name z // signature_documentation // > public final val z: Int -// ^ definition scip-java maven . . snapshots/z.get(). -// kind Method +// ^ definition scip-java maven . . snapshots/getZ(). // display_name z // signature_documentation // > public get(): Int // ^ reference scip-java maven . . snapshots/y. -// ^ reference scip-java maven . . snapshots/y.get(). +// ^ reference scip-java maven . . snapshots/getY(). // ^^^ reference scip-java maven . . kotlin/let(). // ^^^^^^^^^^^ definition local 2 -// kind Parameter // display_name it // signature_documentation // > it: ByteArray // ^^ reference local 2 // ^^^^ reference scip-java maven . . kotlin/ByteArray#size. -// ^^^^ reference scip-java maven . . kotlin/ByteArray#size.get(). +// ^^^^ reference scip-java maven . . kotlin/ByteArray#getSize(). // ⌃ enclosing_range_end scip-java maven . . snapshots/z. -// ⌃ enclosing_range_end scip-java maven . . snapshots/z.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/getZ(). // ⌃ enclosing_range_end local 2 diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt index 6211a42e2..8de86a9f3 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt @@ -10,12 +10,10 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/ObjectKt#``(). object ObjectKt { // ^^^^^^^^ definition scip-java maven . . snapshots/ObjectKt# -// kind Class // display_name ObjectKt // signature_documentation // > public final object ObjectKt : Any // ^^^^^^^^ definition scip-java maven . . snapshots/ObjectKt#``(). -// kind Constructor // display_name ObjectKt // signature_documentation // > private constructor(): ObjectKt @@ -23,16 +21,14 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/ObjectKt#fail().(message) fun fail(message: String?): Nothing { // ^^^^ definition scip-java maven . . snapshots/ObjectKt#fail(). -// kind Method // display_name fail // signature_documentation // > public final fun fail(message: String?): Nothing // ^^^^^^^ definition scip-java maven . . snapshots/ObjectKt#fail().(message) -// kind Parameter // display_name message // signature_documentation // > message: String? -// ^^^^^^^ reference scip-java maven . . kotlin/String# +// ^^^^^^ reference scip-java maven . . kotlin/String# // ^^^^^^^ reference scip-java maven . . kotlin/Nothing# // ⌃ enclosing_range_end scip-java maven . . snapshots/ObjectKt#fail().(message) throw RuntimeException(message) diff --git a/settings.gradle.kts b/settings.gradle.kts index 6b12f3a5b..785b0b434 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,7 +23,6 @@ rootProject.name = "scip-java" include( "scip-shared", "scip-javac", - "scip-kotlinc", "scip-kotlin-analysis", "scip-aggregator", "scip-maven-plugin", From 286e6f0ecea1b9d0b76a871bea32ac058493d0ed Mon Sep 17 00:00:00 2001 From: jupblb Date: Thu, 2 Jul 2026 12:55:50 +0200 Subject: [PATCH 07/33] Update Analysis API engine to 2.4.0 --- .github/renovate.json | 13 ++++++++++++- gradle/libs.versions.toml | 19 +++++++++++-------- scip-kotlin-analysis/build.gradle.kts | 3 +++ settings.gradle.kts | 10 +++++++--- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/.github/renovate.json b/.github/renovate.json index 91989653d..5366f7b8c 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -4,5 +4,16 @@ "config:recommended" ], "automerge": true, - "semanticCommits": false + "semanticCommits": false, + "packageRules": [ + { + "description": "The Analysis API engine pin is managed manually: it lives in the JetBrains intellij-dependencies repository, uses IDE-aligned version schemes, and must move in lockstep with the `kotlin` toolchain version.", + "matchPackagePatterns": [ + "^org\\.jetbrains\\.kotlin:kotlin-compiler$", + "-for-ide$", + "^com\\.intellij\\.platform:" + ], + "enabled": false + } + ] } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0fc209663..a1afd1f66 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,14 +3,16 @@ clikt = "5.1.0" gradle-api = "8.11.1" junit-jupiter = "5.11.4" kotlin = "2.4.21" -# Kotlin Analysis API engine bundled by scip-kotlin-analysis. Independent from -# the `kotlin` toolchain version: `-for-ide` artifacts are only published as -# IDE-aligned builds (see https://redirector.kotlinlang.org/maven/kotlin-ide-plugin-dependencies). -kotlin-analysis-api = "2.2.0-ij251-98" +# Kotlin Analysis API engine bundled by scip-kotlin-analysis. Managed manually +# (see .github/renovate.json): the artifacts live in the JetBrains +# intellij-dependencies repository and the pin must stay <= one minor version +# ahead of `kotlin` so the toolchain can read its metadata. +kotlin-analysis-api = "2.4.0" kotlinx-collections-immutable = "0.3.8" -# Matches the coroutines dependency declared by the kotlin-compiler POM at the -# kotlin-analysis-api version above. -kotlinx-coroutines = "1.8.0" +# The IntelliJ fork of kotlinx-coroutines required by the Analysis API engine +# (provides kotlinx.coroutines.internal.intellij.IntellijCoroutines). Managed +# manually together with kotlin-analysis-api. +kotlinx-coroutines-intellij = "1.8.0-intellij-13" kotlinx-serialization = "1.12.0" lombok = "1.18.48" maven-plugin-annotations = "3.16.0" @@ -34,13 +36,14 @@ kotlin-analysis-api-k2 = { module = "org.jetbrains.kotlin:analysis-api-k2-for-id kotlin-analysis-api-platform-interface = { module = "org.jetbrains.kotlin:analysis-api-platform-interface-for-ide", version.ref = "kotlin-analysis-api" } kotlin-analysis-api-standalone = { module = "org.jetbrains.kotlin:analysis-api-standalone-for-ide", version.ref = "kotlin-analysis-api" } kotlin-analysis-compiler = { module = "org.jetbrains.kotlin:kotlin-compiler", version.ref = "kotlin-analysis-api" } +kotlin-analysis-compiler-common = { module = "org.jetbrains.kotlin:kotlin-compiler-common-for-ide", version.ref = "kotlin-analysis-api" } kotlin-analysis-low-level-api-fir = { module = "org.jetbrains.kotlin:low-level-api-fir-for-ide", version.ref = "kotlin-analysis-api" } kotlin-analysis-symbol-light-classes = { module = "org.jetbrains.kotlin:symbol-light-classes-for-ide", version.ref = "kotlin-analysis-api" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlin-test-junit5 = { module = "org.jetbrains.kotlin:kotlin-test-junit5", version.ref = "kotlin" } kotlinx-collections-immutable-jvm = { module = "org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm", version.ref = "kotlinx-collections-immutable" } -kotlinx-coroutines-core-jvm = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm", version.ref = "kotlinx-coroutines" } +kotlinx-coroutines-core-jvm = { module = "com.intellij.platform:kotlinx-coroutines-core-jvm", version.ref = "kotlinx-coroutines-intellij" } kotlinx-serialization-json-jvm = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json-jvm", version.ref = "kotlinx-serialization" } lombok = { module = "org.projectlombok:lombok", version.ref = "lombok" } maven-plugin-annotations = { module = "org.apache.maven.plugin-tools:maven-plugin-annotations", version.ref = "maven-plugin-annotations" } diff --git a/scip-kotlin-analysis/build.gradle.kts b/scip-kotlin-analysis/build.gradle.kts index 59adaf598..f500d908c 100644 --- a/scip-kotlin-analysis/build.gradle.kts +++ b/scip-kotlin-analysis/build.gradle.kts @@ -19,6 +19,9 @@ dependencies { runtimeOnly(libs.kotlin.analysis.api.impl.base) { isTransitive = false } runtimeOnly(libs.kotlin.analysis.api.k2) { isTransitive = false } + // Provides org.jetbrains.kotlin.analysis.decompiler.* which is no longer + // bundled in kotlin-compiler as of the 2.4 line. + runtimeOnly(libs.kotlin.analysis.compiler.common) { isTransitive = false } runtimeOnly(libs.kotlin.analysis.low.level.api.fir) { isTransitive = false } runtimeOnly(libs.kotlin.analysis.api.platform.`interface`) { isTransitive = false } runtimeOnly(libs.kotlin.analysis.symbol.light.classes) { isTransitive = false } diff --git a/settings.gradle.kts b/settings.gradle.kts index 785b0b434..6747622d7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -11,9 +11,13 @@ dependencyResolutionManagement { repositories { mavenCentral() gradlePluginPortal() - // Kotlin Analysis API (`*-for-ide`) artifacts used by scip-kotlin-analysis. - maven("https://redirector.kotlinlang.org/maven/kotlin-ide-plugin-dependencies") { - content { includeGroupAndSubgroups("org.jetbrains.kotlin") } + // Kotlin Analysis API (`*-for-ide`) artifacts and the IntelliJ coroutines + // fork used by scip-kotlin-analysis. + maven("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies") { + content { + includeGroupAndSubgroups("org.jetbrains.kotlin") + includeGroup("com.intellij.platform") + } } } } From f49ef4f29d5dfada79d84b53fb77af0d22299227 Mon Sep 17 00:00:00 2001 From: jupblb Date: Thu, 2 Jul 2026 13:03:28 +0200 Subject: [PATCH 08/33] Use current renovate matchPackageNames syntax --- .github/renovate.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/renovate.json b/.github/renovate.json index 5366f7b8c..b22febb91 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -8,10 +8,10 @@ "packageRules": [ { "description": "The Analysis API engine pin is managed manually: it lives in the JetBrains intellij-dependencies repository, uses IDE-aligned version schemes, and must move in lockstep with the `kotlin` toolchain version.", - "matchPackagePatterns": [ - "^org\\.jetbrains\\.kotlin:kotlin-compiler$", - "-for-ide$", - "^com\\.intellij\\.platform:" + "matchPackageNames": [ + "org.jetbrains.kotlin:kotlin-compiler", + "*-for-ide", + "com.intellij.platform:*" ], "enabled": false } From fc647fd870899bb477e16f2f1fc52a030a604b61 Mon Sep 17 00:00:00 2001 From: jupblb Date: Thu, 2 Jul 2026 13:19:42 +0200 Subject: [PATCH 09/33] Make primary constructor enclosing ranges contain their anchor --- .../scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt | 6 +++++- .../kotlin/common/src/main/kotlin/snapshots/Annotations.kt | 2 +- .../common/src/main/kotlin/snapshots/Destructuring.kt | 2 +- .../cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt | 2 +- .../kotlin/common/src/main/kotlin/snapshots/Generics.kt | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index 66f2f335b..a16a3f1ae 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -270,7 +270,11 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.symbolForDeclaration(primaryConstructor), range = range, - enclosing = primaryConstructor.textRange, + // Span from the class identifier (the definition anchor) through the + // parameter list, so the enclosing range contains its own anchor — + // scip-kotlinc used the parameter list alone, which did not. + enclosing = + TextRange(range.startOffset, primaryConstructor.textRange.endOffset), displayName = displayName, signatureText = signatures.constructorSignature(primaryConstructor), documentation = docComment(primaryConstructor), diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt index e86954de2..53d3ba257 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt @@ -8,7 +8,7 @@ // ^^^^^ reference scip-java maven . . kotlin/annotation/AnnotationTarget#CLASS. // ^^^^^^^^^^^^^^^^ reference scip-java maven . . kotlin/annotation/AnnotationTarget# // ^^^^^^^^ reference scip-java maven . . kotlin/annotation/AnnotationTarget#FUNCTION. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#``().(tag) // ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#tag. // ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#getTag(). diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt index 56bc9c232..3c3f8f453 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt @@ -2,7 +2,7 @@ // ^^^^^^^^^ reference scip-java maven . . snapshots/ //⌄ enclosing_range_start scip-java maven . . snapshots/Point# -// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#copy(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#``().(x) // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#x. diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt index a87d36f11..b16f4a7ba 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt @@ -7,7 +7,7 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Suit#valueOf().(value) //⌄ enclosing_range_start scip-java maven . . snapshots/Suit#entries. //⌄ enclosing_range_start scip-java maven . . snapshots/Suit#getEntries(). -// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#``().(symbol) // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#symbol. // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#getSymbol(). diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt index 2d37f628a..921285395 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt @@ -2,8 +2,8 @@ // ^^^^^^^^^ reference scip-java maven . . snapshots/ //⌄ enclosing_range_start scip-java maven . . snapshots/Container# +// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#[T] -// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#``().(items) // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#items. // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#getItems(). From 848369ca992e36c50498c15a307e40dbd8a8f258 Mon Sep 17 00:00:00 2001 From: jupblb Date: Thu, 2 Jul 2026 13:40:17 +0200 Subject: [PATCH 10/33] Add Kotlin 1.9/2.3/2.4 repos to indexing workflow --- .github/workflows/repos.yaml | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/.github/workflows/repos.yaml b/.github/workflows/repos.yaml index f3d0792dd..a9fdf629f 100644 --- a/.github/workflows/repos.yaml +++ b/.github/workflows/repos.yaml @@ -119,6 +119,39 @@ jobs: expected_language: kotlin covers: Gradle, Kotlin multiplatform JVM target, Kotlin 2.4.0, Java 25 runtime + - name: gradle-kotlin19-kotlinpoet + repository: square/kotlinpoet + ref: 1.16.0 + directory: . + build_tool: gradle + build_command: ":kotlinpoet:compileKotlinJvm" + java: 17 + bazel_version: "" + expected_language: kotlin + covers: Gradle, Kotlin multiplatform JVM target, legacy Kotlin 1.9.22, Java 17 runtime + + - name: gradle-kotlin23-turbine + repository: cashapp/turbine + ref: ab1e9a0acbbbb175081a0c5d94cb4f4cf4da444a + directory: . + build_tool: gradle + build_command: "compileKotlinJvm" + java: 17 + bazel_version: "" + expected_language: kotlin + covers: Gradle, Kotlin multiplatform JVM target, Kotlin 2.3.21, Java 17 runtime + + - name: gradle-kotlin24-detekt + repository: detekt/detekt + ref: 04f97401d462e899383156b25d026b7b8c181401 + directory: . + build_tool: gradle + build_command: ":detekt-api:compileKotlin" + java: 21 + bazel_version: "" + expected_language: kotlin + covers: Gradle, Kotlin JVM, Kotlin 2.4.0, settings repositories (FAIL_ON_PROJECT_REPOS), Java 21 runtime + - name: gradle-mixed-kotlinpoet repository: square/kotlinpoet ref: 0182a25089ee1f4f2378568b497f82a8ebc3017d From 5ab208503fbf89e5122b2aaa95361637809dda60 Mon Sep 17 00:00:00 2001 From: jupblb Date: Thu, 2 Jul 2026 14:21:43 +0200 Subject: [PATCH 11/33] Remove comments from libs.versions.toml --- gradle/libs.versions.toml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a1afd1f66..3db7eb7a4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,15 +3,8 @@ clikt = "5.1.0" gradle-api = "8.11.1" junit-jupiter = "5.11.4" kotlin = "2.4.21" -# Kotlin Analysis API engine bundled by scip-kotlin-analysis. Managed manually -# (see .github/renovate.json): the artifacts live in the JetBrains -# intellij-dependencies repository and the pin must stay <= one minor version -# ahead of `kotlin` so the toolchain can read its metadata. kotlin-analysis-api = "2.4.0" kotlinx-collections-immutable = "0.3.8" -# The IntelliJ fork of kotlinx-coroutines required by the Analysis API engine -# (provides kotlinx.coroutines.internal.intellij.IntellijCoroutines). Managed -# manually together with kotlin-analysis-api. kotlinx-coroutines-intellij = "1.8.0-intellij-13" kotlinx-serialization = "1.12.0" lombok = "1.18.48" From 3080bdee7fc58f285c435de9d520a46ccaec4b5b Mon Sep 17 00:00:00 2001 From: jupblb Date: Thu, 2 Jul 2026 14:28:03 +0200 Subject: [PATCH 12/33] Import types instead of fully qualifying in ScipGradlePlugin --- .../scip_java/gradle/ScipGradlePlugin.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java b/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java index 1351a8b09..45f5b8f7e 100644 --- a/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java +++ b/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java @@ -1,5 +1,9 @@ package org.scip_code.scip_java.gradle; +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -7,6 +11,7 @@ import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.file.FileCollection; import org.gradle.api.initialization.Settings; import org.gradle.api.internal.GradleInternal; import org.gradle.api.tasks.compile.JavaCompile; @@ -190,22 +195,18 @@ private static void configureKotlinCompileTask(Task task, String sourceRoot, Str // Referring to KotlinCompile directly triggers NoClassDefFoundError - // the plugin classpath is murky and we deliberately don't bundle the // Kotlin Gradle plugin. So we commit the sins of reflection. - org.gradle.api.file.FileCollection libraries; + FileCollection libraries; try { - libraries = - (org.gradle.api.file.FileCollection) - task.getClass().getMethod("getLibraries").invoke(task); + libraries = (FileCollection) task.getClass().getMethod("getLibraries").invoke(task); } catch (ReflectiveOperationException exc) { - libraries = - (org.gradle.api.file.FileCollection) - task.getClass().getMethod("getClasspath").invoke(task); + libraries = (FileCollection) task.getClass().getMethod("getClasspath").invoke(task); } - for (java.io.File entry : libraries.getFiles()) { + for (File entry : libraries.getFiles()) { lines.add("classpath " + entry.getAbsolutePath()); } int sources = 0; - for (java.io.File file : task.getInputs().getSourceFiles().getFiles()) { + for (File file : task.getInputs().getSourceFiles().getFiles()) { if (file.getName().endsWith(".kt") || file.getName().endsWith(".kts")) { lines.add("source " + file.getAbsolutePath()); sources++; @@ -215,10 +216,10 @@ private static void configureKotlinCompileTask(Task task, String sourceRoot, Str return; } - java.nio.file.Path configDir = java.nio.file.Paths.get(targetRoot, "kotlin-configs"); - java.nio.file.Files.createDirectories(configDir); + Path configDir = Paths.get(targetRoot, "kotlin-configs"); + Files.createDirectories(configDir); String fileName = task.getPath().replaceAll("[^A-Za-z0-9]", "-") + ".txt"; - java.nio.file.Files.write(configDir.resolve(fileName), lines); + Files.write(configDir.resolve(fileName), lines); } catch (Exception exc) { throw new RuntimeException( "scip-java: failed to extract Kotlin compile configuration for task '" From 44806a5fc5a2dd69f4add9366937953144554aea Mon Sep 17 00:00:00 2001 From: jupblb Date: Thu, 2 Jul 2026 14:44:25 +0200 Subject: [PATCH 13/33] Trim comments and tighten new indexer code --- .../scip_java/gradle/ScipGradlePlugin.java | 8 +- .../org/scip_code/scip_java/ScipJava.kt | 6 +- .../scip_java/buildtools/GradleBuildTool.kt | 6 +- .../scip_java/buildtools/ScipBuildTool.kt | 6 +- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 146 +++++++----------- .../scip_java/kotlin_analysis/ScipSymbols.kt | 16 +- .../kotlin_analysis/SignatureRenderer.kt | 13 +- .../scip_java/kotlin_analysis/SymbolsCache.kt | 67 ++++---- 8 files changed, 110 insertions(+), 158 deletions(-) diff --git a/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java b/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java index 45f5b8f7e..49dd20305 100644 --- a/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java +++ b/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java @@ -175,11 +175,9 @@ private static boolean tryAddJavacPlugin(Project project, Object javacPluginDep) } /** - * Kotlin sources are no longer indexed inside kotlinc: after each Kotlin compile task runs, its - * sources and classpath are dumped to {@code /kotlin-configs/.txt}, and the - * scip-java CLI indexes them with the standalone Analysis API indexer after the build. This - * removes any binary-compatibility coupling with the Kotlin compiler version of the build being - * indexed. + * Kotlin sources are not indexed inside kotlinc: after each Kotlin compile task runs, its sources + * and classpath are dumped to {@code /kotlin-configs/.txt} for the scip-java + * CLI to index after the build, decoupling indexing from the build's Kotlin compiler version. */ private static void configureKotlinCompileTask(Task task, String sourceRoot, String targetRoot) { if (!task.getClass().getSimpleName().contains("KotlinCompile")) { diff --git a/scip-java/src/main/kotlin/org/scip_code/scip_java/ScipJava.kt b/scip-java/src/main/kotlin/org/scip_code/scip_java/ScipJava.kt index 47b535700..2e6ddbde6 100644 --- a/scip-java/src/main/kotlin/org/scip_code/scip_java/ScipJava.kt +++ b/scip-java/src/main/kotlin/org/scip_code/scip_java/ScipJava.kt @@ -1,6 +1,7 @@ package org.scip_code.scip_java import java.io.PrintStream +import kotlin.system.exitProcess /** * Public entry point for the scip-java CLI. The single [app] instance is shared across test suites @@ -17,9 +18,8 @@ object ScipJava { @JvmStatic fun main(args: Array) { app.runAndExitIfNonZero(args.toList()) - // The Kotlin Analysis API indexer leaves non-daemon threads behind; exit - // explicitly instead of waiting on them. - kotlin.system.exitProcess(0) + // The Analysis API indexer leaves non-daemon threads behind; exit explicitly. + exitProcess(0) } fun printHelp(out: PrintStream) { diff --git a/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/GradleBuildTool.kt b/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/GradleBuildTool.kt index fee71085b..432c17af9 100644 --- a/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/GradleBuildTool.kt +++ b/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/GradleBuildTool.kt @@ -100,10 +100,8 @@ This means our SCIP compiler plugin was not attached to one or more JavaCompile } /** - * The Gradle plugin dumps each Kotlin compile task's sources and classpath under - * `/kotlin-configs/`; index them with the standalone Analysis API indexer. The - * Kotlin version of the indexed build is irrelevant here — the indexer bundles its own analysis - * engine. + * Indexes the sources/classpath dumps the Gradle plugin wrote to `/kotlin-configs/` + * after each Kotlin compile task. */ private fun indexKotlinConfigs() { val configsDir = targetroot().resolve("kotlin-configs") diff --git a/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/ScipBuildTool.kt b/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/ScipBuildTool.kt index 5b8844cde..cf3fe7650 100644 --- a/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/ScipBuildTool.kt +++ b/scip-java/src/main/kotlin/org/scip_code/scip_java/buildtools/ScipBuildTool.kt @@ -153,9 +153,9 @@ class ScipBuildTool(index: IndexCommand) : BuildTool("SCIP", index) { val classpath = config.classpath.map { sourceroot.resolve(it).normalize() } val jdkHome = config.javaHome?.let { Paths.get(it) } ?: Paths.get(System.getProperty("java.home")) - // Unlike the old scip-kotlinc compiler plugin, the Analysis API indexer does - // not compile the sources: unresolved code degrades individual occurrences - // instead of failing the build, so only indexer crashes surface as errors. + // The Analysis API indexer does not compile the sources: unresolved code + // degrades individual occurrences instead of failing the build, so only + // indexer crashes surface as errors. return try { KotlinAnalysisIndexer( sourceroot = sourceroot, diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index a16a3f1ae..73e8f270e 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -4,8 +4,11 @@ import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.TextRange import java.nio.file.Path import java.nio.file.Paths +import java.util.Arrays +import org.jetbrains.kotlin.analysis.api.KaExperimentalApi import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.analyze +import org.jetbrains.kotlin.analysis.api.renderer.types.impl.KaTypeRendererForSource import org.jetbrains.kotlin.analysis.api.standalone.buildStandaloneAnalysisAPISession import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol @@ -17,18 +20,23 @@ import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtLibraryMod import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSdkModule import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSourceModule import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.jvm.JvmPlatforms +import org.jetbrains.kotlin.psi.KtCatchClause import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtConstructor import org.jetbrains.kotlin.psi.KtConstructorCalleeExpression import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtDestructuringDeclaration import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry import org.jetbrains.kotlin.psi.KtEnumEntry +import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtForExpression import org.jetbrains.kotlin.psi.KtFunctionLiteral import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtObjectDeclaration @@ -43,8 +51,10 @@ import org.jetbrains.kotlin.psi.KtTreeVisitorVoid import org.jetbrains.kotlin.psi.KtTypeAlias import org.jetbrains.kotlin.psi.KtTypeParameter import org.jetbrains.kotlin.psi.KtVariableDeclaration +import org.jetbrains.kotlin.psi.KtWhenExpression import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.parents +import org.jetbrains.kotlin.types.Variance import org.scip_code.scip.Document import org.scip_code.scip.Occurrence import org.scip_code.scip.SymbolRole @@ -59,13 +69,12 @@ import org.scip_code.scip_java.shared.ScipShardWriter /** * Indexes Kotlin sources into SCIP documents using the Kotlin Analysis API in standalone mode. * - * Unlike scip-kotlinc, this indexer does not run inside the user's kotlinc process: it bundles its - * own analysis engine, so the Kotlin version of the indexed project only matters as a - * language-version setting, never as a binary compatibility constraint. + * The indexer bundles its own analysis engine, so the Kotlin version of the indexed project only + * matters as a language-version setting, never as a binary-compatibility constraint. * * One SCIP shard is written per source file under - * `/META-INF/scip/.scip`, matching the layout produced by the - * scip-javac and scip-kotlinc compiler plugins. + * `/META-INF/scip/.scip`, the layout produced by the scip-javac + * compiler plugin. */ class KotlinAnalysisIndexer( private val sourceroot: Path, @@ -150,7 +159,7 @@ class KotlinAnalysisIndexer( } } - @OptIn(org.jetbrains.kotlin.analysis.api.KaExperimentalApi::class) + @OptIn(KaExperimentalApi::class) private class IndexingVisitor( private val session: KaSession, private val cache: SymbolsCache, @@ -160,9 +169,8 @@ class KotlinAnalysisIndexer( private val signatures = SignatureRenderer(session) override fun visitDeclaration(dcl: KtDeclaration) { - // Definitions are emitted before visiting children so that same-range - // occurrence order and local-symbol numbering match scip-kotlinc, - // which processed declarations before their bodies. + // Emit before visiting children so same-range occurrence order and + // local-symbol numbering match scip-kotlinc. when (dcl) { is KtEnumEntry -> emitEnumEntry(dcl) is KtClassOrObject -> emitClassLike(dcl) @@ -186,9 +194,6 @@ class KotlinAnalysisIndexer( val target = with(session) { expression.mainReference.resolveToSymbol() } ?: return val range = expression.getReferencedNameElement().textRange - // The implicit `it` lambda parameter has no declaration PSI; its - // definition is anchored at the whole lambda literal, emitted at the - // first reference. val targetPsi = target.psi if ( targetPsi is KtFunctionLiteral && @@ -212,9 +217,8 @@ class KotlinAnalysisIndexer( return } - // Property references fan out to the property and its accessors, - // e.g. `banana` yields `Class#banana.`, `Class#getBanana().` and - // `Class#setBanana().`. + // Property references fan out to the property and its accessors + // (`banana` → `Class#banana.`, `Class#getBanana().`, `Class#setBanana().`). if (target is KaPropertySymbol) { propertyReferenceSymbols(target).forEach { addReference(it, range) } return @@ -223,18 +227,13 @@ class KotlinAnalysisIndexer( addReference(cache.symbolForReference(session, target), range) } - // --------------------------------------------------------------- - // Declarations - // --------------------------------------------------------------- - private fun emitClassLike(declaration: KtClassOrObject) { val isAnonymous = declaration is KtObjectDeclaration && declaration.isObjectLiteral() val range = when { declaration.nameIdentifier != null -> declaration.nameIdentifier!!.textRange isAnonymous -> - (declaration as KtObjectDeclaration).getObjectKeyword()?.textRange - ?: declaration.textRange + declaration.getObjectKeyword()?.textRange ?: declaration.textRange else -> declaration.textRange // unnamed companion object } val displayName = if (isAnonymous) "" else declaration.name ?: "Companion" @@ -248,11 +247,10 @@ class KotlinAnalysisIndexer( documentation = docComment(declaration), relationships = superTypeRelationships(declaration), ) - // Classes and objects (but not interfaces) declare an implicit primary - // constructor when no explicit one is present in source. A primary - // constructor without the `constructor` keyword also anchors at the class - // identifier and is emitted here so the class definition stays first at - // that range (matching scip-kotlinc's order); emitConstructor skips it. + // Classes and objects (not interfaces) get an implicit primary constructor + // when none is present. A keyword-less primary constructor also anchors at + // the class identifier; both are emitted here so the class definition stays + // first at that range (emitConstructor skips the keyword-less case). val isInterface = declaration is KtClass && declaration.isInterface() val primaryConstructor = declaration.primaryConstructor if (!isInterface && primaryConstructor == null) { @@ -270,9 +268,9 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.symbolForDeclaration(primaryConstructor), range = range, - // Span from the class identifier (the definition anchor) through the - // parameter list, so the enclosing range contains its own anchor — - // scip-kotlinc used the parameter list alone, which did not. + // Span from the definition anchor through the parameter list so the + // enclosing range contains its own anchor (scip-kotlinc's + // parameter-list-only range did not). enclosing = TextRange(range.startOffset, primaryConstructor.textRange.endOffset), displayName = displayName, @@ -298,10 +296,9 @@ class KotlinAnalysisIndexer( } /** - * Compiler-generated enum members: `values()`, `valueOf(value)` and `entries` (plus its - * getter), all anchored at the enum class identifier like in scip-kotlinc. Unlike - * scip-kotlinc, the entries getter is owned by the enum class instead of the package — the - * old symbol (`snapshots/getEntries().`) was a bug. + * Compiler-generated enum members — `values()`, `valueOf(value)`, `entries` and its getter + * — anchored at the enum class identifier. Unlike scip-kotlinc, the entries getter is owned + * by the enum class; the old package-owned symbol was a bug. */ private fun emitEnumSynthetics(declaration: KtClass, range: TextRange) { val name = declaration.name.orEmpty() @@ -383,7 +380,7 @@ class KotlinAnalysisIndexer( private fun emitFunction(declaration: KtNamedFunction) { val identifier = declaration.nameIdentifier ?: return val relationships = - if (declaration.hasModifier(org.jetbrains.kotlin.lexer.KtTokens.OVERRIDE_KEYWORD)) { + if (declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) { overriddenSymbols(declaration) } else { emptyList() @@ -435,10 +432,9 @@ class KotlinAnalysisIndexer( private fun emitParameter(declaration: KtParameter) { val loopOrCatchIdentifier = declaration.nameIdentifier // `for` loop variables and `catch` parameters are local declarations, - // mirroring the FIR indexer (which also assigned their local numbers at - // the declaration site). + // numbered at the declaration site like in the FIR indexer. if (loopOrCatchIdentifier != null) { - if (declaration.parent is org.jetbrains.kotlin.psi.KtForExpression) { + if (declaration.parent is KtForExpression) { val type = declaration.typeReference?.text ?: renderedParameterType(declaration) @@ -453,10 +449,7 @@ class KotlinAnalysisIndexer( ) return } - if ( - (declaration.parent as? KtParameterList)?.parent - is org.jetbrains.kotlin.psi.KtCatchClause - ) { + if ((declaration.parent as? KtParameterList)?.parent is KtCatchClause) { emitDefinition( symbol = cache.symbolForDeclaration(declaration), range = loopOrCatchIdentifier.textRange, @@ -480,9 +473,9 @@ class KotlinAnalysisIndexer( signatureText = signatures.parameterSignature(declaration), documentation = null, ) - // A `val`/`var` constructor parameter also declares a property: the - // property initializer references the parameter at the same range, and - // the property plus its synthetic accessors are all anchored there. + // A `val`/`var` constructor parameter also declares a property; it and + // its synthetic accessors are all anchored at the parameter identifier, + // which the property initializer also references. if (declaration.hasValOrVar()) { emitDefinition( symbol = cache.parameterPropertySymbol(declaration), @@ -557,11 +550,11 @@ class KotlinAnalysisIndexer( } /** - * `when (subject)` introduces a synthetic `` local in the FIR desugaring; - * scip-kotlinc emitted a definition for it at the subject expression. Emitted before - * visiting children so local numbering matches. + * `when (subject)` introduces a synthetic `` local in the FIR desugaring, + * defined at the subject expression. Emitted before visiting children so local numbering + * matches. */ - override fun visitWhenExpression(expression: org.jetbrains.kotlin.psi.KtWhenExpression) { + override fun visitWhenExpression(expression: KtWhenExpression) { val subject = expression.subjectExpression if (subject != null && expression.subjectVariable == null) { emitDefinition( @@ -583,7 +576,7 @@ class KotlinAnalysisIndexer( * before visiting children so local numbering matches. */ override fun visitDestructuringDeclaration( - destructuringDeclaration: org.jetbrains.kotlin.psi.KtDestructuringDeclaration + destructuringDeclaration: KtDestructuringDeclaration ) { val isParameter = destructuringDeclaration.parent is KtParameter val type = @@ -606,11 +599,10 @@ class KotlinAnalysisIndexer( private fun emitDestructuringEntry(declaration: KtDestructuringDeclarationEntry) { emitLocalVariable(declaration) val range = declaration.nameIdentifier?.textRange ?: return - val destructuring = - declaration.parent as? org.jetbrains.kotlin.psi.KtDestructuringDeclaration ?: return - // Each entry desugars to a `componentN()` call on the destructured value. + val destructuring = declaration.parent as? KtDestructuringDeclaration ?: return + // Entries desugar to `componentN()` calls on the destructured value. // Standalone analysis does not resolve destructuring-entry references, so - // the symbol is derived from the destructured type and the entry position. + // the symbol is derived from the destructured type and entry position. val index = destructuring.entries.indexOf(declaration) destructuredClassId(destructuring)?.let { classId -> val name = "component${index + 1}" @@ -644,9 +636,7 @@ class KotlinAnalysisIndexer( addReference(cache.syntheticLocalSymbol(destructuring), range) } - private fun destructuredClassId( - destructuring: org.jetbrains.kotlin.psi.KtDestructuringDeclaration - ): ClassId? = + private fun destructuredClassId(destructuring: KtDestructuringDeclaration): ClassId? = with(session) { val type = destructuring.initializer?.expressionType @@ -656,14 +646,11 @@ class KotlinAnalysisIndexer( (type as? KaClassType)?.classId } - private fun renderedExpressionType( - expression: org.jetbrains.kotlin.psi.KtExpression - ): String = + private fun renderedExpressionType(expression: KtExpression): String = with(session) { expression.expressionType?.render( - org.jetbrains.kotlin.analysis.api.renderer.types.impl.KaTypeRendererForSource - .WITH_SHORT_NAMES, - position = org.jetbrains.kotlin.types.Variance.INVARIANT, + KaTypeRendererForSource.WITH_SHORT_NAMES, + position = Variance.INVARIANT, ) } ?: "Any" @@ -672,10 +659,8 @@ class KotlinAnalysisIndexer( (parameter.symbol as? KaCallableSymbol) ?.returnType ?.render( - org.jetbrains.kotlin.analysis.api.renderer.types.impl - .KaTypeRendererForSource - .WITH_SHORT_NAMES, - position = org.jetbrains.kotlin.types.Variance.INVARIANT, + KaTypeRendererForSource.WITH_SHORT_NAMES, + position = Variance.INVARIANT, ) } @@ -756,8 +741,8 @@ class KotlinAnalysisIndexer( private val emittedItParameters = HashSet() /** - * A lambda with a single implicit `it` parameter declares it without any PSI: the FIR - * indexer anchored its definition at the whole lambda literal. + * The implicit `it` parameter has no declaration PSI: its definition is anchored at the + * whole lambda literal and emitted at the first reference. */ private fun emitImplicitItDefinition(literal: KtFunctionLiteral, target: KaCallableSymbol) { if (!emittedItParameters.add(literal)) return @@ -771,14 +756,9 @@ class KotlinAnalysisIndexer( ) } - // --------------------------------------------------------------- - // Symbol helpers - // --------------------------------------------------------------- - private fun propertyReferenceSymbols(target: KaPropertySymbol): List { - val psi = target.psi - return when { - psi is KtProperty -> { + return when (val psi = target.psi) { + is KtProperty -> { val property = cache.symbolForDeclaration(psi) if (property.isLocal()) return listOf(property) buildList { @@ -791,7 +771,7 @@ class KotlinAnalysisIndexer( } } } - psi is KtParameter -> + is KtParameter -> buildList { add(cache.parameterPropertySymbol(psi)) add(cache.syntheticAccessorSymbol(psi, setter = false)) @@ -829,10 +809,6 @@ class KotlinAnalysisIndexer( .toList() } - // --------------------------------------------------------------- - // Emission - // --------------------------------------------------------------- - private fun emitDefinition( symbol: Symbol, range: TextRange, @@ -946,11 +922,9 @@ internal class LineIndex(private val text: String) { } /** - * Occurrence anchor ranges are always single-line in the scip-kotlinc output: multiline - * declarations (unnamed companion objects, multiline secondary constructors) were collapsed - * onto their start line with the end character measured from that line's start. The true extent - * lives in the enclosing range. Reproduced here so anchors stay byte-identical and renderable - * by `scip snapshot`. + * Occurrence anchors are always single-line, like in scip-kotlinc: multiline declarations are + * collapsed onto their start line with the end character measured from that line's start. The + * true extent lives in the enclosing range. */ fun anchorRange(range: TextRange): ScipRange? { if (range.startOffset > text.length || range.endOffset > text.length) return null @@ -966,7 +940,7 @@ internal class LineIndex(private val text: String) { private fun lineStart(line: Int): Int = lineStartOffsets[line] private fun position(offset: Int): Pair { - var line = java.util.Arrays.binarySearch(lineStartOffsets, offset) + var line = Arrays.binarySearch(lineStartOffsets, offset) if (line < 0) line = -line - 2 return line to (offset - lineStartOffsets[line]) } diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipSymbols.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipSymbols.kt index afe9c19a9..2ed76e9bd 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipSymbols.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipSymbols.kt @@ -2,19 +2,14 @@ package org.scip_code.scip_java.kotlin_analysis import org.scip_code.scip_java.shared.ScipSymbols as SharedSymbols -// Ported from scip-kotlinc's ScipSymbols.kt so that the Analysis-API-based indexer -// produces byte-identical SCIP symbol strings. scip-kotlinc is deleted once this -// indexer reaches parity, at which point this file is the only copy. @JvmInline value class Symbol(private val symbol: String) { companion object { val NONE = Symbol(SharedSymbols.NONE) val ROOT_PACKAGE = Symbol(SharedSymbols.ROOT_PACKAGE) - // Note: this intentionally diverges from `SharedSymbols.global` when - // `desc == NONE` — Java returns `NONE`, Kotlin returns the owner. - // SymbolsCache relies on this behavior; do not delegate without first - // updating those call sites. + // Diverges from `SharedSymbols.global` when `desc == NONE`: that returns + // NONE, this returns the owner — SymbolsCache relies on it. fun createGlobal(owner: Symbol, desc: ScipSymbolDescriptor): Symbol = when { desc == ScipSymbolDescriptor.NONE -> owner @@ -32,14 +27,11 @@ value class Symbol(private val symbol: String) { override fun toString(): String = symbol } -fun String.symbol(): Symbol = Symbol(this) - data class ScipSymbolDescriptor( val kind: Kind, val name: String, - // Default differs from `SharedSymbols.Descriptor` (which is "") because - // Kotlin call sites — getters/setters in particular — rely on the no-arg - // overload producing `name().` rather than `name.` for METHOD kinds. + // Defaults to "()" (`SharedSymbols.Descriptor` defaults to ""): call sites + // rely on the no-arg form producing `name().` for METHOD kinds. val disambiguator: String = "()", ) { companion object { diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt index 947e10cfa..8733bc187 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtConstructor import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter @@ -20,10 +21,9 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.types.Variance /** - * Renders hover signatures in the exact textual format that scip-kotlinc's FirRenderer setup - * produced (see the goldens under scip-snapshots/expected/kotlin), so that migrating the indexer - * does not churn signature documentation. Explicitly written type annotations are taken verbatim - * from source; inferred types are rendered with short names via the Analysis API. + * Renders hover signatures in the textual format of scip-kotlinc's FirRenderer setup (see the + * goldens under scip-snapshots/expected/kotlin). Explicit type annotations are taken verbatim from + * source; inferred types are rendered with short names via the Analysis API. */ @OptIn(KaExperimentalApi::class) internal class SignatureRenderer(private val session: KaSession) { @@ -82,7 +82,6 @@ internal class SignatureRenderer(private val session: KaSession) { declaration.getTypeReference()?.text.orEmpty() + "\n" - /** `values()` / `valueOf(value: String)` / `entries` signatures of an enum class. */ fun enumValuesSignature(name: String): String = "public final static fun values(): Array<$name>" fun enumValueOfSignature(name: String): String = @@ -138,7 +137,7 @@ internal class SignatureRenderer(private val session: KaSession) { } fun propertySignature(declaration: KtDeclaration): String { - val name = (declaration as? org.jetbrains.kotlin.psi.KtNamedDeclaration)?.name.orEmpty() + val name = (declaration as? KtNamedDeclaration)?.name.orEmpty() val const = if (declaration.hasModifier(KtTokens.CONST_KEYWORD)) "const " else "" val lateinit = if (declaration.hasModifier(KtTokens.LATEINIT_KEYWORD)) "lateinit " else "" val valOrVar = if (isVar(declaration)) "var" else "val" @@ -265,7 +264,7 @@ internal class SignatureRenderer(private val session: KaSession) { else -> "final" } - /** Modality + `override`, with a trailing space; empty for abstract-less contexts. */ + /** Modality plus `override`, with a trailing space. */ private fun memberModality(declaration: KtDeclaration): String { val containingInterface = (declaration.containingClassOrObject as? KtClass)?.isInterface() == true diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt index 3f260a000..fa2a49068 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt @@ -45,10 +45,9 @@ import org.scip_code.scip_java.shared.LocalSymbolsCache as SharedLocalSymbolsCac /** * Computes SCIP symbol strings for Kotlin declarations and resolved references. * - * This is a PSI-driven port of the FIR-based `SymbolsCache` in scip-kotlinc. Declarations with - * Kotlin source are classified from their PSI structure, which keeps the version-sensitive Analysis - * API surface minimal. References that resolve to declarations without Kotlin source (classpath or - * JDK symbols) fall back to `ClassId`/`CallableId`-derived symbols. + * Declarations with Kotlin source are classified from their PSI structure, which keeps the + * version-sensitive Analysis API surface minimal. References that resolve to declarations without + * Kotlin source (classpath or JDK symbols) fall back to `ClassId`/`CallableId`-derived symbols. * * Instantiate one cache per indexed file: local symbols are numbered per SCIP document. */ @@ -92,9 +91,9 @@ class SymbolsCache { ) /** - * The SCIP symbol of the synthetic getter/setter of a property (or `val`/`var` constructor - * parameter) that has no explicit accessor PSI. Accessors are owned by the property's container - * (`Class#getBanana().`), not by the constructor that declares the parameter. + * The synthetic getter/setter of a property (or `val`/`var` constructor parameter) without + * explicit accessor PSI. Owned by the property's container (`Class#getBanana().`), not by the + * constructor declaring the parameter. */ fun syntheticAccessorSymbol(property: KtDeclaration, setter: Boolean): Symbol { val name = (property as? KtNamedDeclaration)?.name.orEmpty() @@ -121,7 +120,14 @@ class SymbolsCache { } /** The SCIP symbol of a class referenced by its (expanded) [ClassId]. */ - fun classSymbol(classId: ClassId): Symbol = classIdSymbol(classId) + fun classSymbol(classId: ClassId): Symbol { + var symbol = packageSymbol(classId.packageFqName) + for (segment in classId.relativeClassName.pathSegments()) { + symbol = + Symbol.createGlobal(symbol, ScipSymbolDescriptor(Kind.TYPE, segment.asString())) + } + return symbol + } /** Symbol of a compiler-generated member method (enum `values()`, data-class `copy()`, …). */ fun memberMethodSymbol(owner: KtClassOrObject, name: String): Symbol = @@ -158,9 +164,8 @@ class SymbolsCache { ) /** - * The SCIP symbol of the implicit `it` parameter of a lambda. The function literal PSI is used - * as the cache key; this cannot collide with the literal itself because anonymous functions - * never cache a symbol. + * The implicit `it` parameter of a lambda, keyed by the function literal PSI — safe because + * anonymous functions never cache a symbol of their own. */ fun implicitItSymbol(literal: KtFunctionLiteral): Symbol = syntheticLocalSymbol(literal) @@ -168,8 +173,7 @@ class SymbolsCache { fun symbolForReference(session: KaSession, target: KaSymbol): Symbol { val psi = target.psi if (psi is KtDeclaration) { - // A constructor symbol whose PSI is the class itself is an implicit - // primary constructor. + // A constructor symbol whose PSI is the class itself is an implicit primary. if (target is KaConstructorSymbol && psi is KtClassOrObject) { return implicitConstructorSymbol(psi) } @@ -180,7 +184,7 @@ class SymbolsCache { // Source type aliases are expanded like library ones, matching scip-kotlinc. if (psi is KtTypeAlias && target is KaTypeAliasSymbol) { return with(session) { (target.expandedType as? KaClassType)?.classId } - ?.let(::classIdSymbol) ?: symbolForDeclaration(psi) + ?.let(::classSymbol) ?: symbolForDeclaration(psi) } // Compiler-generated callables (data-class componentN/copy, enum // `entries`, …) resolve to PSI of the class or parameter they derive @@ -204,17 +208,17 @@ class SymbolsCache { is KaConstructorSymbol -> target.containingClassId?.let { classId -> Symbol.createGlobal( - classIdSymbol(classId), + classSymbol(classId), ScipSymbolDescriptor(Kind.METHOD, ""), ) } ?: Symbol.NONE - // Library type aliases are expanded to the aliased class, matching the - // FIR indexer (`AutoCloseable` → `java/lang/AutoCloseable#`) and - // keeping Kotlin and Java references to the same class unified. + // Library type aliases are expanded to the aliased class (`AutoCloseable` + // → `java/lang/AutoCloseable#`), unifying Kotlin and Java references. is KaTypeAliasSymbol -> - with(session) { (target.expandedType as? KaClassType)?.classId } - ?.let(::classIdSymbol) ?: target.classId?.let(::classIdSymbol) ?: Symbol.NONE - is KaClassLikeSymbol -> target.classId?.let(::classIdSymbol) ?: Symbol.NONE + with(session) { (target.expandedType as? KaClassType)?.classId }?.let(::classSymbol) + ?: target.classId?.let(::classSymbol) + ?: Symbol.NONE + is KaClassLikeSymbol -> target.classId?.let(::classSymbol) ?: Symbol.NONE is KaCallableSymbol -> externalCallableSymbol(session, target) else -> Symbol.NONE } @@ -227,8 +231,7 @@ class SymbolsCache { setter: Boolean, ): Symbol { val callableId = target.callableId ?: return Symbol.NONE - val owner = - callableId.classId?.let(::classIdSymbol) ?: packageSymbol(callableId.packageName) + val owner = callableId.classId?.let(::classSymbol) ?: packageSymbol(callableId.packageName) return Symbol.createGlobal( owner, accessorDescriptor(callableId.callableName.asString(), setter), @@ -242,13 +245,11 @@ class SymbolsCache { ) private fun uncachedSymbol(declaration: KtDeclaration): Symbol { - // Anonymous functions and lambdas have no symbol of their own, mirroring - // the FIR indexer which returned NONE for FirAnonymousFunctionSymbol. + // Anonymous functions and lambdas have no symbol of their own, mirroring FIR. if (declaration is KtFunctionLiteral) return Symbol.NONE if (declaration is KtNamedFunction && declaration.name == null) return Symbol.NONE - // Anonymous objects are global symbols owned by the file's package, - // mirroring FIR where getContainingDeclaration returns null for them. + // Anonymous objects are global symbols owned by the file's package, mirroring FIR. if (declaration is KtObjectDeclaration && declaration.isObjectLiteral()) { return Symbol.createGlobal( packageSymbol(declaration.containingKtFile.packageFqName), @@ -400,8 +401,7 @@ class SymbolsCache { */ private fun externalCallableSymbol(session: KaSession, target: KaCallableSymbol): Symbol { val callableId = target.callableId ?: return Symbol.NONE - val owner = - callableId.classId?.let(::classIdSymbol) ?: packageSymbol(callableId.packageName) + val owner = callableId.classId?.let(::classSymbol) ?: packageSymbol(callableId.packageName) val name = callableId.callableName.asString() val descriptor = when (target) { @@ -431,13 +431,4 @@ class SymbolsCache { val index = overloads.indexOfFirst { it == target } return if (index <= 0) "()" else "(+$index)" } - - private fun classIdSymbol(classId: ClassId): Symbol { - var symbol = packageSymbol(classId.packageFqName) - for (segment in classId.relativeClassName.pathSegments()) { - symbol = - Symbol.createGlobal(symbol, ScipSymbolDescriptor(Kind.TYPE, segment.asString())) - } - return symbol - } } From 82258493f62652f0ca56892116864c640ee9d716 Mon Sep 17 00:00:00 2001 From: jupblb Date: Thu, 2 Jul 2026 15:08:20 +0200 Subject: [PATCH 14/33] Keep occurrence ranges within source lines --- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 21 ++++++++++++++----- .../main/kotlin/snapshots/CompanionOwner.kt | 16 +++++++------- .../common/src/main/kotlin/snapshots/Enums.kt | 17 +++++++-------- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index 73e8f270e..c33ab80dc 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDestructuringDeclaration import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry import org.jetbrains.kotlin.psi.KtEnumEntry +import org.jetbrains.kotlin.psi.KtEnumEntrySuperclassReferenceExpression import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtForExpression @@ -191,6 +192,10 @@ class KotlinAnalysisIndexer( super.visitSimpleNameExpression(expression) val referencedName = expression.getReferencedName() if (referencedName == "this" || referencedName == "super") return + // The synthetic superclass reference of an enum entry initializer + // (`HEARTS('h')`) has no source token of its own: its "name element" + // is the whole enum class. + if (expression is KtEnumEntrySuperclassReferenceExpression) return val target = with(session) { expression.mainReference.resolveToSymbol() } ?: return val range = expression.getReferencedNameElement().textRange @@ -922,9 +927,9 @@ internal class LineIndex(private val text: String) { } /** - * Occurrence anchors are always single-line, like in scip-kotlinc: multiline declarations are - * collapsed onto their start line with the end character measured from that line's start. The - * true extent lives in the enclosing range. + * Occurrence anchors are always single-line: multiline declarations are collapsed onto their + * start line, clamped to that line's end so the range stays within the source. The true extent + * lives in the enclosing range. */ fun anchorRange(range: TextRange): ScipRange? { if (range.startOffset > text.length || range.endOffset > text.length) return null @@ -933,12 +938,18 @@ internal class LineIndex(private val text: String) { if (startLine == endLine) { return ScipRange.singleLine(startLine, startCharacter, endCharacter) } - val flattenedEnd = range.endOffset - lineStart(startLine) - return ScipRange.singleLine(startLine, startCharacter, flattenedEnd) + return ScipRange.singleLine(startLine, startCharacter, lineLength(startLine)) } private fun lineStart(line: Int): Int = lineStartOffsets[line] + /** Length of [line] excluding its trailing newline. */ + private fun lineLength(line: Int): Int { + val end = + if (line + 1 < lineStartOffsets.size) lineStartOffsets[line + 1] - 1 else text.length + return end - lineStart(line) + } + private fun position(offset: Int): Pair { var line = Arrays.binarySearch(lineStartOffsets, offset) if (line < 0) line = -line - 2 diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt index 37ace2dd4..48e740412 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt @@ -15,14 +15,14 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion# // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion#``(). companion object { -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion# -// display_name Companion -// signature_documentation -// > public final companion object Companion : Any -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion#``(). -// display_name Companion -// signature_documentation -// > private constructor(): CompanionOwner.Companion +// ^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion# +// display_name Companion +// signature_documentation +// > public final companion object Companion : Any +// ^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion#``(). +// display_name Companion +// signature_documentation +// > private constructor(): CompanionOwner.Companion // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion#create(). fun create(): CompanionOwner = CompanionOwner() // ^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion#create(). diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt index b16f4a7ba..12739eb43 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt @@ -12,7 +12,6 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#symbol. // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#getSymbol(). enum class Suit(val symbol: Char) { -//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ reference scip-java maven . . snapshots/Suit# // ^^^^ definition scip-java maven . . snapshots/Suit# // display_name Suit // signature_documentation @@ -90,14 +89,14 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion# // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion#``(). companion object { -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion# -// display_name Companion -// signature_documentation -// > public final companion object Companion : Any -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion#``(). -// display_name Companion -// signature_documentation -// > private constructor(): Suit.Companion +// ^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion# +// display_name Companion +// signature_documentation +// > public final companion object Companion : Any +// ^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion#``(). +// display_name Companion +// signature_documentation +// > private constructor(): Suit.Companion // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion#fromSymbol(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion#fromSymbol().(symbol) // ⌄ enclosing_range_start local 0 From a14e0a247bbd6c2617a0312e5922d2486d3d2bc2 Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Mon, 26 Oct 2026 13:49:17 +0100 Subject: [PATCH 15/33] Port scip-kotlinc test cases from #973 to KotlinAnalysisIndexerTest #973 added 17 test cases to scip-kotlinc's AnalyzerTest and extended `type operators`; replacing scip-kotlinc deleted them. Run the same sources through KotlinAnalysisIndexer instead, using a copy of the ScipBuilders DSL. The #973 output changes are not part of scip-kotlin-analysis yet, so the expectations follow its current output: - No `kind` or `enclosing_symbol` assertions. - Extension functions and properties keep top-level symbols (`sample/foo().` instead of `sample/String#foo().`). - Enum entries render as `val`, and members of an enum entry body are `sample/Op#PLUS.apply().` instead of locals of an anonymous class. - `Foo.Factory` emits separate `Foo#` and `Foo#Factory#` references, an implicit companion receiver references `Bar#Companion#`, and the unnamed companion definition range ends at the end of its first line. Signature assertions for local functions and extension receivers are dropped: scip-kotlin-analysis currently renders `public final fun inner()` for local functions and omits the extension receiver. Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-01a153c0-ec1f-774a-993c-bca3256017ce --- .../KotlinAnalysisIndexerTest.kt | 1504 +++++++++++++++++ .../scip_java/kotlin_analysis/ScipBuilders.kt | 117 ++ 2 files changed, 1621 insertions(+) create mode 100644 scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipBuilders.kt diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index 6391232ae..523dc8592 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -7,6 +7,7 @@ import kotlin.test.Test import kotlin.test.assertContains import kotlin.test.assertEquals import kotlin.test.assertTrue +import org.junit.jupiter.api.io.TempDir import org.scip_code.scip.Document import org.scip_code.scip.SymbolRole @@ -93,6 +94,1509 @@ class KotlinAnalysisIndexerTest { ) } + @Test + fun `lambda parameters`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + fun use() { + val f = { n: Int -> n * 2 } + } + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "local 0" + range { + startLine = 3 + startCharacter = 8 + endLine = 3 + endCharacter = 9 + } + enclosingRange { + startLine = 3 + startCharacter = 4 + endLine = 3 + endCharacter = 31 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "local 1" + range { + startLine = 3 + startCharacter = 14 + endLine = 3 + endCharacter = 15 + } + enclosingRange { + startLine = 3 + startCharacter = 14 + endLine = 3 + endCharacter = 20 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "kotlin/Int#" + range { + startLine = 3 + startCharacter = 17 + endLine = 3 + endCharacter = 20 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "local 1" + range { + startLine = 3 + startCharacter = 24 + endLine = 3 + endCharacter = 25 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/use()." + displayName = "use" + signatureText = "public final fun use(): Unit" + }, + scipSymbol { + symbol = "local 0" + displayName = "f" + signatureText = "local val f: (Int) -> Int" + }, + scipSymbol { + symbol = "local 1" + displayName = "n" + signatureText = "n: Int" + }, + ) + assertContainsAll(document.symbolsList, *symbols) + } + + @Test + fun `local functions`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + fun outer() { + fun inner() {} + fun innerWithReturnType(): Int = 42 + inner() + } + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/outer()." + range { + startLine = 2 + startCharacter = 4 + endLine = 2 + endCharacter = 9 + } + enclosingRange { + startLine = 2 + startCharacter = 0 + endLine = 6 + endCharacter = 1 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "local 0" + range { + startLine = 3 + startCharacter = 8 + endLine = 3 + endCharacter = 13 + } + enclosingRange { + startLine = 3 + startCharacter = 4 + endLine = 3 + endCharacter = 18 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "local 1" + range { + startLine = 4 + startCharacter = 8 + endLine = 4 + endCharacter = 27 + } + enclosingRange { + startLine = 4 + startCharacter = 4 + endLine = 4 + endCharacter = 39 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "kotlin/Int#" + range { + startLine = 4 + startCharacter = 31 + endLine = 4 + endCharacter = 34 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "local 0" + range { + startLine = 5 + startCharacter = 4 + endLine = 5 + endCharacter = 9 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + } + + @Test + fun `user-defined class as return type`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + class MyClass + + fun bar(): MyClass = MyClass() + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/bar()." + range { + startLine = 4 + startCharacter = 4 + endLine = 4 + endCharacter = 7 + } + enclosingRange { + startLine = 4 + startCharacter = 0 + endLine = 4 + endCharacter = 30 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/MyClass#" + range { + startLine = 4 + startCharacter = 11 + endLine = 4 + endCharacter = 18 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/bar()." + displayName = "bar" + signatureText = "public final fun bar(): MyClass" + } + ) + assertContainsAll(document.symbolsList, *symbols) + } + + @Test + fun `typealias`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + typealias MyAlias = Int + val x: MyAlias = 42 + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/MyAlias#" + range { + startLine = 2 + startCharacter = 10 + endLine = 2 + endCharacter = 17 + } + enclosingRange { + startLine = 2 + startCharacter = 0 + endLine = 2 + endCharacter = 23 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/x." + range { + startLine = 3 + startCharacter = 4 + endLine = 3 + endCharacter = 5 + } + enclosingRange { + startLine = 3 + startCharacter = 0 + endLine = 3 + endCharacter = 19 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/MyAlias#" + displayName = "MyAlias" + signatureText = "public final typealias MyAlias = Int\n" + } + ) + assertContainsAll(document.symbolsList, *symbols) + } + + @Test + fun `type parameters`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + fun identity(x: T): T = x + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/identity()." + range { + startLine = 2 + startCharacter = 8 + endLine = 2 + endCharacter = 16 + } + enclosingRange { + startLine = 2 + startCharacter = 0 + endLine = 2 + endCharacter = 29 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/identity().[T]" + range { + startLine = 2 + startCharacter = 5 + endLine = 2 + endCharacter = 6 + } + enclosingRange { + startLine = 2 + startCharacter = 5 + endLine = 2 + endCharacter = 6 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/identity().[T]" + displayName = "T" + signatureText = "T" + } + ) + assertContainsAll(document.symbolsList, *symbols) + } + + @Test + fun `generic class declaration`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + class Box(val value: T) { + fun unwrap(): T = value + } + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/Box#" + range { + startLine = 2 + startCharacter = 6 + endLine = 2 + endCharacter = 9 + } + enclosingRange { + startLine = 2 + endLine = 4 + endCharacter = 1 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/Box#[T]" + range { + startLine = 2 + startCharacter = 10 + endLine = 2 + endCharacter = 11 + } + enclosingRange { + startLine = 2 + startCharacter = 10 + endLine = 2 + endCharacter = 11 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/Box#unwrap()." + range { + startLine = 3 + startCharacter = 8 + endLine = 3 + endCharacter = 14 + } + enclosingRange { + startLine = 3 + startCharacter = 4 + endLine = 3 + endCharacter = 27 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + } + + @Test + fun `type operators`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + fun foo(x: Any) { + when (x) { + is Int -> true + else -> x as Float + } + } + + class Wrapper + fun classify(x: Any) { + if (x is Wrapper) {} + val s = x as? String + val w = x as Wrapper + } + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = REFERENCE + symbol = "kotlin/Int#" + range { + startLine = 4 + startCharacter = 11 + endLine = 4 + endCharacter = 14 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "kotlin/Float#" + range { + startLine = 5 + startCharacter = 21 + endLine = 5 + endCharacter = 26 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/Wrapper#" + range { + startLine = 11 + startCharacter = 13 + endLine = 11 + endCharacter = 20 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "kotlin/String#" + range { + startLine = 12 + startCharacter = 18 + endLine = 12 + endCharacter = 24 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/Wrapper#" + range { + startLine = 13 + startCharacter = 17 + endLine = 13 + endCharacter = 24 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + } + + @Test + fun `extension receiver type reference`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + fun String.foo(): Int = 42 + fun use(s: String) = s.foo() + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/foo()." + range { + startLine = 2 + startCharacter = 11 + endLine = 2 + endCharacter = 14 + } + enclosingRange { + startLine = 2 + startCharacter = 0 + endLine = 2 + endCharacter = 26 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "kotlin/String#" + range { + startLine = 2 + startCharacter = 4 + endLine = 2 + endCharacter = 10 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "kotlin/Int#" + range { + startLine = 2 + startCharacter = 18 + endLine = 2 + endCharacter = 21 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/foo()." + range { + startLine = 3 + startCharacter = 23 + endLine = 3 + endCharacter = 26 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + } + + @Test + fun `extension property receiver type reference`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + val Int.asString: String get() = this.toString() + fun use() = 42.asString + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/asString." + range { + startLine = 2 + startCharacter = 8 + endLine = 2 + endCharacter = 16 + } + enclosingRange { + startLine = 2 + startCharacter = 0 + endLine = 2 + endCharacter = 48 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "kotlin/Int#" + range { + startLine = 2 + startCharacter = 4 + endLine = 2 + endCharacter = 7 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "kotlin/String#" + range { + startLine = 2 + startCharacter = 18 + endLine = 2 + endCharacter = 24 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/asString." + range { + startLine = 3 + startCharacter = 15 + endLine = 3 + endCharacter = 23 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + } + + @Test + fun `extension overload disambiguator`(@TempDir path: Path) { + // A member foo() and an extension foo() on the same class produce distinct symbols. + val document = + compileScip( + path, + """ + package sample + + class MyClass { + fun foo() {} + } + fun MyClass.foo(x: Int) {} + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/MyClass#foo()." + range { + startLine = 3 + startCharacter = 8 + endLine = 3 + endCharacter = 11 + } + enclosingRange { + startLine = 3 + startCharacter = 4 + endLine = 3 + endCharacter = 16 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/foo()." + range { + startLine = 5 + startCharacter = 12 + endLine = 5 + endCharacter = 15 + } + enclosingRange { + startLine = 5 + startCharacter = 0 + endLine = 5 + endCharacter = 26 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + } + + @Test + fun `enum entry definitions`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + enum class Color { RED, GREEN, BLUE } + + fun useEnum(): Color = Color.RED + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/Color#" + range { + startLine = 2 + startCharacter = 11 + endLine = 2 + endCharacter = 16 + } + enclosingRange { + startLine = 2 + startCharacter = 0 + endLine = 2 + endCharacter = 37 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/Color#RED." + range { + startLine = 2 + startCharacter = 19 + endLine = 2 + endCharacter = 22 + } + enclosingRange { + startLine = 2 + startCharacter = 19 + endLine = 2 + endCharacter = 23 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/Color#GREEN." + range { + startLine = 2 + startCharacter = 24 + endLine = 2 + endCharacter = 29 + } + enclosingRange { + startLine = 2 + startCharacter = 24 + endLine = 2 + endCharacter = 30 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/Color#BLUE." + range { + startLine = 2 + startCharacter = 31 + endLine = 2 + endCharacter = 35 + } + enclosingRange { + startLine = 2 + startCharacter = 31 + endLine = 2 + endCharacter = 35 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/Color#" + range { + startLine = 4 + startCharacter = 23 + endLine = 4 + endCharacter = 28 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/Color#RED." + range { + startLine = 4 + startCharacter = 29 + endLine = 4 + endCharacter = 32 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/Color#" + displayName = "Color" + addOverriddenSymbols("kotlin/Enum#") + signatureText = "public final enum class Color : Enum" + }, + scipSymbol { + symbol = "sample/Color#RED." + displayName = "RED" + signatureText = "public final val RED: Color" + }, + scipSymbol { + symbol = "sample/Color#GREEN." + displayName = "GREEN" + signatureText = "public final val GREEN: Color" + }, + scipSymbol { + symbol = "sample/Color#BLUE." + displayName = "BLUE" + signatureText = "public final val BLUE: Color" + }, + ) + assertContainsAll(document.symbolsList, *symbols) + } + + @Test + fun `enum entry with body`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + enum class Op { + PLUS { + override fun apply(a: Int, b: Int) = a + b + }; + + abstract fun apply(a: Int, b: Int): Int + } + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/Op#PLUS." + range { + startLine = 3 + startCharacter = 4 + endLine = 3 + endCharacter = 8 + } + // Body-having enum entry: enclosing_range covers PLUS through the + // trailing `};` that terminates the entry list. + enclosingRange { + startLine = 3 + startCharacter = 4 + endLine = 5 + endCharacter = 6 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/Op#PLUS.apply()." + range { + startLine = 4 + startCharacter = 21 + endLine = 4 + endCharacter = 26 + } + enclosingRange { + startLine = 4 + startCharacter = 8 + endLine = 4 + endCharacter = 50 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/Op#PLUS.apply()." + displayName = "apply" + addOverriddenSymbols("sample/Op#apply().") + signatureText = "public open override fun apply(a: Int, b: Int): Int" + } + ) + assertContainsAll(document.symbolsList, *symbols) + } + + @Test + fun `named object declarations`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + object MySingleton { + fun hello(): String = "hi" + } + fun use() = MySingleton.hello() + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/MySingleton#" + range { + startLine = 2 + startCharacter = 7 + endLine = 2 + endCharacter = 18 + } + enclosingRange { + startLine = 2 + startCharacter = 0 + endLine = 4 + endCharacter = 1 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/MySingleton#hello()." + range { + startLine = 3 + startCharacter = 8 + endLine = 3 + endCharacter = 13 + } + enclosingRange { + startLine = 3 + startCharacter = 4 + endLine = 3 + endCharacter = 30 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/MySingleton#" + range { + startLine = 5 + startCharacter = 12 + endLine = 5 + endCharacter = 23 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/MySingleton#hello()." + range { + startLine = 5 + startCharacter = 24 + endLine = 5 + endCharacter = 29 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/MySingleton#" + displayName = "MySingleton" + signatureText = "public final object MySingleton : Any" + }, + scipSymbol { + symbol = "sample/MySingleton#hello()." + displayName = "hello" + signatureText = "public final fun hello(): String" + }, + ) + assertContainsAll(document.symbolsList, *symbols) + } + + @Test + fun `companion object`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + class Foo { + companion object Factory { + fun create(): Foo = Foo() + } + } + fun use() = Foo.Factory.create() + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/Foo#" + range { + startLine = 2 + startCharacter = 6 + endLine = 2 + endCharacter = 9 + } + enclosingRange { + startLine = 2 + startCharacter = 0 + endLine = 6 + endCharacter = 1 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/Foo#Factory#" + range { + startLine = 3 + startCharacter = 21 + endLine = 3 + endCharacter = 28 + } + enclosingRange { + startLine = 3 + startCharacter = 4 + endLine = 5 + endCharacter = 5 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/Foo#Factory#create()." + range { + startLine = 4 + startCharacter = 12 + endLine = 4 + endCharacter = 18 + } + enclosingRange { + startLine = 4 + startCharacter = 8 + endLine = 4 + endCharacter = 33 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/Foo#" + range { + startLine = 7 + startCharacter = 12 + endLine = 7 + endCharacter = 15 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/Foo#Factory#" + range { + startLine = 7 + startCharacter = 16 + endLine = 7 + endCharacter = 23 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/Foo#Factory#create()." + range { + startLine = 7 + startCharacter = 24 + endLine = 7 + endCharacter = 30 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/Foo#" + displayName = "Foo" + signatureText = "public final class Foo : Any" + }, + scipSymbol { + symbol = "sample/Foo#Factory#" + displayName = "Factory" + signatureText = "public final companion object Factory : Any" + }, + scipSymbol { + symbol = "sample/Foo#Factory#create()." + displayName = "create" + signatureText = "public final fun create(): Foo" + }, + ) + assertContainsAll(document.symbolsList, *symbols) + } + + @Test + fun `unnamed companion object`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + class Bar { + companion object { + fun instance(): Bar = Bar() + } + } + fun use() = Bar.instance() + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/Bar#" + range { + startLine = 2 + startCharacter = 6 + endLine = 2 + endCharacter = 9 + } + enclosingRange { + startLine = 2 + startCharacter = 0 + endLine = 6 + endCharacter = 1 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/Bar#Companion#instance()." + range { + startLine = 4 + startCharacter = 12 + endLine = 4 + endCharacter = 20 + } + enclosingRange { + startLine = 4 + startCharacter = 8 + endLine = 4 + endCharacter = 35 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/Bar#Companion#" + range { + startLine = 7 + startCharacter = 12 + endLine = 7 + endCharacter = 15 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/Bar#Companion#instance()." + range { + startLine = 7 + startCharacter = 16 + endLine = 7 + endCharacter = 24 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/Bar#Companion#" + range { + startLine = 3 + startCharacter = 4 + endLine = 3 + endCharacter = 22 + } + enclosingRange { + startLine = 3 + startCharacter = 4 + endLine = 5 + endCharacter = 5 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/Bar#" + displayName = "Bar" + signatureText = "public final class Bar : Any" + }, + scipSymbol { + symbol = "sample/Bar#Companion#" + displayName = "Companion" + signatureText = "public final companion object Companion : Any" + }, + scipSymbol { + symbol = "sample/Bar#Companion#instance()." + displayName = "instance" + signatureText = "public final fun instance(): Bar" + }, + ) + assertContainsAll(document.symbolsList, *symbols) + } + + @Test + fun `string template references`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + fun greet(name: String) = "Hello, ${'$'}name!" + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/greet()." + range { + startLine = 2 + startCharacter = 4 + endLine = 2 + endCharacter = 9 + } + enclosingRange { + startLine = 2 + startCharacter = 0 + endLine = 2 + endCharacter = 41 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/greet().(name)" + range { + startLine = 2 + startCharacter = 10 + endLine = 2 + endCharacter = 14 + } + enclosingRange { + startLine = 2 + startCharacter = 10 + endLine = 2 + endCharacter = 22 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/greet().(name)" + range { + startLine = 2 + startCharacter = 35 + endLine = 2 + endCharacter = 39 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/greet()." + displayName = "greet" + signatureText = "public final fun greet(name: String): String" + } + ) + assertContainsAll(document.symbolsList, *symbols) + } + + @Test + fun `multiple supertype references`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + interface Named + interface Speakable + class Person : Named, Speakable + fun use(p: Person): Named = p + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/Person#" + range { + startLine = 4 + startCharacter = 6 + endLine = 4 + endCharacter = 12 + } + enclosingRange { + startLine = 4 + startCharacter = 0 + endLine = 4 + endCharacter = 31 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/Named#" + range { + startLine = 4 + startCharacter = 15 + endLine = 4 + endCharacter = 20 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/Speakable#" + range { + startLine = 4 + startCharacter = 22 + endLine = 4 + endCharacter = 31 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/Named#" + range { + startLine = 5 + startCharacter = 20 + endLine = 5 + endCharacter = 25 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/Person#" + displayName = "Person" + addOverriddenSymbols("sample/Named#") + addOverriddenSymbols("sample/Speakable#") + signatureText = "public final class Person : Named, Speakable" + } + ) + assertContainsAll(document.symbolsList, *symbols) + } + + @Test + fun `three-way overload disambiguator`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + fun add(x: Int, y: Int): Int = x + y + fun add(x: Double, y: Double): Double = x + y + fun add(x: String, y: String): String = x + y + fun use() { + add(1, 2) + add(1.0, 2.0) + add("a", "b") + } + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/add()." + range { + startLine = 2 + startCharacter = 4 + endLine = 2 + endCharacter = 7 + } + enclosingRange { + startLine = 2 + startCharacter = 0 + endLine = 2 + endCharacter = 36 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/add(+1)." + range { + startLine = 3 + startCharacter = 4 + endLine = 3 + endCharacter = 7 + } + enclosingRange { + startLine = 3 + startCharacter = 0 + endLine = 3 + endCharacter = 45 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/add(+2)." + range { + startLine = 4 + startCharacter = 4 + endLine = 4 + endCharacter = 7 + } + enclosingRange { + startLine = 4 + startCharacter = 0 + endLine = 4 + endCharacter = 45 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/add()." + range { + startLine = 6 + startCharacter = 4 + endLine = 6 + endCharacter = 7 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/add(+1)." + range { + startLine = 7 + startCharacter = 4 + endLine = 7 + endCharacter = 7 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/add(+2)." + range { + startLine = 8 + startCharacter = 4 + endLine = 8 + endCharacter = 7 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/add()." + displayName = "add" + signatureText = "public final fun add(x: Int, y: Int): Int" + }, + scipSymbol { + symbol = "sample/add(+1)." + displayName = "add" + signatureText = "public final fun add(x: Double, y: Double): Double" + }, + scipSymbol { + symbol = "sample/add(+2)." + displayName = "add" + signatureText = "public final fun add(x: String, y: String): String" + }, + ) + assertContainsAll(document.symbolsList, *symbols) + } + + private fun compileScip(path: Path, code: String): Document { + val sourceroot = path.toRealPath() + val source = Files.createDirectories(sourceroot.resolve("src")).resolve("Test.kt") + Files.writeString(source, code.trimIndent()) + return KotlinAnalysisIndexer( + sourceroot = sourceroot, + targetroot = sourceroot.resolve("scip-targetroot"), + sourceRoots = listOf(source), + classpath = listOf(kotlinStdlibJar()), + ) + .run() + .single() + } + + private fun assertContainsAll(actual: List, vararg expected: T) { + val missing = expected.filterNot { it in actual } + assertTrue( + missing.isEmpty(), + "missing:\n${missing.joinToString("\n")}\nactual:\n${actual.joinToString("\n")}", + ) + } + private fun definitions(document: Document): List = document.occurrencesList .filter { it.symbolRoles and SymbolRole.Definition.number != 0 } diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipBuilders.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipBuilders.kt new file mode 100644 index 000000000..8bd141068 --- /dev/null +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipBuilders.kt @@ -0,0 +1,117 @@ +package org.scip_code.scip_java.kotlin_analysis + +import org.scip_code.scip.Occurrence +import org.scip_code.scip.SymbolInformation +import org.scip_code.scip.SymbolRole +import org.scip_code.scip.relationship +import org.scip_code.scip.signature +import org.scip_code.scip.symbolInformation +import org.scip_code.scip_java.shared.ScipRange + +/** + * Tiny DSL for building SCIP [Occurrence] / [SymbolInformation] test fixtures. + * + *

Example: + * ``` + * scipOccurrence { + * role = DEFINITION + * symbol = "sample/Banana#" + * range { startLine = 1; startCharacter = 6; endLine = 1; endCharacter = 12 } + * enclosingRange { startLine = 1; endLine = 3; endCharacter = 1 } + * } + * ``` + */ +internal val REFERENCE: Int = SymbolRole.UnspecifiedSymbolRole.number +internal val DEFINITION: Int = SymbolRole.Definition.number + +@DslMarker annotation class ScipBuilderDsl + +@ScipBuilderDsl +class ScipRangeBuilder { + var startLine: Int = 0 + var startCharacter: Int = 0 + /** + * Default sentinel: when [endLine] is left untouched, the produced range is single-line at + * [startLine]. + */ + var endLine: Int = -1 + var endCharacter: Int = 0 + + internal fun toScipRange(): ScipRange { + val line = if (endLine < 0) startLine else endLine + return ScipRange.range(startLine, startCharacter, line, endCharacter) + } +} + +@ScipBuilderDsl +class ScipOccurrenceBuilder { + var role: Int = REFERENCE + var symbol: String = "" + private var range: ScipRange? = null + private var enclosingRange: ScipRange? = null + + fun range(block: ScipRangeBuilder.() -> Unit) { + range = ScipRangeBuilder().apply(block).toScipRange() + } + + fun enclosingRange(block: ScipRangeBuilder.() -> Unit) { + enclosingRange = ScipRangeBuilder().apply(block).toScipRange() + } + + internal fun build(): Occurrence { + val builder = Occurrence.newBuilder().setSymbol(symbol).setSymbolRoles(role) + range?.let { + if (it.isSingleLine) builder.singleLineRange = it.toSingleLineRange() + else builder.multiLineRange = it.toMultiLineRange() + } + enclosingRange?.let { + if (it.isSingleLine) builder.singleLineEnclosingRange = it.toSingleLineRange() + else builder.multiLineEnclosingRange = it.toMultiLineRange() + } + return builder.build() + } +} + +@ScipBuilderDsl +class ScipSymbolInformationBuilder { + var symbol: String = "" + var displayName: String = "" + var signatureText: String? = null + private val docs = mutableListOf() + private val overrides = mutableListOf() + + fun documentation(text: String) { + docs += text + } + + /** Appends an `is_implementation` [Relationship]. */ + fun addOverriddenSymbols(vararg symbols: String) { + overrides.addAll(symbols) + } + + internal fun build(): SymbolInformation = symbolInformation { + symbol = this@ScipSymbolInformationBuilder.symbol + if (this@ScipSymbolInformationBuilder.displayName.isNotEmpty()) { + displayName = this@ScipSymbolInformationBuilder.displayName + } + signatureText?.let { sigText -> + signatureDocumentation = signature { + language = "kotlin" + text = sigText + } + } + for (d in docs) documentation += d + for (s in overrides) { + relationships += relationship { + symbol = s + isImplementation = true + } + } + } +} + +internal fun scipOccurrence(block: ScipOccurrenceBuilder.() -> Unit): Occurrence = + ScipOccurrenceBuilder().apply(block).build() + +internal fun scipSymbol(block: ScipSymbolInformationBuilder.() -> Unit): SymbolInformation = + ScipSymbolInformationBuilder().apply(block).build() From 4172a110e0a0ecaba54b4093060760ddb5e121fb Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Mon, 26 Oct 2026 14:08:23 +0100 Subject: [PATCH 16/33] Populate SymbolInformation kind and enclosing_symbol Port the remaining SymbolInformation fields #973 added to scip-kotlinc. - kind follows the declaration: Class (including objects, enums and annotation classes), Interface, TypeAlias, Constructor, Method (including accessors and local functions), Property (including local variables, loop and catch variables, and destructuring entries), Parameter, TypeParameter and EnumMember. - enclosing_symbol is the nearest containing declaration, or the package for top-level declarations. Synthetic accessors are enclosed by their property, and `val`/`var` constructor parameter properties by the class. Declarations inside lambdas have no enclosing symbol. On the Kotlin snapshot corpus, every global symbol and every local emitted by both indexers has the same kind and enclosing_symbol as scip-kotlinc on main. The ported AnalyzerTest cases get back their kind and enclosing_symbol expectations, and the snapshot goldens gain `kind` lines. Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-01a153c0-ec1f-774a-993c-bca3256017ce --- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 105 ++++++++++++++++-- .../KotlinAnalysisIndexerTest.kt | 57 ++++++++++ .../scip_java/kotlin_analysis/ScipBuilders.kt | 4 + .../src/main/kotlin/snapshots/Annotations.kt | 8 ++ .../common/src/main/kotlin/snapshots/Class.kt | 17 +++ .../main/kotlin/snapshots/CompanionOwner.kt | 6 + .../main/kotlin/snapshots/Destructuring.kt | 22 ++++ .../src/main/kotlin/snapshots/Docstrings.kt | 5 + .../common/src/main/kotlin/snapshots/Enums.kt | 21 ++++ .../src/main/kotlin/snapshots/Functions.kt | 2 + .../src/main/kotlin/snapshots/Generics.kt | 18 +++ .../main/kotlin/snapshots/Implementations.kt | 17 +++ .../src/main/kotlin/snapshots/Lambdas.kt | 9 ++ .../src/main/kotlin/snapshots/ObjectKt.kt | 4 + 14 files changed, 288 insertions(+), 7 deletions(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index c33ab80dc..e6abc0c4c 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -2,6 +2,7 @@ package org.scip_code.scip_java.kotlin_analysis import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement import java.nio.file.Path import java.nio.file.Paths import java.util.Arrays @@ -58,6 +59,7 @@ import org.jetbrains.kotlin.psi.psiUtil.parents import org.jetbrains.kotlin.types.Variance import org.scip_code.scip.Document import org.scip_code.scip.Occurrence +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 @@ -246,6 +248,10 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = symbol, range = range, + kind = + if (declaration is KtClass && declaration.isInterface()) Kind.Interface + else Kind.Class, + enclosingSymbol = enclosingSymbol(declaration), enclosing = declaration.textRange, displayName = displayName, signatureText = signatures.classSignature(declaration), @@ -262,6 +268,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.implicitConstructorSymbol(declaration), range = range, + kind = Kind.Constructor, + enclosingSymbol = symbol, enclosing = declaration.textRange, displayName = displayName, signatureText = signatures.implicitConstructorSignature(declaration), @@ -273,6 +281,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.symbolForDeclaration(primaryConstructor), range = range, + kind = Kind.Constructor, + enclosingSymbol = symbol, // Span from the definition anchor through the parameter list so the // enclosing range contains its own anchor (scip-kotlinc's // parameter-list-only range did not). @@ -291,6 +301,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.memberMethodSymbol(declaration, "copy"), range = constructor.textRange, + kind = Kind.Method, + enclosingSymbol = symbol, enclosing = constructor.textRange, displayName = "copy", signatureText = signatures.dataCopySignature(declaration), @@ -307,10 +319,13 @@ class KotlinAnalysisIndexer( */ private fun emitEnumSynthetics(declaration: KtClass, range: TextRange) { val name = declaration.name.orEmpty() + val owner = cache.symbolForDeclaration(declaration) val enclosing = declaration.textRange emitDefinition( symbol = cache.memberMethodSymbol(declaration, "values"), range = range, + kind = Kind.Method, + enclosingSymbol = owner, enclosing = enclosing, displayName = "values", signatureText = signatures.enumValuesSignature(name), @@ -320,6 +335,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = valueOf, range = range, + kind = Kind.Method, + enclosingSymbol = owner, enclosing = enclosing, displayName = "valueOf", signatureText = signatures.enumValueOfSignature(name), @@ -328,14 +345,19 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.methodParameterSymbol(valueOf, "value"), range = range, + kind = Kind.Parameter, + enclosingSymbol = valueOf, enclosing = enclosing, displayName = "value", signatureText = "value: String", documentation = null, ) + val entries = cache.memberTermSymbol(declaration, "entries") emitDefinition( - symbol = cache.memberTermSymbol(declaration, "entries"), + symbol = entries, range = range, + kind = Kind.Property, + enclosingSymbol = owner, enclosing = enclosing, displayName = "entries", signatureText = signatures.enumEntriesSignature(name), @@ -344,6 +366,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.memberMethodSymbol(declaration, "getEntries"), range = range, + kind = Kind.Method, + enclosingSymbol = entries, enclosing = enclosing, displayName = "entries", signatureText = signatures.enumGetEntriesSignature(name), @@ -356,6 +380,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.symbolForDeclaration(declaration), range = identifier.textRange, + kind = Kind.EnumMember, + enclosingSymbol = enclosingSymbol(declaration), enclosing = declaration.textRange, displayName = declaration.name.orEmpty(), signatureText = signatures.propertySignature(declaration), @@ -375,6 +401,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.symbolForDeclaration(declaration), range = range, + kind = Kind.Constructor, + enclosingSymbol = enclosingSymbol(declaration), enclosing = declaration.textRange, displayName = declaration.containingClassOrObject?.name ?: "", signatureText = signatures.constructorSignature(declaration), @@ -393,6 +421,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.symbolForDeclaration(declaration), range = identifier.textRange, + kind = Kind.Method, + enclosingSymbol = enclosingSymbol(declaration), enclosing = declaration.textRange, displayName = declaration.name.orEmpty(), signatureText = signatures.functionSignature(declaration), @@ -408,6 +438,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = symbol, range = identifier.textRange, + kind = Kind.Property, + enclosingSymbol = enclosingSymbol(declaration), enclosing = declaration.textRange, displayName = declaration.name.orEmpty(), signatureText = signatures.localVariableSignature(declaration), @@ -418,6 +450,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = symbol, range = identifier.textRange, + kind = Kind.Property, + enclosingSymbol = enclosingSymbol(declaration), enclosing = declaration.textRange, displayName = declaration.name.orEmpty(), signatureText = signatures.propertySignature(declaration), @@ -447,6 +481,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.symbolForDeclaration(declaration), range = loopOrCatchIdentifier.textRange, + kind = Kind.Property, + enclosingSymbol = enclosingSymbol(declaration), enclosing = declaration.textRange, displayName = declaration.name.orEmpty(), signatureText = "local val ${declaration.name.orEmpty()}: $type", @@ -458,6 +494,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.symbolForDeclaration(declaration), range = loopOrCatchIdentifier.textRange, + kind = Kind.Property, + enclosingSymbol = enclosingSymbol(declaration), enclosing = declaration.textRange, displayName = declaration.name.orEmpty(), signatureText = signatures.parameterSignature(declaration), @@ -473,6 +511,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = symbol, range = identifier.textRange, + kind = Kind.Parameter, + enclosingSymbol = enclosingSymbol(declaration), enclosing = declaration.textRange, displayName = declaration.name.orEmpty(), signatureText = signatures.parameterSignature(declaration), @@ -485,6 +525,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.parameterPropertySymbol(declaration), range = identifier.textRange, + kind = Kind.Property, + enclosingSymbol = enclosingSymbol(declaration.ownerFunction ?: declaration), enclosing = declaration.textRange, displayName = declaration.name.orEmpty(), signatureText = signatures.propertySignature(declaration), @@ -518,18 +560,19 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.memberMethodSymbol(containingClass, "component${index + 1}"), range = range, + kind = Kind.Method, + enclosingSymbol = cache.symbolForDeclaration(containingClass), enclosing = declaration.textRange, displayName = "component${index + 1}", signatureText = signatures.dataComponentSignature(index + 1, declaration), documentation = null, ) + val copy = cache.memberMethodSymbol(containingClass, "copy") emitDefinition( - symbol = - cache.methodParameterSymbol( - cache.memberMethodSymbol(containingClass, "copy"), - declaration.name.orEmpty(), - ), + symbol = cache.methodParameterSymbol(copy, declaration.name.orEmpty()), range = range, + kind = Kind.Parameter, + enclosingSymbol = copy, enclosing = declaration.textRange, displayName = declaration.name.orEmpty(), signatureText = signatures.dataCopyParameterSignature(declaration), @@ -547,6 +590,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.symbolForDeclaration(declaration), range = identifier.textRange, + kind = Kind.TypeAlias, + enclosingSymbol = enclosingSymbol(declaration), enclosing = declaration.textRange, displayName = declaration.name.orEmpty(), signatureText = signatures.typeAliasSignature(declaration), @@ -565,6 +610,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.syntheticLocalSymbol(expression), range = subject.textRange, + kind = Kind.Property, + enclosingSymbol = enclosingSymbol(expression), enclosing = subject.textRange, displayName = "", signatureText = @@ -593,6 +640,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.syntheticLocalSymbol(destructuringDeclaration), range = destructuringDeclaration.textRange, + kind = if (isParameter) Kind.Parameter else Kind.Property, + enclosingSymbol = enclosingSymbol(destructuringDeclaration), enclosing = destructuringDeclaration.textRange, displayName = "", signatureText = signatures.destructSignature(type, isParameter), @@ -678,10 +727,15 @@ class KotlinAnalysisIndexer( hasExplicitSetter: Boolean, isVar: Boolean, ) { + val propertySymbol = + if (property is KtParameter) cache.parameterPropertySymbol(property) + else cache.symbolForDeclaration(property) if (!hasExplicitGetter) { emitDefinition( symbol = cache.syntheticAccessorSymbol(property, setter = false), range = nameRange, + kind = Kind.Method, + enclosingSymbol = propertySymbol, enclosing = enclosing, displayName = displayName, signatureText = signatures.syntheticAccessorSignature(property, setter = false), @@ -689,9 +743,12 @@ class KotlinAnalysisIndexer( ) } if (isVar && !hasExplicitSetter) { + val setter = cache.syntheticAccessorSymbol(property, setter = true) emitDefinition( - symbol = cache.syntheticAccessorSymbol(property, setter = true), + symbol = setter, range = nameRange, + kind = Kind.Method, + enclosingSymbol = propertySymbol, enclosing = enclosing, displayName = displayName, signatureText = signatures.syntheticAccessorSignature(property, setter = true), @@ -700,6 +757,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.syntheticSetterValueSymbol(property), range = nameRange, + kind = Kind.Parameter, + enclosingSymbol = setter, enclosing = enclosing, displayName = "value", signatureText = signatures.setterValueSignature(property), @@ -712,6 +771,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.symbolForDeclaration(declaration), range = declaration.namePlaceholder.textRange, + kind = Kind.Method, + enclosingSymbol = enclosingSymbol(declaration), enclosing = declaration.textRange, displayName = declaration.property.name.orEmpty(), signatureText = signatures.accessorSignature(declaration), @@ -724,6 +785,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.symbolForDeclaration(declaration), range = identifier.textRange, + kind = Kind.TypeParameter, + enclosingSymbol = enclosingSymbol(declaration), enclosing = declaration.textRange, displayName = declaration.name.orEmpty(), signatureText = declaration.text, @@ -736,6 +799,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.symbolForDeclaration(declaration), range = identifier.textRange, + kind = Kind.Property, + enclosingSymbol = enclosingSymbol(declaration), enclosing = declaration.textRange, displayName = declaration.name.orEmpty(), signatureText = signatures.localVariableSignature(declaration), @@ -754,6 +819,8 @@ class KotlinAnalysisIndexer( emitDefinition( symbol = cache.implicitItSymbol(literal), range = literal.textRange, + kind = Kind.Parameter, + enclosingSymbol = Symbol.NONE, enclosing = literal.textRange, displayName = "it", signatureText = signatures.implicitItSignature(target), @@ -817,6 +884,8 @@ class KotlinAnalysisIndexer( private fun emitDefinition( symbol: Symbol, range: TextRange, + kind: Kind, + enclosingSymbol: Symbol, enclosing: TextRange?, displayName: String, signatureText: String?, @@ -843,6 +912,8 @@ class KotlinAnalysisIndexer( out.addSymbol( symbolInformation { this.symbol = symbol.toString() + this.kind = kind + this.enclosingSymbol = enclosingSymbol.toString() this.displayName = displayName if (signatureText != null) { this.signatureDocumentation = signature { @@ -863,6 +934,26 @@ class KotlinAnalysisIndexer( ) } + /** + * The symbol of the nearest declaration containing [element], or the package for top-level + * declarations. Lambdas have no symbol, so declarations inside them have none either. + */ + private fun enclosingSymbol(element: PsiElement): Symbol { + for (parent in element.parents) { + when (parent) { + is KtFunctionLiteral -> return Symbol.NONE + is KtClassOrObject, + is KtNamedFunction, + is KtConstructor<*>, + is KtProperty, + is KtPropertyAccessor -> + return cache.symbolForDeclaration(parent as KtDeclaration) + is KtFile -> return cache.packageSymbol(parent.packageFqName) + } + } + return Symbol.NONE + } + private fun addReference(symbol: Symbol, range: TextRange) { if (symbol == Symbol.NONE) return val scipRange = lines.anchorRange(range) ?: return diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index 523dc8592..9d2a200b1 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -9,6 +9,7 @@ import kotlin.test.assertEquals import kotlin.test.assertTrue import org.junit.jupiter.api.io.TempDir import org.scip_code.scip.Document +import org.scip_code.scip.SymbolInformation.Kind import org.scip_code.scip.SymbolRole class KotlinAnalysisIndexerTest { @@ -73,6 +74,15 @@ class KotlinAnalysisIndexerTest { assertContains(references(greeter), "snapshots/Greeter#greet().") // The stdlib type reference resolves to a classpath symbol. assertContains(references(greeter), "kotlin/String#") + // A `val` constructor parameter's property belongs to the class, and its + // synthetic getter to the property. + val enclosingSymbols = greeter.symbolsList.associate { it.symbol to it.enclosingSymbol } + assertEquals( + "snapshots/Greeter#``().", + enclosingSymbols["snapshots/Greeter#``().(name)"], + ) + assertEquals("snapshots/Greeter#", enclosingSymbols["snapshots/Greeter#name."]) + assertEquals("snapshots/Greeter#name.", enclosingSymbols["snapshots/Greeter#getName()."]) val mainDefinitions = definitions(main) assertContains(mainDefinitions, "snapshots/useGreeter().") @@ -169,16 +179,21 @@ class KotlinAnalysisIndexerTest { arrayOf( scipSymbol { symbol = "sample/use()." + kind = Kind.Method + enclosingSymbol = "sample/" displayName = "use" signatureText = "public final fun use(): Unit" }, scipSymbol { symbol = "local 0" + kind = Kind.Property + enclosingSymbol = "sample/use()." displayName = "f" signatureText = "local val f: (Int) -> Int" }, scipSymbol { symbol = "local 1" + kind = Kind.Parameter displayName = "n" signatureText = "n: Int" }, @@ -325,6 +340,8 @@ class KotlinAnalysisIndexerTest { arrayOf( scipSymbol { symbol = "sample/bar()." + kind = Kind.Method + enclosingSymbol = "sample/" displayName = "bar" signatureText = "public final fun bar(): MyClass" } @@ -386,6 +403,8 @@ class KotlinAnalysisIndexerTest { arrayOf( scipSymbol { symbol = "sample/MyAlias#" + kind = Kind.TypeAlias + enclosingSymbol = "sample/" displayName = "MyAlias" signatureText = "public final typealias MyAlias = Int\n" } @@ -446,6 +465,8 @@ class KotlinAnalysisIndexerTest { arrayOf( scipSymbol { symbol = "sample/identity().[T]" + kind = Kind.TypeParameter + enclosingSymbol = "sample/identity()." displayName = "T" signatureText = "T" } @@ -891,22 +912,30 @@ class KotlinAnalysisIndexerTest { arrayOf( scipSymbol { symbol = "sample/Color#" + kind = Kind.Class + enclosingSymbol = "sample/" displayName = "Color" addOverriddenSymbols("kotlin/Enum#") signatureText = "public final enum class Color : Enum" }, scipSymbol { symbol = "sample/Color#RED." + kind = Kind.EnumMember + enclosingSymbol = "sample/Color#" displayName = "RED" signatureText = "public final val RED: Color" }, scipSymbol { symbol = "sample/Color#GREEN." + kind = Kind.EnumMember + enclosingSymbol = "sample/Color#" displayName = "GREEN" signatureText = "public final val GREEN: Color" }, scipSymbol { symbol = "sample/Color#BLUE." + kind = Kind.EnumMember + enclosingSymbol = "sample/Color#" displayName = "BLUE" signatureText = "public final val BLUE: Color" }, @@ -975,6 +1004,8 @@ class KotlinAnalysisIndexerTest { arrayOf( scipSymbol { symbol = "sample/Op#PLUS.apply()." + kind = Kind.Method + enclosingSymbol = "sample/Op#PLUS." displayName = "apply" addOverriddenSymbols("sample/Op#apply().") signatureText = "public open override fun apply(a: Int, b: Int): Int" @@ -1059,11 +1090,15 @@ class KotlinAnalysisIndexerTest { arrayOf( scipSymbol { symbol = "sample/MySingleton#" + kind = Kind.Class + enclosingSymbol = "sample/" displayName = "MySingleton" signatureText = "public final object MySingleton : Any" }, scipSymbol { symbol = "sample/MySingleton#hello()." + kind = Kind.Method + enclosingSymbol = "sample/MySingleton#" displayName = "hello" signatureText = "public final fun hello(): String" }, @@ -1175,16 +1210,22 @@ class KotlinAnalysisIndexerTest { arrayOf( scipSymbol { symbol = "sample/Foo#" + kind = Kind.Class + enclosingSymbol = "sample/" displayName = "Foo" signatureText = "public final class Foo : Any" }, scipSymbol { symbol = "sample/Foo#Factory#" + kind = Kind.Class + enclosingSymbol = "sample/Foo#" displayName = "Factory" signatureText = "public final companion object Factory : Any" }, scipSymbol { symbol = "sample/Foo#Factory#create()." + kind = Kind.Method + enclosingSymbol = "sample/Foo#Factory#" displayName = "create" signatureText = "public final fun create(): Foo" }, @@ -1286,16 +1327,22 @@ class KotlinAnalysisIndexerTest { arrayOf( scipSymbol { symbol = "sample/Bar#" + kind = Kind.Class + enclosingSymbol = "sample/" displayName = "Bar" signatureText = "public final class Bar : Any" }, scipSymbol { symbol = "sample/Bar#Companion#" + kind = Kind.Class + enclosingSymbol = "sample/Bar#" displayName = "Companion" signatureText = "public final companion object Companion : Any" }, scipSymbol { symbol = "sample/Bar#Companion#instance()." + kind = Kind.Method + enclosingSymbol = "sample/Bar#Companion#" displayName = "instance" signatureText = "public final fun instance(): Bar" }, @@ -1366,6 +1413,8 @@ class KotlinAnalysisIndexerTest { arrayOf( scipSymbol { symbol = "sample/greet()." + kind = Kind.Method + enclosingSymbol = "sample/" displayName = "greet" signatureText = "public final fun greet(name: String): String" } @@ -1443,6 +1492,8 @@ class KotlinAnalysisIndexerTest { arrayOf( scipSymbol { symbol = "sample/Person#" + kind = Kind.Class + enclosingSymbol = "sample/" displayName = "Person" addOverriddenSymbols("sample/Named#") addOverriddenSymbols("sample/Speakable#") @@ -1558,16 +1609,22 @@ class KotlinAnalysisIndexerTest { arrayOf( scipSymbol { symbol = "sample/add()." + kind = Kind.Method + enclosingSymbol = "sample/" displayName = "add" signatureText = "public final fun add(x: Int, y: Int): Int" }, scipSymbol { symbol = "sample/add(+1)." + kind = Kind.Method + enclosingSymbol = "sample/" displayName = "add" signatureText = "public final fun add(x: Double, y: Double): Double" }, scipSymbol { symbol = "sample/add(+2)." + kind = Kind.Method + enclosingSymbol = "sample/" displayName = "add" signatureText = "public final fun add(x: String, y: String): String" }, diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipBuilders.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipBuilders.kt index 8bd141068..0bbf53f6a 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipBuilders.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipBuilders.kt @@ -75,6 +75,8 @@ class ScipOccurrenceBuilder { @ScipBuilderDsl class ScipSymbolInformationBuilder { var symbol: String = "" + var kind: SymbolInformation.Kind = SymbolInformation.Kind.UnspecifiedKind + var enclosingSymbol: String = "" var displayName: String = "" var signatureText: String? = null private val docs = mutableListOf() @@ -91,6 +93,8 @@ class ScipSymbolInformationBuilder { internal fun build(): SymbolInformation = symbolInformation { symbol = this@ScipSymbolInformationBuilder.symbol + kind = this@ScipSymbolInformationBuilder.kind + enclosingSymbol = this@ScipSymbolInformationBuilder.enclosingSymbol if (this@ScipSymbolInformationBuilder.displayName.isNotEmpty()) { displayName = this@ScipSymbolInformationBuilder.displayName } diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt index 53d3ba257..019dec751 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt @@ -14,24 +14,29 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#getTag(). annotation class Tagged(val tag: String) // ^^^^^^ definition scip-java maven . . snapshots/Tagged# +// kind Class // display_name Tagged // signature_documentation // > @Target(...) public final annotation class Tagged : Annotation // relationship scip-java maven . . kotlin/Annotation# implementation // ^^^^^^ definition scip-java maven . . snapshots/Tagged#``(). +// kind Constructor // display_name Tagged // signature_documentation // > public constructor(tag: String): Tagged // ^^^ definition scip-java maven . . snapshots/Tagged#``().(tag) +// kind Parameter // display_name tag // signature_documentation // > tag: String // ^^^ definition scip-java maven . . snapshots/Tagged#tag. +// kind Property // display_name tag // signature_documentation // > public final val tag: String // ^^^ reference scip-java maven . . snapshots/Tagged#``().(tag) // ^^^ definition scip-java maven . . snapshots/Tagged#getTag(). +// kind Method // display_name tag // signature_documentation // > public get(): String @@ -48,10 +53,12 @@ // ^^^^^^ reference scip-java maven . . snapshots/Tagged# class AnnotatedService { // ^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/AnnotatedService# +// kind Class // display_name AnnotatedService // signature_documentation // > @Tagged(...) public final class AnnotatedService : Any // ^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/AnnotatedService#``(). +// kind Constructor // display_name AnnotatedService // signature_documentation // > public constructor(): AnnotatedService @@ -60,6 +67,7 @@ // ^^^^^^ reference scip-java maven . . snapshots/Tagged# fun run(): String = "running" // ^^^ definition scip-java maven . . snapshots/AnnotatedService#run(). +// kind Method // display_name run // signature_documentation // > @Tagged(...) public final fun run(): String diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt index 12e88e573..9ee4aa22d 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt @@ -11,37 +11,45 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``().(apple) class Class constructor(private var banana: Int, apple: String) : // ^^^^^ definition scip-java maven . . snapshots/Class# +// kind Class // display_name Class // signature_documentation // > public final class Class : Throwable // relationship scip-java maven . . kotlin/Throwable# implementation // ^^^^^^^^^^^ definition scip-java maven . . snapshots/Class#``(). +// kind Constructor // display_name Class // signature_documentation // > public constructor(banana: Int, apple: String): Class // ^^^^^^ definition scip-java maven . . snapshots/Class#``().(banana) +// kind Parameter // display_name banana // signature_documentation // > banana: Int // ^^^^^^ definition scip-java maven . . snapshots/Class#banana. +// kind Property // display_name banana // signature_documentation // > private final var banana: Int // ^^^^^^ reference scip-java maven . . snapshots/Class#``().(banana) // ^^^^^^ definition scip-java maven . . snapshots/Class#getBanana(). +// kind Method // display_name banana // signature_documentation // > private get(): Int // ^^^^^^ definition scip-java maven . . snapshots/Class#setBanana(). +// kind Method // display_name banana // signature_documentation // > private set(value: Int): Unit // ^^^^^^ definition scip-java maven . . snapshots/Class#setBanana().(value) +// kind Parameter // display_name value // signature_documentation // > value: Int // ^^^ reference scip-java maven . . kotlin/Int# // ^^^^^ definition scip-java maven . . snapshots/Class#``().(apple) +// kind Parameter // display_name apple // signature_documentation // > apple: String @@ -68,10 +76,12 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#getAsdf(). val asdf = // ^^^^ definition scip-java maven . . snapshots/Class#asdf. +// kind Property // display_name asdf // signature_documentation // > public final val asdf: Any // ^^^^ definition scip-java maven . . snapshots/Class#getAsdf(). +// kind Method // display_name asdf // signature_documentation // > public get(): Any @@ -79,16 +89,19 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/``#``(). object { // ^^^^^^ definition scip-java maven . . snapshots/``# +// kind Class // display_name // signature_documentation // > object : Any // ^^^^^^ definition scip-java maven . . snapshots/``#``(). +// kind Constructor // display_name // signature_documentation // > private constructor(): // ⌄ enclosing_range_start scip-java maven . . snapshots/``#doStuff(). fun doStuff() = Unit // ^^^^^^^ definition scip-java maven . . snapshots/``#doStuff(). +// kind Method // display_name doStuff // signature_documentation // > public final fun doStuff(): Unit @@ -103,6 +116,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``(+1). constructor() : this(1, "") // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Class#``(+1). +// kind Constructor // display_name Class // signature_documentation // > public constructor(): Class @@ -112,10 +126,12 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``(+2).(banana) constructor(banana: Int) : this(banana, "") // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Class#``(+2). +// kind Constructor // display_name Class // signature_documentation // > public constructor(banana: Int): Class // ^^^^^^ definition scip-java maven . . snapshots/Class#``(+2).(banana) +// kind Parameter // display_name banana // signature_documentation // > banana: Int @@ -127,6 +143,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#run(). fun run() { // ^^^ definition scip-java maven . . snapshots/Class#run(). +// kind Method // display_name run // signature_documentation // > public final fun run(): Unit diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt index 48e740412..7b2bc1bbf 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt @@ -5,10 +5,12 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#``(). class CompanionOwner { // ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner# +// kind Class // display_name CompanionOwner // signature_documentation // > public final class CompanionOwner : Any // ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#``(). +// kind Constructor // display_name CompanionOwner // signature_documentation // > public constructor(): CompanionOwner @@ -16,16 +18,19 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion#``(). companion object { // ^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion# +// kind Class // display_name Companion // signature_documentation // > public final companion object Companion : Any // ^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion#``(). +// kind Constructor // display_name Companion // signature_documentation // > private constructor(): CompanionOwner.Companion // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion#create(). fun create(): CompanionOwner = CompanionOwner() // ^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion#create(). +// kind Method // display_name create // signature_documentation // > public final fun create(): CompanionOwner @@ -38,6 +43,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#create(). fun create(): Int = CompanionOwner.create().hashCode() // ^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#create(). +// kind Method // display_name create // signature_documentation // > public final fun create(): Int diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt index 3c3f8f453..987ebd055 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt @@ -16,37 +16,45 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#copy().(y) data class Point(val x: Int, val y: Int) // ^^^^^ definition scip-java maven . . snapshots/Point# +// kind Class // display_name Point // signature_documentation // > public final data class Point : Any // ^^^^^ definition scip-java maven . . snapshots/Point#``(). +// kind Constructor // display_name Point // signature_documentation // > public constructor(x: Int, y: Int): Point // ^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Point#copy(). +// kind Method // display_name copy // signature_documentation // > public final fun copy(x: Int = ..., y: Int = ...): Point // > // ^ definition scip-java maven . . snapshots/Point#``().(x) +// kind Parameter // display_name x // signature_documentation // > x: Int // ^ definition scip-java maven . . snapshots/Point#x. +// kind Property // display_name x // signature_documentation // > public final val x: Int // ^ reference scip-java maven . . snapshots/Point#``().(x) // ^ definition scip-java maven . . snapshots/Point#getX(). +// kind Method // display_name x // signature_documentation // > public get(): Int // ^ definition scip-java maven . . snapshots/Point#component1(). +// kind Method // display_name component1 // signature_documentation // > public final operator fun component1(): Int // > // ^ definition scip-java maven . . snapshots/Point#copy().(x) +// kind Parameter // display_name x // signature_documentation // > x: Int = ... @@ -54,24 +62,29 @@ // ^ reference scip-java maven . . snapshots/Point#getX(). // ^^^ reference scip-java maven . . kotlin/Int# // ^ definition scip-java maven . . snapshots/Point#``().(y) +// kind Parameter // display_name y // signature_documentation // > y: Int // ^ definition scip-java maven . . snapshots/Point#y. +// kind Property // display_name y // signature_documentation // > public final val y: Int // ^ reference scip-java maven . . snapshots/Point#``().(y) // ^ definition scip-java maven . . snapshots/Point#getY(). +// kind Method // display_name y // signature_documentation // > public get(): Int // ^ definition scip-java maven . . snapshots/Point#component2(). +// kind Method // display_name component2 // signature_documentation // > public final operator fun component2(): Int // > // ^ definition scip-java maven . . snapshots/Point#copy().(y) +// kind Parameter // display_name y // signature_documentation // > y: Int = ... @@ -95,6 +108,7 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/destructure(). fun destructure(): Int { // ^^^^^^^^^^^ definition scip-java maven . . snapshots/destructure(). +// kind Method // display_name destructure // signature_documentation // > public final fun destructure(): Int @@ -104,16 +118,19 @@ // ⌄ enclosing_range_start local 2 val (x, y) = Point(1, 2) // ^^^^^^^^^^^^^^^^^^^^^^^^ definition local 0 +// kind Property // display_name // signature_documentation // > local val : Point // ^ definition local 1 +// kind Property // display_name x // signature_documentation // > local val x: Int // ^ reference scip-java maven . . snapshots/Point#component1(). // ^ reference local 0 // ^ definition local 2 +// kind Property // display_name y // signature_documentation // > local val y: Int @@ -126,6 +143,7 @@ // ⌄ enclosing_range_start local 3 val labeled = listOf(Point(3, 4) to "label") // ^^^^^^^ definition local 3 +// kind Property // display_name labeled // signature_documentation // > local val labeled: List> @@ -139,22 +157,26 @@ // ⌄ enclosing_range_start local 7 val total = labeled.sumOf { (point, label) -> point.x + label.length } // ^^^^^ definition local 4 +// kind Property // display_name total // signature_documentation // > local val total: Int // ^^^^^^^ reference local 3 // ^^^^^ reference scip-java maven . . kotlin/collections/sumOf(+66). // ^^^^^^^^^^^^^^ definition local 5 +// kind Parameter // display_name // signature_documentation // > : Pair // ^^^^^ definition local 6 +// kind Property // display_name point // signature_documentation // > local val point: Point // ^^^^^ reference scip-java maven . . kotlin/Pair#component1(). // ^^^^^ reference local 5 // ^^^^^ definition local 7 +// kind Property // display_name label // signature_documentation // > local val label: String diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Docstrings.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Docstrings.kt index b7a60c8a5..69c6333f2 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Docstrings.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Docstrings.kt @@ -10,10 +10,12 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/DocstringSuperclass#``(). abstract class DocstringSuperclass // ^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/DocstringSuperclass# +// kind Class // display_name DocstringSuperclass // signature_documentation // > public abstract class DocstringSuperclass : Any // ^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/DocstringSuperclass#``(). +// kind Constructor // display_name DocstringSuperclass // signature_documentation // > public constructor(): DocstringSuperclass @@ -24,6 +26,7 @@ /** Example class docstring. */ class Docstrings : DocstringSuperclass(), Serializable { // ^^^^^^^^^^ definition scip-java maven . . snapshots/Docstrings# +// kind Class // display_name Docstrings // signature_documentation // > public final class Docstrings : DocstringSuperclass, Serializable @@ -32,6 +35,7 @@ // relationship scip-java maven . . snapshots/DocstringSuperclass# implementation // relationship scip-java maven jdk 17 java/io/Serializable# implementation // ^^^^^^^^^^ definition scip-java maven . . snapshots/Docstrings#``(). +// kind Constructor // display_name Docstrings // signature_documentation // > public constructor(): Docstrings @@ -47,6 +51,7 @@ /** Example method docstring. */ fun docstrings() { } // ^^^^^^^^^^ definition scip-java maven . . snapshots/docstrings(). +// kind Method // display_name docstrings // signature_documentation // > public final fun docstrings(): Unit diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt index 12739eb43..bdd8e94b7 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt @@ -13,44 +13,54 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#getSymbol(). enum class Suit(val symbol: Char) { // ^^^^ definition scip-java maven . . snapshots/Suit# +// kind Class // display_name Suit // signature_documentation // > public final enum class Suit : Enum // relationship scip-java maven . . kotlin/Enum# implementation // ^^^^ definition scip-java maven . . snapshots/Suit#``(). +// kind Constructor // display_name Suit // signature_documentation // > private constructor(symbol: Char): Suit // ^^^^ definition scip-java maven . . snapshots/Suit#values(). +// kind Method // display_name values // signature_documentation // > public final static fun values(): Array // ^^^^ definition scip-java maven . . snapshots/Suit#valueOf(). +// kind Method // display_name valueOf // signature_documentation // > public final static fun valueOf(value: String): Suit // ^^^^ definition scip-java maven . . snapshots/Suit#valueOf().(value) +// kind Parameter // display_name value // signature_documentation // > value: String // ^^^^ definition scip-java maven . . snapshots/Suit#entries. +// kind Property // display_name entries // signature_documentation // > public final static val entries: EnumEntries // ^^^^ definition scip-java maven . . snapshots/Suit#getEntries(). +// kind Method // display_name entries // signature_documentation // > public get(): EnumEntries // ^^^^^^ definition scip-java maven . . snapshots/Suit#``().(symbol) +// kind Parameter // display_name symbol // signature_documentation // > symbol: Char // ^^^^^^ definition scip-java maven . . snapshots/Suit#symbol. +// kind Property // display_name symbol // signature_documentation // > public final val symbol: Char // ^^^^^^ reference scip-java maven . . snapshots/Suit#``().(symbol) // ^^^^^^ definition scip-java maven . . snapshots/Suit#getSymbol(). +// kind Method // display_name symbol // signature_documentation // > public get(): Char @@ -62,6 +72,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#HEARTS. HEARTS('h'), // ^^^^^^ definition scip-java maven . . snapshots/Suit#HEARTS. +// kind EnumMember // display_name HEARTS // signature_documentation // > public final val HEARTS: Suit @@ -69,6 +80,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#SPADES. SPADES('s'); // ^^^^^^ definition scip-java maven . . snapshots/Suit#SPADES. +// kind EnumMember // display_name SPADES // signature_documentation // > public final val SPADES: Suit @@ -77,6 +89,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#isRed(). fun isRed(): Boolean = symbol == 'h' // ^^^^^ definition scip-java maven . . snapshots/Suit#isRed(). +// kind Method // display_name isRed // signature_documentation // > public final fun isRed(): Boolean @@ -90,10 +103,12 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion#``(). companion object { // ^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion# +// kind Class // display_name Companion // signature_documentation // > public final companion object Companion : Any // ^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion#``(). +// kind Constructor // display_name Companion // signature_documentation // > private constructor(): Suit.Companion @@ -102,10 +117,12 @@ // ⌄ enclosing_range_start local 0 fun fromSymbol(symbol: Char): Suit? = entries.find { it.symbol == symbol } // ^^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion#fromSymbol(). +// kind Method // display_name fromSymbol // signature_documentation // > public final fun fromSymbol(symbol: Char): Suit? // ^^^^^^ definition scip-java maven . . snapshots/Suit#Companion#fromSymbol().(symbol) +// kind Parameter // display_name symbol // signature_documentation // > symbol: Char @@ -115,6 +132,7 @@ // ^^^^^^^ reference scip-java maven . . snapshots/Suit#getEntries(). // ^^^^ reference scip-java maven . . kotlin/collections/find(+9). // ^^^^^^^^^^^^^^^^^^^^^^^ definition local 0 +// kind Parameter // display_name it // signature_documentation // > it: Suit @@ -141,10 +159,12 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/describe().(suit) fun describe(suit: Suit): String = // ^^^^^^^^ definition scip-java maven . . snapshots/describe(). +// kind Method // display_name describe // signature_documentation // > public final fun describe(suit: Suit): String // ^^^^ definition scip-java maven . . snapshots/describe().(suit) +// kind Parameter // display_name suit // signature_documentation // > suit: Suit @@ -154,6 +174,7 @@ // ⌄ enclosing_range_start local 1 when (suit) { // ^^^^ definition local 1 +// kind Property // display_name // signature_documentation // > local val : Suit diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt index 2b9224e80..0ee9ed764 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt @@ -5,10 +5,12 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/sampleText().(x) fun sampleText(x: String = "") { // ^^^^^^^^^^ definition scip-java maven . . snapshots/sampleText(). +// kind Method // display_name sampleText // signature_documentation // > public final fun sampleText(x: String = ...): Unit // ^ definition scip-java maven . . snapshots/sampleText().(x) +// kind Parameter // display_name x // signature_documentation // > x: String = ... diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt index 921285395..2c7702164 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt @@ -9,29 +9,35 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#getItems(). class Container>(private val items: MutableList) { // ^^^^^^^^^ definition scip-java maven . . snapshots/Container# +// kind Class // display_name Container // signature_documentation // > public final class Container> : Any // ^^^^^^^^^ definition scip-java maven . . snapshots/Container#``(). +// kind Constructor // display_name Container // signature_documentation // > public constructor>(items: MutableList): Container // ^ definition scip-java maven . . snapshots/Container#[T] +// kind TypeParameter // display_name T // signature_documentation // > T : Comparable // ^^^^^^^^^^ reference scip-java maven . . kotlin/Comparable# // ^ reference scip-java maven . . snapshots/Container#[T] // ^^^^^ definition scip-java maven . . snapshots/Container#``().(items) +// kind Parameter // display_name items // signature_documentation // > items: MutableList // ^^^^^ definition scip-java maven . . snapshots/Container#items. +// kind Property // display_name items // signature_documentation // > private final val items: MutableList // ^^^^^ reference scip-java maven . . snapshots/Container#``().(items) // ^^^^^ definition scip-java maven . . snapshots/Container#getItems(). +// kind Method // display_name items // signature_documentation // > private get(): MutableList @@ -46,10 +52,12 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#add().(item) fun add(item: T): Container { // ^^^ definition scip-java maven . . snapshots/Container#add(). +// kind Method // display_name add // signature_documentation // > public final fun add(item: T): Container // ^^^^ definition scip-java maven . . snapshots/Container#add().(item) +// kind Parameter // display_name item // signature_documentation // > item: T @@ -71,15 +79,18 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#mapItems().(transform) fun mapItems(transform: (T) -> R?): List = items.mapNotNull(transform) // ^ definition scip-java maven . . snapshots/Container#mapItems().[R] +// kind TypeParameter // display_name R // signature_documentation // > R : Any // ^^^ reference scip-java maven . . kotlin/Any# // ^^^^^^^^ definition scip-java maven . . snapshots/Container#mapItems(). +// kind Method // display_name mapItems // signature_documentation // > public final fun mapItems(transform: (T) -> R?): List // ^^^^^^^^^ definition scip-java maven . . snapshots/Container#mapItems().(transform) +// kind Parameter // display_name transform // signature_documentation // > transform: (T) -> R? @@ -103,20 +114,24 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/firstOrSelf().(fallback) fun firstOrSelf(items: List, fallback: T): T = items.firstOrNull() ?: fallback // ^ definition scip-java maven . . snapshots/firstOrSelf().[T] +// kind TypeParameter // display_name T // signature_documentation // > T // ^^^^^^^^^^^ definition scip-java maven . . snapshots/firstOrSelf(). +// kind Method // display_name firstOrSelf // signature_documentation // > public final fun firstOrSelf(items: List, fallback: T): T // ^^^^^ definition scip-java maven . . snapshots/firstOrSelf().(items) +// kind Parameter // display_name items // signature_documentation // > items: List // ^^^^ reference scip-java maven . . kotlin/collections/List# // ^ reference scip-java maven . . snapshots/firstOrSelf().[T] // ^^^^^^^^ definition scip-java maven . . snapshots/firstOrSelf().(fallback) +// kind Parameter // display_name fallback // signature_documentation // > fallback: T @@ -133,6 +148,7 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/StringContainer# typealias StringContainer = Container // ^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/StringContainer# +// kind TypeAlias // display_name StringContainer // signature_documentation // > public final typealias StringContainer = Container @@ -145,10 +161,12 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/useContainer().(container) fun useContainer(container: StringContainer): StringContainer = container.add("hello") // ^^^^^^^^^^^^ definition scip-java maven . . snapshots/useContainer(). +// kind Method // display_name useContainer // signature_documentation // > public final fun useContainer(container: StringContainer): StringContainer // ^^^^^^^^^ definition scip-java maven . . snapshots/useContainer().(container) +// kind Parameter // display_name container // signature_documentation // > container: StringContainer diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt index f0c4f2d03..4f5f27c9c 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt @@ -5,11 +5,13 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Overrides#``(). class Overrides : AutoCloseable { // ^^^^^^^^^ definition scip-java maven . . snapshots/Overrides# +// kind Class // display_name Overrides // signature_documentation // > public final class Overrides : AutoCloseable // relationship scip-java maven jdk 17 java/lang/AutoCloseable# implementation // ^^^^^^^^^ definition scip-java maven . . snapshots/Overrides#``(). +// kind Constructor // display_name Overrides // signature_documentation // > public constructor(): Overrides @@ -17,6 +19,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Overrides#close(). override fun close() { // ^^^^^ definition scip-java maven . . snapshots/Overrides#close(). +// kind Method // display_name close // signature_documentation // > public open override fun close(): Unit @@ -32,6 +35,7 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Animal# interface Animal { // ^^^^^^ definition scip-java maven . . snapshots/Animal# +// kind Interface // display_name Animal // signature_documentation // > public abstract interface Animal : Any @@ -39,10 +43,12 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Animal#getFavoriteNumber(). val favoriteNumber: Int // ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Animal#favoriteNumber. +// kind Property // display_name favoriteNumber // signature_documentation // > public abstract val favoriteNumber: Int // ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Animal#getFavoriteNumber(). +// kind Method // display_name favoriteNumber // signature_documentation // > public get(): Int @@ -52,6 +58,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Animal#sound(). fun sound(): String // ^^^^^ definition scip-java maven . . snapshots/Animal#sound(). +// kind Method // display_name sound // signature_documentation // > public abstract fun sound(): String @@ -64,11 +71,13 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Bird#``(). open class Bird : Animal { // ^^^^ definition scip-java maven . . snapshots/Bird# +// kind Class // display_name Bird // signature_documentation // > public open class Bird : Animal // relationship scip-java maven . . snapshots/Animal# implementation // ^^^^ definition scip-java maven . . snapshots/Bird#``(). +// kind Constructor // display_name Bird // signature_documentation // > public constructor(): Bird @@ -76,6 +85,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Bird#favoriteNumber. override val favoriteNumber: Int // ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Bird#favoriteNumber. +// kind Property // display_name favoriteNumber // signature_documentation // > public open override val favoriteNumber: Int @@ -83,6 +93,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Bird#getFavoriteNumber(). get() = 42 // ^^^ definition scip-java maven . . snapshots/Bird#getFavoriteNumber(). +// kind Method // display_name favoriteNumber // signature_documentation // > public get(): Int @@ -92,6 +103,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Bird#sound(). override fun sound(): String { // ^^^^^ definition scip-java maven . . snapshots/Bird#sound(). +// kind Method // display_name sound // signature_documentation // > public open override fun sound(): String @@ -107,11 +119,13 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#``(). class Seagull : Bird() { // ^^^^^^^ definition scip-java maven . . snapshots/Seagull# +// kind Class // display_name Seagull // signature_documentation // > public final class Seagull : Bird // relationship scip-java maven . . snapshots/Bird# implementation // ^^^^^^^ definition scip-java maven . . snapshots/Seagull#``(). +// kind Constructor // display_name Seagull // signature_documentation // > public constructor(): Seagull @@ -119,6 +133,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#favoriteNumber. override val favoriteNumber: Int // ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Seagull#favoriteNumber. +// kind Property // display_name favoriteNumber // signature_documentation // > public open override val favoriteNumber: Int @@ -126,6 +141,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#getFavoriteNumber(). get() = 1337 // ^^^ definition scip-java maven . . snapshots/Seagull#getFavoriteNumber(). +// kind Method // display_name favoriteNumber // signature_documentation // > public get(): Int @@ -134,6 +150,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#sound(). override fun sound(): String { // ^^^^^ definition scip-java maven . . snapshots/Seagull#sound(). +// kind Method // display_name sound // signature_documentation // > public open override fun sound(): String diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt index 5266b8d31..36c8d9e65 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt @@ -7,10 +7,12 @@ // ⌄ enclosing_range_start local 1 val x = arrayListOf().forEachIndexed { i, s -> println("$i $s") } // ^ definition scip-java maven . . snapshots/x. +// kind Property // display_name x // signature_documentation // > public final val x: Unit // ^ definition scip-java maven . . snapshots/getX(). +// kind Method // display_name x // signature_documentation // > public get(): Unit @@ -18,10 +20,12 @@ // ^^^^^^ reference scip-java maven . . kotlin/String# // ^^^^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/forEachIndexed(+9). // ^ definition local 0 +// kind Parameter // display_name i // signature_documentation // > i: Int // ^ definition local 1 +// kind Parameter // display_name s // signature_documentation // > s: String @@ -37,10 +41,12 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/getY(). val y = "fdsa".run { this.toByteArray() } // ^ definition scip-java maven . . snapshots/y. +// kind Property // display_name y // signature_documentation // > public final val y: ByteArray // ^ definition scip-java maven . . snapshots/getY(). +// kind Method // display_name y // signature_documentation // > public get(): ByteArray @@ -54,10 +60,12 @@ // ⌄ enclosing_range_start local 2 val z = y.let { it.size } // ^ definition scip-java maven . . snapshots/z. +// kind Property // display_name z // signature_documentation // > public final val z: Int // ^ definition scip-java maven . . snapshots/getZ(). +// kind Method // display_name z // signature_documentation // > public get(): Int @@ -65,6 +73,7 @@ // ^ reference scip-java maven . . snapshots/getY(). // ^^^ reference scip-java maven . . kotlin/let(). // ^^^^^^^^^^^ definition local 2 +// kind Parameter // display_name it // signature_documentation // > it: ByteArray diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt index 8de86a9f3..81f0ddc71 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt @@ -10,10 +10,12 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/ObjectKt#``(). object ObjectKt { // ^^^^^^^^ definition scip-java maven . . snapshots/ObjectKt# +// kind Class // display_name ObjectKt // signature_documentation // > public final object ObjectKt : Any // ^^^^^^^^ definition scip-java maven . . snapshots/ObjectKt#``(). +// kind Constructor // display_name ObjectKt // signature_documentation // > private constructor(): ObjectKt @@ -21,10 +23,12 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/ObjectKt#fail().(message) fun fail(message: String?): Nothing { // ^^^^ definition scip-java maven . . snapshots/ObjectKt#fail(). +// kind Method // display_name fail // signature_documentation // > public final fun fail(message: String?): Nothing // ^^^^^^^ definition scip-java maven . . snapshots/ObjectKt#fail().(message) +// kind Parameter // display_name message // signature_documentation // > message: String? From 7e0ce7255a50a4c97892d23df5455c3bc97c761d Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Mon, 26 Oct 2026 14:19:32 +0100 Subject: [PATCH 17/33] Name property accessors as children of their property Port #973's accessor symbols: getters and setters are now `Class#banana.get().` / `Class#banana.set().` instead of `Class#getBanana().` / `Class#setBanana().`, for source properties, `val`/`var` constructor parameters, explicit accessors, the enum `entries` getter and references to library properties (`kotlin/String#length.get().`). Setter parameters follow (`Class#banana.set().(value)`). Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-01a153c0-ec1f-774a-993c-bca3256017ce --- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 4 +- .../scip_java/kotlin_analysis/SymbolsCache.kt | 51 ++++++++----------- .../KotlinAnalysisIndexerTest.kt | 2 +- .../src/main/kotlin/snapshots/Annotations.kt | 6 +-- .../common/src/main/kotlin/snapshots/Class.kt | 32 ++++++------ .../main/kotlin/snapshots/Destructuring.kt | 20 ++++---- .../common/src/main/kotlin/snapshots/Enums.kt | 18 +++---- .../src/main/kotlin/snapshots/Generics.kt | 10 ++-- .../main/kotlin/snapshots/Implementations.kt | 18 +++---- .../src/main/kotlin/snapshots/Lambdas.kt | 22 ++++---- 10 files changed, 86 insertions(+), 97 deletions(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index e6abc0c4c..3d3108c58 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -225,7 +225,7 @@ class KotlinAnalysisIndexer( } // Property references fan out to the property and its accessors - // (`banana` → `Class#banana.`, `Class#getBanana().`, `Class#setBanana().`). + // (`banana` → `Class#banana.`, `Class#banana.get().`, `Class#banana.set().`). if (target is KaPropertySymbol) { propertyReferenceSymbols(target).forEach { addReference(it, range) } return @@ -364,7 +364,7 @@ class KotlinAnalysisIndexer( documentation = null, ) emitDefinition( - symbol = cache.memberMethodSymbol(declaration, "getEntries"), + symbol = cache.accessorSymbol(entries, setter = false), range = range, kind = Kind.Method, enclosingSymbol = entries, diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt index fa2a49068..b2e1a713d 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt @@ -38,7 +38,6 @@ import org.jetbrains.kotlin.psi.KtVariableDeclaration import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.parents -import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly import org.scip_code.scip_java.kotlin_analysis.ScipSymbolDescriptor.Kind import org.scip_code.scip_java.shared.LocalSymbolsCache as SharedLocalSymbolsCache @@ -92,14 +91,19 @@ class SymbolsCache { /** * The synthetic getter/setter of a property (or `val`/`var` constructor parameter) without - * explicit accessor PSI. Owned by the property's container (`Class#getBanana().`), not by the - * constructor declaring the parameter. + * explicit accessor PSI (`Class#banana.get().`). */ fun syntheticAccessorSymbol(property: KtDeclaration, setter: Boolean): Symbol { - val name = (property as? KtNamedDeclaration)?.name.orEmpty() - return Symbol.createGlobal(propertyOwnerSymbol(property), accessorDescriptor(name, setter)) + val propertySymbol = + if (property is KtParameter) parameterPropertySymbol(property) + else symbolForDeclaration(property) + return accessorSymbol(propertySymbol, setter) } + /** The getter or setter of [property] (`Class#banana.get().`, `Class#banana.set().`). */ + fun accessorSymbol(property: Symbol, setter: Boolean): Symbol = + Symbol.createGlobal(property, accessorDescriptor(setter)) + /** The property symbol declared by a `val`/`var` constructor parameter (`Class#banana.`). */ fun parameterPropertySymbol(parameter: KtParameter): Symbol = Symbol.createGlobal( @@ -147,14 +151,10 @@ class SymbolsCache { */ fun syntheticLocalSymbol(key: PsiElement): Symbol = locals.get(key) ?: locals.put(key) - private fun propertyOwnerSymbol(property: KtDeclaration): Symbol = - when (property) { - is KtParameter -> - (ownerDeclarationOfParameter(property) as? KtConstructor<*>) - ?.containingClassOrObject - ?.let { symbolForDeclaration(it) } ?: Symbol.NONE - else -> ownerSymbol(property) - } + private fun propertyOwnerSymbol(parameter: KtParameter): Symbol = + (ownerDeclarationOfParameter(parameter) as? KtConstructor<*>) + ?.containingClassOrObject + ?.let { symbolForDeclaration(it) } ?: Symbol.NONE /** The `(value)` parameter symbol of a synthetic setter. */ fun syntheticSetterValueSymbol(property: KtDeclaration): Symbol = @@ -230,19 +230,13 @@ class SymbolsCache { target: KaCallableSymbol, setter: Boolean, ): Symbol { - val callableId = target.callableId ?: return Symbol.NONE - val owner = callableId.classId?.let(::classSymbol) ?: packageSymbol(callableId.packageName) - return Symbol.createGlobal( - owner, - accessorDescriptor(callableId.callableName.asString(), setter), - ) + val property = externalCallableSymbol(session, target) + if (property == Symbol.NONE) return Symbol.NONE + return accessorSymbol(property, setter) } - private fun accessorDescriptor(propertyName: String, setter: Boolean): ScipSymbolDescriptor = - ScipSymbolDescriptor( - Kind.METHOD, - (if (setter) "set" else "get") + propertyName.capitalizeAsciiOnly(), - ) + private fun accessorDescriptor(setter: Boolean): ScipSymbolDescriptor = + ScipSymbolDescriptor(Kind.METHOD, if (setter) "set" else "get") private fun uncachedSymbol(declaration: KtDeclaration): Symbol { // Anonymous functions and lambdas have no symbol of their own, mirroring FIR. @@ -300,9 +294,7 @@ class SymbolsCache { is KtParameter -> ownerDeclarationOfParameter(declaration)?.let { symbolForDeclaration(it) } ?: Symbol.NONE - is KtPropertyAccessor -> - declaration.property.containingClassOrObject?.let { symbolForDeclaration(it) } - ?: packageSymbol(declaration.containingKtFile.packageFqName) + is KtPropertyAccessor -> symbolForDeclaration(declaration.property) else -> declaration.containingClassOrObject?.let { symbolForDeclaration(it) } ?: packageSymbol(declaration.containingKtFile.packageFqName) @@ -321,10 +313,7 @@ class SymbolsCache { ScipSymbolDescriptor(Kind.TYPE, declaration.name ?: "Companion") declaration is KtClassOrObject -> ScipSymbolDescriptor(Kind.TYPE, declaration.name.orEmpty()) - declaration is KtPropertyAccessor && declaration.isSetter -> - accessorDescriptor(declaration.property.name.orEmpty(), setter = true) - declaration is KtPropertyAccessor -> - accessorDescriptor(declaration.property.name.orEmpty(), setter = false) + declaration is KtPropertyAccessor -> accessorDescriptor(declaration.isSetter) declaration is KtConstructor<*> -> ScipSymbolDescriptor(Kind.METHOD, "", constructorDisambiguator(declaration)) declaration is KtNamedFunction -> diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index 9d2a200b1..5df3f0876 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -82,7 +82,7 @@ class KotlinAnalysisIndexerTest { enclosingSymbols["snapshots/Greeter#``().(name)"], ) assertEquals("snapshots/Greeter#", enclosingSymbols["snapshots/Greeter#name."]) - assertEquals("snapshots/Greeter#name.", enclosingSymbols["snapshots/Greeter#getName()."]) + assertEquals("snapshots/Greeter#name.", enclosingSymbols["snapshots/Greeter#name.get()."]) val mainDefinitions = definitions(main) assertContains(mainDefinitions, "snapshots/useGreeter().") diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt index 019dec751..e147b6b6c 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt @@ -11,7 +11,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#``().(tag) // ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#tag. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#getTag(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#tag.get(). annotation class Tagged(val tag: String) // ^^^^^^ definition scip-java maven . . snapshots/Tagged# // kind Class @@ -35,7 +35,7 @@ // signature_documentation // > public final val tag: String // ^^^ reference scip-java maven . . snapshots/Tagged#``().(tag) -// ^^^ definition scip-java maven . . snapshots/Tagged#getTag(). +// ^^^ definition scip-java maven . . snapshots/Tagged#tag.get(). // kind Method // display_name tag // signature_documentation @@ -43,7 +43,7 @@ // ^^^^^^ reference scip-java maven . . kotlin/String# // ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged#``().(tag) // ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged#tag. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged#getTag(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged#tag.get(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged# // ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged#``(). diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt index 9ee4aa22d..474c38b46 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt @@ -5,9 +5,9 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``().(banana) // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#banana. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#getBanana(). -// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#setBanana(). -// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#setBanana().(value) +// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#banana.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#banana.set(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#banana.set().(value) // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``().(apple) class Class constructor(private var banana: Int, apple: String) : // ^^^^^ definition scip-java maven . . snapshots/Class# @@ -32,17 +32,17 @@ // signature_documentation // > private final var banana: Int // ^^^^^^ reference scip-java maven . . snapshots/Class#``().(banana) -// ^^^^^^ definition scip-java maven . . snapshots/Class#getBanana(). +// ^^^^^^ definition scip-java maven . . snapshots/Class#banana.get(). // kind Method // display_name banana // signature_documentation // > private get(): Int -// ^^^^^^ definition scip-java maven . . snapshots/Class#setBanana(). +// ^^^^^^ definition scip-java maven . . snapshots/Class#banana.set(). // kind Method // display_name banana // signature_documentation // > private set(value: Int): Unit -// ^^^^^^ definition scip-java maven . . snapshots/Class#setBanana().(value) +// ^^^^^^ definition scip-java maven . . snapshots/Class#banana.set().(value) // kind Parameter // display_name value // signature_documentation @@ -56,9 +56,9 @@ // ^^^^^^ reference scip-java maven . . kotlin/String# // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#``().(banana) // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#banana. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#getBanana(). -// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#setBanana(). -// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#setBanana().(value) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#banana.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#banana.set(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#banana.set().(value) // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#``().(apple) // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#``(). Throwable(banana.toString() + apple) { @@ -73,14 +73,14 @@ } // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#asdf. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#getAsdf(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#asdf.get(). val asdf = // ^^^^ definition scip-java maven . . snapshots/Class#asdf. // kind Property // display_name asdf // signature_documentation // > public final val asdf: Any -// ^^^^ definition scip-java maven . . snapshots/Class#getAsdf(). +// ^^^^ definition scip-java maven . . snapshots/Class#asdf.get(). // kind Method // display_name asdf // signature_documentation @@ -109,7 +109,7 @@ // ⌃ enclosing_range_end scip-java maven . . snapshots/``#doStuff(). } // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#asdf. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#getAsdf(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#asdf.get(). // ⌃ enclosing_range_end scip-java maven . . snapshots/``# // ⌃ enclosing_range_end scip-java maven . . snapshots/``#``(). @@ -153,12 +153,12 @@ println("I eat $banana for lunch") // ^^^^^^^ reference scip-java maven . . kotlin/io/println(). // ^^^^^^ reference scip-java maven . . snapshots/Class#banana. -// ^^^^^^ reference scip-java maven . . snapshots/Class#getBanana(). -// ^^^^^^ reference scip-java maven . . snapshots/Class#setBanana(). +// ^^^^^^ reference scip-java maven . . snapshots/Class#banana.get(). +// ^^^^^^ reference scip-java maven . . snapshots/Class#banana.set(). banana = 42 // ^^^^^^ reference scip-java maven . . snapshots/Class#banana. -// ^^^^^^ reference scip-java maven . . snapshots/Class#getBanana(). -// ^^^^^^ reference scip-java maven . . snapshots/Class#setBanana(). +// ^^^^^^ reference scip-java maven . . snapshots/Class#banana.get(). +// ^^^^^^ reference scip-java maven . . snapshots/Class#banana.set(). } // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#run(). } diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt index 987ebd055..31e99ac4b 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt @@ -6,12 +6,12 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#copy(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#``().(x) // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#x. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#getX(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#x.get(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#component1(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#copy().(x) // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#``().(y) // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#y. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#getY(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#y.get(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#component2(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#copy().(y) data class Point(val x: Int, val y: Int) @@ -42,7 +42,7 @@ // signature_documentation // > public final val x: Int // ^ reference scip-java maven . . snapshots/Point#``().(x) -// ^ definition scip-java maven . . snapshots/Point#getX(). +// ^ definition scip-java maven . . snapshots/Point#x.get(). // kind Method // display_name x // signature_documentation @@ -59,7 +59,7 @@ // signature_documentation // > x: Int = ... // ^ reference scip-java maven . . snapshots/Point#x. -// ^ reference scip-java maven . . snapshots/Point#getX(). +// ^ reference scip-java maven . . snapshots/Point#x.get(). // ^^^ reference scip-java maven . . kotlin/Int# // ^ definition scip-java maven . . snapshots/Point#``().(y) // kind Parameter @@ -72,7 +72,7 @@ // signature_documentation // > public final val y: Int // ^ reference scip-java maven . . snapshots/Point#``().(y) -// ^ definition scip-java maven . . snapshots/Point#getY(). +// ^ definition scip-java maven . . snapshots/Point#y.get(). // kind Method // display_name y // signature_documentation @@ -89,16 +89,16 @@ // signature_documentation // > y: Int = ... // ^ reference scip-java maven . . snapshots/Point#y. -// ^ reference scip-java maven . . snapshots/Point#getY(). +// ^ reference scip-java maven . . snapshots/Point#y.get(). // ^^^ reference scip-java maven . . kotlin/Int# // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#``().(x) // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#x. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#getX(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#x.get(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#component1(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#copy().(x) // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#``().(y) // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#y. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#getY(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#y.get(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#component2(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#copy().(y) // ⌃ enclosing_range_end scip-java maven . . snapshots/Point# @@ -184,11 +184,11 @@ // ^^^^^ reference local 5 // ^^^^^ reference local 6 // ^ reference scip-java maven . . snapshots/Point#x. -// ^ reference scip-java maven . . snapshots/Point#getX(). +// ^ reference scip-java maven . . snapshots/Point#x.get(). // ^ reference scip-java maven . . kotlin/Int#plus(+2). // ^^^^^ reference local 7 // ^^^^^^ reference scip-java maven . . kotlin/String#length. -// ^^^^^^ reference scip-java maven . . kotlin/String#getLength(). +// ^^^^^^ reference scip-java maven . . kotlin/String#length.get(). // ⌃ enclosing_range_end local 6 // ⌃ enclosing_range_end local 7 // ⌃ enclosing_range_end local 5 diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt index bdd8e94b7..c85204e92 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt @@ -6,11 +6,11 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Suit#valueOf(). //⌄ enclosing_range_start scip-java maven . . snapshots/Suit#valueOf().(value) //⌄ enclosing_range_start scip-java maven . . snapshots/Suit#entries. -//⌄ enclosing_range_start scip-java maven . . snapshots/Suit#getEntries(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Suit#entries.get(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#``().(symbol) // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#symbol. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#getSymbol(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#symbol.get(). enum class Suit(val symbol: Char) { // ^^^^ definition scip-java maven . . snapshots/Suit# // kind Class @@ -43,7 +43,7 @@ // display_name entries // signature_documentation // > public final static val entries: EnumEntries -// ^^^^ definition scip-java maven . . snapshots/Suit#getEntries(). +// ^^^^ definition scip-java maven . . snapshots/Suit#entries.get(). // kind Method // display_name entries // signature_documentation @@ -59,7 +59,7 @@ // signature_documentation // > public final val symbol: Char // ^^^^^^ reference scip-java maven . . snapshots/Suit#``().(symbol) -// ^^^^^^ definition scip-java maven . . snapshots/Suit#getSymbol(). +// ^^^^^^ definition scip-java maven . . snapshots/Suit#symbol.get(). // kind Method // display_name symbol // signature_documentation @@ -67,7 +67,7 @@ // ^^^^ reference scip-java maven . . kotlin/Char# // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#``().(symbol) // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#symbol. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#getSymbol(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#symbol.get(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#HEARTS. HEARTS('h'), @@ -95,7 +95,7 @@ // > public final fun isRed(): Boolean // ^^^^^^^ reference scip-java maven . . kotlin/Boolean# // ^^^^^^ reference scip-java maven . . snapshots/Suit#symbol. -// ^^^^^^ reference scip-java maven . . snapshots/Suit#getSymbol(). +// ^^^^^^ reference scip-java maven . . snapshots/Suit#symbol.get(). // ^^ reference scip-java maven . . kotlin/Char#equals(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#isRed(). @@ -129,7 +129,7 @@ // ^^^^ reference scip-java maven . . kotlin/Char# // ^^^^ reference scip-java maven . . snapshots/Suit# // ^^^^^^^ reference scip-java maven . . snapshots/Suit#entries. -// ^^^^^^^ reference scip-java maven . . snapshots/Suit#getEntries(). +// ^^^^^^^ reference scip-java maven . . snapshots/Suit#entries.get(). // ^^^^ reference scip-java maven . . kotlin/collections/find(+9). // ^^^^^^^^^^^^^^^^^^^^^^^ definition local 0 // kind Parameter @@ -138,7 +138,7 @@ // > it: Suit // ^^ reference local 0 // ^^^^^^ reference scip-java maven . . snapshots/Suit#symbol. -// ^^^^^^ reference scip-java maven . . snapshots/Suit#getSymbol(). +// ^^^^^^ reference scip-java maven . . snapshots/Suit#symbol.get(). // ^^ reference scip-java maven . . kotlin/Char#equals(). // ^^^^^^ reference scip-java maven . . snapshots/Suit#Companion#fromSymbol().(symbol) // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#Companion#fromSymbol().(symbol) @@ -153,7 +153,7 @@ //⌃ enclosing_range_end scip-java maven . . snapshots/Suit#valueOf(). //⌃ enclosing_range_end scip-java maven . . snapshots/Suit#valueOf().(value) //⌃ enclosing_range_end scip-java maven . . snapshots/Suit#entries. -//⌃ enclosing_range_end scip-java maven . . snapshots/Suit#getEntries(). +//⌃ enclosing_range_end scip-java maven . . snapshots/Suit#entries.get(). //⌄ enclosing_range_start scip-java maven . . snapshots/describe(). // ⌄ enclosing_range_start scip-java maven . . snapshots/describe().(suit) diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt index 2c7702164..4f6121919 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt @@ -6,7 +6,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#[T] // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#``().(items) // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#items. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#getItems(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#items.get(). class Container>(private val items: MutableList) { // ^^^^^^^^^ definition scip-java maven . . snapshots/Container# // kind Class @@ -36,7 +36,7 @@ // signature_documentation // > private final val items: MutableList // ^^^^^ reference scip-java maven . . snapshots/Container#``().(items) -// ^^^^^ definition scip-java maven . . snapshots/Container#getItems(). +// ^^^^^ definition scip-java maven . . snapshots/Container#items.get(). // kind Method // display_name items // signature_documentation @@ -46,7 +46,7 @@ // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#[T] // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#``().(items) // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#items. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Container#getItems(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Container#items.get(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#add(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#add().(item) @@ -67,7 +67,7 @@ // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#add().(item) items.add(item) // ^^^^^ reference scip-java maven . . snapshots/Container#items. -// ^^^^^ reference scip-java maven . . snapshots/Container#getItems(). +// ^^^^^ reference scip-java maven . . snapshots/Container#items.get(). // ^^^ reference scip-java maven . . kotlin/collections/MutableList#add(). // ^^^^ reference scip-java maven . . snapshots/Container#add().(item) return this @@ -99,7 +99,7 @@ // ^^^^ reference scip-java maven . . kotlin/collections/List# // ^ reference scip-java maven . . snapshots/Container#mapItems().[R] // ^^^^^ reference scip-java maven . . snapshots/Container#items. -// ^^^^^ reference scip-java maven . . snapshots/Container#getItems(). +// ^^^^^ reference scip-java maven . . snapshots/Container#items.get(). // ^^^^^^^^^^ reference scip-java maven . . kotlin/collections/mapNotNull(+1). // ^^^^^^^^^ reference scip-java maven . . snapshots/Container#mapItems().(transform) // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#mapItems().[R] diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt index 4f5f27c9c..cb95a1c65 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt @@ -40,21 +40,21 @@ // signature_documentation // > public abstract interface Animal : Any // ⌄ enclosing_range_start scip-java maven . . snapshots/Animal#favoriteNumber. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Animal#getFavoriteNumber(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Animal#favoriteNumber.get(). val favoriteNumber: Int // ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Animal#favoriteNumber. // kind Property // display_name favoriteNumber // signature_documentation // > public abstract val favoriteNumber: Int -// ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Animal#getFavoriteNumber(). +// ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Animal#favoriteNumber.get(). // kind Method // display_name favoriteNumber // signature_documentation // > public get(): Int // ^^^ reference scip-java maven . . kotlin/Int# // ⌃ enclosing_range_end scip-java maven . . snapshots/Animal#favoriteNumber. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Animal#getFavoriteNumber(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Animal#favoriteNumber.get(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Animal#sound(). fun sound(): String // ^^^^^ definition scip-java maven . . snapshots/Animal#sound(). @@ -90,15 +90,15 @@ // signature_documentation // > public open override val favoriteNumber: Int // ^^^ reference scip-java maven . . kotlin/Int# -// ⌄ enclosing_range_start scip-java maven . . snapshots/Bird#getFavoriteNumber(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Bird#favoriteNumber.get(). get() = 42 -// ^^^ definition scip-java maven . . snapshots/Bird#getFavoriteNumber(). +// ^^^ definition scip-java maven . . snapshots/Bird#favoriteNumber.get(). // kind Method // display_name favoriteNumber // signature_documentation // > public get(): Int // ⌃ enclosing_range_end scip-java maven . . snapshots/Bird#favoriteNumber. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Bird#getFavoriteNumber(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Bird#favoriteNumber.get(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Bird#sound(). override fun sound(): String { @@ -138,15 +138,15 @@ // signature_documentation // > public open override val favoriteNumber: Int // ^^^ reference scip-java maven . . kotlin/Int# -// ⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#getFavoriteNumber(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#favoriteNumber.get(). get() = 1337 -// ^^^ definition scip-java maven . . snapshots/Seagull#getFavoriteNumber(). +// ^^^ definition scip-java maven . . snapshots/Seagull#favoriteNumber.get(). // kind Method // display_name favoriteNumber // signature_documentation // > public get(): Int // ⌃ enclosing_range_end scip-java maven . . snapshots/Seagull#favoriteNumber. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Seagull#getFavoriteNumber(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Seagull#favoriteNumber.get(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#sound(). override fun sound(): String { // ^^^^^ definition scip-java maven . . snapshots/Seagull#sound(). diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt index 36c8d9e65..bbfa269d2 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt @@ -2,7 +2,7 @@ // ^^^^^^^^^ reference scip-java maven . . snapshots/ //⌄ enclosing_range_start scip-java maven . . snapshots/x. -//⌄ enclosing_range_start scip-java maven . . snapshots/getX(). +//⌄ enclosing_range_start scip-java maven . . snapshots/x.get(). // ⌄ enclosing_range_start local 0 // ⌄ enclosing_range_start local 1 val x = arrayListOf().forEachIndexed { i, s -> println("$i $s") } @@ -11,7 +11,7 @@ // display_name x // signature_documentation // > public final val x: Unit -// ^ definition scip-java maven . . snapshots/getX(). +// ^ definition scip-java maven . . snapshots/x.get(). // kind Method // display_name x // signature_documentation @@ -35,17 +35,17 @@ // ⌃ enclosing_range_end local 0 // ⌃ enclosing_range_end local 1 // ⌃ enclosing_range_end scip-java maven . . snapshots/x. -// ⌃ enclosing_range_end scip-java maven . . snapshots/getX(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/x.get(). //⌄ enclosing_range_start scip-java maven . . snapshots/y. -//⌄ enclosing_range_start scip-java maven . . snapshots/getY(). +//⌄ enclosing_range_start scip-java maven . . snapshots/y.get(). val y = "fdsa".run { this.toByteArray() } // ^ definition scip-java maven . . snapshots/y. // kind Property // display_name y // signature_documentation // > public final val y: ByteArray -// ^ definition scip-java maven . . snapshots/getY(). +// ^ definition scip-java maven . . snapshots/y.get(). // kind Method // display_name y // signature_documentation @@ -53,10 +53,10 @@ // ^^^ reference scip-java maven . . kotlin/run(+1). // ^^^^^^^^^^^ reference scip-java maven . . kotlin/text/toByteArray(). // ⌃ enclosing_range_end scip-java maven . . snapshots/y. -// ⌃ enclosing_range_end scip-java maven . . snapshots/getY(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/y.get(). //⌄ enclosing_range_start scip-java maven . . snapshots/z. -//⌄ enclosing_range_start scip-java maven . . snapshots/getZ(). +//⌄ enclosing_range_start scip-java maven . . snapshots/z.get(). // ⌄ enclosing_range_start local 2 val z = y.let { it.size } // ^ definition scip-java maven . . snapshots/z. @@ -64,13 +64,13 @@ // display_name z // signature_documentation // > public final val z: Int -// ^ definition scip-java maven . . snapshots/getZ(). +// ^ definition scip-java maven . . snapshots/z.get(). // kind Method // display_name z // signature_documentation // > public get(): Int // ^ reference scip-java maven . . snapshots/y. -// ^ reference scip-java maven . . snapshots/getY(). +// ^ reference scip-java maven . . snapshots/y.get(). // ^^^ reference scip-java maven . . kotlin/let(). // ^^^^^^^^^^^ definition local 2 // kind Parameter @@ -79,8 +79,8 @@ // > it: ByteArray // ^^ reference local 2 // ^^^^ reference scip-java maven . . kotlin/ByteArray#size. -// ^^^^ reference scip-java maven . . kotlin/ByteArray#getSize(). +// ^^^^ reference scip-java maven . . kotlin/ByteArray#size.get(). // ⌃ enclosing_range_end scip-java maven . . snapshots/z. -// ⌃ enclosing_range_end scip-java maven . . snapshots/getZ(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/z.get(). // ⌃ enclosing_range_end local 2 From 60586c0eb16df8d62b7fbe98d9bb39226d653b85 Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Mon, 26 Oct 2026 14:22:05 +0100 Subject: [PATCH 18/33] Nest top-level extensions under their receiver type Port #973's extension symbols: a top-level extension function or property is owned by its receiver type within the extension's own package, so `fun String.foo()` in `sample` is `sample/String#foo().` and `kotlin.collections.forEachIndexed` on `Iterable` is `kotlin/collections/Iterable#forEachIndexed().`. This applies to source declarations and to references to library extensions. Extension disambiguators count the same-named members of the receiver class (when it is in the extension's package) followed by the same-named extensions on that receiver in the package, so a member `MyClass#foo().` and an extension `MyClass#foo(+1).` stay distinct. Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-01a153c0-ec1f-774a-993c-bca3256017ce --- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 3 +- .../scip_java/kotlin_analysis/SymbolsCache.kt | 86 ++++++++++++++++++- .../KotlinAnalysisIndexerTest.kt | 20 +++-- .../main/kotlin/snapshots/Destructuring.kt | 2 +- .../common/src/main/kotlin/snapshots/Enums.kt | 2 +- .../src/main/kotlin/snapshots/Generics.kt | 4 +- .../src/main/kotlin/snapshots/Lambdas.kt | 4 +- 7 files changed, 103 insertions(+), 18 deletions(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index 3d3108c58..6985f57fe 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -140,11 +140,10 @@ class KotlinAnalysisIndexer( } private fun indexFile(ktFile: KtFile): Document { - val cache = SymbolsCache() val text = ktFile.text val lines = LineIndex(text) val builder = ScipDocumentBuilder() - analyze(ktFile) { ktFile.accept(IndexingVisitor(this, cache, lines, builder)) } + analyze(ktFile) { ktFile.accept(IndexingVisitor(this, SymbolsCache(this), lines, builder)) } val relativePath = ScipShardPaths.relativePath(sourceroot, Paths.get(ktFile.virtualFilePath)) return builder.build("kotlin", relativePath, text) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt index b2e1a713d..78432133f 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt @@ -7,14 +7,17 @@ import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaClassLikeSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaConstructorSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaNamedFunctionSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaPackageSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaTypeAliasSymbol import org.jetbrains.kotlin.analysis.api.types.KaClassType import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtAnonymousInitializer import org.jetbrains.kotlin.psi.KtBlockExpression +import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.psi.KtClassBody import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtConstructor @@ -45,12 +48,13 @@ import org.scip_code.scip_java.shared.LocalSymbolsCache as SharedLocalSymbolsCac * Computes SCIP symbol strings for Kotlin declarations and resolved references. * * Declarations with Kotlin source are classified from their PSI structure, which keeps the - * version-sensitive Analysis API surface minimal. References that resolve to declarations without - * Kotlin source (classpath or JDK symbols) fall back to `ClassId`/`CallableId`-derived symbols. + * version-sensitive Analysis API surface minimal; only extension receivers are resolved. References + * that resolve to declarations without Kotlin source (classpath or JDK symbols) fall back to + * `ClassId`/`CallableId`-derived symbols. * * Instantiate one cache per indexed file: local symbols are numbered per SCIP document. */ -class SymbolsCache { +class SymbolsCache(private val session: KaSession) { private val globals = HashMap() private val packages = HashMap() private val locals = @@ -297,9 +301,61 @@ class SymbolsCache { is KtPropertyAccessor -> symbolForDeclaration(declaration.property) else -> declaration.containingClassOrObject?.let { symbolForDeclaration(it) } + ?: sourceExtensionReceiver(declaration)?.let { + extensionOwnerSymbol(declaration.containingKtFile.packageFqName, it) + } ?: packageSymbol(declaration.containingKtFile.packageFqName) } + /** + * Top-level extensions are owned by their receiver type, nested in the extension's own package + * (`sample/String#foo().` for `fun String.foo()` in `sample`), like scip-kotlinc. + */ + private fun extensionOwnerSymbol(packageFqName: FqName, receiver: ClassId): Symbol = + Symbol.createGlobal( + packageSymbol(packageFqName), + ScipSymbolDescriptor(Kind.TYPE, receiver.shortClassName.asString()), + ) + + private fun sourceExtensionReceiver(declaration: KtDeclaration): ClassId? { + if (declaration !is KtCallableDeclaration || declaration.receiverTypeReference == null) { + return null + } + return with(session) { declaration.symbol as? KaCallableSymbol }?.let(::receiverClassId) + } + + private fun receiverClassId(callable: KaCallableSymbol): ClassId? = + with(session) { (callable.receiverParameter?.returnType as? KaClassType)?.classId } + + /** + * The disambiguator of a top-level extension function: like scip-kotlinc, it counts the + * same-named members of the receiver class (when it is in the extension's package) followed by + * the same-named extensions on that receiver in the package. + */ + private fun extensionDisambiguator( + function: KaCallableSymbol, + packageFqName: FqName, + name: Name, + receiver: ClassId, + ): String { + val extensions = + with(session) { + findTopLevelCallables(packageFqName, name) + .filter { it is KaNamedFunctionSymbol && receiverClassId(it) == receiver } + .toList() + } + val index = extensions.indexOf(function) + if (index < 0) return "()" + val members = + if (receiver.packageFqName != packageFqName) 0 + else + with(session) { + findClass(receiver)?.declaredMemberScope?.callables(name)?.count() ?: 0 + } + val count = members + index + return if (count == 0) "()" else "(+$count)" + } + private fun scipDescriptor(declaration: KtDeclaration): ScipSymbolDescriptor = when { declaration is KtObjectDeclaration && declaration.isObjectLiteral() -> @@ -342,6 +398,17 @@ class SymbolsCache { */ private fun methodDisambiguator(function: KtNamedFunction): String { val name = function.name ?: return "()" + if (function.containingClassOrObject == null) { + sourceExtensionReceiver(function)?.let { receiver -> + val symbol = with(session) { function.symbol } + return extensionDisambiguator( + symbol, + function.containingKtFile.packageFqName, + Name.identifier(name), + receiver, + ) + } + } var count = 0 var found = false for (sibling in siblingDeclarations(function)) { @@ -390,7 +457,10 @@ class SymbolsCache { */ private fun externalCallableSymbol(session: KaSession, target: KaCallableSymbol): Symbol { val callableId = target.callableId ?: return Symbol.NONE - val owner = callableId.classId?.let(::classSymbol) ?: packageSymbol(callableId.packageName) + val owner = + callableId.classId?.let(::classSymbol) + ?: receiverClassId(target)?.let { extensionOwnerSymbol(callableId.packageName, it) } + ?: packageSymbol(callableId.packageName) val name = callableId.callableName.asString() val descriptor = when (target) { @@ -411,6 +481,14 @@ class SymbolsCache { with(session) { val classId = callableId.classId if (classId == null) { + receiverClassId(target)?.let { receiver -> + return extensionDisambiguator( + target, + callableId.packageName, + callableId.callableName, + receiver, + ) + } findTopLevelCallables(callableId.packageName, callableId.callableName).toList() } else { val classSymbol = findClass(classId) ?: return "()" diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index 5df3f0876..3429ca64d 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -623,6 +623,10 @@ class KotlinAnalysisIndexerTest { @Test fun `extension receiver type reference`(@TempDir path: Path) { + // String is from the kotlin package; our extension in sample gets + // symbol sample/String#foo(). — distinct from kotlin/String#foo(). + // This means extensions on cross-package types never collide with + // the receiver class's own methods in the symbol table. val document = compileScip( path, @@ -638,7 +642,7 @@ class KotlinAnalysisIndexerTest { arrayOf( scipOccurrence { role = DEFINITION - symbol = "sample/foo()." + symbol = "sample/String#foo()." range { startLine = 2 startCharacter = 11 @@ -674,7 +678,7 @@ class KotlinAnalysisIndexerTest { }, scipOccurrence { role = REFERENCE - symbol = "sample/foo()." + symbol = "sample/String#foo()." range { startLine = 3 startCharacter = 23 @@ -703,7 +707,7 @@ class KotlinAnalysisIndexerTest { arrayOf( scipOccurrence { role = DEFINITION - symbol = "sample/asString." + symbol = "sample/Int#asString." range { startLine = 2 startCharacter = 8 @@ -739,7 +743,7 @@ class KotlinAnalysisIndexerTest { }, scipOccurrence { role = REFERENCE - symbol = "sample/asString." + symbol = "sample/Int#asString." range { startLine = 3 startCharacter = 15 @@ -753,7 +757,11 @@ class KotlinAnalysisIndexerTest { @Test fun `extension overload disambiguator`(@TempDir path: Path) { - // A member foo() and an extension foo() on the same class produce distinct symbols. + // When a class already has a member named foo() and an extension also + // adds foo(), the extension is counted after the member in the combined + // sibling list (class members + same-package extensions on the same + // receiver type), so the extension gets the (+1) disambiguator and the + // two produce distinct symbols. val document = compileScip( path, @@ -787,7 +795,7 @@ class KotlinAnalysisIndexerTest { }, scipOccurrence { role = DEFINITION - symbol = "sample/foo()." + symbol = "sample/MyClass#foo(+1)." range { startLine = 5 startCharacter = 12 diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt index 31e99ac4b..87cb89d6b 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt @@ -162,7 +162,7 @@ // signature_documentation // > local val total: Int // ^^^^^^^ reference local 3 -// ^^^^^ reference scip-java maven . . kotlin/collections/sumOf(+66). +// ^^^^^ reference scip-java maven . . kotlin/collections/Iterable#sumOf(+3). // ^^^^^^^^^^^^^^ definition local 5 // kind Parameter // display_name diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt index c85204e92..e61c62401 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt @@ -130,7 +130,7 @@ // ^^^^ reference scip-java maven . . snapshots/Suit# // ^^^^^^^ reference scip-java maven . . snapshots/Suit#entries. // ^^^^^^^ reference scip-java maven . . snapshots/Suit#entries.get(). -// ^^^^ reference scip-java maven . . kotlin/collections/find(+9). +// ^^^^ reference scip-java maven . . kotlin/collections/Iterable#find(). // ^^^^^^^^^^^^^^^^^^^^^^^ definition local 0 // kind Parameter // display_name it diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt index 4f6121919..52a4180f3 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt @@ -100,7 +100,7 @@ // ^ reference scip-java maven . . snapshots/Container#mapItems().[R] // ^^^^^ reference scip-java maven . . snapshots/Container#items. // ^^^^^ reference scip-java maven . . snapshots/Container#items.get(). -// ^^^^^^^^^^ reference scip-java maven . . kotlin/collections/mapNotNull(+1). +// ^^^^^^^^^^ reference scip-java maven . . kotlin/collections/Iterable#mapNotNull(). // ^^^^^^^^^ reference scip-java maven . . snapshots/Container#mapItems().(transform) // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#mapItems().[R] // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#mapItems().(transform) @@ -138,7 +138,7 @@ // ^ reference scip-java maven . . snapshots/firstOrSelf().[T] // ^ reference scip-java maven . . snapshots/firstOrSelf().[T] // ^^^^^ reference scip-java maven . . snapshots/firstOrSelf().(items) -// ^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/firstOrNull(+19). +// ^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/List#firstOrNull(). // ^^^^^^^^ reference scip-java maven . . snapshots/firstOrSelf().(fallback) // ⌃ enclosing_range_end scip-java maven . . snapshots/firstOrSelf().[T] // ⌃ enclosing_range_end scip-java maven . . snapshots/firstOrSelf().(items) diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt index bbfa269d2..c0051702a 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt @@ -18,7 +18,7 @@ // > public get(): Unit // ^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/arrayListOf(). // ^^^^^^ reference scip-java maven . . kotlin/String# -// ^^^^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/forEachIndexed(+9). +// ^^^^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/Iterable#forEachIndexed(). // ^ definition local 0 // kind Parameter // display_name i @@ -51,7 +51,7 @@ // signature_documentation // > public get(): ByteArray // ^^^ reference scip-java maven . . kotlin/run(+1). -// ^^^^^^^^^^^ reference scip-java maven . . kotlin/text/toByteArray(). +// ^^^^^^^^^^^ reference scip-java maven . . kotlin/text/String#toByteArray(). // ⌃ enclosing_range_end scip-java maven . . snapshots/y. // ⌃ enclosing_range_end scip-java maven . . snapshots/y.get(). From 00d091b763b369d9a6e47237501f494b15d1f695 Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Mon, 26 Oct 2026 14:25:57 +0100 Subject: [PATCH 19/33] Model anonymous objects and enum entry bodies as locals Port #973's local symbols: anonymous objects and their members are now local symbols, numbered in declaration order, instead of `` globals. An enum entry body declares a local anonymous subclass of the enum, anchored at the entry identifier, which owns the body's members, so `PLUS { override fun apply() }` defines `local 0` (`object : Op`), its constructor and `apply` as locals rather than `Op#PLUS.apply().`. The implicit constructor of a local class is now a local symbol too; it was emitted as the invalid symbol "local 0``().". Unlike scip-kotlinc, enum entries with constructor arguments but no body do not declare an anonymous class: that is an artifact of FIR's desugaring, and those entries are plain instances of the enum class. Port the `local classes` and `anonymous object` cases from scip-kotlinc's AnalyzerTest to cover this. Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-01a153c0-ec1f-774a-993c-bca3256017ce --- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 39 +- .../scip_java/kotlin_analysis/SymbolsCache.kt | 45 ++- .../KotlinAnalysisIndexerTest.kt | 366 +++++++++++++++++- .../common/src/main/kotlin/snapshots/Class.kt | 18 +- 4 files changed, 437 insertions(+), 31 deletions(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index 6985f57fe..8a5657d93 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -376,8 +376,9 @@ class KotlinAnalysisIndexer( private fun emitEnumEntry(declaration: KtEnumEntry) { val identifier = declaration.nameIdentifier ?: return + val symbol = cache.symbolForDeclaration(declaration) emitDefinition( - symbol = cache.symbolForDeclaration(declaration), + symbol = symbol, range = identifier.textRange, kind = Kind.EnumMember, enclosingSymbol = enclosingSymbol(declaration), @@ -386,6 +387,41 @@ class KotlinAnalysisIndexer( signatureText = signatures.propertySignature(declaration), documentation = docComment(declaration), ) + if (declaration.body != null) emitEnumEntryBody(declaration, symbol, identifier) + } + + /** + * An enum entry body declares an anonymous subclass of the enum, as in FIR: a local class + * and its constructor, both anchored at the entry identifier. + */ + private fun emitEnumEntryBody( + declaration: KtEnumEntry, + entry: Symbol, + identifier: PsiElement, + ) { + val enumClass = declaration.containingClassOrObject ?: return + val anonymousClass = cache.enumEntryBodySymbol(declaration) + emitDefinition( + symbol = anonymousClass, + range = identifier.textRange, + kind = Kind.Class, + enclosingSymbol = entry, + enclosing = declaration.textRange, + displayName = "", + signatureText = "object : ${enumClass.name.orEmpty()}", + documentation = null, + relationships = listOf(cache.symbolForDeclaration(enumClass)), + ) + emitDefinition( + symbol = cache.enumEntryBodyConstructorSymbol(declaration), + range = identifier.textRange, + kind = Kind.Constructor, + enclosingSymbol = anonymousClass, + enclosing = declaration.textRange, + displayName = declaration.name.orEmpty(), + signatureText = "private constructor(): ", + documentation = null, + ) } private fun emitConstructor(declaration: KtConstructor<*>) { @@ -941,6 +977,7 @@ class KotlinAnalysisIndexer( for (parent in element.parents) { when (parent) { is KtFunctionLiteral -> return Symbol.NONE + is KtEnumEntry -> return cache.enumEntryBodySymbol(parent) is KtClassOrObject, is KtNamedFunction, is KtConstructor<*>, diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt index 78432133f..8245677d1 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt @@ -85,13 +85,30 @@ class SymbolsCache(private val session: KaSession) { /** * The SCIP symbol of the implicit primary constructor of [classOrObject], which has no PSI of - * its own. Explicit constructors go through [symbolForDeclaration]. + * its own. Explicit constructors go through [symbolForDeclaration]. Local classes get a local + * constructor, keyed by their name or `object` keyword. */ - fun implicitConstructorSymbol(classOrObject: KtClassOrObject): Symbol = - Symbol.createGlobal( - symbolForDeclaration(classOrObject), - ScipSymbolDescriptor(Kind.METHOD, ""), - ) + fun implicitConstructorSymbol(classOrObject: KtClassOrObject): Symbol { + val owner = symbolForDeclaration(classOrObject) + if (owner.isLocal()) { + return syntheticLocalSymbol( + classOrObject.nameIdentifier + ?: (classOrObject as? KtObjectDeclaration)?.getObjectKeyword() + ?: classOrObject + ) + } + return Symbol.createGlobal(owner, ScipSymbolDescriptor(Kind.METHOD, "")) + } + + /** + * The anonymous class an enum entry body declares, which owns the body's members, as in FIR. + * Keyed by the body. + */ + fun enumEntryBodySymbol(entry: KtEnumEntry): Symbol = syntheticLocalSymbol(entry.body ?: entry) + + /** The local constructor of the anonymous class of an enum entry body. */ + fun enumEntryBodyConstructorSymbol(entry: KtEnumEntry): Symbol = + syntheticLocalSymbol(entry.nameIdentifier ?: entry) /** * The synthetic getter/setter of a property (or `val`/`var` constructor parameter) without @@ -247,12 +264,9 @@ class SymbolsCache(private val session: KaSession) { if (declaration is KtFunctionLiteral) return Symbol.NONE if (declaration is KtNamedFunction && declaration.name == null) return Symbol.NONE - // Anonymous objects are global symbols owned by the file's package, mirroring FIR. + // Anonymous objects are local, like their members. if (declaration is KtObjectDeclaration && declaration.isObjectLiteral()) { - return Symbol.createGlobal( - packageSymbol(declaration.containingKtFile.packageFqName), - scipDescriptor(declaration), - ) + return locals.put(declaration) } if (isLocalMember(declaration)) return locals.put(declaration) @@ -300,7 +314,9 @@ class SymbolsCache(private val session: KaSession) { ?: Symbol.NONE is KtPropertyAccessor -> symbolForDeclaration(declaration.property) else -> - declaration.containingClassOrObject?.let { symbolForDeclaration(it) } + declaration.containingClassOrObject?.let { + if (it is KtEnumEntry) enumEntryBodySymbol(it) else symbolForDeclaration(it) + } ?: sourceExtensionReceiver(declaration)?.let { extensionOwnerSymbol(declaration.containingKtFile.packageFqName, it) } @@ -358,11 +374,6 @@ class SymbolsCache(private val session: KaSession) { private fun scipDescriptor(declaration: KtDeclaration): ScipSymbolDescriptor = when { - declaration is KtObjectDeclaration && declaration.isObjectLiteral() -> - ScipSymbolDescriptor( - Kind.TYPE, - "", - ) declaration is KtEnumEntry -> ScipSymbolDescriptor(Kind.TERM, declaration.name.orEmpty()) declaration is KtObjectDeclaration && declaration.isCompanion() -> diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index 3429ca64d..60a91e897 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -104,6 +104,350 @@ class KotlinAnalysisIndexerTest { ) } + @Test + fun `local classes`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + fun foo() { + class LocalClass { + fun localClassMethod() {} + } + } + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = DEFINITION + symbol = "sample/foo()." + range { + startLine = 2 + startCharacter = 4 + endLine = 2 + endCharacter = 7 + } + enclosingRange { + startLine = 2 + endLine = 6 + endCharacter = 1 + } + }, + // LocalClass + scipOccurrence { + role = DEFINITION + symbol = "local 0" + range { + startLine = 3 + startCharacter = 8 + endLine = 3 + endCharacter = 18 + } + enclosingRange { + startLine = 3 + startCharacter = 2 + endLine = 5 + endCharacter = 3 + } + }, + // LocalClass constructor + scipOccurrence { + role = DEFINITION + symbol = "local 1" + range { + startLine = 3 + startCharacter = 8 + endLine = 3 + endCharacter = 18 + } + enclosingRange { + startLine = 3 + startCharacter = 2 + endLine = 5 + endCharacter = 3 + } + }, + // localClassMethod + scipOccurrence { + role = DEFINITION + symbol = "local 2" + range { + startLine = 4 + startCharacter = 8 + endLine = 4 + endCharacter = 24 + } + enclosingRange { + startLine = 4 + startCharacter = 4 + endLine = 4 + endCharacter = 29 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/foo()." + kind = Kind.Method + enclosingSymbol = "sample/" + displayName = "foo" + signatureText = "public final fun foo(): Unit" + }, + scipSymbol { + symbol = "local 1" + kind = Kind.Constructor + enclosingSymbol = "local 0" + displayName = "LocalClass" + signatureText = "public constructor(): LocalClass" + }, + scipSymbol { + symbol = "local 2" + kind = Kind.Method + enclosingSymbol = "local 0" + displayName = "localClassMethod" + signatureText = "public final fun localClassMethod(): Unit" + }, + ) + assertContainsAll(document.symbolsList, *symbols) + } + + @Test + fun `anonymous object`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + interface Interface { + fun foo() + } + + fun main() { + val a = object : Interface { + override fun foo() {} + } + val b = object : Interface { + override fun foo() {} + } + } + """, + ) + + val occurrences = + arrayOf( + scipOccurrence { + role = REFERENCE + symbol = "sample/" + range { + startLine = 0 + startCharacter = 8 + endLine = 0 + endCharacter = 14 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/Interface#" + range { + startLine = 2 + startCharacter = 10 + endLine = 2 + endCharacter = 19 + } + enclosingRange { + startLine = 2 + endLine = 4 + endCharacter = 1 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "sample/Interface#foo()." + range { + startLine = 3 + startCharacter = 8 + endLine = 3 + endCharacter = 11 + } + enclosingRange { + startLine = 3 + startCharacter = 4 + endLine = 3 + endCharacter = 13 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "local 1" + range { + startLine = 7 + startCharacter = 12 + endLine = 7 + endCharacter = 18 + } + enclosingRange { + startLine = 7 + startCharacter = 12 + endLine = 9 + endCharacter = 5 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "local 2" + range { + startLine = 7 + startCharacter = 12 + endLine = 7 + endCharacter = 18 + } + enclosingRange { + startLine = 7 + startCharacter = 12 + endLine = 9 + endCharacter = 5 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/Interface#" + range { + startLine = 7 + startCharacter = 21 + endLine = 7 + endCharacter = 30 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "local 3" + range { + startLine = 8 + startCharacter = 21 + endLine = 8 + endCharacter = 24 + } + enclosingRange { + startLine = 8 + startCharacter = 8 + endLine = 8 + endCharacter = 29 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "local 5" + range { + startLine = 10 + startCharacter = 12 + endLine = 10 + endCharacter = 18 + } + enclosingRange { + startLine = 10 + startCharacter = 12 + endLine = 12 + endCharacter = 5 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "local 6" + range { + startLine = 10 + startCharacter = 12 + endLine = 10 + endCharacter = 18 + } + enclosingRange { + startLine = 10 + startCharacter = 12 + endLine = 12 + endCharacter = 5 + } + }, + scipOccurrence { + role = REFERENCE + symbol = "sample/Interface#" + range { + startLine = 10 + startCharacter = 21 + endLine = 10 + endCharacter = 30 + } + }, + scipOccurrence { + role = DEFINITION + symbol = "local 7" + range { + startLine = 11 + startCharacter = 21 + endLine = 11 + endCharacter = 24 + } + enclosingRange { + startLine = 11 + startCharacter = 8 + endLine = 11 + endCharacter = 29 + } + }, + ) + assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/Interface#" + kind = Kind.Interface + enclosingSymbol = "sample/" + displayName = "Interface" + signatureText = "public abstract interface Interface : Any" + }, + scipSymbol { + symbol = "local 1" + kind = Kind.Class + enclosingSymbol = "local 0" + displayName = "" + signatureText = "object : Interface" + addOverriddenSymbols("sample/Interface#") + }, + scipSymbol { + symbol = "local 3" + kind = Kind.Method + enclosingSymbol = "local 1" + displayName = "foo" + signatureText = "public open override fun foo(): Unit" + addOverriddenSymbols("sample/Interface#foo().") + }, + scipSymbol { + symbol = "local 5" + kind = Kind.Class + enclosingSymbol = "local 4" + displayName = "" + signatureText = "object : Interface" + addOverriddenSymbols("sample/Interface#") + }, + scipSymbol { + symbol = "local 7" + kind = Kind.Method + enclosingSymbol = "local 5" + displayName = "foo" + signatureText = "public open override fun foo(): Unit" + addOverriddenSymbols("sample/Interface#foo().") + }, + ) + assertContainsAll(document.symbolsList, *symbols) + } + @Test fun `lambda parameters`(@TempDir path: Path) { val document = @@ -989,9 +1333,13 @@ class KotlinAnalysisIndexerTest { endCharacter = 6 } }, + // An enum entry with a body is modeled as a synthetic anonymous subclass, + // so its overridden member is a local symbol (local2), not a global + // sample/Op#PLUS.apply(). It still gets an enclosing_range spanning the + // function declaration. scipOccurrence { role = DEFINITION - symbol = "sample/Op#PLUS.apply()." + symbol = "local 2" range { startLine = 4 startCharacter = 21 @@ -1010,14 +1358,24 @@ class KotlinAnalysisIndexerTest { val symbols = arrayOf( + // The entry body becomes an anonymous class enclosed by the entry... scipSymbol { - symbol = "sample/Op#PLUS.apply()." - kind = Kind.Method + symbol = "local 0" + kind = Kind.Class enclosingSymbol = "sample/Op#PLUS." + displayName = "" + addOverriddenSymbols("sample/Op#") + signatureText = "object : Op" + }, + // ...and the override is a method of that anonymous class. + scipSymbol { + symbol = "local 2" + kind = Kind.Method + enclosingSymbol = "local 0" displayName = "apply" addOverriddenSymbols("sample/Op#apply().") signatureText = "public open override fun apply(a: Int, b: Int): Int" - } + }, ) assertContainsAll(document.symbolsList, *symbols) } diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt index 474c38b46..288589564 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt @@ -85,33 +85,33 @@ // display_name asdf // signature_documentation // > public get(): Any -// ⌄ enclosing_range_start scip-java maven . . snapshots/``# -// ⌄ enclosing_range_start scip-java maven . . snapshots/``#``(). +// ⌄ enclosing_range_start local 0 +// ⌄ enclosing_range_start local 1 object { -// ^^^^^^ definition scip-java maven . . snapshots/``# +// ^^^^^^ definition local 0 // kind Class // display_name // signature_documentation // > object : Any -// ^^^^^^ definition scip-java maven . . snapshots/``#``(). +// ^^^^^^ definition local 1 // kind Constructor // display_name // signature_documentation // > private constructor(): -// ⌄ enclosing_range_start scip-java maven . . snapshots/``#doStuff(). +// ⌄ enclosing_range_start local 2 fun doStuff() = Unit -// ^^^^^^^ definition scip-java maven . . snapshots/``#doStuff(). +// ^^^^^^^ definition local 2 // kind Method // display_name doStuff // signature_documentation // > public final fun doStuff(): Unit // ^^^^ reference scip-java maven . . kotlin/Unit# -// ⌃ enclosing_range_end scip-java maven . . snapshots/``#doStuff(). +// ⌃ enclosing_range_end local 2 } // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#asdf. // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#asdf.get(). -// ⌃ enclosing_range_end scip-java maven . . snapshots/``# -// ⌃ enclosing_range_end scip-java maven . . snapshots/``#``(). +// ⌃ enclosing_range_end local 0 +// ⌃ enclosing_range_end local 1 // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``(+1). constructor() : this(1, "") From 46f3858ade202815b73687ef019f8e75e36f98ab Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Mon, 26 Oct 2026 14:26:50 +0100 Subject: [PATCH 20/33] Reference the class when its name stands for the companion Port #973's resolved-qualifier references: a class name used as its companion object's receiver (`Bar.instance()`, `CompanionOwner.create()`) now references the class (`Bar#`) instead of the companion (`Bar#Companion#`). Naming the companion explicitly (`Bar.Companion`, `Foo.Factory`) still references it. Amp-Thread-ID: https://ampcode.com/threads/T-01a153c0-ec1f-774a-993c-bca3256017ce Co-authored-by: Amp --- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 15 +++++++++++++++ .../kotlin_analysis/KotlinAnalysisIndexerTest.kt | 2 +- .../src/main/kotlin/snapshots/CompanionOwner.kt | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index 8a5657d93..d5f5ff7fd 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.analysis.api.analyze import org.jetbrains.kotlin.analysis.api.renderer.types.impl.KaTypeRendererForSource import org.jetbrains.kotlin.analysis.api.standalone.buildStandaloneAnalysisAPISession import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaClassKind import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaConstructorSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol @@ -230,6 +231,20 @@ class KotlinAnalysisIndexer( return } + // A class name standing for its companion object (`Bar.instance()`) references the + // class, like scip-kotlinc; `Bar.Companion` still references the companion. + if ( + target is KaClassSymbol && + target.classKind == KaClassKind.COMPANION_OBJECT && + target.name?.asString() != referencedName + ) { + val containingClass = with(session) { target.containingSymbol } + if (containingClass != null) { + addReference(cache.symbolForReference(session, containingClass), range) + return + } + } + addReference(cache.symbolForReference(session, target), range) } diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index 60a91e897..280a51d7b 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -1652,7 +1652,7 @@ class KotlinAnalysisIndexerTest { }, scipOccurrence { role = REFERENCE - symbol = "sample/Bar#Companion#" + symbol = "sample/Bar#" range { startLine = 7 startCharacter = 12 diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt index 7b2bc1bbf..b89f4650f 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt @@ -48,7 +48,7 @@ // signature_documentation // > public final fun create(): Int // ^^^ reference scip-java maven . . kotlin/Int# -// ^^^^^^^^^^^^^^ reference scip-java maven . . snapshots/CompanionOwner#Companion# +// ^^^^^^^^^^^^^^ reference scip-java maven . . snapshots/CompanionOwner# // ^^^^^^ reference scip-java maven . . snapshots/CompanionOwner#Companion#create(). // ^^^^^^^^ reference scip-java maven . . kotlin/Any#hashCode(). // ⌃ enclosing_range_end scip-java maven . . snapshots/CompanionOwner#create(). From 6154a0661c73231f7291d0bd34c2690d4c779f99 Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Mon, 26 Oct 2026 14:27:31 +0100 Subject: [PATCH 21/33] Anchor unnamed companion objects at the companion keyword Port #973's range for unnamed companion objects: the definition, and that of its implicit constructor, now covers the `companion` keyword instead of the declaration's first line (`companion object {`). scip-kotlinc's implicit-constructor range ran past the end of the line; here both definitions share the keyword range. Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-01a153c0-ec1f-774a-993c-bca3256017ce --- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 5 ++++- .../KotlinAnalysisIndexerTest.kt | 2 +- .../main/kotlin/snapshots/CompanionOwner.kt | 20 +++++++++---------- .../common/src/main/kotlin/snapshots/Enums.kt | 20 +++++++++---------- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index d5f5ff7fd..09a9ffe43 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -255,7 +255,10 @@ class KotlinAnalysisIndexer( declaration.nameIdentifier != null -> declaration.nameIdentifier!!.textRange isAnonymous -> declaration.getObjectKeyword()?.textRange ?: declaration.textRange - else -> declaration.textRange // unnamed companion object + // An unnamed companion object is anchored at its `companion` keyword. + else -> + declaration.modifierList?.getModifier(KtTokens.COMPANION_KEYWORD)?.textRange + ?: declaration.textRange } val displayName = if (isAnonymous) "" else declaration.name ?: "Companion" val symbol = cache.symbolForDeclaration(declaration) diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index 280a51d7b..dbfba8816 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -1677,7 +1677,7 @@ class KotlinAnalysisIndexerTest { startLine = 3 startCharacter = 4 endLine = 3 - endCharacter = 22 + endCharacter = 13 } enclosingRange { startLine = 3 diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt index b89f4650f..cb3b082c3 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt @@ -17,16 +17,16 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion# // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion#``(). companion object { -// ^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion# -// kind Class -// display_name Companion -// signature_documentation -// > public final companion object Companion : Any -// ^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion#``(). -// kind Constructor -// display_name Companion -// signature_documentation -// > private constructor(): CompanionOwner.Companion +// ^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion# +// kind Class +// display_name Companion +// signature_documentation +// > public final companion object Companion : Any +// ^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion#``(). +// kind Constructor +// display_name Companion +// signature_documentation +// > private constructor(): CompanionOwner.Companion // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion#create(). fun create(): CompanionOwner = CompanionOwner() // ^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion#create(). diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt index e61c62401..55419b170 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt @@ -102,16 +102,16 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion# // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion#``(). companion object { -// ^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion# -// kind Class -// display_name Companion -// signature_documentation -// > public final companion object Companion : Any -// ^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion#``(). -// kind Constructor -// display_name Companion -// signature_documentation -// > private constructor(): Suit.Companion +// ^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion# +// kind Class +// display_name Companion +// signature_documentation +// > public final companion object Companion : Any +// ^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion#``(). +// kind Constructor +// display_name Companion +// signature_documentation +// > private constructor(): Suit.Companion // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion#fromSymbol(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion#fromSymbol().(symbol) // ⌄ enclosing_range_start local 0 From a02c0ca5b5394cf66823b606a35b93c80bb46b61 Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Mon, 26 Oct 2026 14:29:14 +0100 Subject: [PATCH 22/33] Render signatures like scip-kotlinc on Kotlin 2.4 Match the FirRenderer output that #973's Kotlin 2.4 update introduced: - Enum entries render as `public final static enum entry RED: Color` instead of `public final val RED: Color`, and the `entries` getter as `public static get()`. - Functions and classes declared in a code block render as `local` (`local final fun inner(): Unit`); members of local classes and anonymous objects keep their visibility. - Extension functions and properties render their receiver (`public final fun String.foo(): Int`). The other differences in scip-kotlinc's 2.4 output are renderer internals and are not reproduced: evaluated annotation arguments (`@Tagged(tag = String(run) [evaluated = ...])`) and expanded type alias markers (`{snapshots/StringContainer=} Container`). The ported AnalyzerTest cases now use scip-kotlinc's expectations for these signatures. Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-01a153c0-ec1f-774a-993c-bca3256017ce --- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 2 +- .../kotlin_analysis/SignatureRenderer.kt | 26 ++++++-- .../KotlinAnalysisIndexerTest.kt | 60 ++++++++++++++++++- .../common/src/main/kotlin/snapshots/Enums.kt | 6 +- 4 files changed, 83 insertions(+), 11 deletions(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index 09a9ffe43..5be0ea643 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -402,7 +402,7 @@ class KotlinAnalysisIndexer( enclosingSymbol = enclosingSymbol(declaration), enclosing = declaration.textRange, displayName = declaration.name.orEmpty(), - signatureText = signatures.propertySignature(declaration), + signatureText = signatures.enumEntrySignature(declaration), documentation = docComment(declaration), ) if (declaration.body != null) emitEnumEntryBody(declaration, symbol, identifier) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt index 8733bc187..de1ad1b7f 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt @@ -5,10 +5,13 @@ import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.renderer.types.impl.KaTypeRendererForSource import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.KtBlockExpression +import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtConstructor import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtEnumEntry import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtObjectDeclaration @@ -69,7 +72,7 @@ internal class SignatureRenderer(private val session: KaSession) { val typeParameters = declaration.typeParameterList?.text?.let { "$it " }.orEmpty() val signature = "${annotationsPrefix(declaration)}${visibility(declaration)} " + - "${memberModality(declaration)}fun $typeParameters" + + "${memberModality(declaration)}fun $typeParameters${receiverPrefix(declaration)}" + "${declaration.name.orEmpty()}(${parametersText(declaration.valueParameters)}): " + returnType // FirRenderer emitted a trailing newline for functions without a body @@ -90,7 +93,11 @@ internal class SignatureRenderer(private val session: KaSession) { fun enumEntriesSignature(name: String): String = "public final static val entries: EnumEntries<$name>" - fun enumGetEntriesSignature(name: String): String = "public get(): EnumEntries<$name>" + fun enumGetEntriesSignature(name: String): String = "public static get(): EnumEntries<$name>" + + fun enumEntrySignature(entry: KtEnumEntry): String = + "${annotationsPrefix(entry)}public final static enum entry ${entry.name.orEmpty()}: " + + ownerName(entry.containingClassOrObject) fun dataCopySignature(declaration: KtClass): String { val parameters = @@ -142,7 +149,9 @@ internal class SignatureRenderer(private val session: KaSession) { val lateinit = if (declaration.hasModifier(KtTokens.LATEINIT_KEYWORD)) "lateinit " else "" val valOrVar = if (isVar(declaration)) "var" else "val" val type = explicitPropertyType(declaration) ?: renderedReturnType(declaration) ?: "Any" - return "${visibility(declaration)} ${memberModality(declaration)}$const$lateinit$valOrVar $name: $type" + val receiver = (declaration as? KtProperty)?.let(::receiverPrefix).orEmpty() + return "${visibility(declaration)} ${memberModality(declaration)}$const$lateinit$valOrVar " + + "$receiver$name: $type" } fun localVariableSignature(declaration: KtVariableDeclaration): String { @@ -244,8 +253,17 @@ internal class SignatureRenderer(private val session: KaSession) { else -> false } + /** + * Functions and classes declared in a code block render as `local`, like FirRenderer; members + * of local classes do not. + */ private fun visibility(declaration: KtDeclaration): String = - explicitVisibility(declaration) ?: "public" + explicitVisibility(declaration) + ?: if (declaration.parent is KtBlockExpression) "local" else "public" + + /** The extension receiver of [declaration] with a trailing dot (`String.`), if any. */ + private fun receiverPrefix(declaration: KtCallableDeclaration): String = + declaration.receiverTypeReference?.let { "${it.text}." }.orEmpty() private fun explicitVisibility(declaration: KtDeclaration): String? = when { diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index dbfba8816..2a0a58117 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -200,6 +200,13 @@ class KotlinAnalysisIndexerTest { displayName = "foo" signatureText = "public final fun foo(): Unit" }, + scipSymbol { + symbol = "local 0" + kind = Kind.Class + enclosingSymbol = "sample/foo()." + displayName = "LocalClass" + signatureText = "local final class LocalClass : Any" + }, scipSymbol { symbol = "local 1" kind = Kind.Constructor @@ -579,6 +586,7 @@ class KotlinAnalysisIndexerTest { endCharacter = 1 } }, + // inner() — local named function gets a local symbol scipOccurrence { role = DEFINITION symbol = "local 0" @@ -595,6 +603,7 @@ class KotlinAnalysisIndexerTest { endCharacter = 18 } }, + // innerWithReturnType() — local named function with explicit return type scipOccurrence { role = DEFINITION symbol = "local 1" @@ -611,6 +620,7 @@ class KotlinAnalysisIndexerTest { endCharacter = 39 } }, + // Int return-type reference scipOccurrence { role = REFERENCE symbol = "kotlin/Int#" @@ -621,6 +631,7 @@ class KotlinAnalysisIndexerTest { endCharacter = 34 } }, + // call site inner() references the same local symbol scipOccurrence { role = REFERENCE symbol = "local 0" @@ -633,6 +644,25 @@ class KotlinAnalysisIndexerTest { }, ) assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "local 0" + kind = Kind.Method + enclosingSymbol = "sample/outer()." + displayName = "inner" + signatureText = "local final fun inner(): Unit" + }, + scipSymbol { + symbol = "local 1" + kind = Kind.Method + enclosingSymbol = "sample/outer()." + displayName = "innerWithReturnType" + signatureText = "local final fun innerWithReturnType(): Int" + }, + ) + assertContainsAll(document.symbolsList, *symbols) } @Test @@ -1032,6 +1062,18 @@ class KotlinAnalysisIndexerTest { }, ) assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/String#foo()." + kind = Kind.Method + enclosingSymbol = "sample/" + displayName = "foo" + signatureText = "public final fun String.foo(): Int" + } + ) + assertContainsAll(document.symbolsList, *symbols) } @Test @@ -1097,6 +1139,18 @@ class KotlinAnalysisIndexerTest { }, ) assertContainsAll(document.occurrencesList, *occurrences) + + val symbols = + arrayOf( + scipSymbol { + symbol = "sample/Int#asString." + kind = Kind.Property + enclosingSymbol = "sample/" + displayName = "asString" + signatureText = "public final val Int.asString: String" + } + ) + assertContainsAll(document.symbolsList, *symbols) } @Test @@ -1275,21 +1329,21 @@ class KotlinAnalysisIndexerTest { kind = Kind.EnumMember enclosingSymbol = "sample/Color#" displayName = "RED" - signatureText = "public final val RED: Color" + signatureText = "public final static enum entry RED: Color" }, scipSymbol { symbol = "sample/Color#GREEN." kind = Kind.EnumMember enclosingSymbol = "sample/Color#" displayName = "GREEN" - signatureText = "public final val GREEN: Color" + signatureText = "public final static enum entry GREEN: Color" }, scipSymbol { symbol = "sample/Color#BLUE." kind = Kind.EnumMember enclosingSymbol = "sample/Color#" displayName = "BLUE" - signatureText = "public final val BLUE: Color" + signatureText = "public final static enum entry BLUE: Color" }, ) assertContainsAll(document.symbolsList, *symbols) diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt index 55419b170..63539340d 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt @@ -47,7 +47,7 @@ // kind Method // display_name entries // signature_documentation -// > public get(): EnumEntries +// > public static get(): EnumEntries // ^^^^^^ definition scip-java maven . . snapshots/Suit#``().(symbol) // kind Parameter // display_name symbol @@ -75,7 +75,7 @@ // kind EnumMember // display_name HEARTS // signature_documentation -// > public final val HEARTS: Suit +// > public final static enum entry HEARTS: Suit // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#HEARTS. // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#SPADES. SPADES('s'); @@ -83,7 +83,7 @@ // kind EnumMember // display_name SPADES // signature_documentation -// > public final val SPADES: Suit +// > public final static enum entry SPADES: Suit // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#SPADES. // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#isRed(). From 8051015490e837da623932ff939ede8a66c34225 Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Mon, 26 Oct 2026 17:23:04 +0100 Subject: [PATCH 23/33] Index Kotlin in Android modules without a Kotlin plugin AGP 9 compiles Kotlin in Android modules itself ("built-in Kotlin") without applying a Kotlin Gradle plugin, so the `hasPlugin("kotlin")` check never registered the source dump for those compile tasks and their Kotlin sources were not indexed. The dump is now registered for every project; configureKotlinCompileTask already filters by the KotlinCompile task class. In android/nowinandroid, indexing the main sources of the demoDebug variant went from 16 to 199 of 310 Kotlin files. Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-01a178a8-3c2c-75e0-bfa6-7417585d4372 --- .../scip_code/scip_java/gradle/ScipGradlePlugin.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java b/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java index 49dd20305..5be4995d9 100644 --- a/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java +++ b/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java @@ -115,12 +115,14 @@ private void configureProject(Project project) { triggers.add("compileKotlin"); triggers.add("compileTestKotlin"); } - - project - .getTasks() - .configureEach(task -> configureKotlinCompileTask(task, sourceRoot, targetRoot)); } + // Not limited to the plugin checks above: AGP 9's built-in Kotlin adds + // Kotlin compile tasks to Android modules without applying a Kotlin plugin. + project + .getTasks() + .configureEach(task -> configureKotlinCompileTask(task, sourceRoot, targetRoot)); + project.getTasks().create("scipCompileAll").dependsOn(triggers); project.getTasks().create("scipPrintDependencies", WriteDependencies.class); From 72c9c4f75ea9f908930ffce888af1189e2949c7b Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Mon, 26 Oct 2026 17:23:29 +0100 Subject: [PATCH 24/33] Resolve same-module Java sources when indexing Kotlin The Kotlin source dump listed only .kt files, so the Analysis API session had no view of Java classes compiled in the same module (they are compiled after Kotlin and are not on the classpath). Kotlin references to them were silently dropped: in thunderbird-android's legacy/core, HtmlSignatureRemover.kt had no occurrence for the Java class AdvancedNodeTraversor, only for its package. The dump now also lists the compile task's javaSources. The indexer only emits documents for Kotlin files, so the Java files are used for resolution only. Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-01a178a8-3c2c-75e0-bfa6-7417585d4372 --- .../scip_java/gradle/ScipGradlePlugin.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java b/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java index 5be4995d9..78c605189 100644 --- a/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java +++ b/scip-gradle-plugin/src/main/java/org/scip_code/scip_java/gradle/ScipGradlePlugin.java @@ -212,6 +212,19 @@ private static void configureKotlinCompileTask(Task task, String sourceRoot, Str sources++; } } + // Java sources of the same module are listed too, so Kotlin + // references to them resolve. The indexer only emits Kotlin files. + try { + FileCollection javaSources = + (FileCollection) task.getClass().getMethod("getJavaSources").invoke(task); + for (File file : javaSources.getFiles()) { + if (file.getName().endsWith(".java")) { + lines.add("source " + file.getAbsolutePath()); + } + } + } catch (ReflectiveOperationException exc) { + // Kotlin Gradle Plugin without javaSources (pre 1.7). + } if (sources == 0) { return; } From 570c5fabdd385de1c7485d525ddaa9ab6e9d1a14 Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Tue, 27 Oct 2026 10:23:52 +0100 Subject: [PATCH 25/33] Count only functions as overloads in method disambiguators The disambiguator of a source function counted every earlier declaration with the same name in its file or class, including classes and properties, while references to compiled code counted all same-named callables of the class or package. A top-level function named like a class in the same file, a common factory pattern, was defined as `Foo(+1).` but referenced from other modules as `Foo().`; in kotlinx-datetime, test references to `UtcOffset(...)` and `DateTimePeriod(...)` (323 occurrences) didn't resolve to the definitions in main. Both paths now count only functions. For class members this also matches javac's numbering, which counts only methods: `fun shift()` next to `val shift` is `#shift().` from Kotlin and Java alike. Overloads of a top-level function spread across files still diverge: source counts per file, compiled code per package. Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-01a178a8-3c2c-75e0-bfa6-7417585d4372 --- .../scip_java/kotlin_analysis/SymbolsCache.kt | 24 +++++++++---------- .../KotlinAnalysisIndexerTest.kt | 21 ++++++++++++++++ 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt index 8245677d1..73d335de7 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt @@ -26,7 +26,6 @@ import org.jetbrains.kotlin.psi.KtEnumEntry import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtFunctionLiteral -import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter @@ -404,8 +403,9 @@ class SymbolsCache(private val session: KaSession) { } /** - * Port of FIR's `methodDisambiguator`: the N-th preceding declaration with the same name in the - * containing scope yields `(+N)`, the first one yields `()`. + * Port of FIR's `methodDisambiguator`: the N-th preceding function with the same name in the + * containing scope yields `(+N)`, the first one yields `()`. Classes and properties of the same + * name don't count, as in [externalMethodDisambiguator] and the javac indexer. */ private fun methodDisambiguator(function: KtNamedFunction): String { val name = function.name ?: return "()" @@ -427,7 +427,7 @@ class SymbolsCache(private val session: KaSession) { found = true break } - if (declaredName(sibling) == name) count++ + if (sibling is KtNamedFunction && sibling.name == name) count++ } if (count == 0 || !found) return "()" return "(+$count)" @@ -448,13 +448,6 @@ class SymbolsCache(private val session: KaSession) { else -> listOfNotNull(containingClass.primaryConstructor) + containingClass.declarations } - private fun declaredName(declaration: KtDeclaration): String? = - when (declaration) { - is KtConstructor<*> -> "" - is KtNamedDeclaration -> declaration.name - else -> null - } - private fun ownerDeclarationOfParameter(parameter: KtParameter): KtDeclaration? = (parameter.parent as? KtParameterList)?.parent as? KtDeclaration @@ -500,10 +493,15 @@ class SymbolsCache(private val session: KaSession) { receiver, ) } - findTopLevelCallables(callableId.packageName, callableId.callableName).toList() + findTopLevelCallables(callableId.packageName, callableId.callableName) + .filterIsInstance() + .toList() } else { val classSymbol = findClass(classId) ?: return "()" - classSymbol.declaredMemberScope.callables(callableId.callableName).toList() + classSymbol.declaredMemberScope + .callables(callableId.callableName) + .filterIsInstance() + .toList() } } val index = overloads.indexOfFirst { it == target } diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index 2a0a58117..29528bbeb 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -104,6 +104,27 @@ class KotlinAnalysisIndexerTest { ) } + @Test + fun `classes and properties are not overloads of same-named functions`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + class Offset(val seconds: Int) { + val shift: Int = 0 + fun shift(by: Int): Int = by + } + + fun Offset(): Offset = Offset(0) + """, + ) + + // References to compiled classes number only functions, as does javac. + assertContainsAll(definitions(document), "sample/Offset().", "sample/Offset#shift().") + } + @Test fun `local classes`(@TempDir path: Path) { val document = From a4ff5746aafeee6dc83607e1c2bbf8537256d3a7 Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Tue, 27 Oct 2026 11:04:17 +0100 Subject: [PATCH 26/33] Assign valid local symbols to synthetic Kotlin members Allocate synthetic members of local declarations from the same per-document local symbol cache, keyed by owner and descriptor, instead of appending global descriptors to local symbols. Resolve generated local method references through the same keys as their definitions. Add regression coverage for constructor properties, accessors, setter and copy parameters, component methods, and references across local classes. Corpus verification eliminates 26 malformed locals in kotlinx-datetime and 13 in Thunderbird without changing coverage or dangling references. Co-authored-by: Amp Amp-Thread-ID: https://ampcode.com/threads/T-01a17cb6-8fe3-70a5-861f-762aa723b737 --- .../scip_java/kotlin_analysis/SymbolsCache.kt | 30 +++++-- .../KotlinAnalysisIndexerTest.kt | 86 +++++++++++++++++++ 2 files changed, 108 insertions(+), 8 deletions(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt index 73d335de7..7421bb893 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt @@ -56,8 +56,14 @@ import org.scip_code.scip_java.shared.LocalSymbolsCache as SharedLocalSymbolsCac class SymbolsCache(private val session: KaSession) { private val globals = HashMap() private val packages = HashMap() - private val locals = - SharedLocalSymbolsCache(HashMap()) { Symbol.createLocal(it) } + private val locals = SharedLocalSymbolsCache(HashMap()) { Symbol.createLocal(it) } + + /** Synthetic members of locals need their own IDs, not global descriptor suffixes. */ + private fun memberSymbol(owner: Symbol, descriptor: ScipSymbolDescriptor): Symbol { + if (!owner.isLocal()) return Symbol.createGlobal(owner, descriptor) + val key = owner to descriptor + return locals.get(key) ?: locals.put(key) + } fun packageSymbol(fqName: FqName): Symbol { if (fqName.isRoot) return Symbol.ROOT_PACKAGE @@ -122,11 +128,11 @@ class SymbolsCache(private val session: KaSession) { /** The getter or setter of [property] (`Class#banana.get().`, `Class#banana.set().`). */ fun accessorSymbol(property: Symbol, setter: Boolean): Symbol = - Symbol.createGlobal(property, accessorDescriptor(setter)) + memberSymbol(property, accessorDescriptor(setter)) /** The property symbol declared by a `val`/`var` constructor parameter (`Class#banana.`). */ fun parameterPropertySymbol(parameter: KtParameter): Symbol = - Symbol.createGlobal( + memberSymbol( propertyOwnerSymbol(parameter), ScipSymbolDescriptor(Kind.TERM, parameter.name.orEmpty()), ) @@ -155,15 +161,15 @@ class SymbolsCache(private val session: KaSession) { /** Symbol of a compiler-generated member method (enum `values()`, data-class `copy()`, …). */ fun memberMethodSymbol(owner: KtClassOrObject, name: String): Symbol = - Symbol.createGlobal(symbolForDeclaration(owner), ScipSymbolDescriptor(Kind.METHOD, name)) + memberSymbol(symbolForDeclaration(owner), ScipSymbolDescriptor(Kind.METHOD, name)) /** Symbol of a compiler-generated member property (enum `entries`). */ fun memberTermSymbol(owner: KtClassOrObject, name: String): Symbol = - Symbol.createGlobal(symbolForDeclaration(owner), ScipSymbolDescriptor(Kind.TERM, name)) + memberSymbol(symbolForDeclaration(owner), ScipSymbolDescriptor(Kind.TERM, name)) /** Symbol of a parameter of a compiler-generated method. */ fun methodParameterSymbol(method: Symbol, name: String): Symbol = - Symbol.createGlobal(method, ScipSymbolDescriptor(Kind.PARAMETER, name)) + memberSymbol(method, ScipSymbolDescriptor(Kind.PARAMETER, name)) /** * A per-document local symbol for a compiler-generated declaration without dedicated PSI @@ -178,7 +184,7 @@ class SymbolsCache(private val session: KaSession) { /** The `(value)` parameter symbol of a synthetic setter. */ fun syntheticSetterValueSymbol(property: KtDeclaration): Symbol = - Symbol.createGlobal( + memberSymbol( syntheticAccessorSymbol(property, setter = true), ScipSymbolDescriptor(Kind.PARAMETER, "value"), ) @@ -216,6 +222,14 @@ class SymbolsCache(private val session: KaSession) { psi !is KtPropertyAccessor && psi !is KtFunctionLiteral ) { + // Generated functions of local classes have no callable ID. Use + // the same owner/name key as their synthetic definitions. + if (target is KaNamedFunctionSymbol) { + val owner = with(session) { target.containingSymbol?.psi } as? KtClassOrObject + if (owner != null && symbolForDeclaration(owner).isLocal()) { + return memberMethodSymbol(owner, target.name.asString()) + } + } return externalCallableSymbol(session, target) } if (target is KaCallableSymbol && psi is KtClassOrObject) { diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index 29528bbeb..83543fecd 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -125,6 +125,92 @@ class KotlinAnalysisIndexerTest { assertContainsAll(definitions(document), "sample/Offset().", "sample/Offset#shift().") } + @Test + fun `synthetic members of local classes have distinct local symbols`(@TempDir path: Path) { + val code = + """ + package sample + + fun use() { + data class First(var version: Int, val label: String) + data class Second(val version: Int) + val first = First(1, "one") + val second = Second(2) + first.version = 3 + println(second.version) + first.copy() + second.copy() + first.component1() + first.component2() + second.component1() + val list = object : java.util.ArrayList() {} + list.clear() + } + """ + .trimIndent() + val document = compileScip(path, code) + val locals = document.symbolsList.filter { it.symbol.startsWith("local ") } + val allSymbols = + document.occurrencesList.map { it.symbol } + + document.symbolsList.flatMap { + listOf(it.symbol, it.enclosingSymbol) + it.relationshipsList.map { it.symbol } + } + assertTrue( + allSymbols.filter { it.startsWith("local ") }.all { it.matches(Regex("local [0-9]+")) }, + "malformed locals: ${allSymbols.filter { it.startsWith("local ") }.distinct()}", + ) + assertEquals(locals.size, locals.map { it.symbol }.distinct().size) + val localDefinitions = definitions(document).filter { it.startsWith("local ") }.toSet() + assertTrue( + references(document).filter { it.startsWith("local ") }.all { it in localDefinitions } + ) + + for ((className, receiver) in listOf("First" to "first", "Second" to "second")) { + val owner = locals.single { it.kind == Kind.Class && it.displayName == className } + val members = locals.filter { it.enclosingSymbol == owner.symbol } + val property = + members.single { it.kind == Kind.Property && it.displayName == "version" } + val accessors = locals.filter { it.enclosingSymbol == property.symbol } + assertEquals(if (className == "First") 2 else 1, accessors.size) + if (className == "First") { + val setter = accessors.single { it.signatureDocumentation.text.contains("set(") } + assertEquals( + 1, + locals.count { + it.enclosingSymbol == setter.symbol && it.kind == Kind.Parameter + }, + ) + } + val copy = members.single { it.kind == Kind.Method && it.displayName == "copy" } + assertEquals( + if (className == "First") setOf("version", "label") else setOf("version"), + locals.filter { it.enclosingSymbol == copy.symbol }.map { it.displayName }.toSet(), + ) + val expectedReferences = + mapOf( + "$receiver.version" to (setOf(property.symbol) + accessors.map { it.symbol }), + "$receiver.copy()" to setOf(copy.symbol), + ) + + members + .filter { it.displayName.startsWith("component") } + .associate { "$receiver.${it.displayName}()" to setOf(it.symbol) } + assertEquals(if (className == "First") 4 else 3, expectedReferences.size) + for ((expression, expected) in expectedReferences) { + val line = code.lines().indexOfFirst { expression in it } + val start = code.lines()[line].indexOf(expression) + receiver.length + 1 + val actual = + document.occurrencesList + .filter { + it.singleLineRange.line == line && + it.singleLineRange.startCharacter == start + } + .map { it.symbol } + .toSet() + assertEquals(expected, actual, expression) + } + } + } + @Test fun `local classes`(@TempDir path: Path) { val document = From ba91d65aee98223553aa87844169f62d1db9743f Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Tue, 27 Oct 2026 12:33:06 +0100 Subject: [PATCH 27/33] Resolve inherited methods and properties to their declarations --- .../scip_java/kotlin_analysis/SymbolsCache.kt | 4 +- .../KotlinAnalysisIndexerTest.kt | 84 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt index 7421bb893..a4354041c 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt @@ -473,7 +473,9 @@ class SymbolsCache(private val session: KaSession) { * callables of their container — mirroring FIR, which consulted the class's declared members or * the top-level symbol provider (e.g. `kotlin/collections/forEachIndexed(+9).`). */ - private fun externalCallableSymbol(session: KaSession, target: KaCallableSymbol): Symbol { + private fun externalCallableSymbol(session: KaSession, resolved: KaCallableSymbol): Symbol { + // Inherited members must use their declaration's owner and overload number. + val target = with(session) { resolved.fakeOverrideOriginal } val callableId = target.callableId ?: return Symbol.NONE val owner = callableId.classId?.let(::classSymbol) diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index 83543fecd..efe82ed19 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -104,6 +104,90 @@ class KotlinAnalysisIndexerTest { ) } + // Compiled by the test build, so references use classpath symbols rather than source PSI. + open class Base(open var state: T) { + open fun find(value: T): T = value + + fun find(index: Int): T = state + } + + open class Middle : Base("") + + class Leaf : Middle() + + class Overriding : Middle() { + override var state: String = "override" + + override fun find(value: String): String = value + } + + @Test + fun `inherited binary members reference their declarations`(@TempDir path: Path) { + val sourceroot = path.toRealPath() + val library = Paths.get(Leaf::class.java.protectionDomain.codeSource.location.toURI()) + val code = + """ + package sample + + import org.scip_code.scip_java.kotlin_analysis.KotlinAnalysisIndexerTest.Leaf + import org.scip_code.scip_java.kotlin_analysis.KotlinAnalysisIndexerTest.Overriding + + fun use(leaf: Leaf, overriding: Overriding) { + leaf.find("value") + leaf.find(1) + leaf.state = "updated" + overriding.find("value") + overriding.find(1) + overriding.state = "updated" + } + """ + .trimIndent() + val source = sourceroot.resolve("Use.kt") + Files.writeString(source, code) + val document = + KotlinAnalysisIndexer( + sourceroot = sourceroot, + targetroot = sourceroot.resolve("scip-targetroot"), + sourceRoots = listOf(source), + classpath = listOf(kotlinStdlibJar(), library), + ) + .run() + .single() + val prefix = "org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest#" + val expectedReferences = + mapOf( + "leaf.find(\"value\")" to setOf("${prefix}Base#find()."), + "leaf.find(1)" to setOf("${prefix}Base#find(+1)."), + "leaf.state" to + setOf( + "${prefix}Base#state.", + "${prefix}Base#state.get().", + "${prefix}Base#state.set().", + ), + "overriding.find(\"value\")" to setOf("${prefix}Overriding#find()."), + "overriding.find(1)" to setOf("${prefix}Base#find(+1)."), + "overriding.state" to + setOf( + "${prefix}Overriding#state.", + "${prefix}Overriding#state.get().", + "${prefix}Overriding#state.set().", + ), + ) + for ((expression, expected) in expectedReferences) { + val line = code.lines().indexOfFirst { expression in it } + val start = code.lines()[line].indexOf(expression) + expression.indexOf('.') + 1 + val actual = + document.occurrencesList + .filter { + it.singleLineRange.line == line && + it.singleLineRange.startCharacter == start + } + .map { it.symbol } + .toSet() + assertEquals(expected, actual, expression) + } + } + @Test fun `classes and properties are not overloads of same-named functions`(@TempDir path: Path) { val document = From 5fe1b55ac5d0a7b8455c747efed4bc16bee1ffca Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Tue, 27 Oct 2026 13:24:36 +0100 Subject: [PATCH 28/33] record definitions for generated methods on data class definitions --- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 48 +++++++++++++ .../kotlin_analysis/SignatureRenderer.kt | 19 +++++ .../scip_java/kotlin_analysis/SymbolsCache.kt | 6 ++ .../KotlinAnalysisIndexerTest.kt | 70 ++++++++++++++++++- .../main/kotlin/snapshots/Destructuring.kt | 31 ++++++++ 5 files changed, 172 insertions(+), 2 deletions(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index 5be0ea643..ace4f7ddf 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -16,7 +16,9 @@ import org.jetbrains.kotlin.analysis.api.symbols.KaClassKind import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaConstructorSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaNamedFunctionSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaPropertySymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbolOrigin import org.jetbrains.kotlin.analysis.api.types.KaClassType import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtLibraryModule import org.jetbrains.kotlin.analysis.project.structure.builder.buildKtSdkModule @@ -326,6 +328,52 @@ class KotlinAnalysisIndexer( documentation = null, ) } + emitDataClassObjectMethods(declaration, range) + } + } + + /** Emit only methods Kotlin generates, not explicit or inherited final implementations. */ + private fun emitDataClassObjectMethods(declaration: KtClass, range: TextRange) { + with(session) { + val owner = declaration.symbol as? KaClassSymbol ?: return@with + val scope = owner.memberScope + for (name in listOf("equals", "hashCode", "toString")) { + for (method in + scope + .callables(Name.identifier(name)) + .filterIsInstance()) { + if (method.origin != KaSymbolOrigin.SOURCE_MEMBER_GENERATED) continue + // Use reference resolution so same-named overloads get the same numbering. + val symbol = cache.symbolForReference(session, method) + emitDefinition( + symbol = symbol, + range = range, + kind = Kind.Method, + enclosingSymbol = cache.symbolForDeclaration(declaration), + enclosing = declaration.textRange, + displayName = name, + signatureText = signatures.generatedFunctionSignature(method), + documentation = null, + relationships = + method.directlyOverriddenSymbols + .map { cache.symbolForReference(session, it) } + .toList(), + ) + for (parameter in method.valueParameters) { + emitDefinition( + symbol = + cache.methodParameterSymbol(symbol, parameter.name.asString()), + range = range, + kind = Kind.Parameter, + enclosingSymbol = symbol, + enclosing = declaration.textRange, + displayName = parameter.name.asString(), + signatureText = signatures.parameterSignature(parameter), + documentation = null, + ) + } + } + } } } diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt index de1ad1b7f..37f625b65 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt @@ -4,6 +4,8 @@ import org.jetbrains.kotlin.analysis.api.KaExperimentalApi import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.renderer.types.impl.KaTypeRendererForSource import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaNamedFunctionSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaValueParameterSymbol import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtBlockExpression import org.jetbrains.kotlin.psi.KtCallableDeclaration @@ -113,6 +115,23 @@ internal class SignatureRenderer(private val session: KaSession) { fun dataCopyParameterSignature(parameter: KtParameter): String = "${parameter.name.orEmpty()}: ${parameter.typeReference?.text ?: "Any"} = ..." + fun generatedFunctionSignature(function: KaNamedFunctionSymbol): String = + with(session) { + val parameters = function.valueParameters.joinToString(", ") { parameterSignature(it) } + val returnType = + function.returnType.render( + KaTypeRendererForSource.WITH_SHORT_NAMES, + position = Variance.INVARIANT, + ) + val operator = if (function.isOperator) "operator " else "" + return "public ${function.modality.name.lowercase()} override ${operator}fun ${function.name}($parameters): $returnType" + } + + fun parameterSignature(parameter: KaValueParameterSymbol): String = + with(session) { + "${parameter.name}: ${parameter.returnType.render(KaTypeRendererForSource.WITH_SHORT_NAMES, position = Variance.INVARIANT)}" + } + fun whenSubjectSignature(type: String): String = "local val : $type" fun destructSignature(type: String, isParameter: Boolean): String = diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt index a4354041c..ca8b97073 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaNamedFunctionSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaPackageSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaSymbolOrigin import org.jetbrains.kotlin.analysis.api.symbols.KaTypeAliasSymbol import org.jetbrains.kotlin.analysis.api.types.KaClassType import org.jetbrains.kotlin.name.ClassId @@ -521,6 +522,11 @@ class SymbolsCache(private val session: KaSession) { } } val index = overloads.indexOfFirst { it == target } + // Source declaredMemberScope omits generated Any overrides. They follow + // the explicit overloads in the compiled class, rather than sharing index 0. + if (index < 0 && target.origin == KaSymbolOrigin.SOURCE_MEMBER_GENERATED) { + return if (overloads.isEmpty()) "()" else "(+${overloads.size})" + } return if (index <= 0) "()" else "(+$index)" } } diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index efe82ed19..990a2022e 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -209,6 +209,61 @@ class KotlinAnalysisIndexerTest { assertContainsAll(definitions(document), "sample/Offset().", "sample/Offset#shift().") } + @Test + fun `data class object methods are defined only when generated`(@TempDir path: Path) { + val document = + compileScip( + path, + """ + package sample + + data class Generated(val value: Int) { + fun equals(value: Int): Boolean = this.value == value + fun hashCode(seed: Int): Int = seed + fun toString(prefix: String): String = prefix + } + data class Explicit(val value: Int) { + override fun equals(other: Any?): Boolean = false + override fun hashCode(): Int = value + override fun toString(): String = "explicit" + } + open class FinalBase { + final override fun equals(other: Any?): Boolean = false + final override fun hashCode(): Int = 1 + final override fun toString(): String = "inherited" + } + data class Inherited(val value: Int) : FinalBase() + fun use(generated: Generated, explicit: Explicit, inherited: Inherited) { + generated.equals(null) + generated.hashCode() + generated.toString() + explicit.equals(null) + explicit.hashCode() + explicit.toString() + inherited.equals(null) + inherited.hashCode() + inherited.toString() + } + """, + ) + val defined = definitions(document) + for (name in listOf("equals", "hashCode", "toString")) { + // Same-named overloads must not suppress or collide with the generated method. + for (symbol in + listOf( + "sample/Generated#$name(+1).", + "sample/Explicit#$name().", + "sample/FinalBase#$name().", + )) { + assertEquals(1, defined.count { it == symbol }, symbol) + assertContains(references(document), symbol) + } + assertContains(defined, "sample/Generated#$name().") + assertTrue(defined.none { it.startsWith("sample/Inherited#$name(") }) + } + assertContains(defined, "sample/Generated#equals(+1).(other)") + } + @Test fun `synthetic members of local classes have distinct local symbols`(@TempDir path: Path) { val code = @@ -227,6 +282,12 @@ class KotlinAnalysisIndexerTest { first.component1() first.component2() second.component1() + first.equals(null) + first.hashCode() + first.toString() + second.equals(null) + second.hashCode() + second.toString() val list = object : java.util.ArrayList() {} list.clear() } @@ -274,11 +335,16 @@ class KotlinAnalysisIndexerTest { mapOf( "$receiver.version" to (setOf(property.symbol) + accessors.map { it.symbol }), "$receiver.copy()" to setOf(copy.symbol), + "$receiver.equals(null)" to + setOf(members.single { it.displayName == "equals" }.symbol), ) + members - .filter { it.displayName.startsWith("component") } + .filter { + it.displayName.startsWith("component") || + it.displayName in setOf("hashCode", "toString") + } .associate { "$receiver.${it.displayName}()" to setOf(it.symbol) } - assertEquals(if (className == "First") 4 else 3, expectedReferences.size) + assertEquals(if (className == "First") 7 else 6, expectedReferences.size) for ((expression, expected) in expectedReferences) { val line = code.lines().indexOfFirst { expression in it } val start = code.lines()[line].indexOf(expression) + receiver.length + 1 diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt index 87cb89d6b..9ee48b5c5 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt @@ -2,6 +2,10 @@ // ^^^^^^^^^ reference scip-java maven . . snapshots/ //⌄ enclosing_range_start scip-java maven . . snapshots/Point# +//⌄ enclosing_range_start scip-java maven . . snapshots/Point#equals(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Point#equals().(other) +//⌄ enclosing_range_start scip-java maven . . snapshots/Point#hashCode(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Point#toString(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#copy(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#``().(x) @@ -25,6 +29,29 @@ // display_name Point // signature_documentation // > public constructor(x: Int, y: Int): Point +// ^^^^^ definition scip-java maven . . snapshots/Point#equals(). +// kind Method +// display_name equals +// signature_documentation +// > public open override operator fun equals(other: Any?): Boolean +// relationship scip-java maven . . kotlin/Any#equals(). implementation +// ^^^^^ definition scip-java maven . . snapshots/Point#equals().(other) +// kind Parameter +// display_name other +// signature_documentation +// > other: Any? +// ^^^^^ definition scip-java maven . . snapshots/Point#hashCode(). +// kind Method +// display_name hashCode +// signature_documentation +// > public open override fun hashCode(): Int +// relationship scip-java maven . . kotlin/Any#hashCode(). implementation +// ^^^^^ definition scip-java maven . . snapshots/Point#toString(). +// kind Method +// display_name toString +// signature_documentation +// > public open override fun toString(): String +// relationship scip-java maven . . kotlin/Any#toString(). implementation // ^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Point#copy(). // kind Method // display_name copy @@ -103,6 +130,10 @@ // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#copy().(y) // ⌃ enclosing_range_end scip-java maven . . snapshots/Point# // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#``(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#equals(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#equals().(other) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#hashCode(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#toString(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#copy(). //⌄ enclosing_range_start scip-java maven . . snapshots/destructure(). From 34784f7b86da264949fa59325f1096643cd1343c Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Wed, 28 Oct 2026 09:40:55 +0100 Subject: [PATCH 29/33] emit a symbol definition of INSTANCE for kotlin objects --- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 25 +++++++++++ .../kotlin_analysis/SignatureRenderer.kt | 3 ++ .../KotlinAnalysisIndexerTest.kt | 41 +++++++++++++++++++ .../main/kotlin/snapshots/CompanionOwner.kt | 7 ++++ .../common/src/main/kotlin/snapshots/Enums.kt | 7 ++++ .../src/main/kotlin/snapshots/ObjectKt.kt | 7 ++++ 6 files changed, 90 insertions(+) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index ace4f7ddf..efaa8bae8 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -312,6 +312,9 @@ class KotlinAnalysisIndexer( documentation = docComment(primaryConstructor), ) } + if (declaration is KtObjectDeclaration && !isAnonymous) { + emitObjectField(declaration, range) + } if (declaration is KtClass && declaration.isEnum()) { emitEnumSynthetics(declaration, range) } @@ -332,6 +335,28 @@ class KotlinAnalysisIndexer( } } + /** + * The static field Java code reaches an object through: `INSTANCE` on the object itself, or + * the companion's name on its containing class. + */ + private fun emitObjectField(declaration: KtObjectDeclaration, range: TextRange) { + val owner = + if (declaration.isCompanion()) declaration.containingClassOrObject ?: return + else declaration + val name = + if (declaration.isCompanion()) declaration.name ?: "Companion" else "INSTANCE" + emitDefinition( + symbol = cache.memberTermSymbol(owner, name), + range = range, + kind = Kind.StaticField, + enclosingSymbol = cache.symbolForDeclaration(owner), + enclosing = declaration.textRange, + displayName = name, + signatureText = signatures.objectFieldSignature(name, declaration), + documentation = null, + ) + } + /** Emit only methods Kotlin generates, not explicit or inherited final implementations. */ private fun emitDataClassObjectMethods(declaration: KtClass, range: TextRange) { with(session) { diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt index 37f625b65..a703da574 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SignatureRenderer.kt @@ -97,6 +97,9 @@ internal class SignatureRenderer(private val session: KaSession) { fun enumGetEntriesSignature(name: String): String = "public static get(): EnumEntries<$name>" + fun objectFieldSignature(name: String, declaration: KtObjectDeclaration): String = + "public final static val $name: ${ownerName(declaration)}" + fun enumEntrySignature(entry: KtEnumEntry): String = "${annotationsPrefix(entry)}public final static enum entry ${entry.name.orEmpty()}: " + ownerName(entry.containingClassOrObject) diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index 990a2022e..dd9f11549 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -710,6 +710,8 @@ class KotlinAnalysisIndexerTest { }, ) assertContainsAll(document.symbolsList, *symbols) + // Object expressions have no static instance field. + assertTrue(document.symbolsList.none { it.displayName == "INSTANCE" }) } @Test @@ -1724,6 +1726,23 @@ class KotlinAnalysisIndexerTest { endCharacter = 1 } }, + // Java reaches the object through its static INSTANCE field. + scipOccurrence { + role = DEFINITION + symbol = "sample/MySingleton#INSTANCE." + range { + startLine = 2 + startCharacter = 7 + endLine = 2 + endCharacter = 18 + } + enclosingRange { + startLine = 2 + startCharacter = 0 + endLine = 4 + endCharacter = 1 + } + }, scipOccurrence { role = DEFINITION symbol = "sample/MySingleton#hello()." @@ -1772,6 +1791,13 @@ class KotlinAnalysisIndexerTest { displayName = "MySingleton" signatureText = "public final object MySingleton : Any" }, + scipSymbol { + symbol = "sample/MySingleton#INSTANCE." + kind = Kind.StaticField + enclosingSymbol = "sample/MySingleton#" + displayName = "INSTANCE" + signatureText = "public final static val INSTANCE: MySingleton" + }, scipSymbol { symbol = "sample/MySingleton#hello()." kind = Kind.Method @@ -1899,6 +1925,14 @@ class KotlinAnalysisIndexerTest { displayName = "Factory" signatureText = "public final companion object Factory : Any" }, + // Java reaches a companion through a static field of its containing class. + scipSymbol { + symbol = "sample/Foo#Factory." + kind = Kind.StaticField + enclosingSymbol = "sample/Foo#" + displayName = "Factory" + signatureText = "public final static val Factory: Foo.Factory" + }, scipSymbol { symbol = "sample/Foo#Factory#create()." kind = Kind.Method @@ -2016,6 +2050,13 @@ class KotlinAnalysisIndexerTest { displayName = "Companion" signatureText = "public final companion object Companion : Any" }, + scipSymbol { + symbol = "sample/Bar#Companion." + kind = Kind.StaticField + enclosingSymbol = "sample/Bar#" + displayName = "Companion" + signatureText = "public final static val Companion: Bar.Companion" + }, scipSymbol { symbol = "sample/Bar#Companion#instance()." kind = Kind.Method diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt index cb3b082c3..c5b01af1d 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt @@ -16,6 +16,7 @@ // > public constructor(): CompanionOwner // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion# // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion. companion object { // ^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion# // kind Class @@ -27,6 +28,11 @@ // display_name Companion // signature_documentation // > private constructor(): CompanionOwner.Companion +// ^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion. +// kind StaticField +// display_name Companion +// signature_documentation +// > public final static val Companion: CompanionOwner.Companion // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#Companion#create(). fun create(): CompanionOwner = CompanionOwner() // ^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion#create(). @@ -40,6 +46,7 @@ } // ⌃ enclosing_range_end scip-java maven . . snapshots/CompanionOwner#Companion# // ⌃ enclosing_range_end scip-java maven . . snapshots/CompanionOwner#Companion#``(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/CompanionOwner#Companion. // ⌄ enclosing_range_start scip-java maven . . snapshots/CompanionOwner#create(). fun create(): Int = CompanionOwner.create().hashCode() // ^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#create(). diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt index 63539340d..944eeb179 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt @@ -101,6 +101,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion# // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion. companion object { // ^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion# // kind Class @@ -112,6 +113,11 @@ // display_name Companion // signature_documentation // > private constructor(): Suit.Companion +// ^^^^^^^^^ definition scip-java maven . . snapshots/Suit#Companion. +// kind StaticField +// display_name Companion +// signature_documentation +// > public final static val Companion: Suit.Companion // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion#fromSymbol(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#Companion#fromSymbol().(symbol) // ⌄ enclosing_range_start local 0 @@ -147,6 +153,7 @@ } // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#Companion# // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#Companion#``(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#Companion. } //⌃ enclosing_range_end scip-java maven . . snapshots/Suit# //⌃ enclosing_range_end scip-java maven . . snapshots/Suit#values(). diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt index 81f0ddc71..1e6a2aac8 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt @@ -8,6 +8,7 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/ObjectKt# //⌄ enclosing_range_start scip-java maven . . snapshots/ObjectKt#``(). +//⌄ enclosing_range_start scip-java maven . . snapshots/ObjectKt#INSTANCE. object ObjectKt { // ^^^^^^^^ definition scip-java maven . . snapshots/ObjectKt# // kind Class @@ -19,6 +20,11 @@ // display_name ObjectKt // signature_documentation // > private constructor(): ObjectKt +// ^^^^^^^^ definition scip-java maven . . snapshots/ObjectKt#INSTANCE. +// kind StaticField +// display_name INSTANCE +// signature_documentation +// > public final static val INSTANCE: ObjectKt // ⌄ enclosing_range_start scip-java maven . . snapshots/ObjectKt#fail(). // ⌄ enclosing_range_start scip-java maven . . snapshots/ObjectKt#fail().(message) fun fail(message: String?): Nothing { @@ -43,4 +49,5 @@ } //⌃ enclosing_range_end scip-java maven . . snapshots/ObjectKt# //⌃ enclosing_range_end scip-java maven . . snapshots/ObjectKt#``(). +//⌃ enclosing_range_end scip-java maven . . snapshots/ObjectKt#INSTANCE. From cc0c0e9c4947bbf940e9d1af74a843d94076a6ce Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Wed, 28 Oct 2026 10:37:19 +0100 Subject: [PATCH 30/33] prefer snapshot tests where applicable --- .../KotlinAnalysisIndexerTest.kt | 1989 ----------------- .../scip_java/kotlin_analysis/ScipBuilders.kt | 121 - .../main/kotlin/snapshots/CompanionOwner.kt | 14 + .../common/src/main/kotlin/snapshots/Enums.kt | 13 + .../src/main/kotlin/snapshots/Extensions.kt | 14 + .../src/main/kotlin/snapshots/Functions.kt | 16 + .../src/main/kotlin/snapshots/Generics.kt | 9 + .../main/kotlin/snapshots/Implementations.kt | 5 + .../src/main/kotlin/snapshots/Lambdas.kt | 4 + .../kotlin/snapshots/LocalDeclarations.kt | 27 + .../src/main/kotlin/snapshots/ObjectKt.kt | 6 + .../main/kotlin/snapshots/TypeOperators.kt | 16 + .../main/kotlin/snapshots/CompanionOwner.kt | 121 + .../common/src/main/kotlin/snapshots/Enums.kt | 218 ++ .../src/main/kotlin/snapshots/Extensions.kt | 105 + .../src/main/kotlin/snapshots/Functions.kt | 146 ++ .../src/main/kotlin/snapshots/Generics.kt | 111 + .../main/kotlin/snapshots/Implementations.kt | 54 + .../src/main/kotlin/snapshots/Lambdas.kt | 28 + .../kotlin/snapshots/LocalDeclarations.kt | 164 ++ .../src/main/kotlin/snapshots/ObjectKt.kt | 44 + .../main/kotlin/snapshots/TypeOperators.kt | 93 + 22 files changed, 1208 insertions(+), 2110 deletions(-) delete mode 100644 scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipBuilders.kt create mode 100644 scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Extensions.kt create mode 100644 scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/LocalDeclarations.kt create mode 100644 scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/TypeOperators.kt create mode 100644 scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Extensions.kt create mode 100644 scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/LocalDeclarations.kt create mode 100644 scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/TypeOperators.kt diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index dd9f11549..4ab519ec6 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -361,1995 +361,6 @@ class KotlinAnalysisIndexerTest { } } - @Test - fun `local classes`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun foo() { - class LocalClass { - fun localClassMethod() {} - } - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/foo()." - range { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 7 - } - enclosingRange { - startLine = 2 - endLine = 6 - endCharacter = 1 - } - }, - // LocalClass - scipOccurrence { - role = DEFINITION - symbol = "local 0" - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 18 - } - enclosingRange { - startLine = 3 - startCharacter = 2 - endLine = 5 - endCharacter = 3 - } - }, - // LocalClass constructor - scipOccurrence { - role = DEFINITION - symbol = "local 1" - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 18 - } - enclosingRange { - startLine = 3 - startCharacter = 2 - endLine = 5 - endCharacter = 3 - } - }, - // localClassMethod - scipOccurrence { - role = DEFINITION - symbol = "local 2" - range { - startLine = 4 - startCharacter = 8 - endLine = 4 - endCharacter = 24 - } - enclosingRange { - startLine = 4 - startCharacter = 4 - endLine = 4 - endCharacter = 29 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/foo()." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "foo" - signatureText = "public final fun foo(): Unit" - }, - scipSymbol { - symbol = "local 0" - kind = Kind.Class - enclosingSymbol = "sample/foo()." - displayName = "LocalClass" - signatureText = "local final class LocalClass : Any" - }, - scipSymbol { - symbol = "local 1" - kind = Kind.Constructor - enclosingSymbol = "local 0" - displayName = "LocalClass" - signatureText = "public constructor(): LocalClass" - }, - scipSymbol { - symbol = "local 2" - kind = Kind.Method - enclosingSymbol = "local 0" - displayName = "localClassMethod" - signatureText = "public final fun localClassMethod(): Unit" - }, - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `anonymous object`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - interface Interface { - fun foo() - } - - fun main() { - val a = object : Interface { - override fun foo() {} - } - val b = object : Interface { - override fun foo() {} - } - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = REFERENCE - symbol = "sample/" - range { - startLine = 0 - startCharacter = 8 - endLine = 0 - endCharacter = 14 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Interface#" - range { - startLine = 2 - startCharacter = 10 - endLine = 2 - endCharacter = 19 - } - enclosingRange { - startLine = 2 - endLine = 4 - endCharacter = 1 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Interface#foo()." - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 11 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 13 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "local 1" - range { - startLine = 7 - startCharacter = 12 - endLine = 7 - endCharacter = 18 - } - enclosingRange { - startLine = 7 - startCharacter = 12 - endLine = 9 - endCharacter = 5 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "local 2" - range { - startLine = 7 - startCharacter = 12 - endLine = 7 - endCharacter = 18 - } - enclosingRange { - startLine = 7 - startCharacter = 12 - endLine = 9 - endCharacter = 5 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Interface#" - range { - startLine = 7 - startCharacter = 21 - endLine = 7 - endCharacter = 30 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "local 3" - range { - startLine = 8 - startCharacter = 21 - endLine = 8 - endCharacter = 24 - } - enclosingRange { - startLine = 8 - startCharacter = 8 - endLine = 8 - endCharacter = 29 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "local 5" - range { - startLine = 10 - startCharacter = 12 - endLine = 10 - endCharacter = 18 - } - enclosingRange { - startLine = 10 - startCharacter = 12 - endLine = 12 - endCharacter = 5 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "local 6" - range { - startLine = 10 - startCharacter = 12 - endLine = 10 - endCharacter = 18 - } - enclosingRange { - startLine = 10 - startCharacter = 12 - endLine = 12 - endCharacter = 5 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Interface#" - range { - startLine = 10 - startCharacter = 21 - endLine = 10 - endCharacter = 30 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "local 7" - range { - startLine = 11 - startCharacter = 21 - endLine = 11 - endCharacter = 24 - } - enclosingRange { - startLine = 11 - startCharacter = 8 - endLine = 11 - endCharacter = 29 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Interface#" - kind = Kind.Interface - enclosingSymbol = "sample/" - displayName = "Interface" - signatureText = "public abstract interface Interface : Any" - }, - scipSymbol { - symbol = "local 1" - kind = Kind.Class - enclosingSymbol = "local 0" - displayName = "" - signatureText = "object : Interface" - addOverriddenSymbols("sample/Interface#") - }, - scipSymbol { - symbol = "local 3" - kind = Kind.Method - enclosingSymbol = "local 1" - displayName = "foo" - signatureText = "public open override fun foo(): Unit" - addOverriddenSymbols("sample/Interface#foo().") - }, - scipSymbol { - symbol = "local 5" - kind = Kind.Class - enclosingSymbol = "local 4" - displayName = "" - signatureText = "object : Interface" - addOverriddenSymbols("sample/Interface#") - }, - scipSymbol { - symbol = "local 7" - kind = Kind.Method - enclosingSymbol = "local 5" - displayName = "foo" - signatureText = "public open override fun foo(): Unit" - addOverriddenSymbols("sample/Interface#foo().") - }, - ) - assertContainsAll(document.symbolsList, *symbols) - // Object expressions have no static instance field. - assertTrue(document.symbolsList.none { it.displayName == "INSTANCE" }) - } - - @Test - fun `lambda parameters`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun use() { - val f = { n: Int -> n * 2 } - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "local 0" - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 9 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 31 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "local 1" - range { - startLine = 3 - startCharacter = 14 - endLine = 3 - endCharacter = 15 - } - enclosingRange { - startLine = 3 - startCharacter = 14 - endLine = 3 - endCharacter = 20 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Int#" - range { - startLine = 3 - startCharacter = 17 - endLine = 3 - endCharacter = 20 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "local 1" - range { - startLine = 3 - startCharacter = 24 - endLine = 3 - endCharacter = 25 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/use()." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "use" - signatureText = "public final fun use(): Unit" - }, - scipSymbol { - symbol = "local 0" - kind = Kind.Property - enclosingSymbol = "sample/use()." - displayName = "f" - signatureText = "local val f: (Int) -> Int" - }, - scipSymbol { - symbol = "local 1" - kind = Kind.Parameter - displayName = "n" - signatureText = "n: Int" - }, - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `local functions`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun outer() { - fun inner() {} - fun innerWithReturnType(): Int = 42 - inner() - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/outer()." - range { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 9 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 6 - endCharacter = 1 - } - }, - // inner() — local named function gets a local symbol - scipOccurrence { - role = DEFINITION - symbol = "local 0" - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 13 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 18 - } - }, - // innerWithReturnType() — local named function with explicit return type - scipOccurrence { - role = DEFINITION - symbol = "local 1" - range { - startLine = 4 - startCharacter = 8 - endLine = 4 - endCharacter = 27 - } - enclosingRange { - startLine = 4 - startCharacter = 4 - endLine = 4 - endCharacter = 39 - } - }, - // Int return-type reference - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Int#" - range { - startLine = 4 - startCharacter = 31 - endLine = 4 - endCharacter = 34 - } - }, - // call site inner() references the same local symbol - scipOccurrence { - role = REFERENCE - symbol = "local 0" - range { - startLine = 5 - startCharacter = 4 - endLine = 5 - endCharacter = 9 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "local 0" - kind = Kind.Method - enclosingSymbol = "sample/outer()." - displayName = "inner" - signatureText = "local final fun inner(): Unit" - }, - scipSymbol { - symbol = "local 1" - kind = Kind.Method - enclosingSymbol = "sample/outer()." - displayName = "innerWithReturnType" - signatureText = "local final fun innerWithReturnType(): Int" - }, - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `user-defined class as return type`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - class MyClass - - fun bar(): MyClass = MyClass() - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/bar()." - range { - startLine = 4 - startCharacter = 4 - endLine = 4 - endCharacter = 7 - } - enclosingRange { - startLine = 4 - startCharacter = 0 - endLine = 4 - endCharacter = 30 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/MyClass#" - range { - startLine = 4 - startCharacter = 11 - endLine = 4 - endCharacter = 18 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/bar()." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "bar" - signatureText = "public final fun bar(): MyClass" - } - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `typealias`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - typealias MyAlias = Int - val x: MyAlias = 42 - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/MyAlias#" - range { - startLine = 2 - startCharacter = 10 - endLine = 2 - endCharacter = 17 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 2 - endCharacter = 23 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/x." - range { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 5 - } - enclosingRange { - startLine = 3 - startCharacter = 0 - endLine = 3 - endCharacter = 19 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/MyAlias#" - kind = Kind.TypeAlias - enclosingSymbol = "sample/" - displayName = "MyAlias" - signatureText = "public final typealias MyAlias = Int\n" - } - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `type parameters`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun identity(x: T): T = x - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/identity()." - range { - startLine = 2 - startCharacter = 8 - endLine = 2 - endCharacter = 16 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 2 - endCharacter = 29 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/identity().[T]" - range { - startLine = 2 - startCharacter = 5 - endLine = 2 - endCharacter = 6 - } - enclosingRange { - startLine = 2 - startCharacter = 5 - endLine = 2 - endCharacter = 6 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/identity().[T]" - kind = Kind.TypeParameter - enclosingSymbol = "sample/identity()." - displayName = "T" - signatureText = "T" - } - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `generic class declaration`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - class Box(val value: T) { - fun unwrap(): T = value - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/Box#" - range { - startLine = 2 - startCharacter = 6 - endLine = 2 - endCharacter = 9 - } - enclosingRange { - startLine = 2 - endLine = 4 - endCharacter = 1 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Box#[T]" - range { - startLine = 2 - startCharacter = 10 - endLine = 2 - endCharacter = 11 - } - enclosingRange { - startLine = 2 - startCharacter = 10 - endLine = 2 - endCharacter = 11 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Box#unwrap()." - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 14 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 27 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - } - - @Test - fun `type operators`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun foo(x: Any) { - when (x) { - is Int -> true - else -> x as Float - } - } - - class Wrapper - fun classify(x: Any) { - if (x is Wrapper) {} - val s = x as? String - val w = x as Wrapper - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Int#" - range { - startLine = 4 - startCharacter = 11 - endLine = 4 - endCharacter = 14 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Float#" - range { - startLine = 5 - startCharacter = 21 - endLine = 5 - endCharacter = 26 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Wrapper#" - range { - startLine = 11 - startCharacter = 13 - endLine = 11 - endCharacter = 20 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/String#" - range { - startLine = 12 - startCharacter = 18 - endLine = 12 - endCharacter = 24 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Wrapper#" - range { - startLine = 13 - startCharacter = 17 - endLine = 13 - endCharacter = 24 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - } - - @Test - fun `extension receiver type reference`(@TempDir path: Path) { - // String is from the kotlin package; our extension in sample gets - // symbol sample/String#foo(). — distinct from kotlin/String#foo(). - // This means extensions on cross-package types never collide with - // the receiver class's own methods in the symbol table. - val document = - compileScip( - path, - """ - package sample - - fun String.foo(): Int = 42 - fun use(s: String) = s.foo() - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/String#foo()." - range { - startLine = 2 - startCharacter = 11 - endLine = 2 - endCharacter = 14 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 2 - endCharacter = 26 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/String#" - range { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 10 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Int#" - range { - startLine = 2 - startCharacter = 18 - endLine = 2 - endCharacter = 21 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/String#foo()." - range { - startLine = 3 - startCharacter = 23 - endLine = 3 - endCharacter = 26 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/String#foo()." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "foo" - signatureText = "public final fun String.foo(): Int" - } - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `extension property receiver type reference`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - val Int.asString: String get() = this.toString() - fun use() = 42.asString - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/Int#asString." - range { - startLine = 2 - startCharacter = 8 - endLine = 2 - endCharacter = 16 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 2 - endCharacter = 48 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/Int#" - range { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 7 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "kotlin/String#" - range { - startLine = 2 - startCharacter = 18 - endLine = 2 - endCharacter = 24 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Int#asString." - range { - startLine = 3 - startCharacter = 15 - endLine = 3 - endCharacter = 23 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Int#asString." - kind = Kind.Property - enclosingSymbol = "sample/" - displayName = "asString" - signatureText = "public final val Int.asString: String" - } - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `extension overload disambiguator`(@TempDir path: Path) { - // When a class already has a member named foo() and an extension also - // adds foo(), the extension is counted after the member in the combined - // sibling list (class members + same-package extensions on the same - // receiver type), so the extension gets the (+1) disambiguator and the - // two produce distinct symbols. - val document = - compileScip( - path, - """ - package sample - - class MyClass { - fun foo() {} - } - fun MyClass.foo(x: Int) {} - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/MyClass#foo()." - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 11 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 16 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/MyClass#foo(+1)." - range { - startLine = 5 - startCharacter = 12 - endLine = 5 - endCharacter = 15 - } - enclosingRange { - startLine = 5 - startCharacter = 0 - endLine = 5 - endCharacter = 26 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - } - - @Test - fun `enum entry definitions`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - enum class Color { RED, GREEN, BLUE } - - fun useEnum(): Color = Color.RED - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/Color#" - range { - startLine = 2 - startCharacter = 11 - endLine = 2 - endCharacter = 16 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 2 - endCharacter = 37 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Color#RED." - range { - startLine = 2 - startCharacter = 19 - endLine = 2 - endCharacter = 22 - } - enclosingRange { - startLine = 2 - startCharacter = 19 - endLine = 2 - endCharacter = 23 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Color#GREEN." - range { - startLine = 2 - startCharacter = 24 - endLine = 2 - endCharacter = 29 - } - enclosingRange { - startLine = 2 - startCharacter = 24 - endLine = 2 - endCharacter = 30 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Color#BLUE." - range { - startLine = 2 - startCharacter = 31 - endLine = 2 - endCharacter = 35 - } - enclosingRange { - startLine = 2 - startCharacter = 31 - endLine = 2 - endCharacter = 35 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Color#" - range { - startLine = 4 - startCharacter = 23 - endLine = 4 - endCharacter = 28 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Color#RED." - range { - startLine = 4 - startCharacter = 29 - endLine = 4 - endCharacter = 32 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Color#" - kind = Kind.Class - enclosingSymbol = "sample/" - displayName = "Color" - addOverriddenSymbols("kotlin/Enum#") - signatureText = "public final enum class Color : Enum" - }, - scipSymbol { - symbol = "sample/Color#RED." - kind = Kind.EnumMember - enclosingSymbol = "sample/Color#" - displayName = "RED" - signatureText = "public final static enum entry RED: Color" - }, - scipSymbol { - symbol = "sample/Color#GREEN." - kind = Kind.EnumMember - enclosingSymbol = "sample/Color#" - displayName = "GREEN" - signatureText = "public final static enum entry GREEN: Color" - }, - scipSymbol { - symbol = "sample/Color#BLUE." - kind = Kind.EnumMember - enclosingSymbol = "sample/Color#" - displayName = "BLUE" - signatureText = "public final static enum entry BLUE: Color" - }, - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `enum entry with body`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - enum class Op { - PLUS { - override fun apply(a: Int, b: Int) = a + b - }; - - abstract fun apply(a: Int, b: Int): Int - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/Op#PLUS." - range { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 8 - } - // Body-having enum entry: enclosing_range covers PLUS through the - // trailing `};` that terminates the entry list. - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 5 - endCharacter = 6 - } - }, - // An enum entry with a body is modeled as a synthetic anonymous subclass, - // so its overridden member is a local symbol (local2), not a global - // sample/Op#PLUS.apply(). It still gets an enclosing_range spanning the - // function declaration. - scipOccurrence { - role = DEFINITION - symbol = "local 2" - range { - startLine = 4 - startCharacter = 21 - endLine = 4 - endCharacter = 26 - } - enclosingRange { - startLine = 4 - startCharacter = 8 - endLine = 4 - endCharacter = 50 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - // The entry body becomes an anonymous class enclosed by the entry... - scipSymbol { - symbol = "local 0" - kind = Kind.Class - enclosingSymbol = "sample/Op#PLUS." - displayName = "" - addOverriddenSymbols("sample/Op#") - signatureText = "object : Op" - }, - // ...and the override is a method of that anonymous class. - scipSymbol { - symbol = "local 2" - kind = Kind.Method - enclosingSymbol = "local 0" - displayName = "apply" - addOverriddenSymbols("sample/Op#apply().") - signatureText = "public open override fun apply(a: Int, b: Int): Int" - }, - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `named object declarations`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - object MySingleton { - fun hello(): String = "hi" - } - fun use() = MySingleton.hello() - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/MySingleton#" - range { - startLine = 2 - startCharacter = 7 - endLine = 2 - endCharacter = 18 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 4 - endCharacter = 1 - } - }, - // Java reaches the object through its static INSTANCE field. - scipOccurrence { - role = DEFINITION - symbol = "sample/MySingleton#INSTANCE." - range { - startLine = 2 - startCharacter = 7 - endLine = 2 - endCharacter = 18 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 4 - endCharacter = 1 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/MySingleton#hello()." - range { - startLine = 3 - startCharacter = 8 - endLine = 3 - endCharacter = 13 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 30 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/MySingleton#" - range { - startLine = 5 - startCharacter = 12 - endLine = 5 - endCharacter = 23 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/MySingleton#hello()." - range { - startLine = 5 - startCharacter = 24 - endLine = 5 - endCharacter = 29 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/MySingleton#" - kind = Kind.Class - enclosingSymbol = "sample/" - displayName = "MySingleton" - signatureText = "public final object MySingleton : Any" - }, - scipSymbol { - symbol = "sample/MySingleton#INSTANCE." - kind = Kind.StaticField - enclosingSymbol = "sample/MySingleton#" - displayName = "INSTANCE" - signatureText = "public final static val INSTANCE: MySingleton" - }, - scipSymbol { - symbol = "sample/MySingleton#hello()." - kind = Kind.Method - enclosingSymbol = "sample/MySingleton#" - displayName = "hello" - signatureText = "public final fun hello(): String" - }, - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `companion object`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - class Foo { - companion object Factory { - fun create(): Foo = Foo() - } - } - fun use() = Foo.Factory.create() - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/Foo#" - range { - startLine = 2 - startCharacter = 6 - endLine = 2 - endCharacter = 9 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 6 - endCharacter = 1 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Foo#Factory#" - range { - startLine = 3 - startCharacter = 21 - endLine = 3 - endCharacter = 28 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 5 - endCharacter = 5 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Foo#Factory#create()." - range { - startLine = 4 - startCharacter = 12 - endLine = 4 - endCharacter = 18 - } - enclosingRange { - startLine = 4 - startCharacter = 8 - endLine = 4 - endCharacter = 33 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Foo#" - range { - startLine = 7 - startCharacter = 12 - endLine = 7 - endCharacter = 15 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Foo#Factory#" - range { - startLine = 7 - startCharacter = 16 - endLine = 7 - endCharacter = 23 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Foo#Factory#create()." - range { - startLine = 7 - startCharacter = 24 - endLine = 7 - endCharacter = 30 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Foo#" - kind = Kind.Class - enclosingSymbol = "sample/" - displayName = "Foo" - signatureText = "public final class Foo : Any" - }, - scipSymbol { - symbol = "sample/Foo#Factory#" - kind = Kind.Class - enclosingSymbol = "sample/Foo#" - displayName = "Factory" - signatureText = "public final companion object Factory : Any" - }, - // Java reaches a companion through a static field of its containing class. - scipSymbol { - symbol = "sample/Foo#Factory." - kind = Kind.StaticField - enclosingSymbol = "sample/Foo#" - displayName = "Factory" - signatureText = "public final static val Factory: Foo.Factory" - }, - scipSymbol { - symbol = "sample/Foo#Factory#create()." - kind = Kind.Method - enclosingSymbol = "sample/Foo#Factory#" - displayName = "create" - signatureText = "public final fun create(): Foo" - }, - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `unnamed companion object`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - class Bar { - companion object { - fun instance(): Bar = Bar() - } - } - fun use() = Bar.instance() - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/Bar#" - range { - startLine = 2 - startCharacter = 6 - endLine = 2 - endCharacter = 9 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 6 - endCharacter = 1 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Bar#Companion#instance()." - range { - startLine = 4 - startCharacter = 12 - endLine = 4 - endCharacter = 20 - } - enclosingRange { - startLine = 4 - startCharacter = 8 - endLine = 4 - endCharacter = 35 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Bar#" - range { - startLine = 7 - startCharacter = 12 - endLine = 7 - endCharacter = 15 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Bar#Companion#instance()." - range { - startLine = 7 - startCharacter = 16 - endLine = 7 - endCharacter = 24 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/Bar#Companion#" - range { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 13 - } - enclosingRange { - startLine = 3 - startCharacter = 4 - endLine = 5 - endCharacter = 5 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Bar#" - kind = Kind.Class - enclosingSymbol = "sample/" - displayName = "Bar" - signatureText = "public final class Bar : Any" - }, - scipSymbol { - symbol = "sample/Bar#Companion#" - kind = Kind.Class - enclosingSymbol = "sample/Bar#" - displayName = "Companion" - signatureText = "public final companion object Companion : Any" - }, - scipSymbol { - symbol = "sample/Bar#Companion." - kind = Kind.StaticField - enclosingSymbol = "sample/Bar#" - displayName = "Companion" - signatureText = "public final static val Companion: Bar.Companion" - }, - scipSymbol { - symbol = "sample/Bar#Companion#instance()." - kind = Kind.Method - enclosingSymbol = "sample/Bar#Companion#" - displayName = "instance" - signatureText = "public final fun instance(): Bar" - }, - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `string template references`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun greet(name: String) = "Hello, ${'$'}name!" - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/greet()." - range { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 9 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 2 - endCharacter = 41 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/greet().(name)" - range { - startLine = 2 - startCharacter = 10 - endLine = 2 - endCharacter = 14 - } - enclosingRange { - startLine = 2 - startCharacter = 10 - endLine = 2 - endCharacter = 22 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/greet().(name)" - range { - startLine = 2 - startCharacter = 35 - endLine = 2 - endCharacter = 39 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/greet()." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "greet" - signatureText = "public final fun greet(name: String): String" - } - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `multiple supertype references`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - interface Named - interface Speakable - class Person : Named, Speakable - fun use(p: Person): Named = p - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/Person#" - range { - startLine = 4 - startCharacter = 6 - endLine = 4 - endCharacter = 12 - } - enclosingRange { - startLine = 4 - startCharacter = 0 - endLine = 4 - endCharacter = 31 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Named#" - range { - startLine = 4 - startCharacter = 15 - endLine = 4 - endCharacter = 20 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Speakable#" - range { - startLine = 4 - startCharacter = 22 - endLine = 4 - endCharacter = 31 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/Named#" - range { - startLine = 5 - startCharacter = 20 - endLine = 5 - endCharacter = 25 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/Person#" - kind = Kind.Class - enclosingSymbol = "sample/" - displayName = "Person" - addOverriddenSymbols("sample/Named#") - addOverriddenSymbols("sample/Speakable#") - signatureText = "public final class Person : Named, Speakable" - } - ) - assertContainsAll(document.symbolsList, *symbols) - } - - @Test - fun `three-way overload disambiguator`(@TempDir path: Path) { - val document = - compileScip( - path, - """ - package sample - - fun add(x: Int, y: Int): Int = x + y - fun add(x: Double, y: Double): Double = x + y - fun add(x: String, y: String): String = x + y - fun use() { - add(1, 2) - add(1.0, 2.0) - add("a", "b") - } - """, - ) - - val occurrences = - arrayOf( - scipOccurrence { - role = DEFINITION - symbol = "sample/add()." - range { - startLine = 2 - startCharacter = 4 - endLine = 2 - endCharacter = 7 - } - enclosingRange { - startLine = 2 - startCharacter = 0 - endLine = 2 - endCharacter = 36 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/add(+1)." - range { - startLine = 3 - startCharacter = 4 - endLine = 3 - endCharacter = 7 - } - enclosingRange { - startLine = 3 - startCharacter = 0 - endLine = 3 - endCharacter = 45 - } - }, - scipOccurrence { - role = DEFINITION - symbol = "sample/add(+2)." - range { - startLine = 4 - startCharacter = 4 - endLine = 4 - endCharacter = 7 - } - enclosingRange { - startLine = 4 - startCharacter = 0 - endLine = 4 - endCharacter = 45 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/add()." - range { - startLine = 6 - startCharacter = 4 - endLine = 6 - endCharacter = 7 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/add(+1)." - range { - startLine = 7 - startCharacter = 4 - endLine = 7 - endCharacter = 7 - } - }, - scipOccurrence { - role = REFERENCE - symbol = "sample/add(+2)." - range { - startLine = 8 - startCharacter = 4 - endLine = 8 - endCharacter = 7 - } - }, - ) - assertContainsAll(document.occurrencesList, *occurrences) - - val symbols = - arrayOf( - scipSymbol { - symbol = "sample/add()." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "add" - signatureText = "public final fun add(x: Int, y: Int): Int" - }, - scipSymbol { - symbol = "sample/add(+1)." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "add" - signatureText = "public final fun add(x: Double, y: Double): Double" - }, - scipSymbol { - symbol = "sample/add(+2)." - kind = Kind.Method - enclosingSymbol = "sample/" - displayName = "add" - signatureText = "public final fun add(x: String, y: String): String" - }, - ) - assertContainsAll(document.symbolsList, *symbols) - } - private fun compileScip(path: Path, code: String): Document { val sourceroot = path.toRealPath() val source = Files.createDirectories(sourceroot.resolve("src")).resolve("Test.kt") diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipBuilders.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipBuilders.kt deleted file mode 100644 index 0bbf53f6a..000000000 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/ScipBuilders.kt +++ /dev/null @@ -1,121 +0,0 @@ -package org.scip_code.scip_java.kotlin_analysis - -import org.scip_code.scip.Occurrence -import org.scip_code.scip.SymbolInformation -import org.scip_code.scip.SymbolRole -import org.scip_code.scip.relationship -import org.scip_code.scip.signature -import org.scip_code.scip.symbolInformation -import org.scip_code.scip_java.shared.ScipRange - -/** - * Tiny DSL for building SCIP [Occurrence] / [SymbolInformation] test fixtures. - * - *

Example: - * ``` - * scipOccurrence { - * role = DEFINITION - * symbol = "sample/Banana#" - * range { startLine = 1; startCharacter = 6; endLine = 1; endCharacter = 12 } - * enclosingRange { startLine = 1; endLine = 3; endCharacter = 1 } - * } - * ``` - */ -internal val REFERENCE: Int = SymbolRole.UnspecifiedSymbolRole.number -internal val DEFINITION: Int = SymbolRole.Definition.number - -@DslMarker annotation class ScipBuilderDsl - -@ScipBuilderDsl -class ScipRangeBuilder { - var startLine: Int = 0 - var startCharacter: Int = 0 - /** - * Default sentinel: when [endLine] is left untouched, the produced range is single-line at - * [startLine]. - */ - var endLine: Int = -1 - var endCharacter: Int = 0 - - internal fun toScipRange(): ScipRange { - val line = if (endLine < 0) startLine else endLine - return ScipRange.range(startLine, startCharacter, line, endCharacter) - } -} - -@ScipBuilderDsl -class ScipOccurrenceBuilder { - var role: Int = REFERENCE - var symbol: String = "" - private var range: ScipRange? = null - private var enclosingRange: ScipRange? = null - - fun range(block: ScipRangeBuilder.() -> Unit) { - range = ScipRangeBuilder().apply(block).toScipRange() - } - - fun enclosingRange(block: ScipRangeBuilder.() -> Unit) { - enclosingRange = ScipRangeBuilder().apply(block).toScipRange() - } - - internal fun build(): Occurrence { - val builder = Occurrence.newBuilder().setSymbol(symbol).setSymbolRoles(role) - range?.let { - if (it.isSingleLine) builder.singleLineRange = it.toSingleLineRange() - else builder.multiLineRange = it.toMultiLineRange() - } - enclosingRange?.let { - if (it.isSingleLine) builder.singleLineEnclosingRange = it.toSingleLineRange() - else builder.multiLineEnclosingRange = it.toMultiLineRange() - } - return builder.build() - } -} - -@ScipBuilderDsl -class ScipSymbolInformationBuilder { - var symbol: String = "" - var kind: SymbolInformation.Kind = SymbolInformation.Kind.UnspecifiedKind - var enclosingSymbol: String = "" - var displayName: String = "" - var signatureText: String? = null - private val docs = mutableListOf() - private val overrides = mutableListOf() - - fun documentation(text: String) { - docs += text - } - - /** Appends an `is_implementation` [Relationship]. */ - fun addOverriddenSymbols(vararg symbols: String) { - overrides.addAll(symbols) - } - - internal fun build(): SymbolInformation = symbolInformation { - symbol = this@ScipSymbolInformationBuilder.symbol - kind = this@ScipSymbolInformationBuilder.kind - enclosingSymbol = this@ScipSymbolInformationBuilder.enclosingSymbol - if (this@ScipSymbolInformationBuilder.displayName.isNotEmpty()) { - displayName = this@ScipSymbolInformationBuilder.displayName - } - signatureText?.let { sigText -> - signatureDocumentation = signature { - language = "kotlin" - text = sigText - } - } - for (d in docs) documentation += d - for (s in overrides) { - relationships += relationship { - symbol = s - isImplementation = true - } - } - } -} - -internal fun scipOccurrence(block: ScipOccurrenceBuilder.() -> Unit): Occurrence = - ScipOccurrenceBuilder().apply(block).build() - -internal fun scipSymbol(block: ScipSymbolInformationBuilder.() -> Unit): SymbolInformation = - ScipSymbolInformationBuilder().apply(block).build() diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt index 56cfa7270..99eea4429 100644 --- a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt @@ -6,3 +6,17 @@ class CompanionOwner { } fun create(): Int = CompanionOwner.create().hashCode() } + +class Foo { + companion object Factory { + fun create(): Foo = Foo() + } +} +fun useNamedCompanion() = Foo.Factory.create() + +class Bar { + companion object { + fun instance(): Bar = Bar() + } +} +fun useUnnamedCompanion() = Bar.instance() diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt index 32366c6d5..fcb7189c5 100644 --- a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt @@ -16,3 +16,16 @@ fun describe(suit: Suit): String = Suit.HEARTS -> "red" Suit.SPADES -> "black" } + +enum class Color { RED, GREEN, BLUE } + +fun useEnum(): Color = Color.RED + +enum class Op { + // The entry body is an anonymous subclass; its override has a local symbol. + PLUS { + override fun apply(a: Int, b: Int) = a + b + }; + + abstract fun apply(a: Int, b: Int): Int +} diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Extensions.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Extensions.kt new file mode 100644 index 000000000..2664655b3 --- /dev/null +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Extensions.kt @@ -0,0 +1,14 @@ +package snapshots + +// Cross-package extensions belong to snapshots, not to the receiver's kotlin package. +fun String.foo(): Int = 42 +fun useExtension(s: String) = s.foo() + +val Int.asString: String get() = this.toString() +fun useExtensionProperty() = 42.asString + +class ExtensionReceiver { + fun foo() {} +} +// The extension is numbered after the receiver's member, so it gets foo(+1). +fun ExtensionReceiver.foo(x: Int) {} diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt index ec545c48f..bc1cf9969 100644 --- a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt @@ -3,3 +3,19 @@ package snapshots fun sampleText(x: String = "") { println(x) } + +class MyClass + +fun bar(): MyClass = MyClass() + +fun greet(name: String) = "Hello, $name!" + +fun add(x: Int, y: Int): Int = x + y +fun add(x: Double, y: Double): Double = x + y +fun add(x: String, y: String): String = x + y + +fun useOverloads() { + add(1, 2) + add(1.0, 2.0) + add("a", "b") +} diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt index d917e3c04..e0789c990 100644 --- a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt @@ -14,3 +14,12 @@ fun firstOrSelf(items: List, fallback: T): T = items.firstOrNull() ?: fal typealias StringContainer = Container fun useContainer(container: StringContainer): StringContainer = container.add("hello") + +typealias MyAlias = Int +val aliasValue: MyAlias = 42 + +fun identity(x: T): T = x + +class Box(val value: T) { + fun unwrap(): T = value +} diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt index 11ddd55d1..cb59b8833 100644 --- a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt @@ -25,3 +25,8 @@ class Seagull : Bird() { return "squawk" } } + +interface Named +interface Speakable +class Person : Named, Speakable +fun usePerson(p: Person): Named = p diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt index 445eeb10f..fcbd45caa 100644 --- a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt @@ -5,3 +5,7 @@ val x = arrayListOf().forEachIndexed { i, s -> println("$i $s") } val y = "fdsa".run { this.toByteArray() } val z = y.let { it.size } + +fun useLambda() { + val f = { n: Int -> n * 2 } +} diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/LocalDeclarations.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/LocalDeclarations.kt new file mode 100644 index 000000000..9189d63a1 --- /dev/null +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/LocalDeclarations.kt @@ -0,0 +1,27 @@ +package snapshots + +fun localClasses() { + class LocalClass { + fun localClassMethod() {} + } +} + +interface Interface { + fun foo() +} + +fun anonymousObjects() { + // Each object and its members need distinct local symbols, with no INSTANCE field. + val a = object : Interface { + override fun foo() {} + } + val b = object : Interface { + override fun foo() {} + } +} + +fun outer() { + fun inner() {} + fun innerWithReturnType(): Int = 42 + inner() +} diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt index 5602cacc7..806c547ec 100644 --- a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt @@ -7,3 +7,9 @@ object ObjectKt { throw RuntimeException(message) } } + +// Named objects define an INSTANCE field for Java callers; object expressions do not. +object MySingleton { + fun hello(): String = "hi" +} +fun useSingleton() = MySingleton.hello() diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/TypeOperators.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/TypeOperators.kt new file mode 100644 index 000000000..f49e34429 --- /dev/null +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/TypeOperators.kt @@ -0,0 +1,16 @@ +package snapshots + +fun typeOperators(x: Any) { + when (x) { + is Int -> true + else -> x as Float + } +} + +class Wrapper + +fun classify(x: Any) { + if (x is Wrapper) {} + val s = x as? String + val w = x as Wrapper +} diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt index c5b01af1d..544f5b34d 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/CompanionOwner.kt @@ -63,3 +63,124 @@ //⌃ enclosing_range_end scip-java maven . . snapshots/CompanionOwner# //⌃ enclosing_range_end scip-java maven . . snapshots/CompanionOwner#``(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Foo# +//⌄ enclosing_range_start scip-java maven . . snapshots/Foo#``(). + class Foo { +// ^^^ definition scip-java maven . . snapshots/Foo# +// kind Class +// display_name Foo +// signature_documentation +// > public final class Foo : Any +// ^^^ definition scip-java maven . . snapshots/Foo#``(). +// kind Constructor +// display_name Foo +// signature_documentation +// > public constructor(): Foo +// ⌄ enclosing_range_start scip-java maven . . snapshots/Foo#Factory# +// ⌄ enclosing_range_start scip-java maven . . snapshots/Foo#Factory#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Foo#Factory. + companion object Factory { +// ^^^^^^^ definition scip-java maven . . snapshots/Foo#Factory# +// kind Class +// display_name Factory +// signature_documentation +// > public final companion object Factory : Any +// ^^^^^^^ definition scip-java maven . . snapshots/Foo#Factory#``(). +// kind Constructor +// display_name Factory +// signature_documentation +// > private constructor(): Foo.Factory +// ^^^^^^^ definition scip-java maven . . snapshots/Foo#Factory. +// kind StaticField +// display_name Factory +// signature_documentation +// > public final static val Factory: Foo.Factory +// ⌄ enclosing_range_start scip-java maven . . snapshots/Foo#Factory#create(). + fun create(): Foo = Foo() +// ^^^^^^ definition scip-java maven . . snapshots/Foo#Factory#create(). +// kind Method +// display_name create +// signature_documentation +// > public final fun create(): Foo +// ^^^ reference scip-java maven . . snapshots/Foo# +// ^^^ reference scip-java maven . . snapshots/Foo#``(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Foo#Factory#create(). + } +// ⌃ enclosing_range_end scip-java maven . . snapshots/Foo#Factory# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Foo#Factory#``(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Foo#Factory. + } +//⌃ enclosing_range_end scip-java maven . . snapshots/Foo# +//⌃ enclosing_range_end scip-java maven . . snapshots/Foo#``(). +//⌄ enclosing_range_start scip-java maven . . snapshots/useNamedCompanion(). + fun useNamedCompanion() = Foo.Factory.create() +// ^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/useNamedCompanion(). +// kind Method +// display_name useNamedCompanion +// signature_documentation +// > public final fun useNamedCompanion(): Foo +// ^^^ reference scip-java maven . . snapshots/Foo# +// ^^^^^^^ reference scip-java maven . . snapshots/Foo#Factory# +// ^^^^^^ reference scip-java maven . . snapshots/Foo#Factory#create(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/useNamedCompanion(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/Bar# +//⌄ enclosing_range_start scip-java maven . . snapshots/Bar#``(). + class Bar { +// ^^^ definition scip-java maven . . snapshots/Bar# +// kind Class +// display_name Bar +// signature_documentation +// > public final class Bar : Any +// ^^^ definition scip-java maven . . snapshots/Bar#``(). +// kind Constructor +// display_name Bar +// signature_documentation +// > public constructor(): Bar +// ⌄ enclosing_range_start scip-java maven . . snapshots/Bar#Companion# +// ⌄ enclosing_range_start scip-java maven . . snapshots/Bar#Companion#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Bar#Companion. + companion object { +// ^^^^^^^^^ definition scip-java maven . . snapshots/Bar#Companion# +// kind Class +// display_name Companion +// signature_documentation +// > public final companion object Companion : Any +// ^^^^^^^^^ definition scip-java maven . . snapshots/Bar#Companion#``(). +// kind Constructor +// display_name Companion +// signature_documentation +// > private constructor(): Bar.Companion +// ^^^^^^^^^ definition scip-java maven . . snapshots/Bar#Companion. +// kind StaticField +// display_name Companion +// signature_documentation +// > public final static val Companion: Bar.Companion +// ⌄ enclosing_range_start scip-java maven . . snapshots/Bar#Companion#instance(). + fun instance(): Bar = Bar() +// ^^^^^^^^ definition scip-java maven . . snapshots/Bar#Companion#instance(). +// kind Method +// display_name instance +// signature_documentation +// > public final fun instance(): Bar +// ^^^ reference scip-java maven . . snapshots/Bar# +// ^^^ reference scip-java maven . . snapshots/Bar#``(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Bar#Companion#instance(). + } +// ⌃ enclosing_range_end scip-java maven . . snapshots/Bar#Companion# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Bar#Companion#``(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Bar#Companion. + } +//⌃ enclosing_range_end scip-java maven . . snapshots/Bar# +//⌃ enclosing_range_end scip-java maven . . snapshots/Bar#``(). +//⌄ enclosing_range_start scip-java maven . . snapshots/useUnnamedCompanion(). + fun useUnnamedCompanion() = Bar.instance() +// ^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/useUnnamedCompanion(). +// kind Method +// display_name useUnnamedCompanion +// signature_documentation +// > public final fun useUnnamedCompanion(): Bar +// ^^^ reference scip-java maven . . snapshots/Bar# +// ^^^^^^^^ reference scip-java maven . . snapshots/Bar#Companion#instance(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/useUnnamedCompanion(). + diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt index 944eeb179..afff975fd 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt @@ -196,3 +196,221 @@ } // ⌃ enclosing_range_end scip-java maven . . snapshots/describe(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Color# +//⌄ enclosing_range_start scip-java maven . . snapshots/Color#``(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Color#values(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Color#valueOf(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Color#valueOf().(value) +//⌄ enclosing_range_start scip-java maven . . snapshots/Color#entries. +//⌄ enclosing_range_start scip-java maven . . snapshots/Color#entries.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Color#RED. +// ⌄ enclosing_range_start scip-java maven . . snapshots/Color#GREEN. +// ⌄ enclosing_range_start scip-java maven . . snapshots/Color#BLUE. + enum class Color { RED, GREEN, BLUE } +// ^^^^^ definition scip-java maven . . snapshots/Color# +// kind Class +// display_name Color +// signature_documentation +// > public final enum class Color : Enum +// relationship scip-java maven . . kotlin/Enum# implementation +// ^^^^^ definition scip-java maven . . snapshots/Color#``(). +// kind Constructor +// display_name Color +// signature_documentation +// > private constructor(): Color +// ^^^^^ definition scip-java maven . . snapshots/Color#values(). +// kind Method +// display_name values +// signature_documentation +// > public final static fun values(): Array +// ^^^^^ definition scip-java maven . . snapshots/Color#valueOf(). +// kind Method +// display_name valueOf +// signature_documentation +// > public final static fun valueOf(value: String): Color +// ^^^^^ definition scip-java maven . . snapshots/Color#valueOf().(value) +// kind Parameter +// display_name value +// signature_documentation +// > value: String +// ^^^^^ definition scip-java maven . . snapshots/Color#entries. +// kind Property +// display_name entries +// signature_documentation +// > public final static val entries: EnumEntries +// ^^^^^ definition scip-java maven . . snapshots/Color#entries.get(). +// kind Method +// display_name entries +// signature_documentation +// > public static get(): EnumEntries +// ^^^ definition scip-java maven . . snapshots/Color#RED. +// kind EnumMember +// display_name RED +// signature_documentation +// > public final static enum entry RED: Color +// ^^^^^ definition scip-java maven . . snapshots/Color#GREEN. +// kind EnumMember +// display_name GREEN +// signature_documentation +// > public final static enum entry GREEN: Color +// ^^^^ definition scip-java maven . . snapshots/Color#BLUE. +// kind EnumMember +// display_name BLUE +// signature_documentation +// > public final static enum entry BLUE: Color +// ⌃ enclosing_range_end scip-java maven . . snapshots/Color#RED. +// ⌃ enclosing_range_end scip-java maven . . snapshots/Color#GREEN. +// ⌃ enclosing_range_end scip-java maven . . snapshots/Color#BLUE. +// ⌃ enclosing_range_end scip-java maven . . snapshots/Color# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Color#``(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Color#values(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Color#valueOf(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Color#valueOf().(value) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Color#entries. +// ⌃ enclosing_range_end scip-java maven . . snapshots/Color#entries.get(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/useEnum(). + fun useEnum(): Color = Color.RED +// ^^^^^^^ definition scip-java maven . . snapshots/useEnum(). +// kind Method +// display_name useEnum +// signature_documentation +// > public final fun useEnum(): Color +// ^^^^^ reference scip-java maven . . snapshots/Color# +// ^^^^^ reference scip-java maven . . snapshots/Color# +// ^^^ reference scip-java maven . . snapshots/Color#RED. +// ⌃ enclosing_range_end scip-java maven . . snapshots/useEnum(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/Op# +//⌄ enclosing_range_start scip-java maven . . snapshots/Op#``(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Op#values(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Op#valueOf(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Op#valueOf().(value) +//⌄ enclosing_range_start scip-java maven . . snapshots/Op#entries. +//⌄ enclosing_range_start scip-java maven . . snapshots/Op#entries.get(). + enum class Op { +// ^^ definition scip-java maven . . snapshots/Op# +// kind Class +// display_name Op +// signature_documentation +// > public final enum class Op : Enum +// relationship scip-java maven . . kotlin/Enum# implementation +// ^^ definition scip-java maven . . snapshots/Op#``(). +// kind Constructor +// display_name Op +// signature_documentation +// > private constructor(): Op +// ^^ definition scip-java maven . . snapshots/Op#values(). +// kind Method +// display_name values +// signature_documentation +// > public final static fun values(): Array +// ^^ definition scip-java maven . . snapshots/Op#valueOf(). +// kind Method +// display_name valueOf +// signature_documentation +// > public final static fun valueOf(value: String): Op +// ^^ definition scip-java maven . . snapshots/Op#valueOf().(value) +// kind Parameter +// display_name value +// signature_documentation +// > value: String +// ^^ definition scip-java maven . . snapshots/Op#entries. +// kind Property +// display_name entries +// signature_documentation +// > public final static val entries: EnumEntries +// ^^ definition scip-java maven . . snapshots/Op#entries.get(). +// kind Method +// display_name entries +// signature_documentation +// > public static get(): EnumEntries +// ⌄ enclosing_range_start scip-java maven . . snapshots/Op#PLUS. +// ⌄ enclosing_range_start local 2 +// ⌄ enclosing_range_start local 3 + // The entry body is an anonymous subclass; its override has a local symbol. + PLUS { +// ^^^^ definition scip-java maven . . snapshots/Op#PLUS. +// kind EnumMember +// display_name PLUS +// signature_documentation +// > public final static enum entry PLUS: Op +// ^^^^ definition local 2 +// kind Class +// display_name +// signature_documentation +// > object : Op +// relationship scip-java maven . . snapshots/Op# implementation +// ^^^^ definition local 3 +// kind Constructor +// display_name PLUS +// signature_documentation +// > private constructor(): +// ⌄ enclosing_range_start local 4 +// ⌄ enclosing_range_start local 5 +// ⌄ enclosing_range_start local 6 + override fun apply(a: Int, b: Int) = a + b +// ^^^^^ definition local 4 +// kind Method +// display_name apply +// signature_documentation +// > public open override fun apply(a: Int, b: Int): Int +// relationship scip-java maven . . snapshots/Op#apply(). implementation +// ^ definition local 5 +// kind Parameter +// display_name a +// signature_documentation +// > a: Int +// ^^^ reference scip-java maven . . kotlin/Int# +// ^ definition local 6 +// kind Parameter +// display_name b +// signature_documentation +// > b: Int +// ^^^ reference scip-java maven . . kotlin/Int# +// ^ reference local 5 +// ^ reference scip-java maven . . kotlin/Int#plus(+2). +// ^ reference local 6 +// ⌃ enclosing_range_end local 5 +// ⌃ enclosing_range_end local 6 +// ⌃ enclosing_range_end local 4 + }; +// ⌃ enclosing_range_end scip-java maven . . snapshots/Op#PLUS. +// ⌃ enclosing_range_end local 2 +// ⌃ enclosing_range_end local 3 + +// ⌄ enclosing_range_start scip-java maven . . snapshots/Op#apply(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Op#apply().(a) +// ⌄ enclosing_range_start scip-java maven . . snapshots/Op#apply().(b) + abstract fun apply(a: Int, b: Int): Int +// ^^^^^ definition scip-java maven . . snapshots/Op#apply(). +// kind Method +// display_name apply +// signature_documentation +// > public abstract fun apply(a: Int, b: Int): Int +// > +// ^ definition scip-java maven . . snapshots/Op#apply().(a) +// kind Parameter +// display_name a +// signature_documentation +// > a: Int +// ^^^ reference scip-java maven . . kotlin/Int# +// ^ definition scip-java maven . . snapshots/Op#apply().(b) +// kind Parameter +// display_name b +// signature_documentation +// > b: Int +// ^^^ reference scip-java maven . . kotlin/Int# +// ^^^ reference scip-java maven . . kotlin/Int# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Op#apply().(a) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Op#apply().(b) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Op#apply(). + } +//⌃ enclosing_range_end scip-java maven . . snapshots/Op# +//⌃ enclosing_range_end scip-java maven . . snapshots/Op#``(). +//⌃ enclosing_range_end scip-java maven . . snapshots/Op#values(). +//⌃ enclosing_range_end scip-java maven . . snapshots/Op#valueOf(). +//⌃ enclosing_range_end scip-java maven . . snapshots/Op#valueOf().(value) +//⌃ enclosing_range_end scip-java maven . . snapshots/Op#entries. +//⌃ enclosing_range_end scip-java maven . . snapshots/Op#entries.get(). + diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Extensions.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Extensions.kt new file mode 100644 index 000000000..0c5bca5c9 --- /dev/null +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Extensions.kt @@ -0,0 +1,105 @@ + package snapshots +// ^^^^^^^^^ reference scip-java maven . . snapshots/ + +//⌄ enclosing_range_start scip-java maven . . snapshots/String#foo(). + // Cross-package extensions belong to snapshots, not to the receiver's kotlin package. + fun String.foo(): Int = 42 +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ^^^ definition scip-java maven . . snapshots/String#foo(). +// kind Method +// display_name foo +// signature_documentation +// > public final fun String.foo(): Int +// ^^^ reference scip-java maven . . kotlin/Int# +// ⌃ enclosing_range_end scip-java maven . . snapshots/String#foo(). +//⌄ enclosing_range_start scip-java maven . . snapshots/useExtension(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/useExtension().(s) + fun useExtension(s: String) = s.foo() +// ^^^^^^^^^^^^ definition scip-java maven . . snapshots/useExtension(). +// kind Method +// display_name useExtension +// signature_documentation +// > public final fun useExtension(s: String): Int +// ^ definition scip-java maven . . snapshots/useExtension().(s) +// kind Parameter +// display_name s +// signature_documentation +// > s: String +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ^ reference scip-java maven . . snapshots/useExtension().(s) +// ^^^ reference scip-java maven . . snapshots/String#foo(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/useExtension().(s) +// ⌃ enclosing_range_end scip-java maven . . snapshots/useExtension(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/Int#asString. +// ⌄ enclosing_range_start scip-java maven . . snapshots/Int#asString.get(). + val Int.asString: String get() = this.toString() +// ^^^ reference scip-java maven . . kotlin/Int# +// ^^^^^^^^ definition scip-java maven . . snapshots/Int#asString. +// kind Property +// display_name asString +// signature_documentation +// > public final val Int.asString: String +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ^^^ definition scip-java maven . . snapshots/Int#asString.get(). +// kind Method +// display_name asString +// signature_documentation +// > public get(): String +// ^^^^^^^^ reference scip-java maven . . kotlin/Int#toString(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Int#asString. +// ⌃ enclosing_range_end scip-java maven . . snapshots/Int#asString.get(). +//⌄ enclosing_range_start scip-java maven . . snapshots/useExtensionProperty(). + fun useExtensionProperty() = 42.asString +// ^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/useExtensionProperty(). +// kind Method +// display_name useExtensionProperty +// signature_documentation +// > public final fun useExtensionProperty(): String +// ^^^^^^^^ reference scip-java maven . . snapshots/Int#asString. +// ^^^^^^^^ reference scip-java maven . . snapshots/Int#asString.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/useExtensionProperty(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/ExtensionReceiver# +//⌄ enclosing_range_start scip-java maven . . snapshots/ExtensionReceiver#``(). + class ExtensionReceiver { +// ^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/ExtensionReceiver# +// kind Class +// display_name ExtensionReceiver +// signature_documentation +// > public final class ExtensionReceiver : Any +// ^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/ExtensionReceiver#``(). +// kind Constructor +// display_name ExtensionReceiver +// signature_documentation +// > public constructor(): ExtensionReceiver +// ⌄ enclosing_range_start scip-java maven . . snapshots/ExtensionReceiver#foo(). + fun foo() {} +// ^^^ definition scip-java maven . . snapshots/ExtensionReceiver#foo(). +// kind Method +// display_name foo +// signature_documentation +// > public final fun foo(): Unit +// ⌃ enclosing_range_end scip-java maven . . snapshots/ExtensionReceiver#foo(). + } +//⌃ enclosing_range_end scip-java maven . . snapshots/ExtensionReceiver# +//⌃ enclosing_range_end scip-java maven . . snapshots/ExtensionReceiver#``(). +//⌄ enclosing_range_start scip-java maven . . snapshots/ExtensionReceiver#foo(+1). + // The extension is numbered after the receiver's member, so it gets foo(+1). +// ⌄ enclosing_range_start scip-java maven . . snapshots/ExtensionReceiver#foo(+1).(x) + fun ExtensionReceiver.foo(x: Int) {} +// ^^^^^^^^^^^^^^^^^ reference scip-java maven . . snapshots/ExtensionReceiver# +// ^^^ definition scip-java maven . . snapshots/ExtensionReceiver#foo(+1). +// kind Method +// display_name foo +// signature_documentation +// > public final fun ExtensionReceiver.foo(x: Int): Unit +// ^ definition scip-java maven . . snapshots/ExtensionReceiver#foo(+1).(x) +// kind Parameter +// display_name x +// signature_documentation +// > x: Int +// ^^^ reference scip-java maven . . kotlin/Int# +// ⌃ enclosing_range_end scip-java maven . . snapshots/ExtensionReceiver#foo(+1).(x) +// ⌃ enclosing_range_end scip-java maven . . snapshots/ExtensionReceiver#foo(+1). + diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt index 0ee9ed764..cf5020994 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt @@ -22,3 +22,149 @@ } //⌃ enclosing_range_end scip-java maven . . snapshots/sampleText(). +//⌄ enclosing_range_start scip-java maven . . snapshots/MyClass# +//⌄ enclosing_range_start scip-java maven . . snapshots/MyClass#``(). + class MyClass +// ^^^^^^^ definition scip-java maven . . snapshots/MyClass# +// kind Class +// display_name MyClass +// signature_documentation +// > public final class MyClass : Any +// ^^^^^^^ definition scip-java maven . . snapshots/MyClass#``(). +// kind Constructor +// display_name MyClass +// signature_documentation +// > public constructor(): MyClass +// ⌃ enclosing_range_end scip-java maven . . snapshots/MyClass# +// ⌃ enclosing_range_end scip-java maven . . snapshots/MyClass#``(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/bar(). + fun bar(): MyClass = MyClass() +// ^^^ definition scip-java maven . . snapshots/bar(). +// kind Method +// display_name bar +// signature_documentation +// > public final fun bar(): MyClass +// ^^^^^^^ reference scip-java maven . . snapshots/MyClass# +// ^^^^^^^ reference scip-java maven . . snapshots/MyClass#``(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/bar(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/greet(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/greet().(name) + fun greet(name: String) = "Hello, $name!" +// ^^^^^ definition scip-java maven . . snapshots/greet(). +// kind Method +// display_name greet +// signature_documentation +// > public final fun greet(name: String): String +// ^^^^ definition scip-java maven . . snapshots/greet().(name) +// kind Parameter +// display_name name +// signature_documentation +// > name: String +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ^^^^ reference scip-java maven . . snapshots/greet().(name) +// ⌃ enclosing_range_end scip-java maven . . snapshots/greet().(name) +// ⌃ enclosing_range_end scip-java maven . . snapshots/greet(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/add(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/add().(x) +// ⌄ enclosing_range_start scip-java maven . . snapshots/add().(y) + fun add(x: Int, y: Int): Int = x + y +// ^^^ definition scip-java maven . . snapshots/add(). +// kind Method +// display_name add +// signature_documentation +// > public final fun add(x: Int, y: Int): Int +// ^ definition scip-java maven . . snapshots/add().(x) +// kind Parameter +// display_name x +// signature_documentation +// > x: Int +// ^^^ reference scip-java maven . . kotlin/Int# +// ^ definition scip-java maven . . snapshots/add().(y) +// kind Parameter +// display_name y +// signature_documentation +// > y: Int +// ^^^ reference scip-java maven . . kotlin/Int# +// ^^^ reference scip-java maven . . kotlin/Int# +// ^ reference scip-java maven . . snapshots/add().(x) +// ^ reference scip-java maven . . kotlin/Int#plus(+2). +// ^ reference scip-java maven . . snapshots/add().(y) +// ⌃ enclosing_range_end scip-java maven . . snapshots/add().(x) +// ⌃ enclosing_range_end scip-java maven . . snapshots/add().(y) +// ⌃ enclosing_range_end scip-java maven . . snapshots/add(). +//⌄ enclosing_range_start scip-java maven . . snapshots/add(+1). +// ⌄ enclosing_range_start scip-java maven . . snapshots/add(+1).(x) +// ⌄ enclosing_range_start scip-java maven . . snapshots/add(+1).(y) + fun add(x: Double, y: Double): Double = x + y +// ^^^ definition scip-java maven . . snapshots/add(+1). +// kind Method +// display_name add +// signature_documentation +// > public final fun add(x: Double, y: Double): Double +// ^ definition scip-java maven . . snapshots/add(+1).(x) +// kind Parameter +// display_name x +// signature_documentation +// > x: Double +// ^^^^^^ reference scip-java maven . . kotlin/Double# +// ^ definition scip-java maven . . snapshots/add(+1).(y) +// kind Parameter +// display_name y +// signature_documentation +// > y: Double +// ^^^^^^ reference scip-java maven . . kotlin/Double# +// ^^^^^^ reference scip-java maven . . kotlin/Double# +// ^ reference scip-java maven . . snapshots/add(+1).(x) +// ^ reference scip-java maven . . kotlin/Double#plus(+5). +// ^ reference scip-java maven . . snapshots/add(+1).(y) +// ⌃ enclosing_range_end scip-java maven . . snapshots/add(+1).(x) +// ⌃ enclosing_range_end scip-java maven . . snapshots/add(+1).(y) +// ⌃ enclosing_range_end scip-java maven . . snapshots/add(+1). +//⌄ enclosing_range_start scip-java maven . . snapshots/add(+2). +// ⌄ enclosing_range_start scip-java maven . . snapshots/add(+2).(x) +// ⌄ enclosing_range_start scip-java maven . . snapshots/add(+2).(y) + fun add(x: String, y: String): String = x + y +// ^^^ definition scip-java maven . . snapshots/add(+2). +// kind Method +// display_name add +// signature_documentation +// > public final fun add(x: String, y: String): String +// ^ definition scip-java maven . . snapshots/add(+2).(x) +// kind Parameter +// display_name x +// signature_documentation +// > x: String +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ^ definition scip-java maven . . snapshots/add(+2).(y) +// kind Parameter +// display_name y +// signature_documentation +// > y: String +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ^ reference scip-java maven . . snapshots/add(+2).(x) +// ^ reference scip-java maven . . kotlin/String#plus(). +// ^ reference scip-java maven . . snapshots/add(+2).(y) +// ⌃ enclosing_range_end scip-java maven . . snapshots/add(+2).(x) +// ⌃ enclosing_range_end scip-java maven . . snapshots/add(+2).(y) +// ⌃ enclosing_range_end scip-java maven . . snapshots/add(+2). + +//⌄ enclosing_range_start scip-java maven . . snapshots/useOverloads(). + fun useOverloads() { +// ^^^^^^^^^^^^ definition scip-java maven . . snapshots/useOverloads(). +// kind Method +// display_name useOverloads +// signature_documentation +// > public final fun useOverloads(): Unit + add(1, 2) +// ^^^ reference scip-java maven . . snapshots/add(). + add(1.0, 2.0) +// ^^^ reference scip-java maven . . snapshots/add(+1). + add("a", "b") +// ^^^ reference scip-java maven . . snapshots/add(+2). + } +//⌃ enclosing_range_end scip-java maven . . snapshots/useOverloads(). + diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt index 52a4180f3..40bc9c9ed 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt @@ -177,3 +177,114 @@ // ⌃ enclosing_range_end scip-java maven . . snapshots/useContainer().(container) // ⌃ enclosing_range_end scip-java maven . . snapshots/useContainer(). +//⌄ enclosing_range_start scip-java maven . . snapshots/MyAlias# + typealias MyAlias = Int +// ^^^^^^^ definition scip-java maven . . snapshots/MyAlias# +// kind TypeAlias +// display_name MyAlias +// signature_documentation +// > public final typealias MyAlias = Int +// > +// ^^^ reference scip-java maven . . kotlin/Int# +// ⌃ enclosing_range_end scip-java maven . . snapshots/MyAlias# +//⌄ enclosing_range_start scip-java maven . . snapshots/aliasValue. +//⌄ enclosing_range_start scip-java maven . . snapshots/aliasValue.get(). + val aliasValue: MyAlias = 42 +// ^^^^^^^^^^ definition scip-java maven . . snapshots/aliasValue. +// kind Property +// display_name aliasValue +// signature_documentation +// > public final val aliasValue: MyAlias +// ^^^^^^^^^^ definition scip-java maven . . snapshots/aliasValue.get(). +// kind Method +// display_name aliasValue +// signature_documentation +// > public get(): MyAlias +// ^^^^^^^ reference scip-java maven . . kotlin/Int# +// ⌃ enclosing_range_end scip-java maven . . snapshots/aliasValue. +// ⌃ enclosing_range_end scip-java maven . . snapshots/aliasValue.get(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/identity(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/identity().[T] +// ⌄ enclosing_range_start scip-java maven . . snapshots/identity().(x) + fun identity(x: T): T = x +// ^ definition scip-java maven . . snapshots/identity().[T] +// kind TypeParameter +// display_name T +// signature_documentation +// > T +// ^^^^^^^^ definition scip-java maven . . snapshots/identity(). +// kind Method +// display_name identity +// signature_documentation +// > public final fun identity(x: T): T +// ^ definition scip-java maven . . snapshots/identity().(x) +// kind Parameter +// display_name x +// signature_documentation +// > x: T +// ^ reference scip-java maven . . snapshots/identity().[T] +// ^ reference scip-java maven . . snapshots/identity().[T] +// ^ reference scip-java maven . . snapshots/identity().(x) +// ⌃ enclosing_range_end scip-java maven . . snapshots/identity().[T] +// ⌃ enclosing_range_end scip-java maven . . snapshots/identity().(x) +// ⌃ enclosing_range_end scip-java maven . . snapshots/identity(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/Box# +// ⌄ enclosing_range_start scip-java maven . . snapshots/Box#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Box#[T] +// ⌄ enclosing_range_start scip-java maven . . snapshots/Box#``().(value) +// ⌄ enclosing_range_start scip-java maven . . snapshots/Box#value. +// ⌄ enclosing_range_start scip-java maven . . snapshots/Box#value.get(). + class Box(val value: T) { +// ^^^ definition scip-java maven . . snapshots/Box# +// kind Class +// display_name Box +// signature_documentation +// > public final class Box : Any +// ^^^ definition scip-java maven . . snapshots/Box#``(). +// kind Constructor +// display_name Box +// signature_documentation +// > public constructor(value: T): Box +// ^ definition scip-java maven . . snapshots/Box#[T] +// kind TypeParameter +// display_name T +// signature_documentation +// > T +// ^^^^^ definition scip-java maven . . snapshots/Box#``().(value) +// kind Parameter +// display_name value +// signature_documentation +// > value: T +// ^^^^^ definition scip-java maven . . snapshots/Box#value. +// kind Property +// display_name value +// signature_documentation +// > public final val value: T +// ^^^^^ reference scip-java maven . . snapshots/Box#``().(value) +// ^^^^^ definition scip-java maven . . snapshots/Box#value.get(). +// kind Method +// display_name value +// signature_documentation +// > public get(): T +// ^ reference scip-java maven . . snapshots/Box#[T] +// ⌃ enclosing_range_end scip-java maven . . snapshots/Box#[T] +// ⌃ enclosing_range_end scip-java maven . . snapshots/Box#``().(value) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Box#value. +// ⌃ enclosing_range_end scip-java maven . . snapshots/Box#value.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Box#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Box#unwrap(). + fun unwrap(): T = value +// ^^^^^^ definition scip-java maven . . snapshots/Box#unwrap(). +// kind Method +// display_name unwrap +// signature_documentation +// > public final fun unwrap(): T +// ^ reference scip-java maven . . snapshots/Box#[T] +// ^^^^^ reference scip-java maven . . snapshots/Box#value. +// ^^^^^ reference scip-java maven . . snapshots/Box#value.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Box#unwrap(). + } +//⌃ enclosing_range_end scip-java maven . . snapshots/Box# + diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt index cb95a1c65..cdda4ea2a 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt @@ -163,3 +163,57 @@ //⌃ enclosing_range_end scip-java maven . . snapshots/Seagull# //⌃ enclosing_range_end scip-java maven . . snapshots/Seagull#``(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Named# + interface Named +// ^^^^^ definition scip-java maven . . snapshots/Named# +// kind Interface +// display_name Named +// signature_documentation +// > public abstract interface Named : Any +// ⌃ enclosing_range_end scip-java maven . . snapshots/Named# +//⌄ enclosing_range_start scip-java maven . . snapshots/Speakable# + interface Speakable +// ^^^^^^^^^ definition scip-java maven . . snapshots/Speakable# +// kind Interface +// display_name Speakable +// signature_documentation +// > public abstract interface Speakable : Any +// ⌃ enclosing_range_end scip-java maven . . snapshots/Speakable# +//⌄ enclosing_range_start scip-java maven . . snapshots/Person# +//⌄ enclosing_range_start scip-java maven . . snapshots/Person#``(). + class Person : Named, Speakable +// ^^^^^^ definition scip-java maven . . snapshots/Person# +// kind Class +// display_name Person +// signature_documentation +// > public final class Person : Named, Speakable +// relationship scip-java maven . . snapshots/Named# implementation +// relationship scip-java maven . . snapshots/Speakable# implementation +// ^^^^^^ definition scip-java maven . . snapshots/Person#``(). +// kind Constructor +// display_name Person +// signature_documentation +// > public constructor(): Person +// ^^^^^ reference scip-java maven . . snapshots/Named# +// ^^^^^^^^^ reference scip-java maven . . snapshots/Speakable# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Person# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Person#``(). +//⌄ enclosing_range_start scip-java maven . . snapshots/usePerson(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/usePerson().(p) + fun usePerson(p: Person): Named = p +// ^^^^^^^^^ definition scip-java maven . . snapshots/usePerson(). +// kind Method +// display_name usePerson +// signature_documentation +// > public final fun usePerson(p: Person): Named +// ^ definition scip-java maven . . snapshots/usePerson().(p) +// kind Parameter +// display_name p +// signature_documentation +// > p: Person +// ^^^^^^ reference scip-java maven . . snapshots/Person# +// ^^^^^ reference scip-java maven . . snapshots/Named# +// ^ reference scip-java maven . . snapshots/usePerson().(p) +// ⌃ enclosing_range_end scip-java maven . . snapshots/usePerson().(p) +// ⌃ enclosing_range_end scip-java maven . . snapshots/usePerson(). + diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt index c0051702a..148a88378 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt @@ -84,3 +84,31 @@ // ⌃ enclosing_range_end scip-java maven . . snapshots/z.get(). // ⌃ enclosing_range_end local 2 +//⌄ enclosing_range_start scip-java maven . . snapshots/useLambda(). + fun useLambda() { +// ^^^^^^^^^ definition scip-java maven . . snapshots/useLambda(). +// kind Method +// display_name useLambda +// signature_documentation +// > public final fun useLambda(): Unit +// ⌄ enclosing_range_start local 3 +// ⌄ enclosing_range_start local 4 + val f = { n: Int -> n * 2 } +// ^ definition local 3 +// kind Property +// display_name f +// signature_documentation +// > local val f: (Int) -> Int +// ^ definition local 4 +// kind Parameter +// display_name n +// signature_documentation +// > n: Int +// ^^^ reference scip-java maven . . kotlin/Int# +// ^ reference local 4 +// ^ reference scip-java maven . . kotlin/Int#times(+2). +// ⌃ enclosing_range_end local 4 +// ⌃ enclosing_range_end local 3 + } +//⌃ enclosing_range_end scip-java maven . . snapshots/useLambda(). + diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/LocalDeclarations.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/LocalDeclarations.kt new file mode 100644 index 000000000..a9bbfb96a --- /dev/null +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/LocalDeclarations.kt @@ -0,0 +1,164 @@ + package snapshots +// ^^^^^^^^^ reference scip-java maven . . snapshots/ + +//⌄ enclosing_range_start scip-java maven . . snapshots/localClasses(). + fun localClasses() { +// ^^^^^^^^^^^^ definition scip-java maven . . snapshots/localClasses(). +// kind Method +// display_name localClasses +// signature_documentation +// > public final fun localClasses(): Unit +// ⌄ enclosing_range_start local 0 +// ⌄ enclosing_range_start local 1 + class LocalClass { +// ^^^^^^^^^^ definition local 0 +// kind Class +// display_name LocalClass +// signature_documentation +// > local final class LocalClass : Any +// ^^^^^^^^^^ definition local 1 +// kind Constructor +// display_name LocalClass +// signature_documentation +// > public constructor(): LocalClass +// ⌄ enclosing_range_start local 2 + fun localClassMethod() {} +// ^^^^^^^^^^^^^^^^ definition local 2 +// kind Method +// display_name localClassMethod +// signature_documentation +// > public final fun localClassMethod(): Unit +// ⌃ enclosing_range_end local 2 + } +// ⌃ enclosing_range_end local 0 +// ⌃ enclosing_range_end local 1 + } +//⌃ enclosing_range_end scip-java maven . . snapshots/localClasses(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/Interface# + interface Interface { +// ^^^^^^^^^ definition scip-java maven . . snapshots/Interface# +// kind Interface +// display_name Interface +// signature_documentation +// > public abstract interface Interface : Any +// ⌄ enclosing_range_start scip-java maven . . snapshots/Interface#foo(). + fun foo() +// ^^^ definition scip-java maven . . snapshots/Interface#foo(). +// kind Method +// display_name foo +// signature_documentation +// > public abstract fun foo(): Unit +// > +// ⌃ enclosing_range_end scip-java maven . . snapshots/Interface#foo(). + } +//⌃ enclosing_range_end scip-java maven . . snapshots/Interface# + +//⌄ enclosing_range_start scip-java maven . . snapshots/anonymousObjects(). + fun anonymousObjects() { +// ^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/anonymousObjects(). +// kind Method +// display_name anonymousObjects +// signature_documentation +// > public final fun anonymousObjects(): Unit + // Each object and its members need distinct local symbols, with no INSTANCE field. +// ⌄ enclosing_range_start local 3 +// ⌄ enclosing_range_start local 4 +// ⌄ enclosing_range_start local 5 + val a = object : Interface { +// ^ definition local 3 +// kind Property +// display_name a +// signature_documentation +// > local val a: `` +// ^^^^^^ definition local 4 +// kind Class +// display_name +// signature_documentation +// > object : Interface +// relationship scip-java maven . . snapshots/Interface# implementation +// ^^^^^^ definition local 5 +// kind Constructor +// display_name +// signature_documentation +// > private constructor(): +// ^^^^^^^^^ reference scip-java maven . . snapshots/Interface# +// ⌄ enclosing_range_start local 6 + override fun foo() {} +// ^^^ definition local 6 +// kind Method +// display_name foo +// signature_documentation +// > public open override fun foo(): Unit +// relationship scip-java maven . . snapshots/Interface#foo(). implementation +// ⌃ enclosing_range_end local 6 + } +// ⌃ enclosing_range_end local 3 +// ⌃ enclosing_range_end local 4 +// ⌃ enclosing_range_end local 5 +// ⌄ enclosing_range_start local 7 +// ⌄ enclosing_range_start local 8 +// ⌄ enclosing_range_start local 9 + val b = object : Interface { +// ^ definition local 7 +// kind Property +// display_name b +// signature_documentation +// > local val b: `` +// ^^^^^^ definition local 8 +// kind Class +// display_name +// signature_documentation +// > object : Interface +// relationship scip-java maven . . snapshots/Interface# implementation +// ^^^^^^ definition local 9 +// kind Constructor +// display_name +// signature_documentation +// > private constructor(): +// ^^^^^^^^^ reference scip-java maven . . snapshots/Interface# +// ⌄ enclosing_range_start local 10 + override fun foo() {} +// ^^^ definition local 10 +// kind Method +// display_name foo +// signature_documentation +// > public open override fun foo(): Unit +// relationship scip-java maven . . snapshots/Interface#foo(). implementation +// ⌃ enclosing_range_end local 10 + } +// ⌃ enclosing_range_end local 7 +// ⌃ enclosing_range_end local 8 +// ⌃ enclosing_range_end local 9 + } +//⌃ enclosing_range_end scip-java maven . . snapshots/anonymousObjects(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/outer(). + fun outer() { +// ^^^^^ definition scip-java maven . . snapshots/outer(). +// kind Method +// display_name outer +// signature_documentation +// > public final fun outer(): Unit +// ⌄ enclosing_range_start local 11 + fun inner() {} +// ^^^^^ definition local 11 +// kind Method +// display_name inner +// signature_documentation +// > local final fun inner(): Unit +// ⌃ enclosing_range_end local 11 +// ⌄ enclosing_range_start local 12 + fun innerWithReturnType(): Int = 42 +// ^^^^^^^^^^^^^^^^^^^ definition local 12 +// kind Method +// display_name innerWithReturnType +// signature_documentation +// > local final fun innerWithReturnType(): Int +// ^^^ reference scip-java maven . . kotlin/Int# +// ⌃ enclosing_range_end local 12 + inner() +// ^^^^^ reference local 11 + } +//⌃ enclosing_range_end scip-java maven . . snapshots/outer(). + diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt index 1e6a2aac8..5c213a453 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt @@ -51,3 +51,47 @@ //⌃ enclosing_range_end scip-java maven . . snapshots/ObjectKt#``(). //⌃ enclosing_range_end scip-java maven . . snapshots/ObjectKt#INSTANCE. +//⌄ enclosing_range_start scip-java maven . . snapshots/MySingleton# +//⌄ enclosing_range_start scip-java maven . . snapshots/MySingleton#``(). +//⌄ enclosing_range_start scip-java maven . . snapshots/MySingleton#INSTANCE. + // Named objects define an INSTANCE field for Java callers; object expressions do not. + object MySingleton { +// ^^^^^^^^^^^ definition scip-java maven . . snapshots/MySingleton# +// kind Class +// display_name MySingleton +// signature_documentation +// > public final object MySingleton : Any +// ^^^^^^^^^^^ definition scip-java maven . . snapshots/MySingleton#``(). +// kind Constructor +// display_name MySingleton +// signature_documentation +// > private constructor(): MySingleton +// ^^^^^^^^^^^ definition scip-java maven . . snapshots/MySingleton#INSTANCE. +// kind StaticField +// display_name INSTANCE +// signature_documentation +// > public final static val INSTANCE: MySingleton +// ⌄ enclosing_range_start scip-java maven . . snapshots/MySingleton#hello(). + fun hello(): String = "hi" +// ^^^^^ definition scip-java maven . . snapshots/MySingleton#hello(). +// kind Method +// display_name hello +// signature_documentation +// > public final fun hello(): String +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ⌃ enclosing_range_end scip-java maven . . snapshots/MySingleton#hello(). + } +//⌃ enclosing_range_end scip-java maven . . snapshots/MySingleton# +//⌃ enclosing_range_end scip-java maven . . snapshots/MySingleton#``(). +//⌃ enclosing_range_end scip-java maven . . snapshots/MySingleton#INSTANCE. +//⌄ enclosing_range_start scip-java maven . . snapshots/useSingleton(). + fun useSingleton() = MySingleton.hello() +// ^^^^^^^^^^^^ definition scip-java maven . . snapshots/useSingleton(). +// kind Method +// display_name useSingleton +// signature_documentation +// > public final fun useSingleton(): String +// ^^^^^^^^^^^ reference scip-java maven . . snapshots/MySingleton# +// ^^^^^ reference scip-java maven . . snapshots/MySingleton#hello(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/useSingleton(). + diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/TypeOperators.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/TypeOperators.kt new file mode 100644 index 000000000..d8c976934 --- /dev/null +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/TypeOperators.kt @@ -0,0 +1,93 @@ + package snapshots +// ^^^^^^^^^ reference scip-java maven . . snapshots/ + +//⌄ enclosing_range_start scip-java maven . . snapshots/typeOperators(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/typeOperators().(x) + fun typeOperators(x: Any) { +// ^^^^^^^^^^^^^ definition scip-java maven . . snapshots/typeOperators(). +// kind Method +// display_name typeOperators +// signature_documentation +// > public final fun typeOperators(x: Any): Unit +// ^ definition scip-java maven . . snapshots/typeOperators().(x) +// kind Parameter +// display_name x +// signature_documentation +// > x: Any +// ^^^ reference scip-java maven . . kotlin/Any# +// ⌃ enclosing_range_end scip-java maven . . snapshots/typeOperators().(x) +// ⌄ enclosing_range_start local 0 + when (x) { +// ^ definition local 0 +// kind Property +// display_name +// signature_documentation +// > local val : Any +// ^ reference scip-java maven . . snapshots/typeOperators().(x) +// ⌃ enclosing_range_end local 0 + is Int -> true +// ^^^ reference scip-java maven . . kotlin/Int# + else -> x as Float +// ^ reference scip-java maven . . snapshots/typeOperators().(x) +// ^^^^^ reference scip-java maven . . kotlin/Float# + } + } +//⌃ enclosing_range_end scip-java maven . . snapshots/typeOperators(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/Wrapper# +//⌄ enclosing_range_start scip-java maven . . snapshots/Wrapper#``(). + class Wrapper +// ^^^^^^^ definition scip-java maven . . snapshots/Wrapper# +// kind Class +// display_name Wrapper +// signature_documentation +// > public final class Wrapper : Any +// ^^^^^^^ definition scip-java maven . . snapshots/Wrapper#``(). +// kind Constructor +// display_name Wrapper +// signature_documentation +// > public constructor(): Wrapper +// ⌃ enclosing_range_end scip-java maven . . snapshots/Wrapper# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Wrapper#``(). + +//⌄ enclosing_range_start scip-java maven . . snapshots/classify(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/classify().(x) + fun classify(x: Any) { +// ^^^^^^^^ definition scip-java maven . . snapshots/classify(). +// kind Method +// display_name classify +// signature_documentation +// > public final fun classify(x: Any): Unit +// ^ definition scip-java maven . . snapshots/classify().(x) +// kind Parameter +// display_name x +// signature_documentation +// > x: Any +// ^^^ reference scip-java maven . . kotlin/Any# +// ⌃ enclosing_range_end scip-java maven . . snapshots/classify().(x) + if (x is Wrapper) {} +// ^ reference scip-java maven . . snapshots/classify().(x) +// ^^^^^^^ reference scip-java maven . . snapshots/Wrapper# +// ⌄ enclosing_range_start local 1 + val s = x as? String +// ^ definition local 1 +// kind Property +// display_name s +// signature_documentation +// > local val s: String? +// ^ reference scip-java maven . . snapshots/classify().(x) +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ⌃ enclosing_range_end local 1 +// ⌄ enclosing_range_start local 2 + val w = x as Wrapper +// ^ definition local 2 +// kind Property +// display_name w +// signature_documentation +// > local val w: Wrapper +// ^ reference scip-java maven . . snapshots/classify().(x) +// ^^^^^^^ reference scip-java maven . . snapshots/Wrapper# +// ⌃ enclosing_range_end local 2 + } +//⌃ enclosing_range_end scip-java maven . . snapshots/classify(). + From 93dbc5f0db23b128551339598fe03e2fc10de842 Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Wed, 28 Oct 2026 10:52:18 +0100 Subject: [PATCH 31/33] skip files on indexing failures, rather than crashing Incorporates https://github.com/scip-code/scip-java/pull/1038, but keeps it at the file level. I'd rather not add special cases for invalid programs to the core indexing logic --- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 11 ++++- .../KotlinAnalysisIndexerTest.kt | 46 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index efaa8bae8..76555d9b3 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -131,7 +131,16 @@ class KotlinAnalysisIndexer( val documents = mutableListOf() val ktFiles = session.modulesWithFiles.values.flatten().filterIsInstance() for (ktFile in ktFiles) { - val document = indexFile(ktFile) + val document = + try { + indexFile(ktFile) + } catch (e: Exception) { + val reason = e.message?.lineSequence()?.firstOrNull() + System.err.println( + "scip-kotlin-analysis: skipped ${ktFile.virtualFilePath}: ${e.javaClass.name}: $reason" + ) + continue + } writeShard(ktFile, document) callback(document) documents.add(document) diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index 4ab519ec6..ce55a5923 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -104,6 +104,52 @@ class KotlinAnalysisIndexerTest { ) } + @Test + fun `an analysis failure skips the entire file and continues indexing`(@TempDir path: Path) { + val sourceroot = path.toRealPath() + val src = Files.createDirectories(sourceroot.resolve("src")) + val broken = src.resolve("Broken.kt") + Files.writeString( + broken, + """ + package snapshots + + fun before(): Int = 1 + val (first, second) = Pair(1, 2) + fun after(): Int = before() + """ + .trimIndent(), + ) + val other = src.resolve("Other.kt") + Files.writeString( + other, + """ + package snapshots + + fun other(): Int = after() + """ + .trimIndent(), + ) + val targetroot = sourceroot.resolve("scip-targetroot") + val emitted = mutableListOf() + val documents = + KotlinAnalysisIndexer( + sourceroot = sourceroot, + targetroot = targetroot, + sourceRoots = listOf(broken, other), + classpath = listOf(kotlinStdlibJar()), + callback = { emitted.add(it) }, + ) + .run() + + assertEquals(listOf("src/Other.kt"), documents.map { it.relativePath }) + assertEquals(documents, emitted) + assertContains(definitions(documents.single()), "snapshots/other().") + assertContains(references(documents.single()), "snapshots/after().") + assertTrue(Files.notExists(targetroot.resolve("META-INF/scip/src/Broken.kt.scip"))) + assertTrue(Files.exists(targetroot.resolve("META-INF/scip/src/Other.kt.scip"))) + } + // Compiled by the test build, so references use classpath symbols rather than source PSI. open class Base(open var state: T) { open fun find(value: T): T = value From 892a2bb2a4cfeb85187e34500682761c811cdb72 Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Wed, 28 Oct 2026 11:33:46 +0100 Subject: [PATCH 32/33] number library constructor overloads like scip-java. Expand constructor aliases --- .../scip_java/kotlin_analysis/SymbolsCache.kt | 33 +++++- .../java/snapshots/ConstructorsConsumer.java | 10 ++ .../src/main/kotlin/snapshots/Constructors.kt | 20 ++++ .../java/snapshots/ConstructorsConsumer.java | 34 ++++++ .../src/main/kotlin/snapshots/Constructors.kt | 110 ++++++++++++++++++ .../src/main/kotlin/snapshots/ObjectKt.kt | 2 +- 6 files changed, 206 insertions(+), 3 deletions(-) create mode 100644 scip-snapshots/cases/kotlin/common/src/main/java/snapshots/ConstructorsConsumer.java create mode 100644 scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Constructors.kt create mode 100644 scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/java/snapshots/ConstructorsConsumer.java create mode 100644 scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Constructors.kt diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt index ca8b97073..6059bcbbe 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt @@ -2,6 +2,7 @@ package org.scip_code.scip_java.kotlin_analysis import com.intellij.psi.PsiElement import java.lang.System.err +import org.jetbrains.kotlin.analysis.api.KaExperimentalApi import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaClassLikeSymbol @@ -139,7 +140,9 @@ class SymbolsCache(private val session: KaSession) { ) /** The class symbol owning a constructor reference (`class Seagull : Bird()` → `Bird#`). */ - fun constructorOwnerSymbol(session: KaSession, target: KaConstructorSymbol): Symbol { + @OptIn(KaExperimentalApi::class) + fun constructorOwnerSymbol(session: KaSession, constructor: KaConstructorSymbol): Symbol { + val target = with(session) { constructor.originalConstructorIfTypeAliased } ?: constructor when (val psi = target.psi) { is KtClassOrObject -> return symbolForDeclaration(psi) is KtConstructor<*> -> @@ -197,7 +200,16 @@ class SymbolsCache(private val session: KaSession) { fun implicitItSymbol(literal: KtFunctionLiteral): Symbol = syntheticLocalSymbol(literal) /** The SCIP symbol for the target of a resolved reference. */ + @OptIn(KaExperimentalApi::class) fun symbolForReference(session: KaSession, target: KaSymbol): Symbol { + // A constructor call through a type alias (`HashMap()`) references the aliased + // class's constructor, like javac and scip-kotlinc. + if (target is KaConstructorSymbol) { + with(session) { target.originalConstructorIfTypeAliased } + ?.let { + return symbolForReference(session, it) + } + } val psi = target.psi if (psi is KtDeclaration) { // A constructor symbol whose PSI is the class itself is an implicit primary. @@ -244,7 +256,11 @@ class SymbolsCache(private val session: KaSession) { target.containingClassId?.let { classId -> Symbol.createGlobal( classSymbol(classId), - ScipSymbolDescriptor(Kind.METHOD, ""), + ScipSymbolDescriptor( + Kind.METHOD, + "", + externalConstructorDisambiguator(session, classId, target), + ), ) } ?: Symbol.NONE // Library type aliases are expanded to the aliased class (`AutoCloseable` @@ -496,6 +512,19 @@ class SymbolsCache(private val session: KaSession) { return Symbol.createGlobal(owner, descriptor) } + /** Like javac: constructors in declaration order, private ones included. */ + private fun externalConstructorDisambiguator( + session: KaSession, + classId: ClassId, + target: KaConstructorSymbol, + ): String { + val constructors = + with(session) { findClass(classId)?.declaredMemberScope?.constructors?.toList() } + ?: return "()" + val index = constructors.indexOf(target) + return if (index <= 0) "()" else "(+$index)" + } + private fun externalMethodDisambiguator(session: KaSession, target: KaCallableSymbol): String { val callableId = target.callableId ?: return "()" val overloads: List = diff --git a/scip-snapshots/cases/kotlin/common/src/main/java/snapshots/ConstructorsConsumer.java b/scip-snapshots/cases/kotlin/common/src/main/java/snapshots/ConstructorsConsumer.java new file mode 100644 index 000000000..f098207fb --- /dev/null +++ b/scip-snapshots/cases/kotlin/common/src/main/java/snapshots/ConstructorsConsumer.java @@ -0,0 +1,10 @@ +package snapshots; + +// No JDK classes: with --release, newer JDKs compile against ct.sym, which omits private +// constructors, so javac's overload numbers for them depend on the JDK running the build. +public class ConstructorsConsumer { + public static void run() { + new kotlin.text.Regex("a"); + new Overloaded(1); + } +} diff --git a/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Constructors.kt b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Constructors.kt new file mode 100644 index 000000000..8ddac0b79 --- /dev/null +++ b/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Constructors.kt @@ -0,0 +1,20 @@ +package snapshots + +class Overloaded(val s: String) { + constructor(i: Int) : this(i.toString()) +} + +typealias OverloadedAlias = Overloaded + +typealias StringIntMap = HashMap + +// `Regex` and `OverloadedAlias` reference the same symbols as ConstructorsConsumer.java. +fun constructors() { + // Library constructors are numbered like javac, private ones included. + java.io.File("a", "b") + Regex("a") + // Calls through type aliases reference the aliased class's constructor. + StringBuilder("x") + StringIntMap() + OverloadedAlias(1) +} diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/java/snapshots/ConstructorsConsumer.java b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/java/snapshots/ConstructorsConsumer.java new file mode 100644 index 000000000..ea5127a3c --- /dev/null +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/java/snapshots/ConstructorsConsumer.java @@ -0,0 +1,34 @@ + package snapshots; + + // No JDK classes: with --release, newer JDKs compile against ct.sym, which omits private + // constructors, so javac's overload numbers for them depend on the JDK running the build. +//⌄ enclosing_range_start scip-java maven . . snapshots/ConstructorsConsumer# + public class ConstructorsConsumer { +// ^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/ConstructorsConsumer# +// kind Class +// display_name ConstructorsConsumer +// signature_documentation +// > public class ConstructorsConsumer +// ^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/ConstructorsConsumer#``(). +// kind Constructor +// display_name +// signature_documentation +// > public ConstructorsConsumer() +// ⌄ enclosing_range_start scip-java maven . . snapshots/ConstructorsConsumer#run(). + public static void run() { +// ^^^ definition scip-java maven . . snapshots/ConstructorsConsumer#run(). +// kind StaticMethod +// display_name run +// signature_documentation +// > public static void run() + new kotlin.text.Regex("a"); +// ^^^^^^ reference scip-java maven . . kotlin/ +// ^^^^ reference scip-java maven . . kotlin/text/ +// ^^^^^ reference scip-java maven . . kotlin/text/Regex#``(+1). + new Overloaded(1); +// ^^^^^^^^^^ reference scip-java maven . . snapshots/Overloaded#``(+1). + } +// ⌃ enclosing_range_end scip-java maven . . snapshots/ConstructorsConsumer#run(). + } +//⌃ enclosing_range_end scip-java maven . . snapshots/ConstructorsConsumer# + diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Constructors.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Constructors.kt new file mode 100644 index 000000000..eb55de8b2 --- /dev/null +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Constructors.kt @@ -0,0 +1,110 @@ + package snapshots +// ^^^^^^^^^ reference scip-java maven . . snapshots/ + +//⌄ enclosing_range_start scip-java maven . . snapshots/Overloaded# +// ⌄ enclosing_range_start scip-java maven . . snapshots/Overloaded#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Overloaded#``().(s) +// ⌄ enclosing_range_start scip-java maven . . snapshots/Overloaded#s. +// ⌄ enclosing_range_start scip-java maven . . snapshots/Overloaded#s.get(). + class Overloaded(val s: String) { +// ^^^^^^^^^^ definition scip-java maven . . snapshots/Overloaded# +// kind Class +// display_name Overloaded +// signature_documentation +// > public final class Overloaded : Any +// ^^^^^^^^^^ definition scip-java maven . . snapshots/Overloaded#``(). +// kind Constructor +// display_name Overloaded +// signature_documentation +// > public constructor(s: String): Overloaded +// ^ definition scip-java maven . . snapshots/Overloaded#``().(s) +// kind Parameter +// display_name s +// signature_documentation +// > s: String +// ^ definition scip-java maven . . snapshots/Overloaded#s. +// kind Property +// display_name s +// signature_documentation +// > public final val s: String +// ^ reference scip-java maven . . snapshots/Overloaded#``().(s) +// ^ definition scip-java maven . . snapshots/Overloaded#s.get(). +// kind Method +// display_name s +// signature_documentation +// > public get(): String +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ⌃ enclosing_range_end scip-java maven . . snapshots/Overloaded#``().(s) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Overloaded#s. +// ⌃ enclosing_range_end scip-java maven . . snapshots/Overloaded#s.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Overloaded#``(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Overloaded#``(+1). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Overloaded#``(+1).(i) + constructor(i: Int) : this(i.toString()) +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Overloaded#``(+1). +// kind Constructor +// display_name Overloaded +// signature_documentation +// > public constructor(i: Int): Overloaded +// ^ definition scip-java maven . . snapshots/Overloaded#``(+1).(i) +// kind Parameter +// display_name i +// signature_documentation +// > i: Int +// ^^^ reference scip-java maven . . kotlin/Int# +// ^ reference scip-java maven . . snapshots/Overloaded#``(+1).(i) +// ^^^^^^^^ reference scip-java maven . . kotlin/Int#toString(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Overloaded#``(+1).(i) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Overloaded#``(+1). + } +//⌃ enclosing_range_end scip-java maven . . snapshots/Overloaded# + +//⌄ enclosing_range_start scip-java maven . . snapshots/OverloadedAlias# + typealias OverloadedAlias = Overloaded +// ^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/OverloadedAlias# +// kind TypeAlias +// display_name OverloadedAlias +// signature_documentation +// > public final typealias OverloadedAlias = Overloaded +// > +// ^^^^^^^^^^ reference scip-java maven . . snapshots/Overloaded# +// ⌃ enclosing_range_end scip-java maven . . snapshots/OverloadedAlias# + +//⌄ enclosing_range_start scip-java maven . . snapshots/StringIntMap# + typealias StringIntMap = HashMap +// ^^^^^^^^^^^^ definition scip-java maven . . snapshots/StringIntMap# +// kind TypeAlias +// display_name StringIntMap +// signature_documentation +// > public final typealias StringIntMap = HashMap +// > +// ^^^^^^^ reference scip-java maven jdk 17 java/util/HashMap# +// ^^^^^^ reference scip-java maven . . kotlin/String# +// ^^^ reference scip-java maven . . kotlin/Int# +// ⌃ enclosing_range_end scip-java maven . . snapshots/StringIntMap# + +//⌄ enclosing_range_start scip-java maven . . snapshots/constructors(). + // `Regex` and `OverloadedAlias` reference the same symbols as ConstructorsConsumer.java. + fun constructors() { +// ^^^^^^^^^^^^ definition scip-java maven . . snapshots/constructors(). +// kind Method +// display_name constructors +// signature_documentation +// > public final fun constructors(): Unit + // Library constructors are numbered like javac, private ones included. + java.io.File("a", "b") +// ^^^^ reference scip-java maven . . java/ +// ^^ reference scip-java maven . . java/io/ +// ^^^^ reference scip-java maven jdk 17 java/io/File#``(+3). + Regex("a") +// ^^^^^ reference scip-java maven . . kotlin/text/Regex#``(+1). + // Calls through type aliases reference the aliased class's constructor. + StringBuilder("x") +// ^^^^^^^^^^^^^ reference scip-java maven jdk 17 java/lang/StringBuilder#``(+2). + StringIntMap() +// ^^^^^^^^^^^^ reference scip-java maven jdk 17 java/util/HashMap#``(+2). + OverloadedAlias(1) +// ^^^^^^^^^^^^^^^ reference scip-java maven . . snapshots/Overloaded#``(+1). + } +//⌃ enclosing_range_end scip-java maven . . snapshots/constructors(). + diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt index 5c213a453..acd1b0fab 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/ObjectKt.kt @@ -42,7 +42,7 @@ // ^^^^^^^ reference scip-java maven . . kotlin/Nothing# // ⌃ enclosing_range_end scip-java maven . . snapshots/ObjectKt#fail().(message) throw RuntimeException(message) -// ^^^^^^^^^^^^^^^^ reference scip-java maven jdk 17 java/lang/RuntimeException#``(). +// ^^^^^^^^^^^^^^^^ reference scip-java maven jdk 17 java/lang/RuntimeException#``(+1). // ^^^^^^^ reference scip-java maven . . snapshots/ObjectKt#fail().(message) } // ⌃ enclosing_range_end scip-java maven . . snapshots/ObjectKt#fail(). From 1ccc57faedb262c8b916f0a81755c774e7cbde8d Mon Sep 17 00:00:00 2001 From: Christoph Hegemann Date: Thu, 29 Oct 2026 19:55:46 +0100 Subject: [PATCH 33/33] revert getter/setter symbol renaming from 7e0ce7255a50a4c97892d23df5455c3bc97c761d This restores cross-referencing with java symbols --- .../kotlin_analysis/KotlinAnalysisIndexer.kt | 4 +- .../scip_java/kotlin_analysis/SymbolsCache.kt | 56 ++++++++++++------- .../KotlinAnalysisIndexerTest.kt | 10 ++-- .../src/main/kotlin/snapshots/Annotations.kt | 6 +- .../common/src/main/kotlin/snapshots/Class.kt | 32 +++++------ .../src/main/kotlin/snapshots/Constructors.kt | 6 +- .../main/kotlin/snapshots/Destructuring.kt | 20 +++---- .../common/src/main/kotlin/snapshots/Enums.kt | 30 +++++----- .../src/main/kotlin/snapshots/Extensions.kt | 8 +-- .../src/main/kotlin/snapshots/Generics.kt | 24 ++++---- .../main/kotlin/snapshots/Implementations.kt | 18 +++--- .../src/main/kotlin/snapshots/Lambdas.kt | 22 ++++---- 12 files changed, 125 insertions(+), 111 deletions(-) diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt index 76555d9b3..1c1dd00e4 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexer.kt @@ -236,7 +236,7 @@ class KotlinAnalysisIndexer( } // Property references fan out to the property and its accessors - // (`banana` → `Class#banana.`, `Class#banana.get().`, `Class#banana.set().`). + // (`banana` → `Class#banana.`, `Class#getBanana().`, `Class#setBanana().`). if (target is KaPropertySymbol) { propertyReferenceSymbols(target).forEach { addReference(it, range) } return @@ -463,7 +463,7 @@ class KotlinAnalysisIndexer( documentation = null, ) emitDefinition( - symbol = cache.accessorSymbol(entries, setter = false), + symbol = cache.memberMethodSymbol(declaration, "getEntries"), range = range, kind = Kind.Method, enclosingSymbol = entries, diff --git a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt index 6059bcbbe..6952f23a7 100644 --- a/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt +++ b/scip-kotlin-analysis/src/main/kotlin/org/scip_code/scip_java/kotlin_analysis/SymbolsCache.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.psi.KtEnumEntry import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFunction import org.jetbrains.kotlin.psi.KtFunctionLiteral +import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.KtObjectDeclaration import org.jetbrains.kotlin.psi.KtParameter @@ -42,6 +43,7 @@ import org.jetbrains.kotlin.psi.KtVariableDeclaration import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.parents +import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly import org.scip_code.scip_java.kotlin_analysis.ScipSymbolDescriptor.Kind import org.scip_code.scip_java.shared.LocalSymbolsCache as SharedLocalSymbolsCache @@ -119,19 +121,14 @@ class SymbolsCache(private val session: KaSession) { /** * The synthetic getter/setter of a property (or `val`/`var` constructor parameter) without - * explicit accessor PSI (`Class#banana.get().`). + * explicit accessor PSI. Owned by the property's container (`Class#getBanana().`), not by the + * constructor declaring the parameter. */ fun syntheticAccessorSymbol(property: KtDeclaration, setter: Boolean): Symbol { - val propertySymbol = - if (property is KtParameter) parameterPropertySymbol(property) - else symbolForDeclaration(property) - return accessorSymbol(propertySymbol, setter) + val name = (property as? KtNamedDeclaration)?.name.orEmpty() + return memberSymbol(propertyOwnerSymbol(property), accessorDescriptor(name, setter)) } - /** The getter or setter of [property] (`Class#banana.get().`, `Class#banana.set().`). */ - fun accessorSymbol(property: Symbol, setter: Boolean): Symbol = - memberSymbol(property, accessorDescriptor(setter)) - /** The property symbol declared by a `val`/`var` constructor parameter (`Class#banana.`). */ fun parameterPropertySymbol(parameter: KtParameter): Symbol = memberSymbol( @@ -181,10 +178,14 @@ class SymbolsCache(private val session: KaSession) { */ fun syntheticLocalSymbol(key: PsiElement): Symbol = locals.get(key) ?: locals.put(key) - private fun propertyOwnerSymbol(parameter: KtParameter): Symbol = - (ownerDeclarationOfParameter(parameter) as? KtConstructor<*>) - ?.containingClassOrObject - ?.let { symbolForDeclaration(it) } ?: Symbol.NONE + private fun propertyOwnerSymbol(property: KtDeclaration): Symbol = + when (property) { + is KtParameter -> + (ownerDeclarationOfParameter(property) as? KtConstructor<*>) + ?.containingClassOrObject + ?.let { symbolForDeclaration(it) } ?: Symbol.NONE + else -> ownerSymbol(property) + } /** The `(value)` parameter symbol of a synthetic setter. */ fun syntheticSetterValueSymbol(property: KtDeclaration): Symbol = @@ -278,16 +279,26 @@ class SymbolsCache(private val session: KaSession) { /** The getter/setter symbol of an external (classpath) property reference. */ fun externalAccessorSymbol( session: KaSession, - target: KaCallableSymbol, + resolved: KaCallableSymbol, setter: Boolean, ): Symbol { - val property = externalCallableSymbol(session, target) - if (property == Symbol.NONE) return Symbol.NONE - return accessorSymbol(property, setter) + val target = with(session) { resolved.fakeOverrideOriginal } + val callableId = target.callableId ?: return Symbol.NONE + val owner = + callableId.classId?.let(::classSymbol) + ?: receiverClassId(target)?.let { extensionOwnerSymbol(callableId.packageName, it) } + ?: packageSymbol(callableId.packageName) + return Symbol.createGlobal( + owner, + accessorDescriptor(callableId.callableName.asString(), setter), + ) } - private fun accessorDescriptor(setter: Boolean): ScipSymbolDescriptor = - ScipSymbolDescriptor(Kind.METHOD, if (setter) "set" else "get") + private fun accessorDescriptor(propertyName: String, setter: Boolean): ScipSymbolDescriptor = + ScipSymbolDescriptor( + Kind.METHOD, + (if (setter) "set" else "get") + propertyName.capitalizeAsciiOnly(), + ) private fun uncachedSymbol(declaration: KtDeclaration): Symbol { // Anonymous functions and lambdas have no symbol of their own, mirroring FIR. @@ -342,7 +353,7 @@ class SymbolsCache(private val session: KaSession) { is KtParameter -> ownerDeclarationOfParameter(declaration)?.let { symbolForDeclaration(it) } ?: Symbol.NONE - is KtPropertyAccessor -> symbolForDeclaration(declaration.property) + is KtPropertyAccessor -> ownerSymbol(declaration.property) else -> declaration.containingClassOrObject?.let { if (it is KtEnumEntry) enumEntryBodySymbol(it) else symbolForDeclaration(it) @@ -410,7 +421,10 @@ class SymbolsCache(private val session: KaSession) { ScipSymbolDescriptor(Kind.TYPE, declaration.name ?: "Companion") declaration is KtClassOrObject -> ScipSymbolDescriptor(Kind.TYPE, declaration.name.orEmpty()) - declaration is KtPropertyAccessor -> accessorDescriptor(declaration.isSetter) + declaration is KtPropertyAccessor && declaration.isSetter -> + accessorDescriptor(declaration.property.name.orEmpty(), setter = true) + declaration is KtPropertyAccessor -> + accessorDescriptor(declaration.property.name.orEmpty(), setter = false) declaration is KtConstructor<*> -> ScipSymbolDescriptor(Kind.METHOD, "", constructorDisambiguator(declaration)) declaration is KtNamedFunction -> diff --git a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt index ce55a5923..c44545eaf 100644 --- a/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt +++ b/scip-kotlin-analysis/src/test/kotlin/org/scip_code/scip_java/kotlin_analysis/KotlinAnalysisIndexerTest.kt @@ -82,7 +82,7 @@ class KotlinAnalysisIndexerTest { enclosingSymbols["snapshots/Greeter#``().(name)"], ) assertEquals("snapshots/Greeter#", enclosingSymbols["snapshots/Greeter#name."]) - assertEquals("snapshots/Greeter#name.", enclosingSymbols["snapshots/Greeter#name.get()."]) + assertEquals("snapshots/Greeter#name.", enclosingSymbols["snapshots/Greeter#getName()."]) val mainDefinitions = definitions(main) assertContains(mainDefinitions, "snapshots/useGreeter().") @@ -207,16 +207,16 @@ class KotlinAnalysisIndexerTest { "leaf.state" to setOf( "${prefix}Base#state.", - "${prefix}Base#state.get().", - "${prefix}Base#state.set().", + "${prefix}Base#getState().", + "${prefix}Base#setState().", ), "overriding.find(\"value\")" to setOf("${prefix}Overriding#find()."), "overriding.find(1)" to setOf("${prefix}Base#find(+1)."), "overriding.state" to setOf( "${prefix}Overriding#state.", - "${prefix}Overriding#state.get().", - "${prefix}Overriding#state.set().", + "${prefix}Overriding#getState().", + "${prefix}Overriding#setState().", ), ) for ((expression, expected) in expectedReferences) { diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt index e147b6b6c..019dec751 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Annotations.kt @@ -11,7 +11,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#``().(tag) // ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#tag. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#tag.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Tagged#getTag(). annotation class Tagged(val tag: String) // ^^^^^^ definition scip-java maven . . snapshots/Tagged# // kind Class @@ -35,7 +35,7 @@ // signature_documentation // > public final val tag: String // ^^^ reference scip-java maven . . snapshots/Tagged#``().(tag) -// ^^^ definition scip-java maven . . snapshots/Tagged#tag.get(). +// ^^^ definition scip-java maven . . snapshots/Tagged#getTag(). // kind Method // display_name tag // signature_documentation @@ -43,7 +43,7 @@ // ^^^^^^ reference scip-java maven . . kotlin/String# // ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged#``().(tag) // ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged#tag. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged#tag.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged#getTag(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged# // ⌃ enclosing_range_end scip-java maven . . snapshots/Tagged#``(). diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt index 288589564..9ec788702 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Class.kt @@ -5,9 +5,9 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``().(banana) // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#banana. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#banana.get(). -// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#banana.set(). -// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#banana.set().(value) +// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#getBanana(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#setBanana(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#setBanana().(value) // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#``().(apple) class Class constructor(private var banana: Int, apple: String) : // ^^^^^ definition scip-java maven . . snapshots/Class# @@ -32,17 +32,17 @@ // signature_documentation // > private final var banana: Int // ^^^^^^ reference scip-java maven . . snapshots/Class#``().(banana) -// ^^^^^^ definition scip-java maven . . snapshots/Class#banana.get(). +// ^^^^^^ definition scip-java maven . . snapshots/Class#getBanana(). // kind Method // display_name banana // signature_documentation // > private get(): Int -// ^^^^^^ definition scip-java maven . . snapshots/Class#banana.set(). +// ^^^^^^ definition scip-java maven . . snapshots/Class#setBanana(). // kind Method // display_name banana // signature_documentation // > private set(value: Int): Unit -// ^^^^^^ definition scip-java maven . . snapshots/Class#banana.set().(value) +// ^^^^^^ definition scip-java maven . . snapshots/Class#setBanana().(value) // kind Parameter // display_name value // signature_documentation @@ -56,9 +56,9 @@ // ^^^^^^ reference scip-java maven . . kotlin/String# // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#``().(banana) // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#banana. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#banana.get(). -// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#banana.set(). -// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#banana.set().(value) +// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#getBanana(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#setBanana(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#setBanana().(value) // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#``().(apple) // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#``(). Throwable(banana.toString() + apple) { @@ -73,14 +73,14 @@ } // ⌄ enclosing_range_start scip-java maven . . snapshots/Class#asdf. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#asdf.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Class#getAsdf(). val asdf = // ^^^^ definition scip-java maven . . snapshots/Class#asdf. // kind Property // display_name asdf // signature_documentation // > public final val asdf: Any -// ^^^^ definition scip-java maven . . snapshots/Class#asdf.get(). +// ^^^^ definition scip-java maven . . snapshots/Class#getAsdf(). // kind Method // display_name asdf // signature_documentation @@ -109,7 +109,7 @@ // ⌃ enclosing_range_end local 2 } // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#asdf. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#asdf.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Class#getAsdf(). // ⌃ enclosing_range_end local 0 // ⌃ enclosing_range_end local 1 @@ -153,12 +153,12 @@ println("I eat $banana for lunch") // ^^^^^^^ reference scip-java maven . . kotlin/io/println(). // ^^^^^^ reference scip-java maven . . snapshots/Class#banana. -// ^^^^^^ reference scip-java maven . . snapshots/Class#banana.get(). -// ^^^^^^ reference scip-java maven . . snapshots/Class#banana.set(). +// ^^^^^^ reference scip-java maven . . snapshots/Class#getBanana(). +// ^^^^^^ reference scip-java maven . . snapshots/Class#setBanana(). banana = 42 // ^^^^^^ reference scip-java maven . . snapshots/Class#banana. -// ^^^^^^ reference scip-java maven . . snapshots/Class#banana.get(). -// ^^^^^^ reference scip-java maven . . snapshots/Class#banana.set(). +// ^^^^^^ reference scip-java maven . . snapshots/Class#getBanana(). +// ^^^^^^ reference scip-java maven . . snapshots/Class#setBanana(). } // ⌃ enclosing_range_end scip-java maven . . snapshots/Class#run(). } diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Constructors.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Constructors.kt index eb55de8b2..3b838cb70 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Constructors.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Constructors.kt @@ -5,7 +5,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Overloaded#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Overloaded#``().(s) // ⌄ enclosing_range_start scip-java maven . . snapshots/Overloaded#s. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Overloaded#s.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Overloaded#getS(). class Overloaded(val s: String) { // ^^^^^^^^^^ definition scip-java maven . . snapshots/Overloaded# // kind Class @@ -28,7 +28,7 @@ // signature_documentation // > public final val s: String // ^ reference scip-java maven . . snapshots/Overloaded#``().(s) -// ^ definition scip-java maven . . snapshots/Overloaded#s.get(). +// ^ definition scip-java maven . . snapshots/Overloaded#getS(). // kind Method // display_name s // signature_documentation @@ -36,7 +36,7 @@ // ^^^^^^ reference scip-java maven . . kotlin/String# // ⌃ enclosing_range_end scip-java maven . . snapshots/Overloaded#``().(s) // ⌃ enclosing_range_end scip-java maven . . snapshots/Overloaded#s. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Overloaded#s.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Overloaded#getS(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Overloaded#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Overloaded#``(+1). // ⌄ enclosing_range_start scip-java maven . . snapshots/Overloaded#``(+1).(i) diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt index 9ee48b5c5..d0044ec15 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Destructuring.kt @@ -10,12 +10,12 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#copy(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#``().(x) // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#x. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#x.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#getX(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#component1(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#copy().(x) // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#``().(y) // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#y. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#y.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Point#getY(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#component2(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Point#copy().(y) data class Point(val x: Int, val y: Int) @@ -69,7 +69,7 @@ // signature_documentation // > public final val x: Int // ^ reference scip-java maven . . snapshots/Point#``().(x) -// ^ definition scip-java maven . . snapshots/Point#x.get(). +// ^ definition scip-java maven . . snapshots/Point#getX(). // kind Method // display_name x // signature_documentation @@ -86,7 +86,7 @@ // signature_documentation // > x: Int = ... // ^ reference scip-java maven . . snapshots/Point#x. -// ^ reference scip-java maven . . snapshots/Point#x.get(). +// ^ reference scip-java maven . . snapshots/Point#getX(). // ^^^ reference scip-java maven . . kotlin/Int# // ^ definition scip-java maven . . snapshots/Point#``().(y) // kind Parameter @@ -99,7 +99,7 @@ // signature_documentation // > public final val y: Int // ^ reference scip-java maven . . snapshots/Point#``().(y) -// ^ definition scip-java maven . . snapshots/Point#y.get(). +// ^ definition scip-java maven . . snapshots/Point#getY(). // kind Method // display_name y // signature_documentation @@ -116,16 +116,16 @@ // signature_documentation // > y: Int = ... // ^ reference scip-java maven . . snapshots/Point#y. -// ^ reference scip-java maven . . snapshots/Point#y.get(). +// ^ reference scip-java maven . . snapshots/Point#getY(). // ^^^ reference scip-java maven . . kotlin/Int# // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#``().(x) // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#x. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#x.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#getX(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#component1(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#copy().(x) // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#``().(y) // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#y. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#y.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Point#getY(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#component2(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Point#copy().(y) // ⌃ enclosing_range_end scip-java maven . . snapshots/Point# @@ -215,11 +215,11 @@ // ^^^^^ reference local 5 // ^^^^^ reference local 6 // ^ reference scip-java maven . . snapshots/Point#x. -// ^ reference scip-java maven . . snapshots/Point#x.get(). +// ^ reference scip-java maven . . snapshots/Point#getX(). // ^ reference scip-java maven . . kotlin/Int#plus(+2). // ^^^^^ reference local 7 // ^^^^^^ reference scip-java maven . . kotlin/String#length. -// ^^^^^^ reference scip-java maven . . kotlin/String#length.get(). +// ^^^^^^ reference scip-java maven . . kotlin/String#getLength(). // ⌃ enclosing_range_end local 6 // ⌃ enclosing_range_end local 7 // ⌃ enclosing_range_end local 5 diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt index afff975fd..2d0133f71 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Enums.kt @@ -6,11 +6,11 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Suit#valueOf(). //⌄ enclosing_range_start scip-java maven . . snapshots/Suit#valueOf().(value) //⌄ enclosing_range_start scip-java maven . . snapshots/Suit#entries. -//⌄ enclosing_range_start scip-java maven . . snapshots/Suit#entries.get(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Suit#getEntries(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#``().(symbol) // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#symbol. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#symbol.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#getSymbol(). enum class Suit(val symbol: Char) { // ^^^^ definition scip-java maven . . snapshots/Suit# // kind Class @@ -43,7 +43,7 @@ // display_name entries // signature_documentation // > public final static val entries: EnumEntries -// ^^^^ definition scip-java maven . . snapshots/Suit#entries.get(). +// ^^^^ definition scip-java maven . . snapshots/Suit#getEntries(). // kind Method // display_name entries // signature_documentation @@ -59,7 +59,7 @@ // signature_documentation // > public final val symbol: Char // ^^^^^^ reference scip-java maven . . snapshots/Suit#``().(symbol) -// ^^^^^^ definition scip-java maven . . snapshots/Suit#symbol.get(). +// ^^^^^^ definition scip-java maven . . snapshots/Suit#getSymbol(). // kind Method // display_name symbol // signature_documentation @@ -67,7 +67,7 @@ // ^^^^ reference scip-java maven . . kotlin/Char# // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#``().(symbol) // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#symbol. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#symbol.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#getSymbol(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Suit#HEARTS. HEARTS('h'), @@ -95,7 +95,7 @@ // > public final fun isRed(): Boolean // ^^^^^^^ reference scip-java maven . . kotlin/Boolean# // ^^^^^^ reference scip-java maven . . snapshots/Suit#symbol. -// ^^^^^^ reference scip-java maven . . snapshots/Suit#symbol.get(). +// ^^^^^^ reference scip-java maven . . snapshots/Suit#getSymbol(). // ^^ reference scip-java maven . . kotlin/Char#equals(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#isRed(). @@ -135,7 +135,7 @@ // ^^^^ reference scip-java maven . . kotlin/Char# // ^^^^ reference scip-java maven . . snapshots/Suit# // ^^^^^^^ reference scip-java maven . . snapshots/Suit#entries. -// ^^^^^^^ reference scip-java maven . . snapshots/Suit#entries.get(). +// ^^^^^^^ reference scip-java maven . . snapshots/Suit#getEntries(). // ^^^^ reference scip-java maven . . kotlin/collections/Iterable#find(). // ^^^^^^^^^^^^^^^^^^^^^^^ definition local 0 // kind Parameter @@ -144,7 +144,7 @@ // > it: Suit // ^^ reference local 0 // ^^^^^^ reference scip-java maven . . snapshots/Suit#symbol. -// ^^^^^^ reference scip-java maven . . snapshots/Suit#symbol.get(). +// ^^^^^^ reference scip-java maven . . snapshots/Suit#getSymbol(). // ^^ reference scip-java maven . . kotlin/Char#equals(). // ^^^^^^ reference scip-java maven . . snapshots/Suit#Companion#fromSymbol().(symbol) // ⌃ enclosing_range_end scip-java maven . . snapshots/Suit#Companion#fromSymbol().(symbol) @@ -160,7 +160,7 @@ //⌃ enclosing_range_end scip-java maven . . snapshots/Suit#valueOf(). //⌃ enclosing_range_end scip-java maven . . snapshots/Suit#valueOf().(value) //⌃ enclosing_range_end scip-java maven . . snapshots/Suit#entries. -//⌃ enclosing_range_end scip-java maven . . snapshots/Suit#entries.get(). +//⌃ enclosing_range_end scip-java maven . . snapshots/Suit#getEntries(). //⌄ enclosing_range_start scip-java maven . . snapshots/describe(). // ⌄ enclosing_range_start scip-java maven . . snapshots/describe().(suit) @@ -202,7 +202,7 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Color#valueOf(). //⌄ enclosing_range_start scip-java maven . . snapshots/Color#valueOf().(value) //⌄ enclosing_range_start scip-java maven . . snapshots/Color#entries. -//⌄ enclosing_range_start scip-java maven . . snapshots/Color#entries.get(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Color#getEntries(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Color#RED. // ⌄ enclosing_range_start scip-java maven . . snapshots/Color#GREEN. // ⌄ enclosing_range_start scip-java maven . . snapshots/Color#BLUE. @@ -238,7 +238,7 @@ // display_name entries // signature_documentation // > public final static val entries: EnumEntries -// ^^^^^ definition scip-java maven . . snapshots/Color#entries.get(). +// ^^^^^ definition scip-java maven . . snapshots/Color#getEntries(). // kind Method // display_name entries // signature_documentation @@ -267,7 +267,7 @@ // ⌃ enclosing_range_end scip-java maven . . snapshots/Color#valueOf(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Color#valueOf().(value) // ⌃ enclosing_range_end scip-java maven . . snapshots/Color#entries. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Color#entries.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Color#getEntries(). //⌄ enclosing_range_start scip-java maven . . snapshots/useEnum(). fun useEnum(): Color = Color.RED @@ -287,7 +287,7 @@ //⌄ enclosing_range_start scip-java maven . . snapshots/Op#valueOf(). //⌄ enclosing_range_start scip-java maven . . snapshots/Op#valueOf().(value) //⌄ enclosing_range_start scip-java maven . . snapshots/Op#entries. -//⌄ enclosing_range_start scip-java maven . . snapshots/Op#entries.get(). +//⌄ enclosing_range_start scip-java maven . . snapshots/Op#getEntries(). enum class Op { // ^^ definition scip-java maven . . snapshots/Op# // kind Class @@ -320,7 +320,7 @@ // display_name entries // signature_documentation // > public final static val entries: EnumEntries -// ^^ definition scip-java maven . . snapshots/Op#entries.get(). +// ^^ definition scip-java maven . . snapshots/Op#getEntries(). // kind Method // display_name entries // signature_documentation @@ -412,5 +412,5 @@ //⌃ enclosing_range_end scip-java maven . . snapshots/Op#valueOf(). //⌃ enclosing_range_end scip-java maven . . snapshots/Op#valueOf().(value) //⌃ enclosing_range_end scip-java maven . . snapshots/Op#entries. -//⌃ enclosing_range_end scip-java maven . . snapshots/Op#entries.get(). +//⌃ enclosing_range_end scip-java maven . . snapshots/Op#getEntries(). diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Extensions.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Extensions.kt index 0c5bca5c9..e6cfca31e 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Extensions.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Extensions.kt @@ -32,7 +32,7 @@ // ⌃ enclosing_range_end scip-java maven . . snapshots/useExtension(). //⌄ enclosing_range_start scip-java maven . . snapshots/Int#asString. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Int#asString.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Int#getAsString(). val Int.asString: String get() = this.toString() // ^^^ reference scip-java maven . . kotlin/Int# // ^^^^^^^^ definition scip-java maven . . snapshots/Int#asString. @@ -41,14 +41,14 @@ // signature_documentation // > public final val Int.asString: String // ^^^^^^ reference scip-java maven . . kotlin/String# -// ^^^ definition scip-java maven . . snapshots/Int#asString.get(). +// ^^^ definition scip-java maven . . snapshots/Int#getAsString(). // kind Method // display_name asString // signature_documentation // > public get(): String // ^^^^^^^^ reference scip-java maven . . kotlin/Int#toString(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Int#asString. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Int#asString.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Int#getAsString(). //⌄ enclosing_range_start scip-java maven . . snapshots/useExtensionProperty(). fun useExtensionProperty() = 42.asString // ^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/useExtensionProperty(). @@ -57,7 +57,7 @@ // signature_documentation // > public final fun useExtensionProperty(): String // ^^^^^^^^ reference scip-java maven . . snapshots/Int#asString. -// ^^^^^^^^ reference scip-java maven . . snapshots/Int#asString.get(). +// ^^^^^^^^ reference scip-java maven . . snapshots/Int#getAsString(). // ⌃ enclosing_range_end scip-java maven . . snapshots/useExtensionProperty(). //⌄ enclosing_range_start scip-java maven . . snapshots/ExtensionReceiver# diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt index 40bc9c9ed..b0f77af39 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Generics.kt @@ -6,7 +6,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#[T] // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#``().(items) // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#items. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#items.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Container#getItems(). class Container>(private val items: MutableList) { // ^^^^^^^^^ definition scip-java maven . . snapshots/Container# // kind Class @@ -36,7 +36,7 @@ // signature_documentation // > private final val items: MutableList // ^^^^^ reference scip-java maven . . snapshots/Container#``().(items) -// ^^^^^ definition scip-java maven . . snapshots/Container#items.get(). +// ^^^^^ definition scip-java maven . . snapshots/Container#getItems(). // kind Method // display_name items // signature_documentation @@ -46,7 +46,7 @@ // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#[T] // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#``().(items) // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#items. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Container#items.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Container#getItems(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#add(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Container#add().(item) @@ -67,7 +67,7 @@ // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#add().(item) items.add(item) // ^^^^^ reference scip-java maven . . snapshots/Container#items. -// ^^^^^ reference scip-java maven . . snapshots/Container#items.get(). +// ^^^^^ reference scip-java maven . . snapshots/Container#getItems(). // ^^^ reference scip-java maven . . kotlin/collections/MutableList#add(). // ^^^^ reference scip-java maven . . snapshots/Container#add().(item) return this @@ -99,7 +99,7 @@ // ^^^^ reference scip-java maven . . kotlin/collections/List# // ^ reference scip-java maven . . snapshots/Container#mapItems().[R] // ^^^^^ reference scip-java maven . . snapshots/Container#items. -// ^^^^^ reference scip-java maven . . snapshots/Container#items.get(). +// ^^^^^ reference scip-java maven . . snapshots/Container#getItems(). // ^^^^^^^^^^ reference scip-java maven . . kotlin/collections/Iterable#mapNotNull(). // ^^^^^^^^^ reference scip-java maven . . snapshots/Container#mapItems().(transform) // ⌃ enclosing_range_end scip-java maven . . snapshots/Container#mapItems().[R] @@ -188,21 +188,21 @@ // ^^^ reference scip-java maven . . kotlin/Int# // ⌃ enclosing_range_end scip-java maven . . snapshots/MyAlias# //⌄ enclosing_range_start scip-java maven . . snapshots/aliasValue. -//⌄ enclosing_range_start scip-java maven . . snapshots/aliasValue.get(). +//⌄ enclosing_range_start scip-java maven . . snapshots/getAliasValue(). val aliasValue: MyAlias = 42 // ^^^^^^^^^^ definition scip-java maven . . snapshots/aliasValue. // kind Property // display_name aliasValue // signature_documentation // > public final val aliasValue: MyAlias -// ^^^^^^^^^^ definition scip-java maven . . snapshots/aliasValue.get(). +// ^^^^^^^^^^ definition scip-java maven . . snapshots/getAliasValue(). // kind Method // display_name aliasValue // signature_documentation // > public get(): MyAlias // ^^^^^^^ reference scip-java maven . . kotlin/Int# // ⌃ enclosing_range_end scip-java maven . . snapshots/aliasValue. -// ⌃ enclosing_range_end scip-java maven . . snapshots/aliasValue.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/getAliasValue(). //⌄ enclosing_range_start scip-java maven . . snapshots/identity(). // ⌄ enclosing_range_start scip-java maven . . snapshots/identity().[T] @@ -235,7 +235,7 @@ // ⌄ enclosing_range_start scip-java maven . . snapshots/Box#[T] // ⌄ enclosing_range_start scip-java maven . . snapshots/Box#``().(value) // ⌄ enclosing_range_start scip-java maven . . snapshots/Box#value. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Box#value.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Box#getValue(). class Box(val value: T) { // ^^^ definition scip-java maven . . snapshots/Box# // kind Class @@ -263,7 +263,7 @@ // signature_documentation // > public final val value: T // ^^^^^ reference scip-java maven . . snapshots/Box#``().(value) -// ^^^^^ definition scip-java maven . . snapshots/Box#value.get(). +// ^^^^^ definition scip-java maven . . snapshots/Box#getValue(). // kind Method // display_name value // signature_documentation @@ -272,7 +272,7 @@ // ⌃ enclosing_range_end scip-java maven . . snapshots/Box#[T] // ⌃ enclosing_range_end scip-java maven . . snapshots/Box#``().(value) // ⌃ enclosing_range_end scip-java maven . . snapshots/Box#value. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Box#value.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Box#getValue(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Box#``(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Box#unwrap(). fun unwrap(): T = value @@ -283,7 +283,7 @@ // > public final fun unwrap(): T // ^ reference scip-java maven . . snapshots/Box#[T] // ^^^^^ reference scip-java maven . . snapshots/Box#value. -// ^^^^^ reference scip-java maven . . snapshots/Box#value.get(). +// ^^^^^ reference scip-java maven . . snapshots/Box#getValue(). // ⌃ enclosing_range_end scip-java maven . . snapshots/Box#unwrap(). } //⌃ enclosing_range_end scip-java maven . . snapshots/Box# diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt index cdda4ea2a..5c3989d7a 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt @@ -40,21 +40,21 @@ // signature_documentation // > public abstract interface Animal : Any // ⌄ enclosing_range_start scip-java maven . . snapshots/Animal#favoriteNumber. -// ⌄ enclosing_range_start scip-java maven . . snapshots/Animal#favoriteNumber.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Animal#getFavoriteNumber(). val favoriteNumber: Int // ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Animal#favoriteNumber. // kind Property // display_name favoriteNumber // signature_documentation // > public abstract val favoriteNumber: Int -// ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Animal#favoriteNumber.get(). +// ^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/Animal#getFavoriteNumber(). // kind Method // display_name favoriteNumber // signature_documentation // > public get(): Int // ^^^ reference scip-java maven . . kotlin/Int# // ⌃ enclosing_range_end scip-java maven . . snapshots/Animal#favoriteNumber. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Animal#favoriteNumber.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Animal#getFavoriteNumber(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Animal#sound(). fun sound(): String // ^^^^^ definition scip-java maven . . snapshots/Animal#sound(). @@ -90,15 +90,15 @@ // signature_documentation // > public open override val favoriteNumber: Int // ^^^ reference scip-java maven . . kotlin/Int# -// ⌄ enclosing_range_start scip-java maven . . snapshots/Bird#favoriteNumber.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Bird#getFavoriteNumber(). get() = 42 -// ^^^ definition scip-java maven . . snapshots/Bird#favoriteNumber.get(). +// ^^^ definition scip-java maven . . snapshots/Bird#getFavoriteNumber(). // kind Method // display_name favoriteNumber // signature_documentation // > public get(): Int // ⌃ enclosing_range_end scip-java maven . . snapshots/Bird#favoriteNumber. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Bird#favoriteNumber.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Bird#getFavoriteNumber(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Bird#sound(). override fun sound(): String { @@ -138,15 +138,15 @@ // signature_documentation // > public open override val favoriteNumber: Int // ^^^ reference scip-java maven . . kotlin/Int# -// ⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#favoriteNumber.get(). +// ⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#getFavoriteNumber(). get() = 1337 -// ^^^ definition scip-java maven . . snapshots/Seagull#favoriteNumber.get(). +// ^^^ definition scip-java maven . . snapshots/Seagull#getFavoriteNumber(). // kind Method // display_name favoriteNumber // signature_documentation // > public get(): Int // ⌃ enclosing_range_end scip-java maven . . snapshots/Seagull#favoriteNumber. -// ⌃ enclosing_range_end scip-java maven . . snapshots/Seagull#favoriteNumber.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/Seagull#getFavoriteNumber(). // ⌄ enclosing_range_start scip-java maven . . snapshots/Seagull#sound(). override fun sound(): String { // ^^^^^ definition scip-java maven . . snapshots/Seagull#sound(). diff --git a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt index 148a88378..b5f36c317 100644 --- a/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt +++ b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt @@ -2,7 +2,7 @@ // ^^^^^^^^^ reference scip-java maven . . snapshots/ //⌄ enclosing_range_start scip-java maven . . snapshots/x. -//⌄ enclosing_range_start scip-java maven . . snapshots/x.get(). +//⌄ enclosing_range_start scip-java maven . . snapshots/getX(). // ⌄ enclosing_range_start local 0 // ⌄ enclosing_range_start local 1 val x = arrayListOf().forEachIndexed { i, s -> println("$i $s") } @@ -11,7 +11,7 @@ // display_name x // signature_documentation // > public final val x: Unit -// ^ definition scip-java maven . . snapshots/x.get(). +// ^ definition scip-java maven . . snapshots/getX(). // kind Method // display_name x // signature_documentation @@ -35,17 +35,17 @@ // ⌃ enclosing_range_end local 0 // ⌃ enclosing_range_end local 1 // ⌃ enclosing_range_end scip-java maven . . snapshots/x. -// ⌃ enclosing_range_end scip-java maven . . snapshots/x.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/getX(). //⌄ enclosing_range_start scip-java maven . . snapshots/y. -//⌄ enclosing_range_start scip-java maven . . snapshots/y.get(). +//⌄ enclosing_range_start scip-java maven . . snapshots/getY(). val y = "fdsa".run { this.toByteArray() } // ^ definition scip-java maven . . snapshots/y. // kind Property // display_name y // signature_documentation // > public final val y: ByteArray -// ^ definition scip-java maven . . snapshots/y.get(). +// ^ definition scip-java maven . . snapshots/getY(). // kind Method // display_name y // signature_documentation @@ -53,10 +53,10 @@ // ^^^ reference scip-java maven . . kotlin/run(+1). // ^^^^^^^^^^^ reference scip-java maven . . kotlin/text/String#toByteArray(). // ⌃ enclosing_range_end scip-java maven . . snapshots/y. -// ⌃ enclosing_range_end scip-java maven . . snapshots/y.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/getY(). //⌄ enclosing_range_start scip-java maven . . snapshots/z. -//⌄ enclosing_range_start scip-java maven . . snapshots/z.get(). +//⌄ enclosing_range_start scip-java maven . . snapshots/getZ(). // ⌄ enclosing_range_start local 2 val z = y.let { it.size } // ^ definition scip-java maven . . snapshots/z. @@ -64,13 +64,13 @@ // display_name z // signature_documentation // > public final val z: Int -// ^ definition scip-java maven . . snapshots/z.get(). +// ^ definition scip-java maven . . snapshots/getZ(). // kind Method // display_name z // signature_documentation // > public get(): Int // ^ reference scip-java maven . . snapshots/y. -// ^ reference scip-java maven . . snapshots/y.get(). +// ^ reference scip-java maven . . snapshots/getY(). // ^^^ reference scip-java maven . . kotlin/let(). // ^^^^^^^^^^^ definition local 2 // kind Parameter @@ -79,9 +79,9 @@ // > it: ByteArray // ^^ reference local 2 // ^^^^ reference scip-java maven . . kotlin/ByteArray#size. -// ^^^^ reference scip-java maven . . kotlin/ByteArray#size.get(). +// ^^^^ reference scip-java maven . . kotlin/ByteArray#getSize(). // ⌃ enclosing_range_end scip-java maven . . snapshots/z. -// ⌃ enclosing_range_end scip-java maven . . snapshots/z.get(). +// ⌃ enclosing_range_end scip-java maven . . snapshots/getZ(). // ⌃ enclosing_range_end local 2 //⌄ enclosing_range_start scip-java maven . . snapshots/useLambda().