From 23f635516542a7a353c1c01ab7ed2aeaae6f7676 Mon Sep 17 00:00:00 2001 From: Nicolas Guichard Date: Tue, 7 Jul 2026 13:18:22 +0200 Subject: [PATCH 01/19] Don't add project repositories when settings repositories are set This goes one bit further than a3eb2f23 and doesn't even try to set project repositories when settings repositories are set. This prevents the build from failing on projects that set repositories in Settings but don't set FAIL_ON_PROJECT_REPOS, such as the KotlinConf app. --- .../scip_java/gradle/ScipGradlePlugin.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 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 ed223b72..8ad6ddfa 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 @@ -3,11 +3,12 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import org.gradle.api.InvalidUserCodeException; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.initialization.Settings; +import org.gradle.api.internal.GradleInternal; import org.gradle.api.tasks.compile.JavaCompile; public class ScipGradlePlugin implements Plugin { @@ -18,18 +19,18 @@ public void apply(Project project) { } private void configureProject(Project project) { - // Inject Maven Central/local so the indexer (and plugins like protobuf that - // resolve their own artifacts) can resolve dependencies even when the build - // being indexed doesn't declare any repositories of its own. - try { + // See https://github.com/gradle/gradle/issues/27260 + Settings settings = ((GradleInternal) (project.getGradle())).getSettings(); + + if (settings.getDependencyResolutionManagement().getRepositories().isEmpty()) { + // Inject Maven Central/local so the indexer (and plugins like protobuf that + // resolve their own artifacts) can resolve dependencies even when the build + // being indexed doesn't declare any repositories of its own. project.getRepositories().add(project.getRepositories().mavenCentral()); project.getRepositories().add(project.getRepositories().mavenLocal()); - } catch (InvalidUserCodeException exc) { - // FAIL_ON_PROJECT_REPOS forbids project repositories; they are declared - // in settings instead, so the injection isn't needed (issue #847). - project - .getLogger() - .info("scip-java: not injecting Maven Central/local repositories: " + exc.getMessage()); + } else { + // repositories are declared in settings instead, so the injection isn't needed (issue #847). + project.getLogger().info("scip-java: not injecting Maven Central/local repositories"); } Map extraProperties = From 6a8cc6c1b0aba6838c5e2e7c04b510e3f302b674 Mon Sep 17 00:00:00 2001 From: Nicolas Guichard Date: Fri, 3 Jul 2026 14:52:12 +0200 Subject: [PATCH 02/19] Update to Kotlin 2.2.20 For scip-kotlinc: * CheckerContext.containingFile was renamed to containingFileSymbol. * FirCallableSymbol<*>.directOverriddenSymbolsSafe now takes context by context parameter. * FirCallableSymbol.callableId was made nullable and is replaced by FirCallableSymbol.callableIdForRendering for rendering purpose. * Anonymous objects are now considered as locals. Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/3f4756486010cce6163b8b409441cd1e51278008 --- gradle/libs.versions.toml | 2 +- .../gradle/kotlin-jvm-toolchains/build.gradle | 2 +- .../fixtures/gradle/kotlin2/build.gradle | 2 +- .../scip_java/kotlinc/AnalyzerCheckers.kt | 78 ++++++------------- .../kotlinc/ScipTextDocumentBuilder.kt | 13 ++-- .../scip_java/kotlinc/ScipVisitor.kt | 66 +++++++--------- .../scip_java/kotlinc/test/AnalyzerTest.kt | 20 ++--- .../common/src/main/kotlin/snapshots/Class.kt | 18 ++--- 8 files changed, 83 insertions(+), 118 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d1286f3e..1150452b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ gradle-api = "8.11.1" junit-jupiter = "5.11.4" kctfork = "0.7.1" kotest = "6.2.1" -kotlin = "2.2.0" +kotlin = "2.2.20" kotlinx-serialization = "1.11.0" lombok = "1.18.46" maven-plugin-annotations = "3.15.2" diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle b/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle index 21b68107..712fe1a6 100644 --- a/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.jetbrains.kotlin.jvm' version '2.2.0' + id 'org.jetbrains.kotlin.jvm' version '2.2.20' } java { toolchain { diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle b/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle index d0a96fa8..43952f0b 100644 --- a/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.jetbrains.kotlin.jvm' version '2.2.0' + id 'org.jetbrains.kotlin.jvm' version '2.2.20' } kotlin { jvmToolchain(17) 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 index 37ece4b5..3258bc0c 100644 --- 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 @@ -134,7 +134,7 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio if (names != null) { eachFqNameElement(fqName, source.treeStructure, names) { fqName, name -> - visitor?.visitPackage(fqName, name, context) + visitor?.visitPackage(fqName, name) } } } @@ -161,13 +161,13 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio ) if (klass != null) { - visitor?.visitClassReference(klass, name, context) + visitor?.visitClassReference(klass, name) } else if (callables.isNotEmpty()) { for (callable in callables) { - visitor?.visitCallableReference(callable, name, context) + visitor?.visitCallableReference(callable, name) } } else { - visitor?.visitPackage(fqName, name, context) + visitor?.visitPackage(fqName, name) } } } @@ -179,7 +179,7 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio context(context: CheckerContext, reporter: DiagnosticReporter) override fun check(declaration: FirClassLikeDeclaration) { val source = declaration.source ?: return - val ktFile = context.containingFile?.sourceFile ?: return + val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] val objectKeyword = if (declaration is FirAnonymousObject) { @@ -192,7 +192,6 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio visitor?.visitClassOrObject( declaration, objectKeyword ?: getIdentifier(source), - context, enclosingSource = source, ) @@ -201,7 +200,7 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio val superSymbol = superType.toClassLikeSymbol(context.session) val superSource = superType.source if (superSymbol != null && superSource != null) { - visitor?.visitClassReference(superSymbol, superSource, context) + visitor?.visitClassReference(superSymbol, superSource) } } } @@ -212,7 +211,7 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio context(context: CheckerContext, reporter: DiagnosticReporter) override fun check(declaration: FirConstructor) { val source = declaration.source ?: return - val ktFile = context.containingFile?.sourceFile ?: return + val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] if (declaration.isPrimary) { @@ -238,14 +237,12 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio visitor?.visitPrimaryConstructor( declaration, constructorKeyboard ?: objectKeyword ?: getIdentifier(klassSource), - context, enclosingSource = source, ) } else { visitor?.visitSecondaryConstructor( declaration, getIdentifier(source), - context, enclosingSource = source, ) } @@ -256,12 +253,11 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio context(context: CheckerContext, reporter: DiagnosticReporter) override fun check(declaration: FirSimpleFunction) { val source = declaration.source ?: return - val ktFile = context.containingFile?.sourceFile ?: return + val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] visitor?.visitNamedFunction( declaration, getIdentifier(source), - context, enclosingSource = source, ) @@ -270,7 +266,7 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio if ( klass != null && klassSource != null && klassSource.kind !is KtFakeSourceElementKind ) { - visitor?.visitClassReference(klass, getIdentifier(klassSource), context) + visitor?.visitClassReference(klass, getIdentifier(klassSource)) } } } @@ -280,9 +276,9 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio context(context: CheckerContext, reporter: DiagnosticReporter) override fun check(declaration: FirAnonymousFunction) { val source = declaration.source ?: return - val ktFile = context.containingFile?.sourceFile ?: return + val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] - visitor?.visitNamedFunction(declaration, source, context, enclosingSource = source) + visitor?.visitNamedFunction(declaration, source, enclosingSource = source) } } @@ -290,21 +286,16 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio context(context: CheckerContext, reporter: DiagnosticReporter) override fun check(declaration: FirProperty) { val source = declaration.source ?: return - val ktFile = context.containingFile?.sourceFile ?: return + val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] - visitor?.visitProperty( - declaration, - getIdentifier(source), - context, - enclosingSource = source, - ) + visitor?.visitProperty(declaration, getIdentifier(source), enclosingSource = source) val klass = declaration.returnTypeRef.toClassLikeSymbol(context.session) val klassSource = declaration.returnTypeRef.source if ( klass != null && klassSource != null && klassSource.kind !is KtFakeSourceElementKind ) { - visitor?.visitClassReference(klass, getIdentifier(klassSource), context) + visitor?.visitClassReference(klass, getIdentifier(klassSource)) } } } @@ -313,21 +304,16 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio context(context: CheckerContext, reporter: DiagnosticReporter) override fun check(declaration: FirValueParameter) { val source = declaration.source ?: return - val ktFile = context.containingFile?.sourceFile ?: return + val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] - visitor?.visitParameter( - declaration, - getIdentifier(source), - context, - enclosingSource = source, - ) + visitor?.visitParameter(declaration, getIdentifier(source), enclosingSource = source) val klass = declaration.returnTypeRef.toClassLikeSymbol(context.session) val klassSource = declaration.returnTypeRef.source if ( klass != null && klassSource != null && klassSource.kind !is KtFakeSourceElementKind ) { - visitor?.visitClassReference(klass, getIdentifier(klassSource), context) + visitor?.visitClassReference(klass, getIdentifier(klassSource)) } } } @@ -336,12 +322,11 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio context(context: CheckerContext, reporter: DiagnosticReporter) override fun check(declaration: FirTypeParameter) { val source = declaration.source ?: return - val ktFile = context.containingFile?.sourceFile ?: return + val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] visitor?.visitTypeParameter( declaration, getIdentifier(source), - context, enclosingSource = source, ) } @@ -351,14 +336,9 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio context(context: CheckerContext, reporter: DiagnosticReporter) override fun check(declaration: FirTypeAlias) { val source = declaration.source ?: return - val ktFile = context.containingFile?.sourceFile ?: return + val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] - visitor?.visitTypeAlias( - declaration, - getIdentifier(source), - context, - enclosingSource = source, - ) + visitor?.visitTypeAlias(declaration, getIdentifier(source), enclosingSource = source) } } @@ -367,7 +347,7 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio context(context: CheckerContext, reporter: DiagnosticReporter) override fun check(declaration: FirPropertyAccessor) { val source = declaration.source ?: return - val ktFile = context.containingFile?.sourceFile ?: return + val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] val identifierSource = if (declaration.isGetter) { @@ -382,12 +362,7 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio getIdentifier(source) } - visitor?.visitPropertyAccessor( - declaration, - identifierSource, - context, - enclosingSource = source, - ) + visitor?.visitPropertyAccessor(declaration, identifierSource, enclosingSource = source) } } @@ -401,12 +376,11 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio return } - val ktFile = context.containingFile?.sourceFile ?: return + val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] visitor?.visitSimpleNameExpression( calleeReference, getIdentifier(calleeReference.source ?: source), - context, ) val resolvedSymbol = calleeReference.resolvedSymbol @@ -420,7 +394,6 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio visitor?.visitClassReference( referencedKlass, getIdentifier(calleeReference.source ?: source), - context, ) } } @@ -432,14 +405,12 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio visitor?.visitCallableReference( it, getIdentifier(calleeReference.source ?: source), - context, ) } resolvedSymbol.setterSymbol?.let { visitor?.visitCallableReference( it, getIdentifier(calleeReference.source ?: source), - context, ) } } @@ -454,13 +425,12 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio val source = typeRef.source ?: return val classSymbol = expression.conversionTypeRef.toClassLikeSymbol(context.session) ?: return - val ktFile = context.containingFile?.sourceFile ?: return + val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] visitor?.visitClassReference( classSymbol, getIdentifier(expression.conversionTypeRef.source ?: source), - context, ) } } 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 index f767dda2..55f353fa 100644 --- 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 @@ -39,26 +39,26 @@ class ScipTextDocumentBuilder( fun build(): Document = documentBuilder.build("kotlin", relativePath(), fileText) + context(context: CheckerContext) fun emitScipData( firBasedSymbol: FirBasedSymbol<*>?, symbol: Symbol, element: KtSourceElement, isDefinition: Boolean, - context: CheckerContext, enclosingSource: KtSourceElement? = null, ) { documentBuilder.addOccurrence(occurrence(symbol, element, isDefinition, enclosingSource)) if (isDefinition) { - documentBuilder.addSymbol(symbolInformation(firBasedSymbol, symbol, element, context)) + documentBuilder.addSymbol(symbolInformation(firBasedSymbol, symbol, element)) } } @OptIn(SymbolInternals::class) + context(context: CheckerContext) private fun symbolInformation( firBasedSymbol: FirBasedSymbol<*>?, symbol: Symbol, element: KtSourceElement, - context: CheckerContext, ): SymbolInformation { val supers = when (firBasedSymbol) { @@ -68,7 +68,7 @@ class ScipTextDocumentBuilder( .mapNotNull { it.toClassLikeSymbol(firBasedSymbol.moduleData.session) } .flatMap { cache[it] } is FirFunctionSymbol<*> -> - firBasedSymbol.directOverriddenSymbolsSafe(context).flatMap { cache[it] } + firBasedSymbol.directOverriddenSymbolsSafe().flatMap { cache[it] } else -> emptyList() } return symbolInformation { @@ -184,13 +184,14 @@ class ScipTextDocumentBuilder( } companion object { - @OptIn(SymbolInternals::class) + @OptIn(SymbolInternals::class, RenderingInternals::class) private fun displayName(firBasedSymbol: FirBasedSymbol<*>): String = when (firBasedSymbol) { is FirClassSymbol -> firBasedSymbol.classId.shortClassName.asString() is FirPropertyAccessorSymbol -> firBasedSymbol.fir.propertySymbol.name.asString() is FirFunctionSymbol -> firBasedSymbol.callableId.callableName.asString() - is FirPropertySymbol -> firBasedSymbol.callableId.callableName.asString() + is FirPropertySymbol -> + firBasedSymbol.callableIdForRendering.callableName.asString() is FirVariableSymbol -> firBasedSymbol.name.asString() 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 index 80b9866e..d5d66886 100644 --- 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 @@ -33,10 +33,10 @@ class ScipVisitor( fun build(): Document = documentBuilder.build() + context(context: CheckerContext) private fun Sequence?.emitAll( element: KtSourceElement, isDefinition: Boolean, - context: CheckerContext, enclosingSource: KtSourceElement? = null, ): List? = this?.onEach { (firBasedSymbol, symbol) -> @@ -45,7 +45,6 @@ class ScipVisitor( symbol, element, isDefinition, - context, enclosingSource, ) } @@ -55,132 +54,127 @@ class ScipVisitor( private fun Sequence.with(firBasedSymbol: FirBasedSymbol<*>?) = this.map { SymbolDescriptorPair(firBasedSymbol, it) } - fun visitPackage(pkg: FqName, element: KtSourceElement, context: CheckerContext) { - cache[pkg].with(null).emitAll(element, isDefinition = false, context) + context(context: CheckerContext) + fun visitPackage(pkg: FqName, element: KtSourceElement) { + cache[pkg].with(null).emitAll(element, isDefinition = false) } - fun visitClassReference( - firClassSymbol: FirClassLikeSymbol<*>, - element: KtSourceElement, - context: CheckerContext, - ) { - cache[firClassSymbol].with(firClassSymbol).emitAll(element, isDefinition = false, context) + context(context: CheckerContext) + fun visitClassReference(firClassSymbol: FirClassLikeSymbol<*>, element: KtSourceElement) { + cache[firClassSymbol].with(firClassSymbol).emitAll(element, isDefinition = false) } - fun visitCallableReference( - firClassSymbol: FirCallableSymbol<*>, - element: KtSourceElement, - context: CheckerContext, - ) { - cache[firClassSymbol].with(firClassSymbol).emitAll(element, isDefinition = false, context) + 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, - context: CheckerContext, enclosingSource: KtSourceElement? = null, ) { cache[firClass.symbol] .with(firClass.symbol) - .emitAll(element, isDefinition = true, context, enclosingSource) + .emitAll(element, isDefinition = true, enclosingSource) } + context(context: CheckerContext) fun visitPrimaryConstructor( firConstructor: FirConstructor, source: KtSourceElement, - context: CheckerContext, enclosingSource: KtSourceElement? = null, ) { cache[firConstructor.symbol] .with(firConstructor.symbol) - .emitAll(source, isDefinition = true, context, enclosingSource) + .emitAll(source, isDefinition = true, enclosingSource) } + context(context: CheckerContext) fun visitSecondaryConstructor( firConstructor: FirConstructor, source: KtSourceElement, - context: CheckerContext, enclosingSource: KtSourceElement? = null, ) { cache[firConstructor.symbol] .with(firConstructor.symbol) - .emitAll(source, isDefinition = true, context, enclosingSource) + .emitAll(source, isDefinition = true, enclosingSource) } + context(context: CheckerContext) fun visitNamedFunction( firFunction: FirFunction, source: KtSourceElement, - context: CheckerContext, enclosingSource: KtSourceElement? = null, ) { cache[firFunction.symbol] .with(firFunction.symbol) - .emitAll(source, isDefinition = true, context, enclosingSource) + .emitAll(source, isDefinition = true, enclosingSource) } + context(context: CheckerContext) fun visitProperty( firProperty: FirProperty, source: KtSourceElement, - context: CheckerContext, enclosingSource: KtSourceElement? = null, ) { cache[firProperty.symbol] .with(firProperty.symbol) - .emitAll(source, isDefinition = true, context, enclosingSource) + .emitAll(source, isDefinition = true, enclosingSource) } + context(context: CheckerContext) fun visitParameter( firParameter: FirValueParameter, source: KtSourceElement, - context: CheckerContext, enclosingSource: KtSourceElement? = null, ) { cache[firParameter.symbol] .with(firParameter.symbol) - .emitAll(source, isDefinition = true, context, enclosingSource) + .emitAll(source, isDefinition = true, enclosingSource) } + context(context: CheckerContext) fun visitTypeParameter( firTypeParameter: FirTypeParameter, source: KtSourceElement, - context: CheckerContext, enclosingSource: KtSourceElement? = null, ) { cache[firTypeParameter.symbol] .with(firTypeParameter.symbol) - .emitAll(source, isDefinition = true, context, enclosingSource) + .emitAll(source, isDefinition = true, enclosingSource) } + context(context: CheckerContext) fun visitTypeAlias( firTypeAlias: FirTypeAlias, source: KtSourceElement, - context: CheckerContext, enclosingSource: KtSourceElement? = null, ) { cache[firTypeAlias.symbol] .with(firTypeAlias.symbol) - .emitAll(source, isDefinition = true, context, enclosingSource) + .emitAll(source, isDefinition = true, enclosingSource) } + context(context: CheckerContext) fun visitPropertyAccessor( firPropertyAccessor: FirPropertyAccessor, source: KtSourceElement, - context: CheckerContext, enclosingSource: KtSourceElement? = null, ) { cache[firPropertyAccessor.symbol] .with(firPropertyAccessor.symbol) - .emitAll(source, isDefinition = true, context, enclosingSource) + .emitAll(source, isDefinition = true, enclosingSource) } + context(context: CheckerContext) fun visitSimpleNameExpression( firResolvedNamedReference: FirResolvedNamedReference, source: KtSourceElement, - context: CheckerContext, ) { cache[firResolvedNamedReference.resolvedSymbol] .with(firResolvedNamedReference.resolvedSymbol) - .emitAll(source, isDefinition = false, context) + .emitAll(source, isDefinition = false) } } 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 index 36e94ad5..55b422c3 100644 --- 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 @@ -502,7 +502,7 @@ class AnalyzerTest { }, scipOccurrence { role = DEFINITION - symbol = "sample/``#" + symbol = "local 1" range { startLine = 7 startCharacter = 12 @@ -518,7 +518,7 @@ class AnalyzerTest { }, scipOccurrence { role = DEFINITION - symbol = "sample/``#``()." + symbol = "local 2" range { startLine = 7 startCharacter = 12 @@ -544,7 +544,7 @@ class AnalyzerTest { }, scipOccurrence { role = DEFINITION - symbol = "sample/``#foo()." + symbol = "local 3" range { startLine = 8 startCharacter = 21 @@ -560,7 +560,7 @@ class AnalyzerTest { }, scipOccurrence { role = DEFINITION - symbol = "sample/``#" + symbol = "local 5" range { startLine = 10 startCharacter = 12 @@ -576,7 +576,7 @@ class AnalyzerTest { }, scipOccurrence { role = DEFINITION - symbol = "sample/``#``()." + symbol = "local 6" range { startLine = 10 startCharacter = 12 @@ -602,7 +602,7 @@ class AnalyzerTest { }, scipOccurrence { role = DEFINITION - symbol = "sample/``#foo()." + symbol = "local 7" range { startLine = 11 startCharacter = 21 @@ -627,25 +627,25 @@ class AnalyzerTest { signatureText = "public abstract interface Interface : Any" }, scipSymbol { - symbol = "sample/``#" + symbol = "local 1" displayName = "" signatureText = "object : Interface" addOverriddenSymbols("sample/Interface#") }, scipSymbol { - symbol = "sample/``#foo()." + symbol = "local 3" displayName = "foo" signatureText = "public open override fun foo(): Unit" addOverriddenSymbols("sample/Interface#foo().") }, scipSymbol { - symbol = "sample/``#" + symbol = "local 5" displayName = "" signatureText = "object : Interface" addOverriddenSymbols("sample/Interface#") }, scipSymbol { - symbol = "sample/``#foo()." + symbol = "local 7" displayName = "foo" signatureText = "public open override fun foo(): Unit" addOverriddenSymbols("sample/Interface#foo().") 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 7ee74275..3ec43a15 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 @@ -75,29 +75,29 @@ // 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 // display_name // signature_documentation // > object : Any -// ^^^^^^ definition scip-java maven . . snapshots/``#``(). +// ^^^^^^ definition local 1 // 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 // display_name doStuff // signature_documentation // > public final fun doStuff(): 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#getAsdf(). -// ⌃ 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 e8deeffadc1053c123db28c89975732e7b8b463c Mon Sep 17 00:00:00 2001 From: Nicolas Guichard Date: Fri, 3 Jul 2026 15:14:32 +0200 Subject: [PATCH 03/19] scip-kotlinc: Populate SymbolInformation.Kind This will allow us to distinguish between method and constructors, and get better kind information for locals. Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/6affaf7a525cea82a018cccf8d58bbedcef8a8e1 --- .../kotlinc/ScipTextDocumentBuilder.kt | 20 +++++++++++++++++++ .../scip_java/kotlinc/test/AnalyzerTest.kt | 19 ++++++++++++++++++ .../scip_java/kotlinc/test/ScipBuilders.kt | 2 ++ .../scip_java/kotlinc/test/ScipSymbolsTest.kt | 3 +++ .../common/src/main/kotlin/snapshots/Class.kt | 17 ++++++++++++++++ .../main/kotlin/snapshots/CompanionOwner.kt | 6 ++++++ .../src/main/kotlin/snapshots/Docstrings.kt | 5 +++++ .../src/main/kotlin/snapshots/Functions.kt | 2 ++ .../main/kotlin/snapshots/Implementations.kt | 17 ++++++++++++++++ .../src/main/kotlin/snapshots/Lambdas.kt | 9 +++++++++ .../src/main/kotlin/snapshots/ObjectKt.kt | 4 ++++ 11 files changed, 104 insertions(+) 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 index 55f353fa..dab8f500 100644 --- 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 @@ -5,10 +5,13 @@ import java.nio.file.Paths import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.KtSourceFile import org.jetbrains.kotlin.fir.FirElement +import org.jetbrains.kotlin.fir.FirPackageDirective import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.checkers.directOverriddenSymbolsSafe import org.jetbrains.kotlin.fir.analysis.checkers.toClassLikeSymbol import org.jetbrains.kotlin.fir.analysis.getChild +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.declarations.utils.isInterface import org.jetbrains.kotlin.fir.renderer.* import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.SymbolInternals @@ -19,6 +22,7 @@ import org.jetbrains.kotlin.text import org.scip_code.scip.Document import org.scip_code.scip.Occurrence import org.scip_code.scip.SymbolInformation +import org.scip_code.scip.SymbolInformation.Kind import org.scip_code.scip.SymbolRole import org.scip_code.scip.relationship import org.scip_code.scip.signature @@ -84,6 +88,7 @@ class ScipTextDocumentBuilder( } docComment(firBasedSymbol.fir)?.let { documentation += it } } + this.kind = scipKind(firBasedSymbol?.fir) for (parent in supers) { relationships += relationship { this.symbol = parent.toString() @@ -159,6 +164,21 @@ class ScipTextDocumentBuilder( return stripKdoc(kdoc).ifEmpty { null } } + private fun scipKind(element: FirElement?): Kind = + when (element) { + is FirClass if element.isInterface -> Kind.Interface + 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 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 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 index 55b422c3..338b41b2 100644 --- 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 @@ -15,6 +15,7 @@ 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) @@ -107,11 +108,13 @@ class AnalyzerTest { arrayOf( scipSymbol { symbol = "sample/Banana#" + kind = Kind.Class displayName = "Banana" signatureText = "public final class Banana : Any" }, scipSymbol { symbol = "sample/Banana#foo()." + kind = Kind.Method displayName = "foo" signatureText = "public final fun foo(): Unit" }, @@ -279,21 +282,25 @@ class AnalyzerTest { arrayOf( scipSymbol { symbol = "sample/foo()." + kind = Kind.Method displayName = "foo" signatureText = "public final fun foo(): Unit" }, scipSymbol { symbol = "local 0" + kind = Kind.Class displayName = "LocalClass" signatureText = "local final class LocalClass : Any" }, scipSymbol { symbol = "local 1" + kind = Kind.Constructor displayName = "LocalClass" signatureText = "public constructor(): LocalClass" }, scipSymbol { symbol = "local 2" + kind = Kind.Method displayName = "localClassMethod" signatureText = "public final fun localClassMethod(): Unit" }, @@ -410,22 +417,26 @@ class AnalyzerTest { arrayOf( scipSymbol { symbol = "sample/Interface#" + kind = Kind.Interface displayName = "Interface" signatureText = "public abstract interface Interface : Any" }, scipSymbol { symbol = "sample/Interface#foo()." + kind = Kind.Method displayName = "foo" signatureText = "public abstract fun foo(): Unit\n" }, scipSymbol { symbol = "sample/Class#" + kind = Kind.Class displayName = "Class" signatureText = "public final class Class : Interface" addOverriddenSymbols("sample/Interface#") }, scipSymbol { symbol = "sample/Class#foo()." + kind = Kind.Method displayName = "foo" signatureText = "public open override fun foo(): Unit" addOverriddenSymbols("sample/Interface#foo().") @@ -623,29 +634,34 @@ class AnalyzerTest { arrayOf( scipSymbol { symbol = "sample/Interface#" + kind = Kind.Interface displayName = "Interface" signatureText = "public abstract interface Interface : Any" }, scipSymbol { symbol = "local 1" + kind = Kind.Class displayName = "" signatureText = "object : Interface" addOverriddenSymbols("sample/Interface#") }, scipSymbol { symbol = "local 3" + kind = Kind.Method displayName = "foo" signatureText = "public open override fun foo(): Unit" addOverriddenSymbols("sample/Interface#foo().") }, scipSymbol { symbol = "local 5" + kind = Kind.Class displayName = "" signatureText = "object : Interface" addOverriddenSymbols("sample/Interface#") }, scipSymbol { symbol = "local 7" + kind = Kind.Method displayName = "foo" signatureText = "public open override fun foo(): Unit" addOverriddenSymbols("sample/Interface#foo().") @@ -1300,6 +1316,7 @@ class AnalyzerTest { arrayOf( scipSymbol { symbol = "hello/sample/Apple#" + kind = Kind.Class displayName = "Apple" signatureText = "public final class Apple : Any" } @@ -1385,11 +1402,13 @@ class AnalyzerTest { arrayOf( scipSymbol { symbol = "sample/Banana#" + kind = Kind.Class displayName = "Banana" signatureText = "public final class Banana : Any" }, scipSymbol { symbol = "sample/Banana#foo()." + kind = Kind.Method displayName = "foo" signatureText = "public final fun foo(): Unit" }, 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 index 1e8ee95a..a7d549dd 100644 --- 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 @@ -76,6 +76,7 @@ class ScipOccurrenceBuilder { @ScipBuilderDsl class ScipSymbolInformationBuilder { var symbol: String = "" + var kind: SymbolInformation.Kind = SymbolInformation.Kind.UnspecifiedKind var displayName: String = "" var signatureText: String? = null private val docs = mutableListOf() @@ -95,6 +96,7 @@ class ScipSymbolInformationBuilder { internal fun build(): SymbolInformation = symbolInformation { symbol = this@ScipSymbolInformationBuilder.symbol + kind = this@ScipSymbolInformationBuilder.kind if (this@ScipSymbolInformationBuilder.displayName.isNotEmpty()) { displayName = this@ScipSymbolInformationBuilder.displayName } 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 index 048d33ce..e0865cd2 100644 --- 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 @@ -3,6 +3,7 @@ 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 @@ -775,12 +776,14 @@ class ScipSymbolsTest { listOf( scipSymbol { symbol = "x." + kind = Kind.Property displayName = "x" signatureText = "public final val x: String" documentation("hello world\n test content") }, scipSymbol { symbol = "getX()." + kind = Kind.Method displayName = "x" signatureText = "public get(): String" documentation("hello world\n test content") 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 3ec43a15..e24088f4 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 local 1 object { // ^^^^^^ definition local 0 +// kind Class // display_name // signature_documentation // > object : Any // ^^^^^^ definition local 1 +// kind Constructor // display_name // signature_documentation // > private constructor(): // ⌄ enclosing_range_start local 2 fun doStuff() = Unit // ^^^^^^^ definition local 2 +// kind Method // display_name doStuff // signature_documentation // > public final fun doStuff(): Unit @@ -102,6 +115,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 @@ -111,10 +125,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 @@ -126,6 +142,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 d81175cd..f16b420f 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/Docstrings.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Docstrings.kt index b7a60c8a..69c6333f 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/Functions.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Functions.kt index 2b9224e8..0ee9ed76 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/Implementations.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt index c9c88d89..5a600542 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 : {kotlin/AutoCloseable=} 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 0d80eb5b..af772e26 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,20 +7,24 @@ // ⌄ 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 // ^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/arrayListOf(). // ^^^^^^^^^^^^^^ 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 @@ -36,10 +40,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 @@ -53,10 +59,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 @@ -64,6 +72,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 3b803c7c..6211a42e 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 df1a7fcfd2bd9e258bbadfb6c5872d6a80758404 Mon Sep 17 00:00:00 2001 From: Nicolas Guichard Date: Fri, 3 Jul 2026 15:24:09 +0200 Subject: [PATCH 04/19] scip-kotlinc: Rework getters and setters to be property children This changes the symbols of getters and setters to be `x.get().` and `x.set().` instead of `getX().` and `setX().`. Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/7b4bd701b8cfc83855f0e336fb81588d04a19741 --- .../scip_java/kotlinc/SymbolsCache.kt | 12 ++----- .../scip_java/kotlinc/test/ScipSymbolsTest.kt | 24 +++++++------- .../common/src/main/kotlin/snapshots/Class.kt | 32 +++++++++---------- .../main/kotlin/snapshots/Implementations.kt | 18 +++++------ .../src/main/kotlin/snapshots/Lambdas.kt | 22 ++++++------- 5 files changed, 51 insertions(+), 57 deletions(-) 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 index 57f5b1b6..c7eb0f9a 100644 --- 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 @@ -14,7 +14,6 @@ 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.name.FqName -import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly import org.scip_code.scip_java.kotlinc.ScipSymbolDescriptor.Kind import org.scip_code.scip_java.shared.LocalSymbolsCache as SharedLocalSymbolsCache @@ -115,6 +114,7 @@ class GlobalSymbolsCache(testing: Boolean = false) : Iterable { 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 return symbol.getContainingSymbol(session)?.let { getSymbol(it, locals) } @@ -142,15 +142,9 @@ class GlobalSymbolsCache(testing: Boolean = false) : Iterable { symbol is FirClassLikeSymbol -> ScipSymbolDescriptor(Kind.TYPE, symbol.classId.shortClassName.asString()) symbol is FirPropertyAccessorSymbol && symbol.isSetter -> - ScipSymbolDescriptor( - Kind.METHOD, - "set" + symbol.propertySymbol.fir.name.toString().capitalizeAsciiOnly(), - ) + ScipSymbolDescriptor(Kind.METHOD, "set") symbol is FirPropertyAccessorSymbol && symbol.isGetter -> - ScipSymbolDescriptor( - Kind.METHOD, - "get" + symbol.propertySymbol.fir.name.toString().capitalizeAsciiOnly(), - ) + ScipSymbolDescriptor(Kind.METHOD, "get") symbol is FirConstructorSymbol -> ScipSymbolDescriptor(Kind.METHOD, "", methodDisambiguator(symbol)) symbol is FirFunctionSymbol -> 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 index e0865cd2..b4c7035b 100644 --- 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 @@ -276,7 +276,7 @@ class ScipSymbolsTest { }, scipOccurrence { role = DEFINITION - symbol = "getX()." + symbol = "x.get()." range { startLine = 0 startCharacter = 4 @@ -287,7 +287,7 @@ class ScipSymbolsTest { }, scipOccurrence { role = DEFINITION - symbol = "setX()." + symbol = "x.set()." range { startLine = 0 startCharacter = 4 @@ -328,7 +328,7 @@ class ScipSymbolsTest { }, scipOccurrence { role = DEFINITION - symbol = "setX()." + symbol = "x.set()." range { startLine = 0 startCharacter = 4 @@ -342,7 +342,7 @@ class ScipSymbolsTest { }, scipOccurrence { role = DEFINITION - symbol = "getX()." + symbol = "x.get()." range { startLine = 1 startCharacter = 4 @@ -388,7 +388,7 @@ class ScipSymbolsTest { }, scipOccurrence { role = DEFINITION - symbol = "getX()." + symbol = "x.get()." range { startLine = 0 startCharacter = 4 @@ -402,7 +402,7 @@ class ScipSymbolsTest { }, scipOccurrence { role = DEFINITION - symbol = "setX()." + symbol = "x.set()." range { startLine = 1 startCharacter = 4 @@ -449,7 +449,7 @@ class ScipSymbolsTest { }, scipOccurrence { role = DEFINITION - symbol = "getX()." + symbol = "x.get()." range { startLine = 1 startCharacter = 4 @@ -465,7 +465,7 @@ class ScipSymbolsTest { }, scipOccurrence { role = DEFINITION - symbol = "setX()." + symbol = "x.set()." range { startLine = 2 startCharacter = 4 @@ -528,7 +528,7 @@ class ScipSymbolsTest { }, scipOccurrence { role = DEFINITION - symbol = "Test#getSample()." + symbol = "Test#sample.get()." range { startLine = 0 startCharacter = 15 @@ -542,7 +542,7 @@ class ScipSymbolsTest { }, scipOccurrence { role = DEFINITION - symbol = "Test#setSample()." + symbol = "Test#sample.set()." range { startLine = 0 startCharacter = 15 @@ -576,7 +576,7 @@ class ScipSymbolsTest { }, scipOccurrence { role = REFERENCE - symbol = "Test#getSample()." + symbol = "Test#sample.get()." range { startLine = 2 startCharacter = 16 @@ -782,7 +782,7 @@ class ScipSymbolsTest { documentation("hello world\n test content") }, scipSymbol { - symbol = "getX()." + symbol = "x.get()." kind = Kind.Method displayName = "x" signatureText = "public get(): 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 e24088f4..b5da6c14 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 @@ -108,7 +108,7 @@ // ⌃ enclosing_range_end local 2 } // ⌃ 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 local 0 // ⌃ enclosing_range_end local 1 @@ -151,12 +151,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/Implementations.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Implementations.kt index 5a600542..0041a9f5 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 af772e26..ffcfb768 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 @@ -34,17 +34,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 @@ -52,10 +52,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. @@ -63,13 +63,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 @@ -78,8 +78,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 d199e2156e9360549e2036e517f9d42effff9f9c Mon Sep 17 00:00:00 2001 From: Nicolas Guichard Date: Fri, 3 Jul 2026 15:30:47 +0200 Subject: [PATCH 05/19] scip-kotlinc: Add enclosing_symbol field This will allow us to get the parent symbol for locals as well. For non-locals, it should be equal to the current symbol minus the last segment. Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/63c9b6582e081f62b2cc736c6c640b75b334185a --- .../kotlinc/ScipTextDocumentBuilder.kt | 3 +++ .../scip_java/kotlinc/test/AnalyzerTest.kt | 18 ++++++++++++++++++ .../scip_java/kotlinc/test/ScipBuilders.kt | 2 ++ .../scip_java/kotlinc/test/ScipSymbolsTest.kt | 2 ++ 4 files changed, 25 insertions(+) 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 index dab8f500..3ed4578a 100644 --- 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 @@ -89,6 +89,9 @@ class ScipTextDocumentBuilder( docComment(firBasedSymbol.fir)?.let { documentation += it } } this.kind = scipKind(firBasedSymbol?.fir) + this.enclosingSymbol = + context.containingDeclarations.lastOrNull()?.let { cache[it].last().toString() } + ?: "" for (parent in supers) { relationships += relationship { this.symbol = parent.toString() 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 index 338b41b2..ade938cf 100644 --- 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 @@ -109,12 +109,14 @@ class AnalyzerTest { 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" }, @@ -283,24 +285,28 @@ class AnalyzerTest { 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" }, @@ -418,18 +424,21 @@ class AnalyzerTest { 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#") @@ -437,6 +446,7 @@ class AnalyzerTest { scipSymbol { symbol = "sample/Class#foo()." kind = Kind.Method + enclosingSymbol = "sample/Class#" displayName = "foo" signatureText = "public open override fun foo(): Unit" addOverriddenSymbols("sample/Interface#foo().") @@ -635,12 +645,14 @@ class AnalyzerTest { 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#") @@ -648,6 +660,7 @@ class AnalyzerTest { scipSymbol { symbol = "local 3" kind = Kind.Method + enclosingSymbol = "local 1" displayName = "foo" signatureText = "public open override fun foo(): Unit" addOverriddenSymbols("sample/Interface#foo().") @@ -655,6 +668,7 @@ class AnalyzerTest { scipSymbol { symbol = "local 5" kind = Kind.Class + enclosingSymbol = "local 4" displayName = "" signatureText = "object : Interface" addOverriddenSymbols("sample/Interface#") @@ -662,6 +676,7 @@ class AnalyzerTest { scipSymbol { symbol = "local 7" kind = Kind.Method + enclosingSymbol = "local 5" displayName = "foo" signatureText = "public open override fun foo(): Unit" addOverriddenSymbols("sample/Interface#foo().") @@ -1317,6 +1332,7 @@ class AnalyzerTest { scipSymbol { symbol = "hello/sample/Apple#" kind = Kind.Class + enclosingSymbol = "hello/sample/" displayName = "Apple" signatureText = "public final class Apple : Any" } @@ -1403,12 +1419,14 @@ class AnalyzerTest { 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" }, 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 index a7d549dd..fe8bdebc 100644 --- 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 @@ -77,6 +77,7 @@ class ScipOccurrenceBuilder { 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() @@ -97,6 +98,7 @@ 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-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 index b4c7035b..0bc8349b 100644 --- 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 @@ -777,6 +777,7 @@ class ScipSymbolsTest { scipSymbol { symbol = "x." kind = Kind.Property + enclosingSymbol = "_root_/" displayName = "x" signatureText = "public final val x: String" documentation("hello world\n test content") @@ -784,6 +785,7 @@ class ScipSymbolsTest { scipSymbol { symbol = "x.get()." kind = Kind.Method + enclosingSymbol = "x." displayName = "x" signatureText = "public get(): String" documentation("hello world\n test content") From 44d0d50fe8f7fc8c50cc9a12d40add8ab8065be8 Mon Sep 17 00:00:00 2001 From: Nicolas Guichard Date: Fri, 3 Jul 2026 15:31:59 +0200 Subject: [PATCH 06/19] scip-kotlinc: Ignore FirFileSymbols without warning Reduces the debug log clutter. Ported from https://github.com//mozsearch/semanticdb-kotlinc/commit/4b7afc21337cba75c2c7b724043bcc9204eb9eb0 --- .../main/kotlin/org/scip_code/scip_java/kotlinc/SymbolsCache.kt | 1 + 1 file changed, 1 insertion(+) 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 index c7eb0f9a..04469bd1 100644 --- 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 @@ -158,6 +158,7 @@ class GlobalSymbolsCache(testing: Boolean = false) : Iterable { 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 From 9dc22e64738e08c93c633772516bcdea15e9a402 Mon Sep 17 00:00:00 2001 From: Nicolas Guichard Date: Fri, 3 Jul 2026 15:33:14 +0200 Subject: [PATCH 07/19] scip-kotlinc: Clear AnalyzerCheckers.visitors after consuming them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should reduce memory usage a bit and avoids lots of “given file is not under the sourceroot” clutter when running the tests. Ported from https://github.com//mozsearch/semanticdb-kotlinc/commit/65b1898de26c02929f1fbd445a3c9c92e8fd3bab --- .../org/scip_code/scip_java/kotlinc/PostAnalysisExtension.kt | 1 + 1 file changed, 1 insertion(+) 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 index 54ea38fa..af74b578 100644 --- 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 @@ -45,6 +45,7 @@ class PostAnalysisExtension( } catch (e: Exception) { handleException(e) } + AnalyzerCheckers.visitors.clear() } private fun scipShardPathForFile(file: KtSourceFile): Path? { From 9a3bd3910a73fb194f705b67bbbfd4ae1401a086 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 3 Jul 2026 15:43:13 +0200 Subject: [PATCH 08/19] Update to Kotlin 2.3.10 scip-kotlinc changes: getContainingSymbol was moved from o.j.k.fir.analysis.checkers to org.jetbrains.kotlin.fir.resolve. CompilerPluginRegistrar now has a virtual pluginId which must match the CommandLineProcessor. Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/cde86db9a62d29afa680de072ace1c60d117c37f --- gradle/libs.versions.toml | 4 ++-- .../fixtures/gradle/kotlin-jvm-toolchains/build.gradle | 2 +- .../src/test/resources/fixtures/gradle/kotlin2/build.gradle | 2 +- .../org/scip_code/scip_java/kotlinc/AnalyzerCheckers.kt | 2 +- .../scip_java/kotlinc/AnalyzerCommandLineProcessor.kt | 4 +++- .../org/scip_code/scip_java/kotlinc/AnalyzerRegistrar.kt | 2 ++ .../kotlin/org/scip_code/scip_java/kotlinc/SymbolsCache.kt | 2 +- .../test/kotlin/org/scip_code/scip_java/kotlinc/test/Utils.kt | 2 ++ 8 files changed, 13 insertions(+), 7 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1150452b..ce921f61 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,9 +2,9 @@ clikt = "5.1.0" gradle-api = "8.11.1" junit-jupiter = "5.11.4" -kctfork = "0.7.1" +kctfork = "0.12.1" kotest = "6.2.1" -kotlin = "2.2.20" +kotlin = "2.3.10" kotlinx-serialization = "1.11.0" lombok = "1.18.46" maven-plugin-annotations = "3.15.2" diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle b/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle index 712fe1a6..13a1ab32 100644 --- a/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.jetbrains.kotlin.jvm' version '2.2.20' + id 'org.jetbrains.kotlin.jvm' version '2.3.10' } java { toolchain { diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle b/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle index 43952f0b..42f3cbd1 100644 --- a/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.jetbrains.kotlin.jvm' version '2.2.20' + id 'org.jetbrains.kotlin.jvm' version '2.3.10' } kotlin { jvmToolchain(17) 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 index 3258bc0c..40675112 100644 --- 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 @@ -12,7 +12,6 @@ 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.FirTypeOperatorCallChecker -import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol import org.jetbrains.kotlin.fir.analysis.checkers.toClassLikeSymbol import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension import org.jetbrains.kotlin.fir.declarations.* @@ -20,6 +19,7 @@ import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression 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 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 index 45bb987b..60151109 100644 --- 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 @@ -15,9 +15,11 @@ 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 = "scip-kotlinc" + override val pluginId: String = PLUGIN_ID override val pluginOptions: Collection = listOf( CliOption( 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 index 289a0cf4..0b05005e 100644 --- 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 @@ -26,6 +26,8 @@ class AnalyzerRegistrar(private val callback: (Document) -> Unit = {}) : Compile ) } + 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/SymbolsCache.kt b/scip-kotlinc/src/main/kotlin/org/scip_code/scip_java/kotlinc/SymbolsCache.kt index 04469bd1..73e4be90 100644 --- 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 @@ -2,13 +2,13 @@ package org.scip_code.scip_java.kotlinc import java.lang.System.err import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember -import org.jetbrains.kotlin.fir.analysis.checkers.getContainingSymbol import org.jetbrains.kotlin.fir.declarations.DirectDeclarationsAccess import org.jetbrains.kotlin.fir.declarations.FirClass 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 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 index 4576e989..0d3da7c4 100644 --- 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 @@ -200,6 +200,8 @@ fun scipVisitorAnalyzer( ) } + override val pluginId = PLUGIN_ID + override val supportsK2: Boolean get() = true } From c7c6385fe87e02cdf0af655c6764aa185e2fad55 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 3 Jul 2026 15:52:32 +0200 Subject: [PATCH 09/19] scip-kotlinc: Fix compiler warnings - Remove unnecessary cast to FirClass in SymbolsCache (FirClassSymbol.fir already returns FirClass) - Use parameterless toClassLikeSymbol() overload in AnalyzerCheckers - Remove unnecessary inline modifier from test snippet Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/eb683777e27a40bee4771b5b1200aaf83ef71bae --- .../org/scip_code/scip_java/kotlinc/AnalyzerCheckers.kt | 3 +-- .../kotlin/org/scip_code/scip_java/kotlinc/SymbolsCache.kt | 4 +--- .../org/scip_code/scip_java/kotlinc/test/AnalyzerTest.kt | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) 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 index 40675112..5e120bda 100644 --- 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 @@ -388,8 +388,7 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio resolvedSymbol.origin == FirDeclarationOrigin.SamConstructor && resolvedSymbol is FirSyntheticFunctionSymbol ) { - val referencedKlass = - resolvedSymbol.resolvedReturnType.toClassLikeSymbol(context.session) + val referencedKlass = resolvedSymbol.resolvedReturnType.toClassLikeSymbol() if (referencedKlass != null) { visitor?.visitClassReference( referencedKlass, 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 index 73e4be90..bfff11f0 100644 --- 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 @@ -3,7 +3,6 @@ package org.scip_code.scip_java.kotlinc import java.lang.System.err import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember import org.jetbrains.kotlin.fir.declarations.DirectDeclarationsAccess -import org.jetbrains.kotlin.fir.declarations.FirClass import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin import org.jetbrains.kotlin.fir.declarations.utils.memberDeclarationNameOrNull import org.jetbrains.kotlin.fir.packageFqName @@ -172,8 +171,7 @@ class GlobalSymbolsCache(testing: Boolean = false) : Iterable { val siblings = when (val containingSymbol = symbol.getContainingSymbol(session)) { - is FirClassSymbol -> - (containingSymbol.fir as FirClass).declarations.map { it.symbol } + is FirClassSymbol -> containingSymbol.fir.declarations.map { it.symbol } is FirFileSymbol -> containingSymbol.fir.declarations.map { it.symbol } null -> symbol.moduleData.session.symbolProvider.getTopLevelCallableSymbols( 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 index ade938cf..7e36e926 100644 --- 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 @@ -1451,7 +1451,7 @@ class AnalyzerTest { * Example method docstring * **/ - inline fun docstrings(msg: String): Int { return msg.length } + fun docstrings(msg: String): Int { return msg.length } """ .trimIndent(), ) From 81a6715e20f9ae7a690378268575386e1733b52b Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 3 Jul 2026 15:57:15 +0200 Subject: [PATCH 10/19] scip-kotlinc: Fix PostAnalysisExtension to use the compilation's message collector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously handleException constructed a fresh CompilerConfiguration() to retrieve the message collector key, which always fell back to PrintingMessageCollector(System.err) — ignoring whatever collector the actual compilation was configured with. Pass the real CompilerConfiguration into PostAnalysisExtension so that exception messages are routed through the same collector as all other diagnostics. Also change the severity from EXCEPTION to WARNING, since EXCEPTION is treated as isError=true by the Kotlin compiler and would cause the build to fail — contrary to the plugin's intentional "log-and-continue" behaviour. The exception test now captures compiler output via result.messages and asserts that the warning is actually emitted with the expected content, rather than only checking the exit code. Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/e6b72f5267de9f28813594a5cd290437b4d67d56 --- .../scip_code/scip_java/kotlinc/AnalyzerRegistrar.kt | 1 + .../scip_java/kotlinc/PostAnalysisExtension.kt | 12 ++++++------ .../scip_code/scip_java/kotlinc/test/AnalyzerTest.kt | 4 ++++ .../org/scip_code/scip_java/kotlinc/test/Utils.kt | 1 + 4 files changed, 12 insertions(+), 6 deletions(-) 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 index 0b05005e..5833d888 100644 --- 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 @@ -19,6 +19,7 @@ class AnalyzerRegistrar(private val callback: (Document) -> Unit = {}) : Compile FirExtensionRegistrarAdapter.registerExtension(AnalyzerFirExtensionRegistrar(options)) IrGenerationExtension.registerExtension( PostAnalysisExtension( + configuration = configuration, sourceRoot = options.sourceroot, targetRoot = options.targetroot, callback = callback, 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 index af74b578..e814439c 100644 --- 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 @@ -25,6 +25,7 @@ import org.scip_code.scip_java.shared.ScipShardWriter * outside the source root are skipped with a stderr warning. */ class PostAnalysisExtension( + private val configuration: CompilerConfiguration, private val sourceRoot: Path, private val targetRoot: Path, private val callback: (Document) -> Unit, @@ -61,11 +62,10 @@ class PostAnalysisExtension( } private val messageCollector = - CompilerConfiguration() - .get( - CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY, - PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false), - ) + configuration.get( + CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY, + PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false), + ) private fun handleException(e: Exception) { val writer = @@ -74,7 +74,7 @@ class PostAnalysisExtension( val buf = StringBuffer() override fun close() = - messageCollector.report(CompilerMessageSeverity.EXCEPTION, buf.toString()) + messageCollector.report(CompilerMessageSeverity.WARNING, buf.toString()) override fun flush() = Unit 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 index 7e36e926..c8f2bd05 100644 --- 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 @@ -7,6 +7,7 @@ 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 @@ -807,6 +808,7 @@ class AnalyzerTest { compilerPluginRegistrars = listOf(AnalyzerRegistrar { throw Exception("sample text") }) verbose = false + messageOutputStream = java.io.OutputStream.nullOutputStream() pluginOptions = listOf( PluginOption("scip-kotlinc", "sourceroot", path.toString()), @@ -818,6 +820,8 @@ class AnalyzerTest { .compile() result.exitCode shouldBe KotlinCompilation.ExitCode.OK + result.messages shouldContain "Exception in scip-kotlin compiler plugin:" + result.messages shouldContain "sample text" } @Test 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 index 0d3da7c4..5622fe59 100644 --- 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 @@ -193,6 +193,7 @@ fun scipVisitorAnalyzer( ) IrGenerationExtension.registerExtension( PostAnalysisExtension( + configuration = configuration, sourceRoot = sourceroot, targetRoot = Paths.get(""), callback = hook, From 05ad50607c2d63cc439efc9b2c8a15d01185e063 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 3 Jul 2026 16:06:37 +0200 Subject: [PATCH 11/19] scip-kotlinc: Add test for lambda parameters This is a test for 88f6272784c5e56eb135019a4b0dda5dd0808017. Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/2c1f17047f1cb4ed15e0f53332737f82cbbcdc8b --- .../scip_java/kotlinc/test/AnalyzerTest.kt | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) 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 index c8f2bd05..1f37b561 100644 --- 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 @@ -315,6 +315,108 @@ class AnalyzerTest { 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 overrides(@TempDir path: Path) { val document = From 5b0d4ae097fbde89db840b412e9f17233b93b60d Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 3 Jul 2026 16:18:26 +0200 Subject: [PATCH 12/19] scip-kotlinc: Add tests for local functions and user-defined class return types - local functions: verifies that named functions declared inside a function body receive local symbols (local0, local1), that explicit return type references are emitted (kotlin/Int#), and that call-site references resolve to the same local symbol. Also adds a SemanticdbSymbolsTest entry confirming the locals counter increments correctly. - user-defined class as return type: verifies that SemanticSimple- FunctionChecker emits a REFERENCE occurrence for a user-defined class appearing in the return-type position (sample/MyClass#), exercising the returnTypeRef path that built-in types do not reach. Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/ff23a7ec482dcd7ab2d79ac3004ffb91e2d92d60 --- .../scip_java/kotlinc/test/AnalyzerTest.kt | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) 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 index 1f37b561..514dda5c 100644 --- 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 @@ -417,6 +417,178 @@ class AnalyzerTest { 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 overrides(@TempDir path: Path) { val document = From abe07513a687e968327ef1e9d9b7511b75b198f0 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 3 Jul 2026 16:20:29 +0200 Subject: [PATCH 13/19] scip-kotlinc: Fix displayName() for type aliases and type parameters displayName() in SemanticdbTextDocumentBuilder fell through to `firBasedSymbol.toString()` for two symbol types, producing strings like "FirTypeAliasSymbol sample/MyAlias" and "FirTypeParameterSymbol T" instead of the short name. - Broaden the FirClassSymbol branch to FirClassLikeSymbol so that FirTypeAliasSymbol (a subtype of FirClassLikeSymbol but not of FirClassSymbol) also uses classId.shortClassName. - Add an explicit FirTypeParameterSymbol branch that returns symbol.name.asString(). Add tests that exercise the corrected paths: - typealias: first test for SemanticTypeAliasChecker; verifies the DEFINITION occurrence and SymbolInformation (including the now-correct displayName "MyAlias"). Notes that type-alias references in value declarations are not currently tracked (the property checker resolves aliases to their expansion). - type parameters: first test for SemanticTypeParameterChecker; verifies the DEFINITION occurrence and SymbolInformation (displayName "T"). Notes that T in type-annotation positions does not produce REFERENCE occurrences because toClassLikeSymbol() returns null for type parameters. Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/20b1d20d53232a00388a6036cf39caf484a71b99 --- .../kotlinc/ScipTextDocumentBuilder.kt | 4 +- .../scip_java/kotlinc/test/AnalyzerTest.kt | 131 ++++++++++++++++++ 2 files changed, 134 insertions(+), 1 deletion(-) 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 index 3ed4578a..ad09c9dc 100644 --- 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 @@ -170,6 +170,7 @@ class ScipTextDocumentBuilder( 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 @@ -210,12 +211,13 @@ class ScipTextDocumentBuilder( @OptIn(SymbolInternals::class, RenderingInternals::class) private fun displayName(firBasedSymbol: FirBasedSymbol<*>): String = when (firBasedSymbol) { - is FirClassSymbol -> firBasedSymbol.classId.shortClassName.asString() + is FirClassLikeSymbol -> firBasedSymbol.classId.shortClassName.asString() is FirPropertyAccessorSymbol -> firBasedSymbol.fir.propertySymbol.name.asString() is FirFunctionSymbol -> firBasedSymbol.callableId.callableName.asString() is FirPropertySymbol -> firBasedSymbol.callableIdForRendering.callableName.asString() is FirVariableSymbol -> firBasedSymbol.name.asString() + is FirTypeParameterSymbol -> firBasedSymbol.name.asString() else -> firBasedSymbol.toString() } } 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 index 514dda5c..20fcaf0f 100644 --- 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 @@ -589,6 +589,137 @@ class AnalyzerTest { 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 overrides(@TempDir path: Path) { val document = From 50d70ca8b302ce06242412e54ebf3388658d9d9a Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 3 Jul 2026 17:12:41 +0200 Subject: [PATCH 14/19] scip-kotlinc: Fix extension receiver, enum entry, and is/as type occurrences - Fix SemanticSimpleFunctionChecker and SemanticPropertyChecker to emit REFERENCE occurrences for extension receiver types - Add SemanticEnumEntryChecker to emit DEFINITION occurrences for enum entries (previously missing from AnalyzerDeclarationCheckers) - Fix semanticdbKind() to return Kind.ENUM_MEMBER for FirEnumEntry (FirEnumEntry extends FirVariable, which would otherwise map to Kind.LOCAL) - Fix SemanticClassReferenceExpressionChecker (is/as operators) to use the already-extracted typeRef and source locals consistently rather than re-accessing expression.conversionTypeRef on each line - Extract emitTypeRef() helper to reduce duplication across checkers; use it for supertype references in SemanticClassLikeChecker so they get the same fake-source guard as other type reference emissions - Add tests for extension receivers, enum entries, named/unnamed companion objects, string template references, and is/as type references Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/d823d350cee7d182706781e6e8c5c32582960d9b --- .../scip_java/kotlinc/AnalyzerCheckers.kt | 126 +-- .../kotlinc/ScipTextDocumentBuilder.kt | 1 + .../scip_java/kotlinc/ScipVisitor.kt | 11 + .../scip_java/kotlinc/SymbolsCache.kt | 73 +- .../scip_java/kotlinc/test/AnalyzerTest.kt | 765 +++++++++++++++++- .../common/src/main/kotlin/snapshots/Class.kt | 2 + .../main/kotlin/snapshots/CompanionOwner.kt | 11 +- .../src/main/kotlin/snapshots/Lambdas.kt | 4 +- 8 files changed, 922 insertions(+), 71 deletions(-) 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 index 5e120bda..e5675daa 100644 --- 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 @@ -11,11 +11,14 @@ 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 @@ -24,6 +27,7 @@ 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.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 @@ -36,6 +40,15 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio 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 @@ -48,8 +61,10 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio Set = setOf(SemanticQualifiedAccessExpressionChecker()) - override val typeOperatorCallCheckers: - Set = + override val resolvedQualifierCheckers: Set = + setOf(SemanticResolvedQualifierChecker()) + + override val typeOperatorCallCheckers: Set = setOf(SemanticClassReferenceExpressionChecker()) } @@ -71,6 +86,7 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio 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) : @@ -189,19 +205,32 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio } 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 ?: getIdentifier(source), + objectKeyword ?: companionKeyword ?: identifierSource, enclosingSource = source, ) if (declaration is FirClass) { for (superType in declaration.superTypeRefs) { - val superSymbol = superType.toClassLikeSymbol(context.session) - val superSource = superType.source - if (superSymbol != null && superSource != null) { - visitor?.visitClassReference(superSymbol, superSource) - } + visitor?.emitTypeRef(superType) } } } @@ -260,14 +289,8 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio getIdentifier(source), enclosingSource = source, ) - - val klass = declaration.returnTypeRef.toClassLikeSymbol(context.session) - val klassSource = declaration.returnTypeRef.source - if ( - klass != null && klassSource != null && klassSource.kind !is KtFakeSourceElementKind - ) { - visitor?.visitClassReference(klass, getIdentifier(klassSource)) - } + visitor?.emitTypeRef(declaration.returnTypeRef) + declaration.receiverParameter?.typeRef?.let { visitor?.emitTypeRef(it) } } } @@ -289,14 +312,8 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] visitor?.visitProperty(declaration, getIdentifier(source), enclosingSource = source) - - val klass = declaration.returnTypeRef.toClassLikeSymbol(context.session) - val klassSource = declaration.returnTypeRef.source - if ( - klass != null && klassSource != null && klassSource.kind !is KtFakeSourceElementKind - ) { - visitor?.visitClassReference(klass, getIdentifier(klassSource)) - } + visitor?.emitTypeRef(declaration.returnTypeRef) + declaration.receiverParameter?.typeRef?.let { visitor?.emitTypeRef(it) } } } @@ -307,14 +324,7 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] visitor?.visitParameter(declaration, getIdentifier(source), enclosingSource = source) - - val klass = declaration.returnTypeRef.toClassLikeSymbol(context.session) - val klassSource = declaration.returnTypeRef.source - if ( - klass != null && klassSource != null && klassSource.kind !is KtFakeSourceElementKind - ) { - visitor?.visitClassReference(klass, getIdentifier(klassSource)) - } + visitor?.emitTypeRef(declaration.returnTypeRef) } } @@ -366,6 +376,29 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio } } + 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) { + context(context: CheckerContext, reporter: DiagnosticReporter) + override fun check(expression: FirResolvedQualifier) { + val symbol = expression.symbol ?: 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) @@ -378,10 +411,8 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] - visitor?.visitSimpleNameExpression( - calleeReference, - getIdentifier(calleeReference.source ?: source), - ) + val identifierSource = getIdentifier(calleeReference.source ?: source) + visitor?.visitSimpleNameExpression(calleeReference, identifierSource) val resolvedSymbol = calleeReference.resolvedSymbol if ( @@ -390,10 +421,7 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio ) { val referencedKlass = resolvedSymbol.resolvedReturnType.toClassLikeSymbol() if (referencedKlass != null) { - visitor?.visitClassReference( - referencedKlass, - getIdentifier(calleeReference.source ?: source), - ) + visitor?.visitClassReference(referencedKlass, identifierSource) } } @@ -401,16 +429,10 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio // symbols if (resolvedSymbol is FirPropertySymbol) { resolvedSymbol.getterSymbol?.let { - visitor?.visitCallableReference( - it, - getIdentifier(calleeReference.source ?: source), - ) + visitor?.visitCallableReference(it, identifierSource) } resolvedSymbol.setterSymbol?.let { - visitor?.visitCallableReference( - it, - getIdentifier(calleeReference.source ?: source), - ) + visitor?.visitCallableReference(it, identifierSource) } } } @@ -422,15 +444,11 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio override fun check(expression: FirTypeOperatorCall) { val typeRef = expression.conversionTypeRef val source = typeRef.source ?: return - val classSymbol = - expression.conversionTypeRef.toClassLikeSymbol(context.session) ?: return + val classSymbol = typeRef.toClassLikeSymbol(context.session) ?: return val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] - visitor?.visitClassReference( - classSymbol, - getIdentifier(expression.conversionTypeRef.source ?: source), - ) + visitor?.visitClassReference(classSymbol, getIdentifier(source)) } } } 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 index ad09c9dc..94849761 100644 --- 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 @@ -177,6 +177,7 @@ class ScipTextDocumentBuilder( 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 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 index d5d66886..5cfbe6c5 100644 --- 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 @@ -168,6 +168,17 @@ class ScipVisitor( .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, 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 index bfff11f0..b628e452 100644 --- 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 @@ -12,6 +12,8 @@ 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 @@ -116,8 +118,27 @@ class GlobalSymbolsCache(testing: Boolean = false) : Iterable { is FirPropertyAccessorSymbol -> return getSymbol(symbol.propertySymbol, locals) is FirCallableSymbol -> { val session = symbol.fir.moduleData.session - return symbol.getContainingSymbol(session)?.let { getSymbol(it, locals) } - ?: getSymbol(symbol.packageFqName()) + 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 @@ -172,12 +193,42 @@ class GlobalSymbolsCache(testing: Boolean = false) : Iterable { val siblings = when (val containingSymbol = symbol.getContainingSymbol(session)) { is FirClassSymbol -> containingSymbol.fir.declarations.map { it.symbol } - is FirFileSymbol -> containingSymbol.fir.declarations.map { it.symbol } - null -> - symbol.moduleData.session.symbolProvider.getTopLevelCallableSymbols( - symbol.packageFqName(), - symbol.name, - ) + 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 "()" } @@ -194,7 +245,11 @@ class GlobalSymbolsCache(testing: Boolean = false) : Iterable { } } - if (count == 0 || !found) return "()" + if (!found) { + err.println("methodDisambiguator: ${symbol.callableId} not found in sibling list") + return "()" + } + if (count == 0) return "()" return "(+${count})" } 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 index 20fcaf0f..76a039d5 100644 --- 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 @@ -1868,10 +1868,773 @@ class AnalyzerTest { 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 `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) + } + private fun Document.assertDocumentation(symbol: String, expectedDocumentation: String) { val info = this.symbolsList.find { it.symbol == symbol } - ?: fail("no SymbolInformation for symbol $symbol") + ?: fail("no scipSymbol for symbol $symbol") val obtainedDocumentation = info.documentationList.joinToString("\n").trim() assertEquals(expectedDocumentation, obtainedDocumentation) } 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 b5da6c14..28858956 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 @@ -105,6 +105,7 @@ // 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/Class#asdf. @@ -148,6 +149,7 @@ // > public final fun run(): Unit println(Class::class) // ^^^^^^^ reference scip-java maven . . kotlin/io/println(). +// ^^^^^ reference scip-java maven . . snapshots/Class# println("I eat $banana for lunch") // ^^^^^^^ reference scip-java maven . . kotlin/io/println(). // ^^^^^^ reference scip-java maven . . snapshots/Class#banana. 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 f16b420f..db2d168b 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,11 +17,11 @@ // ⌄ 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 Class +// display_name Companion +// signature_documentation +// > public final companion object Companion : Any // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition scip-java maven . . snapshots/CompanionOwner#Companion#``(). // kind Constructor // display_name Companion @@ -48,6 +48,7 @@ // 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#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/Lambdas.kt b/scip-snapshots/expected/kotlin/common/scip-snapshots/cases/kotlin/common/src/main/kotlin/snapshots/Lambdas.kt index ffcfb768..1748c0b3 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 @@ -17,7 +17,7 @@ // signature_documentation // > public get(): Unit // ^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/arrayListOf(). -// ^^^^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/forEachIndexed(+9). +// ^^^^^^^^^^^^^^ reference scip-java maven . . kotlin/collections/Iterable#forEachIndexed(). // ^ definition local 0 // kind Parameter // display_name i @@ -50,7 +50,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 2681cbeb585160bbb3410c522e60c4cf4955b456 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 3 Jul 2026 17:22:24 +0200 Subject: [PATCH 15/19] scip-kotlinc: Add tests for multiple supertypes and overload disambiguators - multiple supertype references: verifies that SemanticClassLikeChecker emits REFERENCE occurrences for every entry in superTypeRefs, and that SymbolInformation.overriddenSymbols is populated for all supertypes - three-way overload disambiguator: extends the existing two-overload coverage to three, verifying the (+1)/(+2) suffix counting logic in methodDisambiguator() Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/24b64333c0ac45cd0a6bb1021423881133d2e755 --- .../scip_java/kotlinc/test/AnalyzerTest.kt | 247 ++++++++++++++++++ 1 file changed, 247 insertions(+) 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 index 76a039d5..1b91275b 100644 --- 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 @@ -1174,6 +1174,13 @@ class AnalyzerTest { else -> x as Float } } + + class Wrapper + fun classify(x: Any) { + if (x is Wrapper) {} + val s = x as? String + val w = x as Wrapper + } """, ) @@ -1199,6 +1206,36 @@ class AnalyzerTest { 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) } @@ -2631,6 +2668,216 @@ class AnalyzerTest { 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 } From f489b7ff24d11ee463f003734a5d083ce0eccb07 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 3 Jul 2026 17:24:25 +0200 Subject: [PATCH 16/19] scip-kotlinc: Fix misleading LineMap docstrings The "non-0-based" phrasing predates this branch but is misleading: line helpers return 1-based values (callers subtract 1 for protobuf), while column helpers already return 0-based values. Make the docstrings say what they actually return. Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/305951777e98bfe80dfa5a2cf159fd940dab3bec --- .../kotlin/org/scip_code/scip_java/kotlinc/LineMap.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 index c2da05fd..afd51bd0 100644 --- 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 @@ -10,22 +10,22 @@ class LineMap(private val file: FirFile) { private fun offsetToLineAndCol(offset: Int): Pair? = file.sourceFileLinesMapping?.getLineAndColumnByOffset(offset) - /** Returns the non-0-based line number for a given offset */ + /** Returns the 1-based line number for a given offset (subtract 1 for protobuf). */ fun lineNumberForOffset(offset: Int): Int = file.sourceFileLinesMapping?.getLineByOffset(offset)?.let { it + 1 } ?: 0 - /** Returns the non-0-based column number for a given offset */ + /** Returns the 0-based column number for a given offset. */ fun columnForOffset(offset: Int): Int = offsetToLineAndCol(offset)?.second ?: 0 - /** Returns the non-0-based start character */ + /** Returns the 0-based start character. */ fun startCharacter(element: KtSourceElement): Int = offsetToLineAndCol(element.startOffset)?.second ?: 0 - /** Returns the non-0-based end character */ + /** Returns the 0-based end character. */ fun endCharacter(element: KtSourceElement): Int = startCharacter(element) + nameForOffset(element).length - /** Returns the non-0-based line number */ + /** Returns the 1-based line number (subtract 1 for protobuf). */ fun lineNumber(element: KtSourceElement): Int = file.sourceFileLinesMapping?.getLineByOffset(element.startOffset)?.let { it + 1 } ?: 0 From 6ebc606ace2f155d9b4124ac556596ac36c53087 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 3 Jul 2026 17:28:51 +0200 Subject: [PATCH 17/19] scip-kotlinc: Extend enclosing_range test coverage Add two test cases: - enum entry with body (Op.PLUS { override ... }): the body is modeled as a synthetic anonymous subclass, so the entry gets a multi-line enclosing_range and the overridden member surfaces as a local symbol rather than a sample/Op#PLUS.apply() global. Asserts both. - multi-line generic class declaration: general coverage we lacked for generic classes. Asserts enclosing_range for the class, its type parameter, and a member. Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/979f1f392a82c0d3cf8c17673ee3af20bb6b73c3 --- .../scip_java/kotlinc/test/AnalyzerTest.kt | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) 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 index 1b91275b..4f77e9a8 100644 --- 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 @@ -720,6 +720,73 @@ class AnalyzerTest { 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 = @@ -2259,6 +2326,91 @@ class AnalyzerTest { 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 = From e940c1889767a81347387067a375320dc6f5d83e Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Fri, 3 Jul 2026 17:36:07 +0200 Subject: [PATCH 18/19] Update to Kotlin 2.3.20 Changes in scip-kotlinc: - Rename FirSimpleFunction -> FirNamedFunction in SemanticSimpleFunctionChecker - Replace removed isLocalMember with isLocalDeclaredInBlock (moved within org.jetbrains.kotlin.fir.analysis.checkers.declaration) Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/832bf44ec114c14d2c65826a920d057d120dae0c --- gradle/libs.versions.toml | 2 +- .../fixtures/gradle/kotlin-jvm-toolchains/build.gradle | 2 +- .../src/test/resources/fixtures/gradle/kotlin2/build.gradle | 2 +- .../org/scip_code/scip_java/kotlinc/AnalyzerCheckers.kt | 2 +- .../kotlin/org/scip_code/scip_java/kotlinc/SymbolsCache.kt | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ce921f61..993c81dd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ gradle-api = "8.11.1" junit-jupiter = "5.11.4" kctfork = "0.12.1" kotest = "6.2.1" -kotlin = "2.3.10" +kotlin = "2.3.20" kotlinx-serialization = "1.11.0" lombok = "1.18.46" maven-plugin-annotations = "3.15.2" diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle b/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle index 13a1ab32..74aa8b74 100644 --- a/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.jetbrains.kotlin.jvm' version '2.3.10' + id 'org.jetbrains.kotlin.jvm' version '2.3.20' } java { toolchain { diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle b/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle index 42f3cbd1..eccb6c79 100644 --- a/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.jetbrains.kotlin.jvm' version '2.3.10' + id 'org.jetbrains.kotlin.jvm' version '2.3.20' } kotlin { jvmToolchain(17) 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 index e5675daa..1251f3a8 100644 --- 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 @@ -280,7 +280,7 @@ open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtensio private class SemanticSimpleFunctionChecker : FirSimpleFunctionChecker(MppCheckerKind.Common) { context(context: CheckerContext, reporter: DiagnosticReporter) - override fun check(declaration: FirSimpleFunction) { + override fun check(declaration: FirNamedFunction) { val source = declaration.source ?: return val ktFile = context.containingFileSymbol?.sourceFile ?: return val visitor = visitors[ktFile] 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 index b628e452..7ba63f96 100644 --- 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 @@ -1,7 +1,7 @@ package org.scip_code.scip_java.kotlinc import java.lang.System.err -import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember +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 @@ -80,7 +80,7 @@ class GlobalSymbolsCache(testing: Boolean = false) : Iterable { private fun uncachedSymbol(symbol: FirBasedSymbol<*>?, locals: LocalSymbolsCache): Symbol { if (symbol == null || symbol is FirAnonymousFunctionSymbol) return Symbol.NONE - if (symbol.fir.isLocalMember) return locals + symbol + if (symbol.fir.isLocalDeclaredInBlock) return locals + symbol val owner = getParentSymbol(symbol, locals) From a269bdbe570799083e37ae42dc2253ec46b5b96f Mon Sep 17 00:00:00 2001 From: Nicolas Guichard Date: Fri, 3 Jul 2026 17:38:05 +0200 Subject: [PATCH 19/19] Update to Kotlin 2.4.0 Changes to scip-kotlinc: - -Xcontext-parameters argument isn't required anymore - FirAllModifierRenderer now takes an argument This also replaces the Kotlin test repos with repos using Kotlin 2.4.0. Ported from https://github.com/mozsearch/semanticdb-kotlinc/commit/89427a5a132de2e706c1e84256023709cc79da51 --- .github/workflows/repos.yaml | 28 +++++++++---------- gradle/libs.versions.toml | 4 +-- .../gradle/kotlin-jvm-toolchains/build.gradle | 2 +- .../fixtures/gradle/kotlin2/build.gradle | 2 +- scip-kotlinc/build.gradle.kts | 4 --- .../kotlinc/ScipTextDocumentBuilder.kt | 2 +- 6 files changed, 19 insertions(+), 23 deletions(-) diff --git a/.github/workflows/repos.yaml b/.github/workflows/repos.yaml index 422a7348..fceffb8b 100644 --- a/.github/workflows/repos.yaml +++ b/.github/workflows/repos.yaml @@ -98,33 +98,33 @@ jobs: bazel_version: "" covers: Gradle, Java project target 21, Java 21 runtime - - name: gradle-kotlin-okio - repository: square/okio - ref: parent-3.16.0 + - name: gradle-kotlin-kotlinpoet + repository: square/kotlinpoet + ref: 0182a25089ee1f4f2378568b497f82a8ebc3017d directory: . build_tool: gradle - build_command: "-Dkjs=false -Dkwasm=false :okio:compileKotlinJvm" + build_command: "-Dkjs=false -Dkwasm=false compileKotlinJvm" java: 17 bazel_version: "" - covers: Gradle, Kotlin, Kotlin 2.2.0 + covers: Gradle, Kotlin, Kotlin 2.4.0 - - name: gradle-kotlin-flowext - repository: hoc081098/FlowExt - ref: 7341f3853d670af0283258f57643a68ed273f8ac + - name: gradle-kotlin-kotlinconf-app + repository: JetBrains/kotlinconf-app + ref: b5b0c8b4bb4476a59673dd857b598ef586100f52 directory: . build_tool: gradle build_command: "compileKotlinJvm" - java: 17 + java: 25 bazel_version: "" expected_language: kotlin - covers: Gradle, Kotlin multiplatform JVM target, Kotlin 2.2.0, Java 17 runtime + covers: Gradle, Kotlin multiplatform JVM target, Kotlin 2.4.0, Java 25 runtime - - name: gradle-mixed-okio-jmh - repository: square/okio - ref: parent-3.16.0 + - name: gradle-mixed-kotlinpoet + repository: square/kotlinpoet + ref: 0182a25089ee1f4f2378568b497f82a8ebc3017d directory: . build_tool: gradle - build_command: "-Dkjs=false -Dkwasm=false :okio:jvm:jmh:jmhClasses" + build_command: "-Dkjs=false -Dkwasm=false compileKotlinJvm compileJava" java: 21 bazel_version: "" covers: Gradle, mixed Java/Kotlin, Java 21 runtime diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 993c81dd..07bb6371 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,9 +2,9 @@ clikt = "5.1.0" gradle-api = "8.11.1" junit-jupiter = "5.11.4" -kctfork = "0.12.1" +kctfork = "0.13.0" kotest = "6.2.1" -kotlin = "2.3.20" +kotlin = "2.4.0" kotlinx-serialization = "1.11.0" lombok = "1.18.46" maven-plugin-annotations = "3.15.2" diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle b/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle index 74aa8b74..e097254a 100644 --- a/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin-jvm-toolchains/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id 'org.jetbrains.kotlin.jvm' version '2.3.20' + id 'org.jetbrains.kotlin.jvm' version '2.4.0' } java { toolchain { diff --git a/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle b/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle index eccb6c79..b559907c 100644 --- a/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle +++ b/scip-java/src/test/resources/fixtures/gradle/kotlin2/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.jetbrains.kotlin.jvm' version '2.3.20' + id 'org.jetbrains.kotlin.jvm' version '2.4.0' } kotlin { jvmToolchain(17) diff --git a/scip-kotlinc/build.gradle.kts b/scip-kotlinc/build.gradle.kts index 699d24dd..aad935ac 100644 --- a/scip-kotlinc/build.gradle.kts +++ b/scip-kotlinc/build.gradle.kts @@ -24,10 +24,6 @@ dependencies { testImplementation(libs.kctfork.core) } -tasks.withType().configureEach { - compilerOptions.freeCompilerArgs.add("-Xcontext-parameters") -} - tasks.named("test") { maxHeapSize = "2g" } 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 index 94849761..4e904b3e 100644 --- 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 @@ -154,7 +154,7 @@ class ScipTextDocumentBuilder( bodyRenderer = null, propertyAccessorRenderer = null, callArgumentsRenderer = FirCallNoArgumentsRenderer(), - modifierRenderer = FirAllModifierRenderer(), + modifierRenderer = FirAllModifierRenderer(FirModifierRenderer.StaticPolicy.Default), callableSignatureRenderer = FirCallableSignatureRendererForReadability(), declarationRenderer = FirDeclarationRenderer("local "), )