├── .github ├── labeler.yml ├── workflows │ ├── ci.yml │ ├── labeler.yml │ ├── release.yml │ └── pr-auditor.yml └── PULL_REQUEST_TEMPLATE.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── semanticdb-kotlinc ├── minimized │ └── src │ │ ├── main │ │ ├── kotlin │ │ │ └── snapshots │ │ │ │ ├── Functions.kt │ │ │ │ ├── Lambdas.kt │ │ │ │ ├── ObjectKt.kt │ │ │ │ ├── CompanionOwner.kt │ │ │ │ ├── Docstrings.kt │ │ │ │ ├── Class.kt │ │ │ │ └── Implementations.kt │ │ └── java │ │ │ └── snapshots │ │ │ ├── ObjectKtConsumer.java │ │ │ ├── ClassConsumer.java │ │ │ ├── CompanionConsumer.java │ │ │ ├── KotlinClass.java │ │ │ └── KotlinLambdas.java │ │ └── generatedSnapshots │ │ └── resources │ │ └── semanticdb-kotlinc │ │ └── minimized │ │ └── src │ │ └── main │ │ ├── kotlin │ │ └── snapshots │ │ │ ├── Functions.kt │ │ │ ├── ObjectKt.kt │ │ │ ├── CompanionOwner.kt │ │ │ ├── Docstrings.kt │ │ │ ├── Lambdas.kt │ │ │ ├── Class.kt │ │ │ └── Implementations.kt │ │ └── java │ │ └── snapshots │ │ ├── ObjectKtConsumer.java │ │ ├── CompanionConsumer.java │ │ ├── ClassConsumer.java │ │ ├── KotlinClass.java │ │ └── KotlinLambdas.java ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ ├── org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar │ │ │ │ └── org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor │ │ └── kotlin │ │ │ └── com │ │ │ └── sourcegraph │ │ │ └── semanticdb_kotlinc │ │ │ ├── AnalyzerFirExtensionRegistrar.kt │ │ │ ├── AnalyzerParamsProvider.kt │ │ │ ├── AnalyzerRegistrar.kt │ │ │ ├── LineMap.kt │ │ │ ├── AnalyzerCommandLineProcessor.kt │ │ │ ├── SemanticdbSymbols.kt │ │ │ ├── PostAnalysisExtension.kt │ │ │ ├── SemanticdbVisitor.kt │ │ │ ├── SemanticdbTextDocumentBuilder.kt │ │ │ ├── SymbolsCache.kt │ │ │ └── AnalyzerCheckers.kt │ ├── snapshots │ │ └── kotlin │ │ │ └── com │ │ │ └── sourcegraph │ │ │ └── lsif_kotlin │ │ │ └── Snapshot.kt │ └── test │ │ └── kotlin │ │ └── com │ │ └── sourcegraph │ │ └── semanticdb_kotlinc │ │ └── test │ │ ├── Utils.kt │ │ └── SemanticdbSymbolsTest.kt └── build.gradle.kts ├── .gitignore ├── renovate.json ├── settings.gradle.kts ├── debug-project ├── src │ └── main │ │ └── kotlin │ │ └── sample │ │ └── Main.kt └── build.gradle.kts ├── .run ├── lsif-kotlin debug.run.xml ├── lsif-kotlin test.run.xml ├── lsif-kotlin_semanticdb-kotlinc [test].run.xml ├── lsif-kotlin_semanticdb-kotlin [build].run.xml └── lsif-kotlin_debug-project [compileKotlin].run.xml ├── semanticdb-kotlin ├── build.gradle.kts └── src │ └── main │ ├── proto │ └── com.sourcegraph.semanticdb_kotlin │ │ └── semanticdb.proto │ └── java │ └── com │ └── sourcegraph │ └── semanticdb_kotlinc │ └── SemanticdbBuilders.kt ├── gradlew.bat ├── README.md ├── gradlew └── LICENSE /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | team/graph: 2 | - '/.*/' 3 | graph/scip-kotlin: 4 | - '/.*/' 5 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.code.style=official 2 | org.gradle.parallel=true 3 | org.gradle.caching=true 4 | 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sourcegraph/scip-kotlin/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/main/kotlin/snapshots/Functions.kt: -------------------------------------------------------------------------------- 1 | package snapshots 2 | 3 | fun sampleText(x: String = "") { 4 | println(x) 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/build/* 2 | .idea/* 3 | .gradle/* 4 | semanticdb-*.jar 5 | *.lsif 6 | generated/* 7 | *.svg 8 | /.kotlin/ 9 | /semanticdb-kotlinc/META-INF/ 10 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "local>sourcegraph/renovate-config" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar: -------------------------------------------------------------------------------- 1 | com.sourcegraph.semanticdb_kotlinc.AnalyzerRegistrar -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor: -------------------------------------------------------------------------------- 1 | com.sourcegraph.semanticdb_kotlinc.AnalyzerCommandLineProcessor 2 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/main/java/snapshots/ObjectKtConsumer.java: -------------------------------------------------------------------------------- 1 | package snapshots; 2 | 3 | public class ObjectKtConsumer { 4 | public static void run() { 5 | ObjectKt.INSTANCE.fail("boom"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/main/java/snapshots/ClassConsumer.java: -------------------------------------------------------------------------------- 1 | package snapshots; 2 | 3 | public class ClassConsumer { 4 | public static void run() { 5 | System.out.println(new Class().getAsdf()); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/main/kotlin/snapshots/Lambdas.kt: -------------------------------------------------------------------------------- 1 | package snapshots 2 | 3 | val x = arrayListOf().forEachIndexed { i, s -> println("$i $s") } 4 | 5 | val y = "fdsa".run { this.toByteArray() } 6 | 7 | val z = y.let { it.size } 8 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/main/kotlin/snapshots/ObjectKt.kt: -------------------------------------------------------------------------------- 1 | package snapshots 2 | 3 | import java.lang.RuntimeException 4 | 5 | object ObjectKt { 6 | fun fail(message: String?): Nothing { 7 | throw RuntimeException(message) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "lsif-kotlin" 2 | 3 | include( 4 | "semanticdb-kotlin", 5 | "semanticdb-kotlinc", 6 | "semanticdb-kotlinc:minimized", 7 | "debug-project" 8 | ) 9 | 10 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 11 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/main/kotlin/snapshots/CompanionOwner.kt: -------------------------------------------------------------------------------- 1 | package snapshots 2 | 3 | class CompanionOwner { 4 | companion object { 5 | fun create(): CompanionOwner = CompanionOwner() 6 | } 7 | fun create(): Int = CompanionOwner.create().hashCode() 8 | } 9 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/main/java/snapshots/CompanionConsumer.java: -------------------------------------------------------------------------------- 1 | package snapshots; 2 | 3 | public class CompanionConsumer { 4 | CompanionConsumer() { 5 | CompanionOwner.Companion.create(); 6 | new CompanionOwner().create(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/main/kotlin/snapshots/Docstrings.kt: -------------------------------------------------------------------------------- 1 | package snapshots 2 | 3 | import java.io.Serializable 4 | 5 | abstract class DocstringSuperclass 6 | /** Example class docstring. */ 7 | class Docstrings : DocstringSuperclass(), Serializable { 8 | } 9 | 10 | /** Example method docstring. */ 11 | fun docstrings() { } 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | pull_request: 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-java@v4 11 | with: 12 | java-version: '8' 13 | distribution: 'adopt' 14 | cache: 'gradle' 15 | - name: Test 16 | run: ./gradlew test 17 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/main/java/snapshots/KotlinClass.java: -------------------------------------------------------------------------------- 1 | package snapshots; 2 | 3 | public class KotlinClass { 4 | KotlinClass() throws Class { 5 | throw new Class(); 6 | } 7 | 8 | void test() throws Class { 9 | throw new Class(1, ""); 10 | } 11 | 12 | void other() throws Class { 13 | throw new Class(1); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: "Issue Labeler" 2 | on: 3 | issues: 4 | types: [opened, edited] 5 | 6 | permissions: 7 | issues: write 8 | contents: read 9 | 10 | jobs: 11 | triage: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: github/issue-labeler@v3.4 15 | with: 16 | configuration-path: .github/labeler.yml 17 | enable-versioned-regex: 0 18 | repo-token: ${{ github.token }} 19 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/main/java/snapshots/KotlinLambdas.java: -------------------------------------------------------------------------------- 1 | package snapshots; 2 | 3 | import kotlin.collections.CollectionsKt; 4 | import kotlin.text.StringsKt; 5 | 6 | 7 | public class KotlinLambdas { 8 | public void test() { 9 | LambdasKt.getX();// TODO figure out emit getX on kotlin side 10 | 11 | kotlin.collections.CollectionsKt.listOf(); 12 | FunctionsKt.sampleText(""); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Test plan 2 | 3 | 10 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/AnalyzerFirExtensionRegistrar.kt: -------------------------------------------------------------------------------- 1 | package com.sourcegraph.semanticdb_kotlinc 2 | 3 | import java.nio.file.Path 4 | import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar 5 | 6 | class AnalyzerFirExtensionRegistrar( 7 | private val sourceroot: Path, 8 | ) : FirExtensionRegistrar() { 9 | override fun ExtensionRegistrarContext.configurePlugin() { 10 | +AnalyzerParamsProvider.getFactory(sourceroot) 11 | +::AnalyzerCheckers 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/main/kotlin/snapshots/Class.kt: -------------------------------------------------------------------------------- 1 | package snapshots 2 | 3 | class Class constructor(private var banana: Int, apple: String) : 4 | Throwable(banana.toString() + apple) { 5 | init { 6 | println("") 7 | } 8 | 9 | val asdf = 10 | object { 11 | fun doStuff() = Unit 12 | } 13 | 14 | constructor() : this(1, "") 15 | 16 | constructor(banana: Int) : this(banana, "") 17 | 18 | fun run() { 19 | println(Class::class) 20 | println("I eat $banana for lunch") 21 | banana = 42 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /debug-project/src/main/kotlin/sample/Main.kt: -------------------------------------------------------------------------------- 1 | package sample 2 | 3 | class Main { 4 | private val test: String = "" 5 | 6 | fun method(something: Any): Array? { 7 | var i = 1 8 | val j = 2 9 | println("$i $j") 10 | return null 11 | } 12 | 13 | fun method(burger: String) {} 14 | 15 | val helloWorld = 16 | object { 17 | val hello = "Hello" 18 | val world = "World" 19 | 20 | override fun toString() = "$hello $world" 21 | } 22 | } 23 | 24 | val bananas = 1 25 | 26 | fun test() = Unit 27 | 28 | typealias Stringer = String 29 | -------------------------------------------------------------------------------- /.run/lsif-kotlin debug.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 16 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/main/kotlin/snapshots/Implementations.kt: -------------------------------------------------------------------------------- 1 | package snapshots 2 | 3 | class Overrides : AutoCloseable { 4 | override fun close() { 5 | TODO("Not yet implemented") 6 | } 7 | } 8 | 9 | interface Animal { 10 | val favoriteNumber: Int 11 | fun sound(): String 12 | } 13 | open class Bird : Animal { 14 | override val favoriteNumber: Int 15 | get() = 42 16 | 17 | override fun sound(): String { 18 | return "tweet" 19 | } 20 | } 21 | class Seagull : Bird() { 22 | override val favoriteNumber: Int 23 | get() = 1337 24 | override fun sound(): String { 25 | return "squawk" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/AnalyzerParamsProvider.kt: -------------------------------------------------------------------------------- 1 | package com.sourcegraph.semanticdb_kotlinc 2 | 3 | import java.nio.file.Path 4 | import org.jetbrains.kotlin.fir.FirSession 5 | import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent 6 | import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent.Factory 7 | 8 | open class AnalyzerParamsProvider( 9 | session: FirSession, 10 | val sourceroot: Path, 11 | ) : FirExtensionSessionComponent(session) { 12 | companion object { 13 | fun getFactory(sourceroot: Path): Factory { 14 | return Factory { AnalyzerParamsProvider(it, sourceroot) } 15 | } 16 | } 17 | } 18 | 19 | val FirSession.analyzerParamsProvider: AnalyzerParamsProvider by FirSession 20 | .sessionComponentAccessor() 21 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/semanticdb-kotlinc/minimized/src/main/kotlin/snapshots/Functions.kt: -------------------------------------------------------------------------------- 1 | package snapshots 2 | // ^^^^^^^^^ reference semanticdb maven . . snapshots/ 3 | 4 | fun sampleText(x: String = "") { 5 | // ^^^^^^^^^^ definition semanticdb maven . . snapshots/FunctionsKt#sampleText(). 6 | // documentation ```kotlin\npublic fun sampleText(x: kotlin.String = ...)\n``` 7 | // ^ definition semanticdb maven . . snapshots/FunctionsKt#sampleText().(x) 8 | // documentation ```kotlin\nvalue-parameter x: kotlin.String = ...\n``` 9 | // ^^^^^^ reference semanticdb maven . . kotlin/String# 10 | println(x) 11 | //^^^^^^^ reference semanticdb maven . . kotlin/io/ConsoleKt#println(+1). 12 | // ^ reference semanticdb maven . . snapshots/FunctionsKt#sampleText().(x) 13 | } 14 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: [main] 5 | tags: ["v*"] 6 | jobs: 7 | publish: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | with: 12 | fetch-depth: 0 13 | - name: Get tag 14 | id: tag 15 | run: echo "version=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_OUTPUT 16 | - uses: actions/setup-java@v4 17 | with: 18 | java-version: '8' 19 | distribution: 'adopt' 20 | cache: 'gradle' 21 | - name: Publish ${{ github.ref }} 22 | run: ./gradlew -Pversion=${{ steps.tag.outputs.version }} publish 23 | env: 24 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 25 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 26 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET }} 27 | ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.PGP_PASSPHRASE }} 28 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/snapshots/kotlin/com/sourcegraph/lsif_kotlin/Snapshot.kt: -------------------------------------------------------------------------------- 1 | package com.sourcegraph.lsif_kotlin 2 | 3 | import com.sourcegraph.scip_java.ScipJava 4 | import kotlin.io.path.Path 5 | 6 | fun main() { 7 | val snapshotDir = Path(System.getProperty("snapshotDir")) 8 | val sourceroot = Path(System.getProperty("sourceroot")) 9 | val targetroot = Path(System.getProperty("targetroot")) 10 | 11 | ScipJava.main( 12 | arrayOf( 13 | "index-semanticdb", 14 | "--no-emit-inverse-relationships", 15 | "--cwd", 16 | sourceroot.toString(), 17 | "--output", 18 | targetroot.resolve("index.scip").toString(), 19 | targetroot.toString())) 20 | ScipJava.main( 21 | arrayOf( 22 | "snapshot", 23 | "--cwd", 24 | sourceroot.toString(), 25 | "--output", 26 | snapshotDir.toString(), 27 | targetroot.toString())) 28 | } 29 | -------------------------------------------------------------------------------- /.github/workflows/pr-auditor.yml: -------------------------------------------------------------------------------- 1 | # See https://docs.sourcegraph.com/dev/background-information/ci#pr-auditor 2 | name: pr-auditor 3 | on: 4 | pull_request_target: 5 | types: [ closed, edited, opened, synchronize, ready_for_review ] 6 | workflow_dispatch: 7 | 8 | jobs: 9 | check-pr: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | with: 14 | repository: 'sourcegraph/devx-service' 15 | token: ${{ secrets.PR_AUDITOR_TOKEN }} 16 | - uses: actions/setup-go@v4 17 | with: { go-version: '1.22' } 18 | 19 | - run: 'go run ./cmd/pr-auditor' 20 | env: 21 | GITHUB_EVENT_PATH: ${{ env.GITHUB_EVENT_PATH }} 22 | GITHUB_TOKEN: ${{ secrets.PR_AUDITOR_TOKEN }} 23 | GITHUB_RUN_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} 24 | report_failure: 25 | needs: check-pr 26 | if: ${{ failure() }} 27 | uses: sourcegraph/workflows/.github/workflows/report-job-failure.yml@main 28 | secrets: inherit 29 | -------------------------------------------------------------------------------- /.run/lsif-kotlin test.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.run/lsif-kotlin_semanticdb-kotlinc [test].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/semanticdb-kotlinc/minimized/src/main/java/snapshots/ObjectKtConsumer.java: -------------------------------------------------------------------------------- 1 | package snapshots; 2 | 3 | public class ObjectKtConsumer { 4 | // ^^^^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/ObjectKtConsumer# 5 | // documentation ```java\npublic class ObjectKtConsumer\n``` 6 | // ^^^^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/ObjectKtConsumer#``(). 7 | // documentation ```java\npublic ObjectKtConsumer()\n``` 8 | public static void run() { 9 | // ^^^ definition semanticdb maven . . snapshots/ObjectKtConsumer#run(). 10 | // documentation ```java\npublic static void run()\n``` 11 | ObjectKt.INSTANCE.fail("boom"); 12 | // ^^^^^^^^ reference semanticdb maven . . snapshots/ObjectKt# 13 | // ^^^^^^^^ reference semanticdb maven . . snapshots/ObjectKt#INSTANCE. 14 | // ^^^^ reference semanticdb maven . . snapshots/ObjectKt#fail(). 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.run/lsif-kotlin_semanticdb-kotlin [build].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.run/lsif-kotlin_debug-project [compileKotlin].run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/semanticdb-kotlinc/minimized/src/main/java/snapshots/CompanionConsumer.java: -------------------------------------------------------------------------------- 1 | package snapshots; 2 | 3 | public class CompanionConsumer { 4 | // ^^^^^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/CompanionConsumer# 5 | // documentation ```java\npublic class CompanionConsumer\n``` 6 | CompanionConsumer() { 7 | // ^^^^^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/CompanionConsumer#``(). 8 | // documentation ```java\nCompanionConsumer()\n``` 9 | CompanionOwner.Companion.create(); 10 | // ^^^^^^^^^^^^^^ reference semanticdb maven . . snapshots/CompanionOwner# 11 | // ^^^^^^^^^ reference semanticdb maven . . snapshots/CompanionOwner#Companion. 12 | // ^^^^^^ reference semanticdb maven . . snapshots/CompanionOwner#Companion#create(). 13 | new CompanionOwner().create(); 14 | // ^^^^^^^^^^^^^^ reference semanticdb maven . . snapshots/CompanionOwner#``(). 15 | // ^^^^^^ reference semanticdb maven . . snapshots/CompanionOwner#create(). 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /debug-project/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 2 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 3 | 4 | plugins { 5 | kotlin("jvm") 6 | } 7 | 8 | group = "org.example" 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | val semanticdbJar: Configuration by configurations.creating { 15 | isCanBeConsumed = false 16 | isCanBeResolved = true 17 | } 18 | 19 | dependencies { 20 | implementation(kotlin("stdlib")) 21 | semanticdbJar(project( 22 | path = ":${projects.semanticdbKotlinc.name}", 23 | configuration = "semanticdbJar" 24 | )) 25 | } 26 | 27 | tasks.withType { 28 | dependsOn(":${projects.semanticdbKotlinc.name}:shadowJar") 29 | } 30 | 31 | kotlin { 32 | val targetroot = File(project.buildDir, "semanticdb-targetroot") 33 | compilerOptions { 34 | jvmTarget = JvmTarget.JVM_11 35 | freeCompilerArgs.addAll( 36 | "-Xplugin=${semanticdbJar.first()}", 37 | "-P", 38 | "plugin:semanticdb-kotlinc:sourceroot=${projectDir.path}", 39 | "-P", 40 | "plugin:semanticdb-kotlinc:targetroot=${targetroot}" 41 | ) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/semanticdb-kotlinc/minimized/src/main/java/snapshots/ClassConsumer.java: -------------------------------------------------------------------------------- 1 | package snapshots; 2 | 3 | public class ClassConsumer { 4 | // ^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/ClassConsumer# 5 | // documentation ```java\npublic class ClassConsumer\n``` 6 | // ^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/ClassConsumer#``(). 7 | // documentation ```java\npublic ClassConsumer()\n``` 8 | public static void run() { 9 | // ^^^ definition semanticdb maven . . snapshots/ClassConsumer#run(). 10 | // documentation ```java\npublic static void run()\n``` 11 | System.out.println(new Class().getAsdf()); 12 | // ^^^^^^ reference semanticdb maven jdk 8 java/lang/System# 13 | // ^^^ reference semanticdb maven jdk 8 java/lang/System#out. 14 | // ^^^^^^^ reference semanticdb maven jdk 8 java/io/PrintStream#println(+9). 15 | // ^^^^^ reference semanticdb maven . . snapshots/Class#``(+1). 16 | // ^^^^^^^ reference semanticdb maven . . snapshots/Class#getAsdf(). 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/AnalyzerRegistrar.kt: -------------------------------------------------------------------------------- 1 | package com.sourcegraph.semanticdb_kotlinc 2 | 3 | import kotlin.contracts.ExperimentalContracts 4 | import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension 5 | import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar 6 | import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi 7 | import org.jetbrains.kotlin.config.CompilerConfiguration 8 | import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter 9 | 10 | @OptIn(ExperimentalCompilerApi::class) 11 | @ExperimentalContracts 12 | class AnalyzerRegistrar(private val callback: (Semanticdb.TextDocument) -> Unit = {}) : 13 | CompilerPluginRegistrar() { 14 | override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) { 15 | FirExtensionRegistrarAdapter.registerExtension( 16 | AnalyzerFirExtensionRegistrar(sourceroot = configuration[KEY_SOURCES]!!) 17 | ) 18 | IrGenerationExtension.registerExtension( 19 | PostAnalysisExtension( 20 | sourceRoot = configuration[KEY_SOURCES]!!, 21 | targetRoot = configuration[KEY_TARGET]!!, 22 | callback = callback)) 23 | } 24 | 25 | override val supportsK2: Boolean 26 | get() = true 27 | } 28 | -------------------------------------------------------------------------------- /semanticdb-kotlin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import com.google.protobuf.gradle.* 2 | 3 | plugins { 4 | kotlin("jvm") 5 | id("com.google.protobuf") version "0.9.4" 6 | } 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | buildscript { 13 | repositories { 14 | mavenCentral() 15 | } 16 | 17 | dependencies { 18 | classpath("com.google.protobuf:protobuf-java:3.17.3") 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation(kotlin("stdlib")) 24 | implementation("com.google.protobuf:protobuf-java:3.17.3") 25 | compileOnly("com.sourcegraph", "semanticdb-javac", "0.8.23") 26 | } 27 | 28 | 29 | afterEvaluate { 30 | tasks.processResources { 31 | dependsOn(tasks.getByName("generateProto")) 32 | } 33 | 34 | tasks.compileKotlin { 35 | dependsOn(tasks.getByName("generateProto")) 36 | } 37 | 38 | tasks.withType { 39 | val sourceroot = rootDir.path 40 | val targetroot = this.project.buildDir.resolve( "semanticdb-targetroot") 41 | options.compilerArgs = options.compilerArgs + listOf( 42 | "-Xplugin:semanticdb -sourceroot:$sourceroot -targetroot:$targetroot" 43 | ) 44 | } 45 | } 46 | 47 | 48 | protobuf { 49 | protoc { 50 | artifact = "com.google.protobuf:protoc:3.17.3" 51 | } 52 | 53 | plugins { 54 | kotlin { } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/semanticdb-kotlinc/minimized/src/main/kotlin/snapshots/ObjectKt.kt: -------------------------------------------------------------------------------- 1 | package snapshots 2 | // ^^^^^^^^^ reference semanticdb maven . . snapshots/ 3 | 4 | import java.lang.RuntimeException 5 | // ^^^^ reference semanticdb maven . . java/ 6 | // ^^^^ reference semanticdb maven . . java/lang/ 7 | // ^^^^^^^^^^^^^^^^ reference semanticdb maven jdk 8 java/lang/RuntimeException# 8 | 9 | object ObjectKt { 10 | // ^^^^^^^^ definition semanticdb maven . . snapshots/ObjectKt# 11 | // documentation ```kotlin\npublic object ObjectKt\n``` 12 | fun fail(message: String?): Nothing { 13 | // ^^^^ definition semanticdb maven . . snapshots/ObjectKt#fail(). 14 | // documentation ```kotlin\npublic final fun fail(message: kotlin.String?): kotlin.Nothing\n``` 15 | // ^^^^^^^ definition semanticdb maven . . snapshots/ObjectKt#fail().(message) 16 | // documentation ```kotlin\nvalue-parameter message: kotlin.String?\n``` 17 | // ^^^^^^ reference semanticdb maven . . kotlin/String# 18 | // ^^^^^^^ reference semanticdb maven . . kotlin/Nothing# 19 | throw RuntimeException(message) 20 | // ^^^^^^^^^^^^^^^^ reference semanticdb maven jdk 8 java/lang/RuntimeException#``(+1). 21 | // ^^^^^^^ reference semanticdb maven . . snapshots/ObjectKt#fail().(message) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/LineMap.kt: -------------------------------------------------------------------------------- 1 | package com.sourcegraph.semanticdb_kotlinc 2 | 3 | import org.jetbrains.kotlin.KtSourceElement 4 | import org.jetbrains.kotlin.com.intellij.navigation.NavigationItem 5 | import org.jetbrains.kotlin.fir.declarations.FirFile 6 | import org.jetbrains.kotlin.text 7 | 8 | /** Maps between an element and its identifier positions */ 9 | class LineMap(private val file: FirFile) { 10 | private fun offsetToLineAndCol(offset: Int): Pair? = 11 | file.sourceFileLinesMapping?.getLineAndColumnByOffset(offset) 12 | 13 | /** Returns the non-0-based start character */ 14 | fun startCharacter(element: KtSourceElement): Int = 15 | offsetToLineAndCol(element.startOffset)?.second ?: 0 16 | 17 | /** Returns the non-0-based end character */ 18 | fun endCharacter(element: KtSourceElement): Int = 19 | startCharacter(element) + nameForOffset(element).length 20 | 21 | /** Returns the non-0-based line number */ 22 | fun lineNumber(element: KtSourceElement): Int = 23 | file.sourceFileLinesMapping?.getLineByOffset(element.startOffset)?.let { it + 1 } ?: 0 24 | 25 | companion object { 26 | fun nameForOffset(element: KtSourceElement): String = 27 | when (element) { 28 | is NavigationItem -> element.name ?: element.text?.toString() ?: "" 29 | else -> element.text?.toString() ?: "" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/semanticdb-kotlinc/minimized/src/main/java/snapshots/KotlinClass.java: -------------------------------------------------------------------------------- 1 | package snapshots; 2 | 3 | public class KotlinClass { 4 | // ^^^^^^^^^^^ definition semanticdb maven . . snapshots/KotlinClass# 5 | // documentation ```java\npublic class KotlinClass\n``` 6 | KotlinClass() throws Class { 7 | // ^^^^^^^^^^^ definition semanticdb maven . . snapshots/KotlinClass#``(). 8 | // documentation ```java\nKotlinClass() throws Class\n``` 9 | // ^^^^^ reference semanticdb maven . . snapshots/Class# 10 | throw new Class(); 11 | // ^^^^^ reference semanticdb maven . . snapshots/Class#``(+1). 12 | } 13 | 14 | void test() throws Class { 15 | // ^^^^ definition semanticdb maven . . snapshots/KotlinClass#test(). 16 | // documentation ```java\nvoid test() throws Class\n``` 17 | // ^^^^^ reference semanticdb maven . . snapshots/Class# 18 | throw new Class(1, ""); 19 | // ^^^^^ reference semanticdb maven . . snapshots/Class#``(). 20 | } 21 | 22 | void other() throws Class { 23 | // ^^^^^ definition semanticdb maven . . snapshots/KotlinClass#other(). 24 | // documentation ```java\nvoid other() throws Class\n``` 25 | // ^^^^^ reference semanticdb maven . . snapshots/Class# 26 | throw new Class(1); 27 | // ^^^^^ reference semanticdb maven . . snapshots/Class#``(+2). 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/AnalyzerCommandLineProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.sourcegraph.semanticdb_kotlinc 2 | 3 | import java.nio.file.Path 4 | import java.nio.file.Paths 5 | import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption 6 | import org.jetbrains.kotlin.compiler.plugin.CliOption 7 | import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor 8 | import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi 9 | import org.jetbrains.kotlin.config.CompilerConfiguration 10 | import org.jetbrains.kotlin.config.CompilerConfigurationKey 11 | 12 | const val VAL_SOURCES = "sourceroot" 13 | val KEY_SOURCES = CompilerConfigurationKey(VAL_SOURCES) 14 | 15 | const val VAL_TARGET = "targetroot" 16 | val KEY_TARGET = CompilerConfigurationKey(VAL_TARGET) 17 | 18 | @OptIn(ExperimentalCompilerApi::class) 19 | class AnalyzerCommandLineProcessor : CommandLineProcessor { 20 | override val pluginId: String = "semanticdb-kotlinc" 21 | override val pluginOptions: Collection = 22 | listOf( 23 | CliOption( 24 | VAL_SOURCES, 25 | "", 26 | "the absolute path to the root of the Kotlin sources", 27 | required = true), 28 | CliOption( 29 | VAL_TARGET, 30 | "", 31 | "the absolute path to the directory where to generate SemanticDB files.", 32 | required = true)) 33 | 34 | override fun processOption( 35 | option: AbstractCliOption, 36 | value: String, 37 | configuration: CompilerConfiguration 38 | ) { 39 | when (option.optionName) { 40 | VAL_SOURCES -> configuration.put(KEY_SOURCES, Paths.get(value)) 41 | VAL_TARGET -> configuration.put(KEY_TARGET, Paths.get(value)) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/semanticdb-kotlinc/minimized/src/main/kotlin/snapshots/CompanionOwner.kt: -------------------------------------------------------------------------------- 1 | package snapshots 2 | // ^^^^^^^^^ reference semanticdb maven . . snapshots/ 3 | 4 | class CompanionOwner { 5 | // ^^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/CompanionOwner# 6 | // documentation ```kotlin\npublic final class CompanionOwner\n``` 7 | // ^^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/CompanionOwner#``(). 8 | // documentation ```kotlin\npublic constructor CompanionOwner()\n``` 9 | companion object { 10 | // ^^^^^^^^ definition semanticdb maven . . snapshots/CompanionOwner#Companion# 1:0 11 | // documentation ```kotlin\npublic companion object\n``` 12 | fun create(): CompanionOwner = CompanionOwner() 13 | // ^^^^^^ definition semanticdb maven . . snapshots/CompanionOwner#Companion#create(). 14 | // documentation ```kotlin\npublic final fun create(): snapshots.CompanionOwner\n``` 15 | // ^^^^^^^^^^^^^^ reference semanticdb maven . . snapshots/CompanionOwner# 16 | // ^^^^^^^^^^^^^^ reference semanticdb maven . . snapshots/CompanionOwner#``(). 17 | } 18 | fun create(): Int = CompanionOwner.create().hashCode() 19 | // ^^^^^^ definition semanticdb maven . . snapshots/CompanionOwner#create(). 20 | // documentation ```kotlin\npublic final fun create(): kotlin.Int\n``` 21 | // ^^^ reference semanticdb maven . . kotlin/Int# 22 | // ^^^^^^^^^^^^^^ reference semanticdb maven . . snapshots/CompanionOwner#Companion# 23 | // ^^^^^^ reference semanticdb maven . . snapshots/CompanionOwner#Companion#create(). 24 | // ^^^^^^^^ reference semanticdb maven . . snapshots/CompanionOwner#hashCode(+-1). 25 | } 26 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/semanticdb-kotlinc/minimized/src/main/kotlin/snapshots/Docstrings.kt: -------------------------------------------------------------------------------- 1 | package snapshots 2 | // ^^^^^^^^^ reference semanticdb maven . . snapshots/ 3 | 4 | import java.io.Serializable 5 | // ^^^^ reference semanticdb maven . . java/ 6 | // ^^ reference semanticdb maven . . java/io/ 7 | // ^^^^^^^^^^^^ reference semanticdb maven jdk 8 java/io/Serializable# 8 | 9 | abstract class DocstringSuperclass 10 | // ^^^^^^^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/DocstringSuperclass# 11 | // documentation ```kotlin\npublic abstract class DocstringSuperclass\n``` 12 | // ^^^^^^^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/DocstringSuperclass#``(). 13 | // documentation ```kotlin\npublic constructor DocstringSuperclass()\n``` 14 | /** Example class docstring. */ 15 | class Docstrings : DocstringSuperclass(), Serializable { 16 | // ^^^^^^^^^^ definition semanticdb maven . . snapshots/Docstrings# 17 | // documentation ```kotlin\npublic final class Docstrings : snapshots.DocstringSuperclass, java.io.Serializable\n```\n\n----\n\n Example class docstring. 18 | // relationship is_reference is_implementation semanticdb maven . . snapshots/DocstringSuperclass# 19 | // ^^^^^^^^^^ definition semanticdb maven . . snapshots/Docstrings#``(). 20 | // documentation ```kotlin\npublic constructor Docstrings()\n```\n\n----\n\n Example class docstring. 21 | // ^^^^^^^^^^^^^^^^^^^ reference semanticdb maven . . snapshots/DocstringSuperclass#``(). 22 | // ^^^^^^^^^^^^ reference semanticdb maven jdk 8 java/io/Serializable# 23 | } 24 | 25 | /** Example method docstring. */ 26 | fun docstrings() { } 27 | // ^^^^^^^^^^ definition semanticdb maven . . snapshots/DocstringsKt#docstrings(). 28 | // documentation ```kotlin\npublic fun docstrings()\n```\n\n----\n\n Example method docstring. 29 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/semanticdb-kotlinc/minimized/src/main/java/snapshots/KotlinLambdas.java: -------------------------------------------------------------------------------- 1 | package snapshots; 2 | 3 | import kotlin.collections.CollectionsKt; 4 | // ^^^^^^ reference semanticdb maven . . kotlin/ 5 | // ^^^^^^^^^^^ reference semanticdb maven . . kotlin/collections/ 6 | // ^^^^^^^^^^^^^ reference semanticdb maven . . kotlin/collections/CollectionsKt# 7 | import kotlin.text.StringsKt; 8 | // ^^^^^^ reference semanticdb maven . . kotlin/ 9 | // ^^^^ reference semanticdb maven . . kotlin/text/ 10 | // ^^^^^^^^^ reference semanticdb maven . . kotlin/text/StringsKt# 11 | 12 | 13 | public class KotlinLambdas { 14 | // ^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/KotlinLambdas# 15 | // documentation ```java\npublic class KotlinLambdas\n``` 16 | // ^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/KotlinLambdas#``(). 17 | // documentation ```java\npublic KotlinLambdas()\n``` 18 | public void test() { 19 | // ^^^^ definition semanticdb maven . . snapshots/KotlinLambdas#test(). 20 | // documentation ```java\npublic void test()\n``` 21 | LambdasKt.getX();// TODO figure out emit getX on kotlin side 22 | // ^^^^^^^^^ reference semanticdb maven . . snapshots/LambdasKt# 23 | // ^^^^ reference semanticdb maven . . snapshots/LambdasKt#getX(). 24 | 25 | kotlin.collections.CollectionsKt.listOf(); 26 | // ^^^^^^ reference semanticdb maven . . kotlin/ 27 | // ^^^^^^^^^^^ reference semanticdb maven . . kotlin/collections/ 28 | // ^^^^^^^^^^^^^ reference semanticdb maven . . kotlin/collections/CollectionsKt# 29 | // ^^^^^^ reference semanticdb maven . . kotlin/collections/CollectionsKt__CollectionsKt#listOf(). 30 | FunctionsKt.sampleText(""); 31 | // ^^^^^^^^^^^ reference semanticdb maven . . snapshots/FunctionsKt# 32 | // ^^^^^^^^^^ reference semanticdb maven . . snapshots/FunctionsKt#sampleText(). 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbSymbols.kt: -------------------------------------------------------------------------------- 1 | package com.sourcegraph.semanticdb_kotlinc 2 | 3 | @JvmInline 4 | value class Symbol(private val symbol: String) { 5 | companion object { 6 | val NONE = Symbol("") 7 | val ROOT_PACKAGE = Symbol("_root_/") 8 | 9 | fun createGlobal(owner: Symbol, desc: SemanticdbSymbolDescriptor): Symbol = 10 | when { 11 | desc == SemanticdbSymbolDescriptor.NONE -> owner 12 | owner != ROOT_PACKAGE -> Symbol(owner.symbol + desc.encode().symbol) 13 | else -> desc.encode() 14 | } 15 | 16 | fun createLocal(i: Int) = Symbol("local$i") 17 | } 18 | 19 | fun isGlobal() = !isLocal() 20 | 21 | fun isLocal() = symbol.startsWith("local") 22 | 23 | override fun toString(): String = symbol 24 | } 25 | 26 | fun String.symbol(): Symbol = Symbol(this) 27 | 28 | data class SemanticdbSymbolDescriptor( 29 | val kind: Kind, 30 | val name: String, 31 | val disambiguator: String = "()" 32 | ) { 33 | companion object { 34 | val NONE = SemanticdbSymbolDescriptor(Kind.NONE, "") 35 | 36 | private fun encodeName(name: String): String { 37 | if (name.isEmpty()) return "``" 38 | val isStartOk = Character.isJavaIdentifierStart(name[0]) 39 | var isPartsOk = true 40 | var i = 1 41 | while (isPartsOk && i < name.length) { 42 | isPartsOk = Character.isJavaIdentifierPart(name[i]) 43 | i++ 44 | } 45 | return if (isStartOk && isPartsOk) name else "`$name`" 46 | } 47 | } 48 | 49 | enum class Kind { 50 | NONE, 51 | TERM, 52 | METHOD, 53 | TYPE, 54 | PACKAGE, 55 | PARAMETER, 56 | TYPE_PARAMETER 57 | } 58 | 59 | fun encode() = 60 | Symbol( 61 | when (kind) { 62 | Kind.NONE -> "" 63 | Kind.TERM -> "${encodeName(name)}." 64 | Kind.METHOD -> "${encodeName(name)}${disambiguator}." 65 | Kind.TYPE -> "${encodeName(name)}#" 66 | Kind.PACKAGE -> "${encodeName(name)}/" 67 | Kind.PARAMETER -> "(${encodeName(name)})" 68 | Kind.TYPE_PARAMETER -> "[${encodeName(name)}]" 69 | }) 70 | } 71 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/semanticdb-kotlinc/minimized/src/main/kotlin/snapshots/Lambdas.kt: -------------------------------------------------------------------------------- 1 | package snapshots 2 | // ^^^^^^^^^ reference semanticdb maven . . snapshots/ 3 | 4 | val x = arrayListOf().forEachIndexed { i, s -> println("$i $s") } 5 | // ^ definition semanticdb maven . . snapshots/LambdasKt#getX(). 6 | // documentation ```kotlin\npublic val x: kotlin.Unit\n``` 7 | // ^ definition semanticdb maven . . snapshots/LambdasKt#x. 8 | // documentation ```kotlin\npublic val x: kotlin.Unit\n``` 9 | // ^^^^^^^^^^^ reference semanticdb maven . . kotlin/collections/CollectionsKt#arrayListOf(). 10 | // ^^^^^^ reference semanticdb maven . . kotlin/String# 11 | // ^^^^^^^^^^^^^^ reference semanticdb maven . . kotlin/collections/CollectionsKt#forEachIndexed(+9). 12 | // ^ definition local 0 13 | // documentation ```kotlin\nvalue-parameter i: kotlin.Int\n``` 14 | // ^ definition local 1 15 | // documentation ```kotlin\nvalue-parameter s: kotlin.String\n``` 16 | // ^^^^^^^ reference semanticdb maven . . kotlin/io/ConsoleKt#println(+1). 17 | // ^ reference local 0 18 | // ^ reference local 1 19 | 20 | val y = "fdsa".run { this.toByteArray() } 21 | // ^ definition semanticdb maven . . snapshots/LambdasKt#getY(). 22 | // documentation ```kotlin\npublic val y: kotlin.ByteArray\n``` 23 | // ^ definition semanticdb maven . . snapshots/LambdasKt#y. 24 | // documentation ```kotlin\npublic val y: kotlin.ByteArray\n``` 25 | // ^^^ reference semanticdb maven . . kotlin/StandardKt#run(+1). 26 | // ^^^^^^^^^^^ reference semanticdb maven . . kotlin/text/StringsKt#toByteArray(). 27 | 28 | val z = y.let { it.size } 29 | // ^ definition semanticdb maven . . snapshots/LambdasKt#getZ(). 30 | // documentation ```kotlin\npublic val z: kotlin.Int\n``` 31 | // ^ definition semanticdb maven . . snapshots/LambdasKt#z. 32 | // documentation ```kotlin\npublic val z: kotlin.Int\n``` 33 | // ^ reference semanticdb maven . . snapshots/LambdasKt#getY(). 34 | // ^ reference semanticdb maven . . snapshots/LambdasKt#y. 35 | // ^^^ reference semanticdb maven . . kotlin/StandardKt#let(). 36 | // ^^ reference local 2 37 | // ^^^^ reference semanticdb maven . . kotlin/ByteArray#getSize(). 38 | // ^^^^ reference semanticdb maven . . kotlin/ByteArray#size. 39 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kotlin SCIP support 2 | 3 | 4 | This codebase implements a Kotlin compiler plugin that can be used together with 5 | [scip-java](https://sourcegraph.github.io/scip-java) to emit 6 | [SCIP](https://github.com/sourcegraph/scip) indexes for Kotlin projects. 7 | 8 | 9 | ## Getting started 10 | 11 | This project must be used together with scip-java. Visit 12 | [scip-java](https://sourcegraph.github.io/scip-java/) for instructions on how to 13 | index Kotlin projects with scip-java. Note that scip-java indexes Kotlin sources 14 | even if you have no Java code. 15 | 16 | ## Kotlin version compatibility 17 | 18 | Any given release of scip-kotlin only supports one major version of Kotlin. 19 | Use the table below to find the version of scip-kotlin that matches the Kotlin 20 | version you are using in your project. 21 | 22 | | Kotlin version | scip-kotlin version | 23 | |----------------|---------------------| 24 | | 1.8.x | 0.3.2 | 25 | | 1.9.x | 0.4.0 | 26 | 27 | ## SemanticDB support 28 | 29 | This project is implemented as a 30 | [SemanticDB](https://scalameta.org/docs/semanticdb/specification.html) compiler 31 | plugin. To generate SCIP, you first compile the Kotlin sources with the 32 | SemanticDB compiler plugin and then convert SemanticDB files into SCIP using 33 | `scip-java index-semanticdb`. See [Low-level usage](#low-level-usage) for more 34 | details on how to generate SemanticDB files and convert SemanticDB into SCIP. 35 | 36 | ## Low-level usage 37 | 38 | 39 | First, fetch the jar file of the SemanticDB compiler plugin: 40 | [`com.sourcegraph:semanticdb-kotlinc:VERSION`](https://mvnrepository.com/artifact/com.sourcegraph/semanticdb-kotlinc). 41 | For example, you can use 42 | [Coursier](https://get-coursier.io) to download the jar file. 43 | 44 | ```sh 45 | curl -fLo coursier https://github.com/coursier/launchers/raw/master/coursier && chmod +x ./coursier 46 | export SEMANTICDB_KOTLIN_VERSION="latest.release" # or replace with a particular version 47 | export SEMANTICDB_KOTLIN_JAR=$(./coursier fetch com.sourcegraph:semanticdb-kotlinc:$SEMANTICDB_KOTLIN_VERSION) 48 | ``` 49 | 50 | Once you have the jar file, you need to determine two compiler options: 51 | 52 | - `sourceroot`: the absolute path to the root directory of your codebase. All 53 | source files that you want to index should be under this directory. For Gradle 54 | codebases, this is typically the toplevel `build.gradle` file. For Maven 55 | codebases, this is typically the toplevel `pom.xml` file. 56 | - `targetroot`: the absolute path to the directory where you want the compiler 57 | plugin to write SemanticDB files. This can be any directory on your computer. 58 | 59 | Now you have all the necessary parameters to invoke the Kotlin compiler with 60 | the SemanticDB compiler plugin. 61 | 62 | ```sh 63 | kotlinc -Xplugin=${SEMANTICDB_KOTLIN_JAR} \ 64 | -P plugin:semanticdb-kotlinc:sourceroot=SOURCEROOT_DIRECTORY \ 65 | -P plugin:semanticdb-kotlinc:targetroot=TARGETROOT_DIRECTORY 66 | ``` 67 | 68 | Once the compilation is complete, the targetroot should contain `*.semanticdb` 69 | files in the `META-INF/semanticdb` sub-directory. 70 | 71 | To convert the SemanticDB files into SCIP, run `scip-java index TARGETROOT_DIRECTORY`. 72 | If you have Coursier installed, you can run scip-java directly like this 73 | 74 | ```sh 75 | cd $SOURCEROOT_DIRECTORY 76 | ./coursier launch --contrib scip-java -- index-semanticdb TARGETROOT_DIRECTORY 77 | ``` 78 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/PostAnalysisExtension.kt: -------------------------------------------------------------------------------- 1 | package com.sourcegraph.semanticdb_kotlinc 2 | 3 | import java.io.PrintWriter 4 | import java.io.Writer 5 | import java.nio.file.Files 6 | import java.nio.file.Path 7 | import java.nio.file.Paths 8 | import kotlin.contracts.ExperimentalContracts 9 | import org.jetbrains.kotlin.KtSourceFile 10 | import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension 11 | import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext 12 | import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity 13 | import org.jetbrains.kotlin.cli.common.messages.MessageRenderer 14 | import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector 15 | import org.jetbrains.kotlin.config.CommonConfigurationKeys 16 | import org.jetbrains.kotlin.config.CompilerConfiguration 17 | import org.jetbrains.kotlin.ir.declarations.IrModuleFragment 18 | 19 | class PostAnalysisExtension( 20 | private val sourceRoot: Path, 21 | private val targetRoot: Path, 22 | private val callback: (Semanticdb.TextDocument) -> Unit 23 | ) : IrGenerationExtension { 24 | @OptIn(ExperimentalContracts::class) 25 | override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) { 26 | try { 27 | for ((ktSourceFile, visitor) in AnalyzerCheckers.visitors) { 28 | try { 29 | val document = visitor.build() 30 | semanticdbOutPathForFile(ktSourceFile)?.apply { 31 | Files.write(this, TextDocuments { addDocuments(document) }.toByteArray()) 32 | } 33 | callback(document) 34 | } catch (e: Exception) { 35 | handleException(e) 36 | } 37 | } 38 | } catch (e: Exception) { 39 | handleException(e) 40 | } 41 | } 42 | 43 | private fun semanticdbOutPathForFile(file: KtSourceFile): Path? { 44 | val normalizedPath = Paths.get(file.path).normalize() 45 | if (normalizedPath.startsWith(sourceRoot)) { 46 | val relative = sourceRoot.relativize(normalizedPath) 47 | val filename = relative.fileName.toString() + ".semanticdb" 48 | val semanticdbOutPath = 49 | targetRoot 50 | .resolve("META-INF") 51 | .resolve("semanticdb") 52 | .resolve(relative) 53 | .resolveSibling(filename) 54 | 55 | Files.createDirectories(semanticdbOutPath.parent) 56 | return semanticdbOutPath 57 | } 58 | System.err.println( 59 | "given file is not under the sourceroot.\n\tSourceroot: $sourceRoot\n\tFile path: ${file.path}\n\tNormalized file path: $normalizedPath") 60 | return null 61 | } 62 | 63 | private val messageCollector = 64 | CompilerConfiguration() 65 | .get( 66 | CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY, 67 | PrintingMessageCollector(System.err, MessageRenderer.PLAIN_FULL_PATHS, false)) 68 | 69 | private fun handleException(e: Exception) { 70 | val writer = 71 | PrintWriter( 72 | object : Writer() { 73 | val buf = StringBuffer() 74 | override fun close() = 75 | messageCollector.report(CompilerMessageSeverity.EXCEPTION, buf.toString()) 76 | 77 | override fun flush() = Unit 78 | override fun write(data: CharArray, offset: Int, len: Int) { 79 | buf.append(data, offset, len) 80 | } 81 | }, 82 | false) 83 | writer.println("Exception in semanticdb-kotlin compiler plugin:") 84 | e.printStackTrace(writer) 85 | writer.println( 86 | "Please report a bug to https://github.com/sourcegraph/lsif-kotlin with the stack trace above.") 87 | writer.close() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /semanticdb-kotlin/src/main/proto/com.sourcegraph.semanticdb_kotlin/semanticdb.proto: -------------------------------------------------------------------------------- 1 | // Original source: https://github.com/scalameta/scalameta/blob/cf796cf2436b40494baf2bdc266623dc65264ad5/semanticdb/semanticdb/semanticdb.proto 2 | // Local modifications: 3 | // - Removes unused fields to minimize the amount of generated code. 4 | // - Adds SymbolInformation.documentation that is pending upstream approval. 5 | 6 | syntax = "proto3"; 7 | 8 | package com.sourcegraph.semanticdb_kotlinc; 9 | 10 | option java_package = "com.sourcegraph.semanticdb_kotlinc"; 11 | option java_multiple_files = false; 12 | 13 | enum Schema { 14 | LEGACY = 0; 15 | SEMANTICDB3 = 3; 16 | SEMANTICDB4 = 4; 17 | } 18 | 19 | message TextDocuments { 20 | repeated TextDocument documents = 1; 21 | } 22 | 23 | message TextDocument { 24 | reserved 4, 8, 9; 25 | Schema schema = 1; 26 | string uri = 2; 27 | string text = 3; 28 | string md5 = 11; 29 | Language language = 10; 30 | repeated SymbolInformation symbols = 5; 31 | repeated SymbolOccurrence occurrences = 6; 32 | } 33 | 34 | enum Language { 35 | UNKNOWN_LANGUAGE = 0; 36 | SCALA = 1; 37 | JAVA = 2; 38 | KOTLIN = 3; 39 | } 40 | 41 | message Range { 42 | int32 start_line = 1; 43 | int32 start_character = 2; 44 | int32 end_line = 3; 45 | int32 end_character = 4; 46 | } 47 | 48 | message Signature { 49 | oneof sealed_value { 50 | ClassSignature class_signature = 1; 51 | MethodSignature method_signature = 2; 52 | TypeSignature type_signature = 3; 53 | ValueSignature value_signature = 4; 54 | } 55 | } 56 | 57 | message ClassSignature { 58 | Scope type_parameters = 1; 59 | repeated Type parents = 2; 60 | Scope declarations = 4; 61 | } 62 | 63 | message MethodSignature { 64 | Scope type_parameters = 1; 65 | repeated Scope parameter_lists = 2; 66 | Type return_type = 3; 67 | } 68 | 69 | message TypeSignature { 70 | Scope type_parameters = 1; 71 | Type lower_bound = 2; 72 | Type upper_bound = 3; 73 | } 74 | 75 | message ValueSignature { 76 | Type tpe = 1; 77 | } 78 | 79 | message SymbolInformation { 80 | enum Kind { 81 | reserved 1, 2, 4, 5, 15, 16; 82 | UNKNOWN_KIND = 0; 83 | LOCAL = 19; 84 | FIELD = 20; 85 | METHOD = 3; 86 | CONSTRUCTOR = 21; 87 | TYPE = 7; 88 | PARAMETER = 8; 89 | TYPE_PARAMETER = 9; 90 | PACKAGE = 11; 91 | CLASS = 13; 92 | INTERFACE = 18; 93 | } 94 | enum Property { 95 | UNKNOWN_PROPERTY = 0; 96 | reserved 0x1; 97 | reserved 0x2; 98 | ABSTRACT = 0x4; 99 | FINAL = 0x8; 100 | SEALED = 0x10; 101 | STATIC = 0x1000; 102 | ENUM = 0x4000; 103 | } 104 | reserved 2, 6, 7, 8, 9, 10, 11, 12, 14, 15; 105 | string symbol = 1; 106 | Language language = 16; 107 | Kind kind = 3; 108 | int32 properties = 4; 109 | string display_name = 5; 110 | Signature signature = 17; 111 | Access access = 18; 112 | repeated string overridden_symbols = 19; 113 | Documentation documentation = 20; 114 | } 115 | 116 | message Access { 117 | oneof sealed_value { 118 | PrivateAccess private_access = 1; 119 | PrivateWithinAccess private_within_access = 3; 120 | ProtectedAccess protected_access = 4; 121 | PublicAccess public_access = 7; 122 | } 123 | } 124 | 125 | message PrivateAccess {} 126 | 127 | message PrivateWithinAccess { 128 | string symbol = 1; 129 | } 130 | 131 | message ProtectedAccess {} 132 | 133 | message PublicAccess {} 134 | 135 | message Documentation { 136 | enum Format { 137 | HTML = 0; 138 | MARKDOWN = 1; 139 | JAVADOC = 2; 140 | SCALADOC = 3; 141 | KDOC = 4; 142 | } 143 | string message = 1; 144 | Format format = 2; 145 | } 146 | 147 | message SymbolOccurrence { 148 | enum Role { 149 | UNKNOWN_ROLE = 0; 150 | REFERENCE = 1; 151 | DEFINITION = 2; 152 | } 153 | Range range = 1; 154 | string symbol = 2; 155 | Role role = 3; 156 | } 157 | 158 | message Scope { 159 | repeated string symlinks = 1; 160 | repeated SymbolInformation hardlinks = 2; 161 | } 162 | 163 | message Type { 164 | reserved 1, 3, 4, 5, 6, 11, 12, 15, 16; 165 | oneof sealed_value { 166 | TypeRef type_ref = 2; 167 | ExistentialType existential_type = 9; 168 | IntersectionType intersection_type = 17; 169 | } 170 | } 171 | 172 | message TypeRef { 173 | string symbol = 2; 174 | repeated Type type_arguments = 3; 175 | } 176 | 177 | message IntersectionType { 178 | repeated Type types = 1; 179 | } 180 | 181 | message ExistentialType { 182 | reserved 2; 183 | Type tpe = 1; 184 | Scope declarations = 3; 185 | } 186 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/semanticdb-kotlinc/minimized/src/main/kotlin/snapshots/Class.kt: -------------------------------------------------------------------------------- 1 | package snapshots 2 | // ^^^^^^^^^ reference semanticdb maven . . snapshots/ 3 | 4 | class Class constructor(private var banana: Int, apple: String) : 5 | // ^^^^^ definition semanticdb maven . . snapshots/Class# 6 | // documentation ```kotlin\npublic final class Class : kotlin.Throwable\n``` 7 | // relationship is_reference is_implementation semanticdb maven . . kotlin/Throwable# 8 | // ^^^^^^^^^^^ definition semanticdb maven . . snapshots/Class#``(). 9 | // documentation ```kotlin\npublic constructor Class(banana: kotlin.Int, apple: kotlin.String)\n``` 10 | // ^^^^^^ definition semanticdb maven . . snapshots/Class#``().(banana) 11 | // documentation ```kotlin\nvalue-parameter banana: kotlin.Int\n``` 12 | // ^^^^^^ definition semanticdb maven . . snapshots/Class#banana. 13 | // documentation ```kotlin\nprivate final var banana: kotlin.Int\n``` 14 | // ^^^^^^ definition semanticdb maven . . snapshots/Class#getBanana(). 15 | // documentation ```kotlin\nprivate final var banana: kotlin.Int\n``` 16 | // ^^^^^^ definition semanticdb maven . . snapshots/Class#setBanana(). 17 | // documentation ```kotlin\nprivate final var banana: kotlin.Int\n``` 18 | // ^^^ reference semanticdb maven . . kotlin/Int# 19 | // ^^^^^ definition semanticdb maven . . snapshots/Class#``().(apple) 20 | // documentation ```kotlin\nvalue-parameter apple: kotlin.String\n``` 21 | // ^^^^^^ reference semanticdb maven . . kotlin/String# 22 | Throwable(banana.toString() + apple) { 23 | // ^^^^^^^^^ reference semanticdb maven . . kotlin/Throwable#``(). 24 | // ^^^^^^ reference semanticdb maven . . snapshots/Class#``().(banana) 25 | // ^^^^^^^^ reference semanticdb maven . . kotlin/Int#toString(). 26 | // ^ reference semanticdb maven . . kotlin/String#plus(). 27 | // ^^^^^ reference semanticdb maven . . snapshots/Class#``().(apple) 28 | init { 29 | println("") 30 | // ^^^^^^^ reference semanticdb maven . . kotlin/io/ConsoleKt#println(+1). 31 | } 32 | 33 | val asdf = 34 | // ^^^^ definition semanticdb maven . . snapshots/Class#asdf. 35 | // documentation ```kotlin\npublic final val asdf: kotlin.Any\n``` 36 | // ^^^^ definition semanticdb maven . . snapshots/Class#getAsdf(). 37 | // documentation ```kotlin\npublic final val asdf: kotlin.Any\n``` 38 | object { 39 | fun doStuff() = Unit 40 | // ^^^^^^^ definition local 0 41 | // documentation ```kotlin\npublic final fun doStuff()\n``` 42 | // ^^^^ reference semanticdb maven . . kotlin/Unit# 43 | } 44 | 45 | constructor() : this(1, "") 46 | //^^^^^^^^^^^ definition semanticdb maven . . snapshots/Class#``(+1). 47 | // documentation ```kotlin\npublic constructor Class()\n``` 48 | 49 | constructor(banana: Int) : this(banana, "") 50 | //^^^^^^^^^^^ definition semanticdb maven . . snapshots/Class#``(+2). 51 | // documentation ```kotlin\npublic constructor Class(banana: kotlin.Int)\n``` 52 | // ^^^^^^ definition semanticdb maven . . snapshots/Class#``(+2).(banana) 53 | // documentation ```kotlin\nvalue-parameter banana: kotlin.Int\n``` 54 | // ^^^ reference semanticdb maven . . kotlin/Int# 55 | // ^^^^^^ reference semanticdb maven . . snapshots/Class#``(+2).(banana) 56 | 57 | fun run() { 58 | // ^^^ definition semanticdb maven . . snapshots/Class#run(). 59 | // documentation ```kotlin\npublic final fun run()\n``` 60 | println(Class::class) 61 | // ^^^^^^^ reference semanticdb maven . . kotlin/io/ConsoleKt#println(+1). 62 | // ^^^^^ reference semanticdb maven . . snapshots/Class# 63 | println("I eat $banana for lunch") 64 | // ^^^^^^^ reference semanticdb maven . . kotlin/io/ConsoleKt#println(+1). 65 | // ^^^^^^ reference semanticdb maven . . snapshots/Class#banana. 66 | // ^^^^^^ reference semanticdb maven . . snapshots/Class#getBanana(). 67 | // ^^^^^^ reference semanticdb maven . . snapshots/Class#setBanana(). 68 | banana = 42 69 | // ^^^^^^ reference semanticdb maven . . snapshots/Class#banana. 70 | // ^^^^^^ reference semanticdb maven . . snapshots/Class#getBanana(). 71 | // ^^^^^^ reference semanticdb maven . . snapshots/Class#setBanana(). 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/minimized/src/generatedSnapshots/resources/semanticdb-kotlinc/minimized/src/main/kotlin/snapshots/Implementations.kt: -------------------------------------------------------------------------------- 1 | package snapshots 2 | // ^^^^^^^^^ reference semanticdb maven . . snapshots/ 3 | 4 | class Overrides : AutoCloseable { 5 | // ^^^^^^^^^ definition semanticdb maven . . snapshots/Overrides# 6 | // documentation ```kotlin\npublic final class Overrides : java.lang.AutoCloseable\n``` 7 | // relationship is_reference is_implementation semanticdb maven jdk 8 java/lang/AutoCloseable# 8 | // ^^^^^^^^^ definition semanticdb maven . . snapshots/Overrides#``(). 9 | // documentation ```kotlin\npublic constructor Overrides()\n``` 10 | // ^^^^^^^^^^^^^ reference semanticdb maven jdk 8 java/lang/AutoCloseable# 11 | override fun close() { 12 | // ^^^^^ definition semanticdb maven . . snapshots/Overrides#close(). 13 | // documentation ```kotlin\npublic open fun close()\n``` 14 | // relationship is_reference is_implementation semanticdb maven jdk 8 java/lang/AutoCloseable#close(). 15 | TODO("Not yet implemented") 16 | // ^^^^ reference semanticdb maven . . kotlin/StandardKt#TODO(+1). 17 | } 18 | } 19 | 20 | interface Animal { 21 | // ^^^^^^ definition semanticdb maven . . snapshots/Animal# 22 | // documentation ```kotlin\npublic interface Animal\n``` 23 | val favoriteNumber: Int 24 | // ^^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/Animal#favoriteNumber. 25 | // documentation ```kotlin\npublic abstract val favoriteNumber: kotlin.Int\n``` 26 | // ^^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/Animal#getFavoriteNumber(). 27 | // documentation ```kotlin\npublic abstract val favoriteNumber: kotlin.Int\n``` 28 | // ^^^ reference semanticdb maven . . kotlin/Int# 29 | fun sound(): String 30 | // ^^^^^ definition semanticdb maven . . snapshots/Animal#sound(). 31 | // documentation ```kotlin\npublic abstract fun sound(): kotlin.String\n``` 32 | // ^^^^^^ reference semanticdb maven . . kotlin/String# 33 | } 34 | open class Bird : Animal { 35 | // ^^^^ definition semanticdb maven . . snapshots/Bird# 36 | // documentation ```kotlin\npublic open class Bird : snapshots.Animal\n``` 37 | // relationship is_reference is_implementation semanticdb maven . . snapshots/Animal# 38 | // ^^^^ definition semanticdb maven . . snapshots/Bird#``(). 39 | // documentation ```kotlin\npublic constructor Bird()\n``` 40 | // ^^^^^^ reference semanticdb maven . . snapshots/Animal# 41 | override val favoriteNumber: Int 42 | // ^^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/Bird#favoriteNumber. 43 | // documentation ```kotlin\npublic open val favoriteNumber: kotlin.Int\n``` 44 | // ^^^ reference semanticdb maven . . kotlin/Int# 45 | get() = 42 46 | // ^^^ definition semanticdb maven . . snapshots/Bird#getFavoriteNumber(). 47 | // documentation ```kotlin\npublic open fun ``(): kotlin.Int\n``` 48 | 49 | override fun sound(): String { 50 | // ^^^^^ definition semanticdb maven . . snapshots/Bird#sound(). 51 | // documentation ```kotlin\npublic open fun sound(): kotlin.String\n``` 52 | // relationship is_reference is_implementation semanticdb maven . . snapshots/Animal#sound(). 53 | // ^^^^^^ reference semanticdb maven . . kotlin/String# 54 | return "tweet" 55 | } 56 | } 57 | class Seagull : Bird() { 58 | // ^^^^^^^ definition semanticdb maven . . snapshots/Seagull# 59 | // documentation ```kotlin\npublic final class Seagull : snapshots.Bird\n``` 60 | // relationship is_reference is_implementation semanticdb maven . . snapshots/Animal# 61 | // relationship is_reference is_implementation semanticdb maven . . snapshots/Bird# 62 | // ^^^^^^^ definition semanticdb maven . . snapshots/Seagull#``(). 63 | // documentation ```kotlin\npublic constructor Seagull()\n``` 64 | // ^^^^ reference semanticdb maven . . snapshots/Bird#``(). 65 | override val favoriteNumber: Int 66 | // ^^^^^^^^^^^^^^ definition semanticdb maven . . snapshots/Seagull#favoriteNumber. 67 | // documentation ```kotlin\npublic open val favoriteNumber: kotlin.Int\n``` 68 | // ^^^ reference semanticdb maven . . kotlin/Int# 69 | get() = 1337 70 | // ^^^ definition semanticdb maven . . snapshots/Seagull#getFavoriteNumber(). 71 | // documentation ```kotlin\npublic open fun ``(): kotlin.Int\n``` 72 | override fun sound(): String { 73 | // ^^^^^ definition semanticdb maven . . snapshots/Seagull#sound(). 74 | // documentation ```kotlin\npublic open fun sound(): kotlin.String\n``` 75 | // relationship is_reference is_implementation semanticdb maven . . snapshots/Animal#sound(). 76 | // relationship is_reference is_implementation semanticdb maven . . snapshots/Bird#sound(). 77 | // ^^^^^^ reference semanticdb maven . . kotlin/String# 78 | return "squawk" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbVisitor.kt: -------------------------------------------------------------------------------- 1 | package com.sourcegraph.semanticdb_kotlinc 2 | 3 | import com.sourcegraph.semanticdb_kotlinc.Semanticdb.SymbolOccurrence.Role 4 | import java.nio.file.Path 5 | import kotlin.contracts.ExperimentalContracts 6 | import org.jetbrains.kotlin.KtSourceElement 7 | import org.jetbrains.kotlin.KtSourceFile 8 | import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext 9 | import org.jetbrains.kotlin.fir.declarations.* 10 | import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference 11 | import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol 12 | import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol 13 | import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol 14 | import org.jetbrains.kotlin.name.FqName 15 | 16 | @ExperimentalContracts 17 | class SemanticdbVisitor( 18 | sourceroot: Path, 19 | file: KtSourceFile, 20 | lineMap: LineMap, 21 | globals: GlobalSymbolsCache, 22 | locals: LocalSymbolsCache = LocalSymbolsCache() 23 | ) { 24 | private val cache = SymbolsCache(globals, locals) 25 | private val documentBuilder = SemanticdbTextDocumentBuilder(sourceroot, file, lineMap, cache) 26 | 27 | private data class SymbolDescriptorPair( 28 | val firBasedSymbol: FirBasedSymbol<*>?, 29 | val symbol: Symbol 30 | ) 31 | 32 | fun build(): Semanticdb.TextDocument { 33 | return documentBuilder.build() 34 | } 35 | 36 | private fun Sequence?.emitAll( 37 | element: KtSourceElement, 38 | role: Role, 39 | context: CheckerContext, 40 | ): List? = 41 | this?.onEach { (firBasedSymbol, symbol) -> 42 | documentBuilder.emitSemanticdbData(firBasedSymbol, symbol, element, role, context) 43 | } 44 | ?.map { it.symbol } 45 | ?.toList() 46 | 47 | private fun Sequence.with(firBasedSymbol: FirBasedSymbol<*>?) = 48 | this.map { SymbolDescriptorPair(firBasedSymbol, it) } 49 | 50 | fun visitPackage(pkg: FqName, element: KtSourceElement, context: CheckerContext) { 51 | cache[pkg].with(null).emitAll(element, Role.REFERENCE, context) 52 | } 53 | 54 | fun visitClassReference(firClassSymbol: FirClassLikeSymbol<*>, element: KtSourceElement, context: CheckerContext) { 55 | cache[firClassSymbol].with(firClassSymbol).emitAll(element, Role.REFERENCE, context) 56 | } 57 | 58 | fun visitCallableReference(firClassSymbol: FirCallableSymbol<*>, element: KtSourceElement, context: CheckerContext) { 59 | cache[firClassSymbol].with(firClassSymbol).emitAll(element, Role.REFERENCE, context) 60 | } 61 | 62 | fun visitClassOrObject(firClass: FirClassLikeDeclaration, element: KtSourceElement, context: CheckerContext) { 63 | cache[firClass.symbol].with(firClass.symbol).emitAll(element, Role.DEFINITION, context) 64 | } 65 | 66 | fun visitPrimaryConstructor(firConstructor: FirConstructor, source: KtSourceElement, context: CheckerContext) { 67 | // if the constructor is not denoted by the 'constructor' keyword, we want to link it to the 68 | // class ident 69 | cache[firConstructor.symbol].with(firConstructor.symbol).emitAll(source, Role.DEFINITION, context) 70 | } 71 | 72 | fun visitSecondaryConstructor(firConstructor: FirConstructor, source: KtSourceElement, context: CheckerContext) { 73 | cache[firConstructor.symbol].with(firConstructor.symbol).emitAll(source, Role.DEFINITION, context) 74 | } 75 | 76 | fun visitNamedFunction(firFunction: FirFunction, source: KtSourceElement, context: CheckerContext) { 77 | cache[firFunction.symbol].with(firFunction.symbol).emitAll(source, Role.DEFINITION, context) 78 | } 79 | 80 | fun visitProperty(firProperty: FirProperty, source: KtSourceElement, context: CheckerContext) { 81 | cache[firProperty.symbol].with(firProperty.symbol).emitAll(source, Role.DEFINITION, context) 82 | } 83 | 84 | fun visitParameter(firParameter: FirValueParameter, source: KtSourceElement, context: CheckerContext) { 85 | cache[firParameter.symbol].with(firParameter.symbol).emitAll(source, Role.DEFINITION, context) 86 | } 87 | 88 | fun visitTypeParameter(firTypeParameter: FirTypeParameter, source: KtSourceElement, context: CheckerContext) { 89 | cache[firTypeParameter.symbol] 90 | .with(firTypeParameter.symbol) 91 | .emitAll(source, Role.DEFINITION, context) 92 | } 93 | 94 | fun visitTypeAlias(firTypeAlias: FirTypeAlias, source: KtSourceElement, context: CheckerContext) { 95 | cache[firTypeAlias.symbol].with(firTypeAlias.symbol).emitAll(source, Role.DEFINITION, context) 96 | } 97 | 98 | fun visitPropertyAccessor(firPropertyAccessor: FirPropertyAccessor, source: KtSourceElement, context: CheckerContext) { 99 | cache[firPropertyAccessor.symbol] 100 | .with(firPropertyAccessor.symbol) 101 | .emitAll(source, Role.DEFINITION, context) 102 | } 103 | 104 | fun visitSimpleNameExpression( 105 | firResolvedNamedReference: FirResolvedNamedReference, 106 | source: KtSourceElement, context: CheckerContext, 107 | ) { 108 | cache[firResolvedNamedReference.resolvedSymbol] 109 | .with(firResolvedNamedReference.resolvedSymbol) 110 | .emitAll(source, Role.REFERENCE, context) 111 | } 112 | } 113 | 114 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/test/kotlin/com/sourcegraph/semanticdb_kotlinc/test/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.sourcegraph.semanticdb_kotlinc.test 2 | 3 | import com.sourcegraph.semanticdb_kotlinc.* 4 | import com.sourcegraph.semanticdb_kotlinc.AnalyzerCheckers.Companion.visitors 5 | import com.tschuchort.compiletesting.KotlinCompilation 6 | import com.tschuchort.compiletesting.SourceFile 7 | import io.kotest.assertions.assertSoftly 8 | import io.kotest.assertions.throwables.shouldNotThrowAny 9 | import io.kotest.matchers.collections.shouldContainInOrder 10 | import io.kotest.matchers.shouldBe 11 | import java.nio.file.Path 12 | import java.nio.file.Paths 13 | import kotlin.contracts.ExperimentalContracts 14 | import org.intellij.lang.annotations.Language 15 | import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension 16 | import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar 17 | import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi 18 | import org.jetbrains.kotlin.config.CompilerConfiguration 19 | import org.jetbrains.kotlin.diagnostics.DiagnosticReporter 20 | import org.jetbrains.kotlin.fir.FirSession 21 | import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind 22 | import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext 23 | import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers 24 | import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirFileChecker 25 | import org.jetbrains.kotlin.fir.declarations.FirFile 26 | import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar 27 | import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrarAdapter 28 | import org.junit.jupiter.api.Assumptions.assumeFalse 29 | import org.junit.jupiter.api.DynamicTest 30 | import org.junit.jupiter.api.DynamicTest.dynamicTest 31 | 32 | data class ExpectedSymbols( 33 | val testName: String, 34 | val source: SourceFile, 35 | val symbolsCacheData: SymbolCacheData? = null, 36 | val semanticdb: SemanticdbData? = null 37 | ) { 38 | data class SemanticdbData( 39 | val expectedOccurrences: List? = null, 40 | val expectedSymbols: List? = null 41 | ) 42 | 43 | data class SymbolCacheData( 44 | val expectedGlobals: List? = null, 45 | val localsCount: Int? = null 46 | ) 47 | } 48 | 49 | fun SourceFile.Companion.testKt(@Language("kotlin") contents: String): SourceFile = 50 | kotlin("Test.kt", contents) 51 | 52 | @ExperimentalCompilerApi 53 | @ExperimentalContracts 54 | fun List.mapCheckExpectedSymbols(): List = 55 | this.flatMap { (testName, source, symbolsData, semanticdbData) -> 56 | val globals = GlobalSymbolsCache(testing = true) 57 | val locals = LocalSymbolsCache() 58 | lateinit var document: Semanticdb.TextDocument 59 | val compilation = configureTestCompiler(source, globals, locals) { document = it } 60 | listOf( 61 | dynamicTest("$testName - compilation") { 62 | val result = shouldNotThrowAny { compilation.compile() } 63 | result.exitCode shouldBe KotlinCompilation.ExitCode.OK 64 | }, 65 | dynamicTest("$testName - symbols") { 66 | symbolsData?.apply { 67 | println( 68 | "checking symbols: ${expectedGlobals?.size ?: 0} globals and presence of $localsCount locals") 69 | checkContainsExpectedSymbols(globals, locals, expectedGlobals, localsCount) 70 | } 71 | ?: assumeFalse(true) 72 | }, 73 | dynamicTest("$testName - semanticdb") { 74 | semanticdbData?.apply { 75 | println( 76 | "checking semanticdb: ${expectedOccurrences?.size ?: 0} occurrences and ${expectedSymbols?.size ?: 0} symbols") 77 | checkContainsExpectedSemanticdb(document, expectedOccurrences, expectedSymbols) 78 | } 79 | ?: assumeFalse(true) 80 | }) 81 | } 82 | 83 | @ExperimentalContracts 84 | fun checkContainsExpectedSymbols( 85 | globals: GlobalSymbolsCache, 86 | locals: LocalSymbolsCache, 87 | expectedGlobals: List?, 88 | localsCount: Int? = null 89 | ) { 90 | assertSoftly(globals) { expectedGlobals?.let { this.shouldContainInOrder(it) } } 91 | localsCount?.also { locals.size shouldBe it } 92 | } 93 | 94 | @ExperimentalContracts 95 | fun checkContainsExpectedSemanticdb( 96 | document: Semanticdb.TextDocument, 97 | expectedOccurrences: List?, 98 | expectedSymbols: List? 99 | ) { 100 | assertSoftly(document.occurrencesList) { 101 | expectedOccurrences?.let { this.shouldContainInOrder(it) } 102 | } 103 | assertSoftly(document.symbolsList) { expectedSymbols?.let { this.shouldContainInOrder(it) } } 104 | } 105 | 106 | @OptIn(ExperimentalCompilerApi::class) 107 | @ExperimentalContracts 108 | private fun configureTestCompiler( 109 | source: SourceFile, 110 | globals: GlobalSymbolsCache, 111 | locals: LocalSymbolsCache, 112 | hook: (Semanticdb.TextDocument) -> Unit = {} 113 | ): KotlinCompilation { 114 | val compilation = 115 | KotlinCompilation().apply { 116 | sources = listOf(source) 117 | inheritClassPath = true 118 | verbose = false 119 | } 120 | 121 | val analyzer = semanticdbVisitorAnalyzer(globals, locals, compilation.workingDir.toPath(), hook) 122 | compilation.apply { compilerPluginRegistrars = listOf(analyzer) } 123 | return compilation 124 | } 125 | 126 | @OptIn(ExperimentalContracts::class) 127 | private class TestAnalyzerDeclarationCheckers( 128 | globals: GlobalSymbolsCache, 129 | locals: LocalSymbolsCache, 130 | sourceRoot: Path 131 | ) : AnalyzerCheckers.AnalyzerDeclarationCheckers(sourceRoot) { 132 | override val fileCheckers: Set = 133 | setOf( 134 | object : FirFileChecker(MppCheckerKind.Common) { 135 | context(context: CheckerContext, reporter: DiagnosticReporter) 136 | override fun check(declaration: FirFile) { 137 | val ktFile = declaration.sourceFile ?: return 138 | val lineMap = LineMap(declaration) 139 | val visitor = SemanticdbVisitor(sourceRoot, ktFile, lineMap, globals, locals) 140 | visitors[ktFile] = visitor 141 | } 142 | }, 143 | AnalyzerCheckers.SemanticImportsChecker()) 144 | } 145 | 146 | private class TestAnalyzerCheckers(session: FirSession) : AnalyzerCheckers(session) { 147 | @OptIn(ExperimentalContracts::class) 148 | override val declarationCheckers: DeclarationCheckers 149 | get() = 150 | TestAnalyzerDeclarationCheckers( 151 | session.testAnalyzerParamsProvider.globals, 152 | session.testAnalyzerParamsProvider.locals, 153 | session.testAnalyzerParamsProvider.sourceroot 154 | ) 155 | } 156 | 157 | @OptIn(ExperimentalContracts::class) 158 | class TestAnalyzerParamsProvider( 159 | session: FirSession, 160 | var globals: GlobalSymbolsCache, 161 | var locals: LocalSymbolsCache, 162 | sourceroot: Path, 163 | ) : AnalyzerParamsProvider(session, sourceroot) { 164 | companion object { 165 | fun getFactory( 166 | globals: GlobalSymbolsCache, 167 | locals: LocalSymbolsCache, 168 | sourceroot: Path, 169 | ): Factory { 170 | return Factory { TestAnalyzerParamsProvider(it, globals, locals, sourceroot) } 171 | } 172 | } 173 | } 174 | 175 | val FirSession.testAnalyzerParamsProvider: TestAnalyzerParamsProvider by FirSession 176 | .sessionComponentAccessor() 177 | 178 | @OptIn(ExperimentalCompilerApi::class) 179 | @ExperimentalContracts 180 | fun semanticdbVisitorAnalyzer( 181 | globals: GlobalSymbolsCache, 182 | locals: LocalSymbolsCache, 183 | sourceroot: Path, 184 | hook: (Semanticdb.TextDocument) -> Unit = {} 185 | ): CompilerPluginRegistrar { 186 | return object : CompilerPluginRegistrar() { 187 | override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) { 188 | FirExtensionRegistrarAdapter.registerExtension( 189 | object : FirExtensionRegistrar() { 190 | override fun ExtensionRegistrarContext.configurePlugin() { 191 | +TestAnalyzerParamsProvider.getFactory(globals, locals, sourceroot) 192 | +::TestAnalyzerCheckers 193 | } 194 | }) 195 | IrGenerationExtension.registerExtension( 196 | PostAnalysisExtension( 197 | sourceRoot = sourceroot, targetRoot = Paths.get(""), callback = hook)) 198 | } 199 | 200 | override val supportsK2: Boolean 201 | get() = true 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SemanticdbTextDocumentBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.sourcegraph.semanticdb_kotlinc 2 | 3 | import com.sourcegraph.semanticdb_kotlinc.Semanticdb.SymbolOccurrence.Role 4 | import java.nio.file.Path 5 | import java.nio.file.Paths 6 | import java.security.MessageDigest 7 | import kotlin.contracts.ExperimentalContracts 8 | import org.jetbrains.kotlin.KtSourceElement 9 | import org.jetbrains.kotlin.KtSourceFile 10 | import org.jetbrains.kotlin.com.intellij.lang.java.JavaLanguage 11 | import org.jetbrains.kotlin.fir.FirElement 12 | import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext 13 | import org.jetbrains.kotlin.fir.analysis.checkers.directOverriddenSymbolsSafe 14 | import org.jetbrains.kotlin.fir.analysis.checkers.toClassLikeSymbol 15 | import org.jetbrains.kotlin.fir.analysis.getChild 16 | import org.jetbrains.kotlin.fir.renderer.* 17 | import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol 18 | import org.jetbrains.kotlin.fir.symbols.SymbolInternals 19 | import org.jetbrains.kotlin.fir.symbols.impl.* 20 | import org.jetbrains.kotlin.fir.types.impl.FirImplicitAnyTypeRef 21 | import org.jetbrains.kotlin.idea.KotlinLanguage 22 | import org.jetbrains.kotlin.lexer.KtTokens 23 | import org.jetbrains.kotlin.psi 24 | import org.jetbrains.kotlin.text 25 | 26 | @ExperimentalContracts 27 | class SemanticdbTextDocumentBuilder( 28 | private val sourceroot: Path, 29 | private val file: KtSourceFile, 30 | private val lineMap: LineMap, 31 | private val cache: SymbolsCache, 32 | ) { 33 | private val occurrences = mutableListOf() 34 | private val symbols = mutableListOf() 35 | private val fileText = file.getContentsAsStream().reader().readText() 36 | private val semanticMd5 = semanticdbMD5() 37 | 38 | fun build() = TextDocument { 39 | this.text = fileText 40 | this.uri = semanticdbURI() 41 | this.md5 = semanticMd5 42 | this.schema = Semanticdb.Schema.SEMANTICDB4 43 | this.language = Semanticdb.Language.KOTLIN 44 | occurrences.sortWith(compareBy({ it.range.startLine }, { it.range.startCharacter })) 45 | this.addAllOccurrences(occurrences) 46 | this.addAllSymbols(symbols) 47 | } 48 | 49 | fun emitSemanticdbData( 50 | firBasedSymbol: FirBasedSymbol<*>?, 51 | symbol: Symbol, 52 | element: KtSourceElement, 53 | role: Role, 54 | context: CheckerContext, 55 | ) { 56 | symbolOccurrence(symbol, element, role).let { 57 | if (!occurrences.contains(it)) { 58 | occurrences.add(it) 59 | } 60 | } 61 | val symbolInformation = symbolInformation(firBasedSymbol, symbol, element, context) 62 | if (role == Role.DEFINITION && !symbols.contains(symbolInformation)) 63 | symbols.add(symbolInformation) 64 | } 65 | 66 | @OptIn(SymbolInternals::class) 67 | private fun symbolInformation( 68 | firBasedSymbol: FirBasedSymbol<*>?, 69 | symbol: Symbol, 70 | element: KtSourceElement, 71 | context: CheckerContext, 72 | ): Semanticdb.SymbolInformation { 73 | val supers = 74 | when (firBasedSymbol) { 75 | is FirClassSymbol -> 76 | firBasedSymbol 77 | .resolvedSuperTypeRefs 78 | .filter { it !is FirImplicitAnyTypeRef } 79 | .map { it.toClassLikeSymbol(firBasedSymbol.moduleData.session) } 80 | .filterNotNull() 81 | .flatMap { cache[it] } 82 | is FirFunctionSymbol<*> -> 83 | firBasedSymbol.directOverriddenSymbolsSafe(context).flatMap { cache[it] } 84 | else -> emptyList().asIterable() 85 | } 86 | return SymbolInformation { 87 | this.symbol = symbol.toString() 88 | this.displayName = 89 | if (firBasedSymbol != null) { 90 | displayName(firBasedSymbol) 91 | } else { 92 | element.text.toString() 93 | } 94 | this.documentation = 95 | if (firBasedSymbol != null) { 96 | semanticdbDocumentation(firBasedSymbol.fir) 97 | } else { 98 | Documentation { 99 | format = Semanticdb.Documentation.Format.MARKDOWN 100 | message = "" 101 | } 102 | } 103 | this.addAllOverriddenSymbols(supers.map { it.toString() }) 104 | this.language = 105 | when (element.psi?.language ?: KotlinLanguage.INSTANCE) { 106 | is KotlinLanguage -> Semanticdb.Language.KOTLIN 107 | is JavaLanguage -> Semanticdb.Language.JAVA 108 | else -> throw IllegalArgumentException("unexpected language") 109 | } 110 | } 111 | } 112 | 113 | private fun symbolOccurrence( 114 | symbol: Symbol, 115 | element: KtSourceElement, 116 | role: Role 117 | ): Semanticdb.SymbolOccurrence { 118 | return SymbolOccurrence { 119 | this.symbol = symbol.toString() 120 | this.role = role 121 | this.range = semanticdbRange(element) 122 | } 123 | } 124 | 125 | private fun semanticdbRange(element: KtSourceElement): Semanticdb.Range { 126 | return Range { 127 | startCharacter = lineMap.startCharacter(element) 128 | startLine = lineMap.lineNumber(element) - 1 129 | endCharacter = lineMap.endCharacter(element) 130 | endLine = lineMap.lineNumber(element) - 1 131 | } 132 | } 133 | 134 | private fun semanticdbURI(): String { 135 | // TODO: unix-style only 136 | val relative = sourceroot.relativize(Paths.get(file.path)) 137 | return relative.toString() 138 | } 139 | 140 | private fun semanticdbMD5(): String = 141 | MessageDigest.getInstance("MD5") 142 | .digest(file.getContentsAsStream().readBytes()) 143 | .joinToString("") { "%02X".format(it) } 144 | 145 | private fun semanticdbDocumentation(element: FirElement): Semanticdb.Documentation = Documentation { 146 | format = Semanticdb.Documentation.Format.MARKDOWN 147 | // Like FirRenderer().forReadability, but using FirAllModifierRenderer instead of FirPartialModifierRenderer 148 | val renderer = FirRenderer( 149 | typeRenderer = ConeTypeRenderer(), 150 | idRenderer = ConeIdShortRenderer(), 151 | classMemberRenderer = FirNoClassMemberRenderer(), 152 | bodyRenderer = null, 153 | propertyAccessorRenderer = null, 154 | callArgumentsRenderer = FirCallNoArgumentsRenderer(), 155 | modifierRenderer = FirAllModifierRenderer(), 156 | callableSignatureRenderer = FirCallableSignatureRendererForReadability(), 157 | declarationRenderer = FirDeclarationRenderer("local "), 158 | ) 159 | val renderOutput = renderer.renderElementAsString(element) 160 | val kdoc = element.source?.getChild(KtTokens.DOC_COMMENT)?.text?.toString() ?: "" 161 | message = "```kotlin\n$renderOutput\n```${stripKDocAsterisks(kdoc)}" 162 | } 163 | 164 | // Returns the kdoc string with all leading and trailing "/*" tokens removed. Naive 165 | // implementation that can 166 | // be replaced with a utility method from the compiler in the future, if one exists. 167 | private fun stripKDocAsterisks(kdoc: String): String { 168 | if (kdoc.isEmpty()) return kdoc 169 | val out = StringBuilder().append("\n\n").append("----").append("\n") 170 | kdoc.lineSequence().forEach { line -> 171 | if (line.isEmpty()) return@forEach 172 | var start = 0 173 | while (start < line.length && line[start].isWhitespace()) { 174 | start++ 175 | } 176 | if (start < line.length && line[start] == '/') { 177 | start++ 178 | } 179 | while (start < line.length && line[start] == '*') { 180 | start++ 181 | } 182 | var end = line.length - 1 183 | if (end > start && line[end] == '/') { 184 | end-- 185 | } 186 | while (end > start && line[end] == '*') { 187 | end-- 188 | } 189 | while (end > start && line[end].isWhitespace()) { 190 | end-- 191 | } 192 | start = minOf(start, line.length - 1) 193 | if (end > start) { 194 | end++ 195 | } 196 | out.append("\n").append(line, start, end) 197 | } 198 | return out.toString() 199 | } 200 | 201 | companion object { 202 | @OptIn(SymbolInternals::class) 203 | private fun displayName(firBasedSymbol: FirBasedSymbol<*>): String = 204 | when (firBasedSymbol) { 205 | is FirClassSymbol -> firBasedSymbol.classId.shortClassName.asString() 206 | is FirPropertyAccessorSymbol -> firBasedSymbol.fir.propertySymbol.name.asString() 207 | is FirFunctionSymbol -> firBasedSymbol.callableId.callableName.asString() 208 | is FirPropertySymbol -> firBasedSymbol.callableId.callableName.asString() 209 | is FirVariableSymbol -> firBasedSymbol.name.asString() 210 | else -> firBasedSymbol.toString() 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s 90 | ' "$PWD" ) || exit 91 | 92 | # Use the maximum available, or set MAX_FD != -1 to use that value. 93 | MAX_FD=maximum 94 | 95 | warn () { 96 | echo "$*" 97 | } >&2 98 | 99 | die () { 100 | echo 101 | echo "$*" 102 | echo 103 | exit 1 104 | } >&2 105 | 106 | # OS specific support (must be 'true' or 'false'). 107 | cygwin=false 108 | msys=false 109 | darwin=false 110 | nonstop=false 111 | case "$( uname )" in #( 112 | CYGWIN* ) cygwin=true ;; #( 113 | Darwin* ) darwin=true ;; #( 114 | MSYS* | MINGW* ) msys=true ;; #( 115 | NONSTOP* ) nonstop=true ;; 116 | esac 117 | 118 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 119 | 120 | 121 | # Determine the Java command to use to start the JVM. 122 | if [ -n "$JAVA_HOME" ] ; then 123 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 124 | # IBM's JDK on AIX uses strange locations for the executables 125 | JAVACMD=$JAVA_HOME/jre/sh/java 126 | else 127 | JAVACMD=$JAVA_HOME/bin/java 128 | fi 129 | if [ ! -x "$JAVACMD" ] ; then 130 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 131 | 132 | Please set the JAVA_HOME variable in your environment to match the 133 | location of your Java installation." 134 | fi 135 | else 136 | JAVACMD=java 137 | if ! command -v java >/dev/null 2>&1 138 | then 139 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 140 | 141 | Please set the JAVA_HOME variable in your environment to match the 142 | location of your Java installation." 143 | fi 144 | fi 145 | 146 | # Increase the maximum file descriptors if we can. 147 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 148 | case $MAX_FD in #( 149 | max*) 150 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 151 | # shellcheck disable=SC2039,SC3045 152 | MAX_FD=$( ulimit -H -n ) || 153 | warn "Could not query maximum file descriptor limit" 154 | esac 155 | case $MAX_FD in #( 156 | '' | soft) :;; #( 157 | *) 158 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 159 | # shellcheck disable=SC2039,SC3045 160 | ulimit -n "$MAX_FD" || 161 | warn "Could not set maximum file descriptor limit to $MAX_FD" 162 | esac 163 | fi 164 | 165 | # Collect all arguments for the java command, stacking in reverse order: 166 | # * args from the command line 167 | # * the main class name 168 | # * -classpath 169 | # * -D...appname settings 170 | # * --module-path (only if needed) 171 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 172 | 173 | # For Cygwin or MSYS, switch paths to Windows format before running java 174 | if "$cygwin" || "$msys" ; then 175 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 176 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 177 | 178 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 179 | 180 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 181 | for arg do 182 | if 183 | case $arg in #( 184 | -*) false ;; # don't mess with options #( 185 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 186 | [ -e "$t" ] ;; #( 187 | *) false ;; 188 | esac 189 | then 190 | arg=$( cygpath --path --ignore --mixed "$arg" ) 191 | fi 192 | # Roll the args list around exactly as many times as the number of 193 | # args, so each arg winds up back in the position where it started, but 194 | # possibly modified. 195 | # 196 | # NB: a `for` loop captures its iteration list before it begins, so 197 | # changing the positional parameters here affects neither the number of 198 | # iterations, nor the values presented in `arg`. 199 | shift # remove old arg 200 | set -- "$@" "$arg" # push replacement arg 201 | done 202 | fi 203 | 204 | 205 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 206 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 207 | 208 | # Collect all arguments for the java command: 209 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 210 | # and any embedded shellness will be escaped. 211 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 212 | # treated as '${Hostname}' itself on the command line. 213 | 214 | set -- \ 215 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 216 | -classpath "$CLASSPATH" \ 217 | org.gradle.wrapper.GradleWrapperMain \ 218 | "$@" 219 | 220 | # Stop when "xargs" is not available. 221 | if ! command -v xargs >/dev/null 2>&1 222 | then 223 | die "xargs is not available" 224 | fi 225 | 226 | # Use "xargs" to parse quoted args. 227 | # 228 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 229 | # 230 | # In Bash we could simply go: 231 | # 232 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 233 | # set -- "${ARGS[@]}" "$@" 234 | # 235 | # but POSIX shell has neither arrays nor command substitution, so instead we 236 | # post-process each arg (as a line of input to sed) to backslash-escape any 237 | # character that might be a shell metacharacter, then use eval to reverse 238 | # that process (while maintaining the separation between arguments), and wrap 239 | # the whole thing up as a single "set" statement. 240 | # 241 | # This will of course break if any of these variables contains a newline or 242 | # an unmatched quote. 243 | # 244 | 245 | eval "set -- $( 246 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 247 | xargs -n1 | 248 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 249 | tr '\n' ' ' 250 | )" '"$@"' 251 | 252 | exec "$JAVACMD" "$@" 253 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import java.net.URI 2 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 3 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 4 | import org.gradle.api.tasks.testing.logging.TestExceptionFormat 5 | import org.gradle.api.publish.maven.MavenPublication 6 | 7 | plugins { 8 | kotlin("jvm") 9 | id("com.github.johnrengelman.shadow") 10 | id("maven-publish") 11 | signing 12 | } 13 | 14 | repositories { 15 | mavenLocal() 16 | mavenCentral() 17 | } 18 | 19 | // create a new sourceset for the subproject JavaExec tasks to consume as a runtime classpath 20 | // maybe we should move snapshot to its own subproject? 21 | val snapshots: SourceSet by sourceSets.creating { 22 | java.srcDirs("src/snapshots/kotlin") 23 | } 24 | 25 | // create a new configuration independent of the one consumed by the shadowJar task 26 | val snapshotsImplementation: Configuration by configurations.getting { 27 | extendsFrom(configurations.testImplementation.get()) 28 | } 29 | 30 | dependencies { 31 | implementation(kotlin("stdlib")) 32 | compileOnly(kotlin("compiler-embeddable")) 33 | implementation("com.google.protobuf", "protobuf-java", "3.15.7") 34 | implementation(projects.semanticdbKotlin) 35 | 36 | testImplementation(kotlin("compiler-embeddable")) 37 | testImplementation(kotlin("test")) 38 | testImplementation("io.kotest", "kotest-assertions-core", "4.6.3") 39 | 40 | // Unable to use com.github.tschuchortdev:kotlin-compile-testing until 1.9.x support is fixed 41 | // https://github.com/tschuchortdev/kotlin-compile-testing/issues/390 42 | // Until then, we use the fork from https://github.com/ZacSweers/kotlin-compile-testing instead. 43 | // testImplementation("com.github.tschuchortdev", "kotlin-compile-testing", "1.5.0") 44 | testImplementation("dev.zacsweers.kctfork", "core", "0.7.1") 45 | 46 | testImplementation("org.junit.jupiter", "junit-jupiter-params", "5.8.1") 47 | testImplementation("org.jetbrains.kotlin", "kotlin-stdlib-jdk8", "1.5.0") { 48 | version { 49 | strictly("1.5.0") 50 | } 51 | }.because("transitive dependencies introduce 1.4.31 to the classpath which conflicts, can't use testRuntimeOnly") 52 | testImplementation(kotlin("reflect")) 53 | testImplementation(kotlin("script-runtime", "1.5.0")) 54 | 55 | snapshotsImplementation("com.sourcegraph", "scip-java_2.13", "0.8.24") 56 | } 57 | 58 | tasks.withType { 59 | dependsOn(":${projects.semanticdbKotlin.name}:build") 60 | } 61 | 62 | kotlin { 63 | compilerOptions { 64 | freeCompilerArgs.addAll( 65 | "-Xinline-classes", 66 | "-Xcontext-parameters", 67 | ) 68 | } 69 | } 70 | 71 | val semanticdbJar: Configuration by configurations.creating { 72 | isCanBeConsumed = true 73 | isCanBeResolved = false 74 | } 75 | 76 | artifacts { 77 | add(semanticdbJar.name, tasks.shadowJar) 78 | } 79 | 80 | tasks.jar { 81 | archiveClassifier.set("slim") 82 | manifest { 83 | attributes["Specification-Title"] = project.name 84 | attributes["Specification-Version"] = project.version 85 | attributes["Implementation-Title"] = "semanticdb-kotlinc" 86 | attributes["Implementation-Version"] = project.version 87 | } 88 | } 89 | 90 | tasks.shadowJar { 91 | archiveClassifier.set("") 92 | relocate("com.intellij", "org.jetbrains.kotlin.com.intellij") 93 | minimize() 94 | } 95 | 96 | val sourceJar = task("sourceJar") { 97 | dependsOn(tasks.classes) 98 | archiveClassifier.set("sources") 99 | from(sourceSets.main.get().allSource) 100 | } 101 | 102 | val javadocJar = task("javadocJar") { 103 | dependsOn(tasks.javadoc) 104 | archiveClassifier.set("javadoc") 105 | from(tasks.javadoc.get().destinationDir) 106 | } 107 | 108 | publishing { 109 | publications { 110 | create("shadow") { 111 | this.apply { 112 | pom { 113 | name.set("semanticdb-kotlinc") 114 | description.set("A kotlinc plugin to emit SemanticDB information") 115 | url.set("https://github.com/sourcegraph/scip-kotlin") 116 | developers { 117 | developer { 118 | id.set("strum355") 119 | name.set("Noah Santschi-Cooney") 120 | email.set("noah@sourcegraph.com") 121 | } 122 | developer { 123 | id.set("olafurpg") 124 | name.set("Ólafur Páll Geirsson") 125 | email.set("olafurpg@sourcegraph.com") 126 | } 127 | } 128 | licenses { 129 | license { 130 | name.set("The Apache License, Version 2.0") 131 | url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") 132 | } 133 | } 134 | scm { 135 | url.set("https://github.com/sourcegraph/lsif-kotlin") 136 | } 137 | } 138 | shadow.component(this) 139 | artifact(sourceJar) 140 | artifact(javadocJar) 141 | } 142 | } 143 | } 144 | repositories { 145 | mavenLocal() 146 | maven { 147 | name = "sonatype" 148 | url = 149 | if (!(version as String).endsWith("-SNAPSHOT")) 150 | URI("https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/") 151 | else 152 | URI("https://central.sonatype.com/repository/maven-snapshots/") 153 | credentials { 154 | username = System.getenv("SONATYPE_USERNAME") 155 | password = System.getenv("SONATYPE_PASSWORD") 156 | } 157 | } 158 | } 159 | } 160 | 161 | signing { 162 | val signingKey: String? by project 163 | val signingPassword: String? by project 164 | useInMemoryPgpKeys(signingKey, signingPassword) 165 | sign(publishing.publications["shadow"]) 166 | } 167 | 168 | tasks.withType().configureEach { 169 | onlyIf { !(project.version as String).endsWith("SNAPSHOT") } 170 | } 171 | 172 | tasks.test { 173 | useJUnitPlatform() 174 | testLogging { 175 | showStandardStreams = true 176 | exceptionFormat = TestExceptionFormat.FULL 177 | events("passed", "failed") 178 | } 179 | maxHeapSize = "2g" 180 | } 181 | 182 | subprojects { 183 | apply(plugin = "org.jetbrains.kotlin.jvm") 184 | 185 | repositories { 186 | mavenCentral() 187 | } 188 | 189 | dependencies { 190 | implementation(kotlin("stdlib")) 191 | compileOnly("com.sourcegraph", "semanticdb-javac", "0.6.12") 192 | } 193 | 194 | afterEvaluate { 195 | val semanticdbJar: Configuration by configurations.creating { 196 | isCanBeConsumed = false 197 | isCanBeResolved = true 198 | } 199 | 200 | dependencies { 201 | semanticdbJar(project(mapOf( 202 | "path" to projects.semanticdbKotlinc.dependencyProject.path, 203 | "configuration" to "semanticdbJar" 204 | ))) 205 | } 206 | 207 | val sourceroot = rootDir.path 208 | val targetroot = project.buildDir.resolve( "semanticdb-targetroot") 209 | 210 | tasks.withType { 211 | dependsOn(projects.semanticdbKotlinc.dependencyProject.tasks.shadowJar.get().path) 212 | outputs.upToDateWhen { false } 213 | } 214 | 215 | kotlin { 216 | val pluginJar = semanticdbJar.incoming.artifacts.artifactFiles.first().path 217 | compilerOptions { 218 | jvmTarget = JvmTarget.JVM_1_8 219 | freeCompilerArgs.addAll( 220 | "-Xplugin=$pluginJar", 221 | "-P", 222 | "plugin:semanticdb-kotlinc:sourceroot=${sourceroot}", 223 | "-P", 224 | "plugin:semanticdb-kotlinc:targetroot=${targetroot}", 225 | ) 226 | } 227 | } 228 | 229 | tasks.withType { 230 | dependsOn(projects.semanticdbKotlinc.dependencyProject.tasks.shadowJar.get().path) 231 | outputs.upToDateWhen { false } 232 | options.compilerArgs = options.compilerArgs + listOf( 233 | "-Xplugin:semanticdb -sourceroot:$sourceroot -targetroot:$targetroot" 234 | ) 235 | } 236 | 237 | // create a sourceset in which to output the generated snapshots. 238 | // we may choose to not use sourcesets down the line 239 | val generatedSnapshots: SourceSet by sourceSets.creating { 240 | resources.srcDir("generatedSnapshots") 241 | } 242 | 243 | // for each subproject e.g. 'minimized', create a JavaExec task that invokes the snapshot creating main class 244 | task("snapshots") { 245 | javaLauncher.set(javaToolchains.launcherFor { 246 | languageVersion.set(JavaLanguageVersion.of(8)) 247 | }) 248 | dependsOn( 249 | project.tasks.compileKotlin.get().path, 250 | project.tasks.compileJava.get().path 251 | ) 252 | outputs.upToDateWhen { false } 253 | mainClass.set("com.sourcegraph.lsif_kotlin.SnapshotKt") 254 | // this is required as the main class SnapshotKt is in this classpath 255 | classpath = snapshots.runtimeClasspath 256 | args = listOf( 257 | kotlin.sourceSets.main.get().kotlin.srcDirs.first().canonicalPath, 258 | sourceSets.main.get().java.srcDirs.first().canonicalPath 259 | ) 260 | systemProperties = mapOf( 261 | "sourceroot" to sourceroot, 262 | "targetroot" to project.buildDir.resolve("semanticdb-targetroot"), 263 | "snapshotDir" to generatedSnapshots.resources.srcDirs.first()) 264 | } 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/SymbolsCache.kt: -------------------------------------------------------------------------------- 1 | package com.sourcegraph.semanticdb_kotlinc 2 | 3 | import com.sourcegraph.semanticdb_kotlinc.SemanticdbSymbolDescriptor.Kind 4 | import java.lang.System.err 5 | import kotlin.contracts.ExperimentalContracts 6 | import kotlin.contracts.contract 7 | import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember 8 | import org.jetbrains.kotlin.fir.analysis.checkers.getContainingSymbol 9 | import org.jetbrains.kotlin.fir.declarations.DirectDeclarationsAccess 10 | import org.jetbrains.kotlin.fir.declarations.FirClass 11 | import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin 12 | import org.jetbrains.kotlin.fir.declarations.utils.memberDeclarationNameOrNull 13 | import org.jetbrains.kotlin.fir.packageFqName 14 | import org.jetbrains.kotlin.fir.resolve.getContainingDeclaration 15 | import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider 16 | import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol 17 | import org.jetbrains.kotlin.fir.symbols.SymbolInternals 18 | import org.jetbrains.kotlin.fir.symbols.impl.* 19 | import org.jetbrains.kotlin.name.FqName 20 | import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly 21 | 22 | @ExperimentalContracts 23 | class GlobalSymbolsCache(testing: Boolean = false) : Iterable { 24 | private val globals = 25 | if (testing) LinkedHashMap, Symbol>() 26 | else HashMap, Symbol>() 27 | private val packages = 28 | if (testing) LinkedHashMap() else HashMap() 29 | 30 | operator fun get(symbol: FirBasedSymbol<*>, locals: LocalSymbolsCache): Sequence = 31 | sequence { 32 | emitSymbols(symbol, locals) 33 | } 34 | 35 | operator fun get(symbol: FqName): Sequence = sequence { emitSymbols(symbol) } 36 | 37 | /** 38 | * called whenever a new symbol should be yielded in the sequence e.g. for properties we also 39 | * want to yield for every implicit getter/setter, but wouldn't want to yield for e.g. the 40 | * package symbol parts that a class symbol is composed of. 41 | */ 42 | @OptIn(SymbolInternals::class) 43 | private suspend fun SequenceScope.emitSymbols( 44 | symbol: FirBasedSymbol<*>, 45 | locals: LocalSymbolsCache 46 | ) { 47 | yield(getSymbol(symbol, locals)) 48 | if (symbol is FirPropertySymbol) { 49 | if (symbol.fir.getter?.origin is FirDeclarationOrigin.Synthetic) 50 | emitSymbols(symbol.fir.getter!!.symbol, locals) 51 | if (symbol.fir.setter?.origin is FirDeclarationOrigin.Synthetic) 52 | emitSymbols(symbol.fir.setter!!.symbol, locals) 53 | } 54 | } 55 | 56 | private suspend fun SequenceScope.emitSymbols(symbol: FqName) { 57 | yield(getSymbol(symbol)) 58 | } 59 | 60 | /** 61 | * Entrypoint for building or looking-up a symbol without yielding a value in the sequence. 62 | * Called recursively for every part of a symbol, unless a cached result short circuits. 63 | */ 64 | private fun getSymbol(symbol: FirBasedSymbol<*>, locals: LocalSymbolsCache): Symbol { 65 | globals[symbol]?.let { 66 | return it 67 | } 68 | locals[symbol]?.let { 69 | return it 70 | } 71 | return uncachedSemanticdbSymbol(symbol, locals).also { 72 | if (it.isGlobal()) globals[symbol] = it 73 | } 74 | } 75 | private fun getSymbol(symbol: FqName): Symbol { 76 | packages[symbol]?.let { 77 | return it 78 | } 79 | return uncachedSemanticdbSymbol(symbol).also { if (it.isGlobal()) packages[symbol] = it } 80 | } 81 | 82 | private fun skip(symbol: FirBasedSymbol<*>?): Boolean { 83 | contract { returns(false) implies (symbol != null) } 84 | return symbol == null || symbol is FirAnonymousFunctionSymbol 85 | } 86 | 87 | @OptIn(SymbolInternals::class) 88 | private fun uncachedSemanticdbSymbol( 89 | symbol: FirBasedSymbol<*>?, 90 | locals: LocalSymbolsCache 91 | ): Symbol { 92 | if (skip(symbol)) return Symbol.NONE 93 | 94 | if (symbol.fir.isLocalMember) return locals + symbol 95 | 96 | val owner = getParentSymbol(symbol, locals) 97 | 98 | if (owner.isLocal()) return locals + symbol 99 | 100 | val semanticdbDescriptor = semanticdbDescriptor(symbol) 101 | 102 | return Symbol.createGlobal(owner, semanticdbDescriptor) 103 | } 104 | 105 | private fun uncachedSemanticdbSymbol(symbol: FqName): Symbol { 106 | if (symbol.isRoot) return Symbol.ROOT_PACKAGE 107 | 108 | val owner = this.getSymbol(symbol.parent()) 109 | return Symbol.createGlobal( 110 | owner, SemanticdbSymbolDescriptor(Kind.PACKAGE, symbol.shortName().asString())) 111 | } 112 | 113 | /** 114 | * Returns the parent DeclarationDescriptor for a given DeclarationDescriptor. For most 115 | * descriptor types, this simply returns the 'containing' descriptor. For Module- or 116 | * PackageFragmentDescriptors, it returns the descriptor for the parent fqName of the current 117 | * descriptors fqName e.g. for the fqName `test.sample.main`, the parent fqName would be 118 | * `test.sample`. 119 | */ 120 | @OptIn(SymbolInternals::class) 121 | private fun getParentSymbol(symbol: FirBasedSymbol<*>, locals: LocalSymbolsCache): Symbol { 122 | when (symbol) { 123 | is FirTypeParameterSymbol -> 124 | return getSymbol(symbol.containingDeclarationSymbol, locals) 125 | is FirValueParameterSymbol -> return getSymbol(symbol.containingDeclarationSymbol, locals) 126 | is FirCallableSymbol -> { 127 | val session = symbol.fir.moduleData.session 128 | return symbol.getContainingSymbol(session)?.let { getSymbol(it, locals) } 129 | ?: getSymbol(symbol.packageFqName()) 130 | } 131 | is FirClassLikeSymbol -> { 132 | val session = symbol.fir.moduleData.session 133 | return symbol.getContainingDeclaration(session)?.let { getSymbol(it, locals) } 134 | ?: getSymbol(symbol.packageFqName()) 135 | } 136 | is FirFileSymbol -> { 137 | return getSymbol(symbol.fir.packageFqName) 138 | } 139 | else -> return Symbol.NONE 140 | } 141 | } 142 | 143 | @OptIn(SymbolInternals::class) 144 | private fun semanticdbDescriptor(symbol: FirBasedSymbol<*>): SemanticdbSymbolDescriptor { 145 | return when { 146 | symbol is FirAnonymousObjectSymbol -> 147 | symbol.source?.let { source -> 148 | SemanticdbSymbolDescriptor(Kind.TYPE, "") 149 | } ?: SemanticdbSymbolDescriptor.NONE 150 | symbol is FirClassLikeSymbol -> 151 | SemanticdbSymbolDescriptor(Kind.TYPE, symbol.classId.shortClassName.asString()) 152 | symbol is FirPropertyAccessorSymbol && symbol.isSetter -> 153 | SemanticdbSymbolDescriptor( 154 | Kind.METHOD, 155 | "set" + symbol.propertySymbol.fir.name.toString().capitalizeAsciiOnly()) 156 | symbol is FirPropertyAccessorSymbol && symbol.isGetter -> 157 | SemanticdbSymbolDescriptor( 158 | Kind.METHOD, 159 | "get" + symbol.propertySymbol.fir.name.toString().capitalizeAsciiOnly()) 160 | symbol is FirConstructorSymbol -> 161 | SemanticdbSymbolDescriptor(Kind.METHOD, "", methodDisambiguator(symbol)) 162 | symbol is FirFunctionSymbol -> 163 | SemanticdbSymbolDescriptor( 164 | Kind.METHOD, symbol.name.toString(), methodDisambiguator(symbol)) 165 | symbol is FirTypeParameterSymbol -> 166 | SemanticdbSymbolDescriptor(Kind.TYPE_PARAMETER, symbol.name.toString()) 167 | symbol is FirValueParameterSymbol -> 168 | SemanticdbSymbolDescriptor(Kind.PARAMETER, symbol.name.toString()) 169 | symbol is FirVariableSymbol -> 170 | SemanticdbSymbolDescriptor(Kind.TERM, symbol.name.toString()) 171 | else -> { 172 | err.println("unknown symbol kind ${symbol.javaClass.simpleName}") 173 | SemanticdbSymbolDescriptor.NONE 174 | } 175 | } 176 | } 177 | 178 | @OptIn(SymbolInternals::class, DirectDeclarationsAccess::class) 179 | private fun methodDisambiguator(symbol: FirFunctionSymbol<*>): String { 180 | val session = symbol.moduleData.session 181 | 182 | val siblings = 183 | when (val containingSymbol = symbol.getContainingSymbol(session)) { 184 | is FirClassSymbol -> 185 | (containingSymbol.fir as FirClass).declarations.map { it.symbol } 186 | is FirFileSymbol -> containingSymbol.fir.declarations.map { it.symbol } 187 | null -> 188 | symbol.moduleData.session.symbolProvider.getTopLevelCallableSymbols( 189 | symbol.packageFqName(), symbol.name) 190 | else -> return "()" 191 | } 192 | 193 | var count = 0 194 | var found = false 195 | for (decl in siblings) { 196 | if (decl == symbol) { 197 | found = true 198 | break 199 | } 200 | 201 | if (decl.memberDeclarationNameOrNull?.equals(symbol.name) == true) { 202 | count++ 203 | } 204 | } 205 | 206 | if (count == 0 || !found) return "()" 207 | return "(+${count})" 208 | } 209 | 210 | override fun iterator(): Iterator = globals.values.iterator() 211 | } 212 | 213 | class LocalSymbolsCache : Iterable { 214 | private val symbols = HashMap, Symbol>() 215 | private var localsCounter = 0 216 | 217 | val iterator: Iterable, Symbol>> 218 | get() = symbols.asIterable() 219 | 220 | val size: Int 221 | get() = symbols.size 222 | 223 | operator fun get(symbol: FirBasedSymbol<*>): Symbol? = symbols[symbol] 224 | 225 | operator fun plus(symbol: FirBasedSymbol<*>): Symbol { 226 | val result = Symbol.createLocal(localsCounter++) 227 | symbols[symbol] = result 228 | return result 229 | } 230 | 231 | override fun iterator(): Iterator = symbols.values.iterator() 232 | } 233 | 234 | @ExperimentalContracts 235 | class SymbolsCache(private val globals: GlobalSymbolsCache, private val locals: LocalSymbolsCache) { 236 | operator fun get(symbol: FirBasedSymbol<*>) = globals[symbol, locals] 237 | operator fun get(symbol: FqName) = globals[symbol] 238 | } 239 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ##################################################################################### 2 | ## ## 3 | ## NOTE: The following license applies to all files in this repository, ## 4 | ## except those in the enterprise/ and client/web/src/enterprise/ directories. ## 5 | ## ## 6 | ##################################################################################### 7 | 8 | 9 | 10 | Apache License 11 | Version 2.0, January 2004 12 | http://www.apache.org/licenses/ 13 | 14 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 15 | 16 | 1. Definitions. 17 | 18 | "License" shall mean the terms and conditions for use, reproduction, 19 | and distribution as defined by Sections 1 through 9 of this document. 20 | 21 | "Licensor" shall mean the copyright owner or entity authorized by 22 | the copyright owner that is granting the License. 23 | 24 | "Legal Entity" shall mean the union of the acting entity and all 25 | other entities that control, are controlled by, or are under common 26 | control with that entity. For the purposes of this definition, 27 | "control" means (i) the power, direct or indirect, to cause the 28 | direction or management of such entity, whether by contract or 29 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 30 | outstanding shares, or (iii) beneficial ownership of such entity. 31 | 32 | "You" (or "Your") shall mean an individual or Legal Entity 33 | exercising permissions granted by this License. 34 | 35 | "Source" form shall mean the preferred form for making modifications, 36 | including but not limited to software source code, documentation 37 | source, and configuration files. 38 | 39 | "Object" form shall mean any form resulting from mechanical 40 | transformation or translation of a Source form, including but 41 | not limited to compiled object code, generated documentation, 42 | and conversions to other media types. 43 | 44 | "Work" shall mean the work of authorship, whether in Source or 45 | Object form, made available under the License, as indicated by a 46 | copyright notice that is included in or attached to the work 47 | (an example is provided in the Appendix below). 48 | 49 | "Derivative Works" shall mean any work, whether in Source or Object 50 | form, that is based on (or derived from) the Work and for which the 51 | editorial revisions, annotations, elaborations, or other modifications 52 | represent, as a whole, an original work of authorship. For the purposes 53 | of this License, Derivative Works shall not include works that remain 54 | separable from, or merely link (or bind by name) to the interfaces of, 55 | the Work and Derivative Works thereof. 56 | 57 | "Contribution" shall mean any work of authorship, including 58 | the original version of the Work and any modifications or additions 59 | to that Work or Derivative Works thereof, that is intentionally 60 | submitted to Licensor for inclusion in the Work by the copyright owner 61 | or by an individual or Legal Entity authorized to submit on behalf of 62 | the copyright owner. For the purposes of this definition, "submitted" 63 | means any form of electronic, verbal, or written communication sent 64 | to the Licensor or its representatives, including but not limited to 65 | communication on electronic mailing lists, source code control systems, 66 | and issue tracking systems that are managed by, or on behalf of, the 67 | Licensor for the purpose of discussing and improving the Work, but 68 | excluding communication that is conspicuously marked or otherwise 69 | designated in writing by the copyright owner as "Not a Contribution." 70 | 71 | "Contributor" shall mean Licensor and any individual or Legal Entity 72 | on behalf of whom a Contribution has been received by Licensor and 73 | subsequently incorporated within the Work. 74 | 75 | 2. Grant of Copyright License. Subject to the terms and conditions of 76 | this License, each Contributor hereby grants to You a perpetual, 77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 78 | copyright license to reproduce, prepare Derivative Works of, 79 | publicly display, publicly perform, sublicense, and distribute the 80 | Work and such Derivative Works in Source or Object form. 81 | 82 | 3. Grant of Patent License. Subject to the terms and conditions of 83 | this License, each Contributor hereby grants to You a perpetual, 84 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 85 | (except as stated in this section) patent license to make, have made, 86 | use, offer to sell, sell, import, and otherwise transfer the Work, 87 | where such license applies only to those patent claims licensable 88 | by such Contributor that are necessarily infringed by their 89 | Contribution(s) alone or by combination of their Contribution(s) 90 | with the Work to which such Contribution(s) was submitted. If You 91 | institute patent litigation against any entity (including a 92 | cross-claim or counterclaim in a lawsuit) alleging that the Work 93 | or a Contribution incorporated within the Work constitutes direct 94 | or contributory patent infringement, then any patent licenses 95 | granted to You under this License for that Work shall terminate 96 | as of the date such litigation is filed. 97 | 98 | 4. Redistribution. You may reproduce and distribute copies of the 99 | Work or Derivative Works thereof in any medium, with or without 100 | modifications, and in Source or Object form, provided that You 101 | meet the following conditions: 102 | 103 | (a) You must give any other recipients of the Work or 104 | Derivative Works a copy of this License; and 105 | 106 | (b) You must cause any modified files to carry prominent notices 107 | stating that You changed the files; and 108 | 109 | (c) You must retain, in the Source form of any Derivative Works 110 | that You distribute, all copyright, patent, trademark, and 111 | attribution notices from the Source form of the Work, 112 | excluding those notices that do not pertain to any part of 113 | the Derivative Works; and 114 | 115 | (d) If the Work includes a "NOTICE" text file as part of its 116 | distribution, then any Derivative Works that You distribute must 117 | include a readable copy of the attribution notices contained 118 | within such NOTICE file, excluding those notices that do not 119 | pertain to any part of the Derivative Works, in at least one 120 | of the following places: within a NOTICE text file distributed 121 | as part of the Derivative Works; within the Source form or 122 | documentation, if provided along with the Derivative Works; or, 123 | within a display generated by the Derivative Works, if and 124 | wherever such third-party notices normally appear. The contents 125 | of the NOTICE file are for informational purposes only and 126 | do not modify the License. You may add Your own attribution 127 | notices within Derivative Works that You distribute, alongside 128 | or as an addendum to the NOTICE text from the Work, provided 129 | that such additional attribution notices cannot be construed 130 | as modifying the License. 131 | 132 | You may add Your own copyright statement to Your modifications and 133 | may provide additional or different license terms and conditions 134 | for use, reproduction, or distribution of Your modifications, or 135 | for any such Derivative Works as a whole, provided Your use, 136 | reproduction, and distribution of the Work otherwise complies with 137 | the conditions stated in this License. 138 | 139 | 5. Submission of Contributions. Unless You explicitly state otherwise, 140 | any Contribution intentionally submitted for inclusion in the Work 141 | by You to the Licensor shall be under the terms and conditions of 142 | this License, without any additional terms or conditions. 143 | Notwithstanding the above, nothing herein shall supersede or modify 144 | the terms of any separate license agreement you may have executed 145 | with Licensor regarding such Contributions. 146 | 147 | 6. Trademarks. This License does not grant permission to use the trade 148 | names, trademarks, service marks, or product names of the Licensor, 149 | except as required for reasonable and customary use in describing the 150 | origin of the Work and reproducing the content of the NOTICE file. 151 | 152 | 7. Disclaimer of Warranty. Unless required by applicable law or 153 | agreed to in writing, Licensor provides the Work (and each 154 | Contributor provides its Contributions) on an "AS IS" BASIS, 155 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 156 | implied, including, without limitation, any warranties or conditions 157 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 158 | PARTICULAR PURPOSE. You are solely responsible for determining the 159 | appropriateness of using or redistributing the Work and assume any 160 | risks associated with Your exercise of permissions under this License. 161 | 162 | 8. Limitation of Liability. In no event and under no legal theory, 163 | whether in tort (including negligence), contract, or otherwise, 164 | unless required by applicable law (such as deliberate and grossly 165 | negligent acts) or agreed to in writing, shall any Contributor be 166 | liable to You for damages, including any direct, indirect, special, 167 | incidental, or consequential damages of any character arising as a 168 | result of this License or out of the use or inability to use the 169 | Work (including but not limited to damages for loss of goodwill, 170 | work stoppage, computer failure or malfunction, or any and all 171 | other commercial damages or losses), even if such Contributor 172 | has been advised of the possibility of such damages. 173 | 174 | 9. Accepting Warranty or Additional Liability. While redistributing 175 | the Work or Derivative Works thereof, You may choose to offer, 176 | and charge a fee for, acceptance of support, warranty, indemnity, 177 | or other liability obligations and/or rights consistent with this 178 | License. However, in accepting such obligations, You may act only 179 | on Your own behalf and on Your sole responsibility, not on behalf 180 | of any other Contributor, and only if You agree to indemnify, 181 | defend, and hold each Contributor harmless for any liability 182 | incurred by, or claims asserted against, such Contributor by reason 183 | of your accepting any such warranty or additional liability. 184 | 185 | END OF TERMS AND CONDITIONS 186 | 187 | APPENDIX: How to apply the Apache License to your work. 188 | 189 | To apply the Apache License to your work, attach the following 190 | boilerplate notice, with the fields enclosed by brackets "[]" 191 | replaced with your own identifying information. (Don't include 192 | the brackets!) The text should be enclosed in the appropriate 193 | comment syntax for the file format. We also recommend that a 194 | file or class name and description of purpose be included on the 195 | same "printed page" as the copyright notice for easier 196 | identification within third-party archives. 197 | 198 | Copyright 2022 Sourcegraph, Inc. 199 | 200 | Licensed under the Apache License, Version 2.0 (the "License"); 201 | you may not use this file except in compliance with the License. 202 | You may obtain a copy of the License at 203 | 204 | http://www.apache.org/licenses/LICENSE-2.0 205 | 206 | Unless required by applicable law or agreed to in writing, software 207 | distributed under the License is distributed on an "AS IS" BASIS, 208 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 209 | See the License for the specific language governing permissions and 210 | limitations under the License. 211 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/main/kotlin/com/sourcegraph/semanticdb_kotlinc/AnalyzerCheckers.kt: -------------------------------------------------------------------------------- 1 | package com.sourcegraph.semanticdb_kotlinc 2 | 3 | import java.nio.file.Path 4 | import kotlin.contracts.ExperimentalContracts 5 | import kotlin.math.exp 6 | import org.jetbrains.kotlin.* 7 | import org.jetbrains.kotlin.com.intellij.lang.LighterASTNode 8 | import org.jetbrains.kotlin.com.intellij.util.diff.FlyweightCapableTreeStructure 9 | import org.jetbrains.kotlin.diagnostics.* 10 | import org.jetbrains.kotlin.fir.FirSession 11 | import org.jetbrains.kotlin.fir.analysis.checkers.MppCheckerKind 12 | import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext 13 | import org.jetbrains.kotlin.fir.analysis.checkers.declaration.* 14 | import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers 15 | import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirBasicExpressionChecker 16 | import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirClassReferenceExpressionChecker 17 | import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirQualifiedAccessExpressionChecker 18 | import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirTypeOperatorCallChecker 19 | import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol 20 | import org.jetbrains.kotlin.fir.analysis.checkers.toClassLikeSymbol 21 | import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension 22 | import org.jetbrains.kotlin.fir.declarations.* 23 | import org.jetbrains.kotlin.fir.expressions.FirClassReferenceExpression 24 | import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression 25 | import org.jetbrains.kotlin.fir.expressions.FirStatement 26 | import org.jetbrains.kotlin.fir.expressions.FirTypeOperatorCall 27 | import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference 28 | import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticFunctionSymbol 29 | import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider 30 | import org.jetbrains.kotlin.fir.resolve.toClassLikeSymbol 31 | import org.jetbrains.kotlin.fir.symbols.impl.FirAnonymousObjectSymbol 32 | import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol 33 | import org.jetbrains.kotlin.fir.types.coneType 34 | import org.jetbrains.kotlin.fir.types.resolvedType 35 | import org.jetbrains.kotlin.lexer.KtTokens 36 | import org.jetbrains.kotlin.name.ClassId 37 | import org.jetbrains.kotlin.name.FqName 38 | 39 | open class AnalyzerCheckers(session: FirSession) : FirAdditionalCheckersExtension(session) { 40 | companion object { 41 | @OptIn(ExperimentalContracts::class) 42 | val visitors: MutableMap = mutableMapOf() 43 | 44 | private fun getIdentifier(element: KtSourceElement): KtSourceElement = 45 | element 46 | .treeStructure 47 | .findChildByType(element.lighterASTNode, KtTokens.IDENTIFIER) 48 | ?.toKtLightSourceElement(element.treeStructure) ?: element 49 | } 50 | override val declarationCheckers: DeclarationCheckers 51 | get() = AnalyzerDeclarationCheckers(session.analyzerParamsProvider.sourceroot) 52 | 53 | override val expressionCheckers: ExpressionCheckers 54 | get() = 55 | object : ExpressionCheckers() { 56 | override val qualifiedAccessExpressionCheckers: 57 | Set = 58 | setOf(SemanticQualifiedAccessExpressionChecker()) 59 | 60 | override val typeOperatorCallCheckers: 61 | Set = setOf(SemanticClassReferenceExpressionChecker()) 62 | } 63 | 64 | open class AnalyzerDeclarationCheckers(sourceroot: Path) : DeclarationCheckers() { 65 | override val fileCheckers: Set = 66 | setOf(SemanticFileChecker(sourceroot), SemanticImportsChecker()) 67 | override val classLikeCheckers: Set = 68 | setOf(SemanticClassLikeChecker()) 69 | override val constructorCheckers: Set = 70 | setOf(SemanticConstructorChecker()) 71 | override val simpleFunctionCheckers: Set = 72 | setOf(SemanticSimpleFunctionChecker()) 73 | override val anonymousFunctionCheckers: Set = 74 | setOf(SemanticAnonymousFunctionChecker()) 75 | override val propertyCheckers: Set = setOf(SemanticPropertyChecker()) 76 | override val valueParameterCheckers: Set = 77 | setOf(SemanticValueParameterChecker()) 78 | override val typeParameterCheckers: Set = 79 | setOf(SemanticTypeParameterChecker()) 80 | override val typeAliasCheckers: Set = setOf(SemanticTypeAliasChecker()) 81 | override val propertyAccessorCheckers: Set = 82 | setOf(SemanticPropertyAccessorChecker()) 83 | } 84 | 85 | private class SemanticFileChecker(private val sourceroot: Path) : 86 | FirFileChecker(MppCheckerKind.Common) { 87 | companion object { 88 | @OptIn(ExperimentalContracts::class) val globals = GlobalSymbolsCache() 89 | } 90 | 91 | @OptIn(ExperimentalContracts::class) 92 | context(context: CheckerContext, reporter: DiagnosticReporter) 93 | override fun check(declaration: FirFile) { 94 | val ktFile = declaration.sourceFile ?: return 95 | val lineMap = LineMap(declaration) 96 | val visitor = SemanticdbVisitor(sourceroot, ktFile, lineMap, globals) 97 | visitors[ktFile] = visitor 98 | } 99 | } 100 | 101 | class SemanticImportsChecker : FirFileChecker(MppCheckerKind.Common) { 102 | @OptIn(ExperimentalContracts::class) 103 | context(context: CheckerContext, reporter: DiagnosticReporter) 104 | override fun check(declaration: FirFile) { 105 | val ktFile = declaration.sourceFile ?: return 106 | val visitor = visitors[ktFile] 107 | 108 | val eachFqNameElement = 109 | { 110 | fqName: FqName, 111 | tree: FlyweightCapableTreeStructure, 112 | names: LighterASTNode, 113 | callback: (FqName, KtLightSourceElement) -> Unit -> 114 | val nameList = 115 | if (names.tokenType == KtNodeTypes.REFERENCE_EXPRESSION) listOf(names) 116 | else tree.collectDescendantsOfType(names, KtNodeTypes.REFERENCE_EXPRESSION) 117 | 118 | var ancestor = fqName 119 | var depth = 0 120 | 121 | while (ancestor != FqName.ROOT) { 122 | val nameNode = nameList[nameList.lastIndex - depth] 123 | val nameSource = nameNode.toKtLightSourceElement(tree) 124 | 125 | callback(ancestor, nameSource) 126 | 127 | ancestor = ancestor.parent() 128 | depth++ 129 | } 130 | } 131 | 132 | val packageDirective = declaration.packageDirective 133 | val fqName = packageDirective.packageFqName 134 | val source = packageDirective.source 135 | if (source != null) { 136 | val names = source.treeStructure.findChildByType(source.lighterASTNode, KtNodeTypes.DOT_QUALIFIED_EXPRESSION) ?: 137 | source.treeStructure.findChildByType(source.lighterASTNode, KtNodeTypes.REFERENCE_EXPRESSION) 138 | 139 | if (names != null) { 140 | eachFqNameElement(fqName, source.treeStructure, names) { fqName, name -> 141 | visitor?.visitPackage(fqName, name, context) 142 | } 143 | } 144 | } 145 | 146 | declaration.imports.forEach { import -> 147 | val source = import.source ?: return@forEach 148 | val fqName = import.importedFqName ?: return@forEach 149 | 150 | val names = source.treeStructure.findDescendantByType(source.lighterASTNode, KtNodeTypes.DOT_QUALIFIED_EXPRESSION) 151 | if (names != null) { 152 | eachFqNameElement(fqName, source.treeStructure, names) { fqName, name -> 153 | val symbolProvider = context.session.symbolProvider 154 | 155 | val klass = 156 | symbolProvider.getClassLikeSymbolByClassId(ClassId.topLevel(fqName)) 157 | val callables = 158 | symbolProvider.getTopLevelCallableSymbols( 159 | fqName.parent(), fqName.shortName()) 160 | 161 | if (klass != null) { 162 | visitor?.visitClassReference(klass, name, context) 163 | } else if (callables.isNotEmpty()) { 164 | for (callable in callables) { 165 | visitor?.visitCallableReference(callable, name, context) 166 | } 167 | } else { 168 | visitor?.visitPackage(fqName, name, context) 169 | } 170 | } 171 | } 172 | } 173 | } 174 | } 175 | 176 | private class SemanticClassLikeChecker : FirClassLikeChecker(MppCheckerKind.Common) { 177 | @OptIn(ExperimentalContracts::class) 178 | context(context: CheckerContext, reporter: DiagnosticReporter) 179 | override fun check(declaration: FirClassLikeDeclaration) { 180 | val source = declaration.source ?: return 181 | val ktFile = context.containingFile?.sourceFile ?: return 182 | val visitor = visitors[ktFile] 183 | val objectKeyword = if (declaration is FirAnonymousObject) { 184 | source 185 | .treeStructure 186 | .findChildByType(source.lighterASTNode, KtTokens.OBJECT_KEYWORD) 187 | ?.toKtLightSourceElement(source.treeStructure) 188 | } else { 189 | null 190 | } 191 | visitor?.visitClassOrObject(declaration, objectKeyword ?: getIdentifier(source), context) 192 | 193 | if (declaration is FirClass) { 194 | for (superType in declaration.superTypeRefs) { 195 | val superSymbol = superType.toClassLikeSymbol(context.session) 196 | val superSource = superType.source 197 | if (superSymbol != null && superSource != null) { 198 | visitor?.visitClassReference(superSymbol, superSource, context) 199 | } 200 | } 201 | } 202 | } 203 | } 204 | 205 | private class SemanticConstructorChecker : FirConstructorChecker(MppCheckerKind.Common) { 206 | @OptIn(ExperimentalContracts::class) 207 | context(context: CheckerContext, reporter: DiagnosticReporter) 208 | override fun check(declaration: FirConstructor) { 209 | val source = declaration.source ?: return 210 | val ktFile = context.containingFile?.sourceFile ?: return 211 | val visitor = visitors[ktFile] 212 | 213 | if (declaration.isPrimary) { 214 | // if the constructor is not denoted by the 'constructor' keyword, we want to link it to the 215 | // class identifier 216 | val klass = declaration.symbol.getContainingClassSymbol() 217 | val klassSource = klass?.source ?: source 218 | val constructorKeyboard = 219 | source 220 | .treeStructure 221 | .findChildByType(source.lighterASTNode, KtTokens.CONSTRUCTOR_KEYWORD) 222 | ?.toKtLightSourceElement(source.treeStructure) 223 | 224 | val objectKeyword = if (klass is FirAnonymousObjectSymbol) { 225 | source 226 | .treeStructure 227 | .findChildByType(source.lighterASTNode, KtTokens.OBJECT_KEYWORD) 228 | ?.toKtLightSourceElement(source.treeStructure) 229 | } else { 230 | null 231 | } 232 | 233 | visitor?.visitPrimaryConstructor(declaration, constructorKeyboard ?: objectKeyword ?: getIdentifier(klassSource), context) 234 | } else { 235 | visitor?.visitSecondaryConstructor(declaration, getIdentifier(source), context) 236 | } 237 | } 238 | } 239 | 240 | private class SemanticSimpleFunctionChecker : FirSimpleFunctionChecker(MppCheckerKind.Common) { 241 | @OptIn(ExperimentalContracts::class) 242 | context(context: CheckerContext, reporter: DiagnosticReporter) 243 | override fun check(declaration: FirSimpleFunction) { 244 | val source = declaration.source ?: return 245 | val ktFile = context.containingFile?.sourceFile ?: return 246 | val visitor = visitors[ktFile] 247 | visitor?.visitNamedFunction(declaration, getIdentifier(source), context) 248 | 249 | val klass = declaration.returnTypeRef.toClassLikeSymbol(context.session) 250 | val klassSource = declaration.returnTypeRef.source 251 | if (klass != null && klassSource != null && klassSource.kind !is KtFakeSourceElementKind) { 252 | visitor?.visitClassReference(klass, getIdentifier(klassSource), context) 253 | } 254 | } 255 | } 256 | 257 | private class SemanticAnonymousFunctionChecker : 258 | FirAnonymousFunctionChecker(MppCheckerKind.Common) { 259 | @OptIn(ExperimentalContracts::class) 260 | context(context: CheckerContext, reporter: DiagnosticReporter) 261 | override fun check(declaration: FirAnonymousFunction) { 262 | val source = declaration.source ?: return 263 | val ktFile = context.containingFile?.sourceFile ?: return 264 | val visitor = visitors[ktFile] 265 | visitor?.visitNamedFunction(declaration, source, context) 266 | } 267 | } 268 | 269 | private class SemanticPropertyChecker : FirPropertyChecker(MppCheckerKind.Common) { 270 | @OptIn(ExperimentalContracts::class) 271 | context(context: CheckerContext, reporter: DiagnosticReporter) 272 | override fun check(declaration: FirProperty) { 273 | val source = declaration.source ?: return 274 | val ktFile = context.containingFile?.sourceFile ?: return 275 | val visitor = visitors[ktFile] 276 | visitor?.visitProperty(declaration, getIdentifier(source), context) 277 | 278 | val klass = declaration.returnTypeRef.toClassLikeSymbol(context.session) 279 | val klassSource = declaration.returnTypeRef.source 280 | if (klass != null && klassSource != null && klassSource.kind !is KtFakeSourceElementKind) { 281 | visitor?.visitClassReference(klass, getIdentifier(klassSource), context) 282 | } 283 | } 284 | } 285 | 286 | private class SemanticValueParameterChecker : FirValueParameterChecker(MppCheckerKind.Common) { 287 | @OptIn(ExperimentalContracts::class) 288 | context(context: CheckerContext, reporter: DiagnosticReporter) 289 | override fun check(declaration: FirValueParameter) { 290 | val source = declaration.source ?: return 291 | val ktFile = context.containingFile?.sourceFile ?: return 292 | val visitor = visitors[ktFile] 293 | visitor?.visitParameter(declaration, getIdentifier(source), context) 294 | 295 | val klass = declaration.returnTypeRef.toClassLikeSymbol(context.session) 296 | val klassSource = declaration.returnTypeRef.source 297 | if (klass != null && klassSource != null && klassSource.kind !is KtFakeSourceElementKind) { 298 | visitor?.visitClassReference(klass, getIdentifier(klassSource), context) 299 | } 300 | } 301 | } 302 | 303 | private class SemanticTypeParameterChecker : FirTypeParameterChecker(MppCheckerKind.Common) { 304 | @OptIn(ExperimentalContracts::class) 305 | context(context: CheckerContext, reporter: DiagnosticReporter) 306 | override fun check(declaration: FirTypeParameter) { 307 | val source = declaration.source ?: return 308 | val ktFile = context.containingFile?.sourceFile ?: return 309 | val visitor = visitors[ktFile] 310 | visitor?.visitTypeParameter(declaration, getIdentifier(source), context) 311 | } 312 | } 313 | 314 | private class SemanticTypeAliasChecker : FirTypeAliasChecker(MppCheckerKind.Common) { 315 | @OptIn(ExperimentalContracts::class) 316 | context(context: CheckerContext, reporter: DiagnosticReporter) 317 | override fun check(declaration: FirTypeAlias) { 318 | val source = declaration.source ?: return 319 | val ktFile = context.containingFile?.sourceFile ?: return 320 | val visitor = visitors[ktFile] 321 | visitor?.visitTypeAlias(declaration, getIdentifier(source), context) 322 | } 323 | } 324 | 325 | private class SemanticPropertyAccessorChecker : 326 | FirPropertyAccessorChecker(MppCheckerKind.Common) { 327 | @OptIn(ExperimentalContracts::class) 328 | context(context: CheckerContext, reporter: DiagnosticReporter) 329 | override fun check(declaration: FirPropertyAccessor) { 330 | val source = declaration.source ?: return 331 | val ktFile = context.containingFile?.sourceFile ?: return 332 | val visitor = visitors[ktFile] 333 | val identifierSource = 334 | if (declaration.isGetter) { 335 | source 336 | .treeStructure 337 | .findChildByType(source.lighterASTNode, KtTokens.GET_KEYWORD) 338 | ?.toKtLightSourceElement(source.treeStructure) 339 | ?: getIdentifier(source) 340 | } else if (declaration.isSetter) { 341 | source 342 | .treeStructure 343 | .findChildByType(source.lighterASTNode, KtTokens.SET_KEYWORD) 344 | ?.toKtLightSourceElement(source.treeStructure) 345 | ?: getIdentifier(source) 346 | } else { 347 | getIdentifier(source) 348 | } 349 | 350 | visitor?.visitPropertyAccessor(declaration, identifierSource, context) 351 | } 352 | } 353 | 354 | private class SemanticQualifiedAccessExpressionChecker : 355 | FirQualifiedAccessExpressionChecker(MppCheckerKind.Common) { 356 | @OptIn(ExperimentalContracts::class) 357 | context(context: CheckerContext, reporter: DiagnosticReporter) 358 | override fun check(expression: FirQualifiedAccessExpression) { 359 | val source = expression.source ?: return 360 | val calleeReference = expression.calleeReference 361 | if ((calleeReference as? FirResolvedNamedReference) == null) { 362 | return 363 | } 364 | 365 | val ktFile = context.containingFile?.sourceFile ?: return 366 | val visitor = visitors[ktFile] 367 | visitor?.visitSimpleNameExpression(calleeReference, getIdentifier(calleeReference.source ?: source), context) 368 | 369 | val resolvedSymbol = calleeReference.resolvedSymbol 370 | if (resolvedSymbol.origin == FirDeclarationOrigin.SamConstructor && resolvedSymbol is FirSyntheticFunctionSymbol) { 371 | val referencedKlass = resolvedSymbol.resolvedReturnType.toClassLikeSymbol(context.session) 372 | if (referencedKlass != null) { 373 | visitor?.visitClassReference(referencedKlass, getIdentifier(calleeReference.source ?: source), context) 374 | } 375 | } 376 | 377 | // When encountering a reference to a property symbol, emit both getter and setter symbols 378 | if (resolvedSymbol is FirPropertySymbol) { 379 | resolvedSymbol.getterSymbol?.let { 380 | visitor?.visitCallableReference(it, getIdentifier(calleeReference.source ?: source), context) 381 | } 382 | resolvedSymbol.setterSymbol?.let { 383 | visitor?.visitCallableReference(it, getIdentifier(calleeReference.source ?: source), context) 384 | } 385 | } 386 | } 387 | } 388 | 389 | private class SemanticClassReferenceExpressionChecker : 390 | FirTypeOperatorCallChecker(MppCheckerKind.Common) { 391 | @OptIn(ExperimentalContracts::class) 392 | context(context: CheckerContext, reporter: DiagnosticReporter) 393 | override fun check(expression: FirTypeOperatorCall) { 394 | val typeRef = expression.conversionTypeRef 395 | val source = typeRef.source ?: return 396 | val classSymbol = expression.conversionTypeRef.toClassLikeSymbol(context.session) ?: return 397 | val ktFile = context.containingFile?.sourceFile ?: return 398 | val visitor = visitors[ktFile] 399 | 400 | visitor?.visitClassReference(classSymbol, getIdentifier(expression.conversionTypeRef.source ?: source), context) 401 | } 402 | } 403 | } 404 | -------------------------------------------------------------------------------- /semanticdb-kotlin/src/main/java/com/sourcegraph/semanticdb_kotlinc/SemanticdbBuilders.kt: -------------------------------------------------------------------------------- 1 | // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | @file:JvmName("-SemanticdbBuilders") 3 | 4 | package com.sourcegraph.semanticdb_kotlinc 5 | 6 | import kotlin.DslMarker 7 | import kotlin.Unit 8 | import kotlin.annotation.AnnotationRetention 9 | import kotlin.annotation.AnnotationTarget 10 | import kotlin.annotation.Retention 11 | import kotlin.annotation.Target 12 | import kotlin.jvm.JvmName 13 | 14 | inline fun Semanticdb.TextDocuments.copy(block: Semanticdb.TextDocuments.Builder.() -> Unit): 15 | Semanticdb.TextDocuments = this.toBuilder().apply(block).build() 16 | 17 | operator fun Semanticdb.TextDocuments.plus(other: Semanticdb.TextDocuments): 18 | Semanticdb.TextDocuments = this.toBuilder().mergeFrom(other).build() 19 | 20 | fun Semanticdb.TextDocuments?.orDefault(): Semanticdb.TextDocuments = this ?: 21 | Semanticdb.TextDocuments.getDefaultInstance() 22 | 23 | inline fun Semanticdb.TextDocuments.Builder.addDocuments(block: 24 | Semanticdb.TextDocument.Builder.() -> Unit): Semanticdb.TextDocuments.Builder = 25 | this.addDocuments(Semanticdb.TextDocument.newBuilder().apply(block).build()) 26 | 27 | inline fun Semanticdb.TextDocument.copy(block: Semanticdb.TextDocument.Builder.() -> Unit): 28 | Semanticdb.TextDocument = this.toBuilder().apply(block).build() 29 | 30 | operator fun Semanticdb.TextDocument.plus(other: Semanticdb.TextDocument): Semanticdb.TextDocument = 31 | this.toBuilder().mergeFrom(other).build() 32 | 33 | fun Semanticdb.TextDocument?.orDefault(): Semanticdb.TextDocument = this ?: 34 | Semanticdb.TextDocument.getDefaultInstance() 35 | 36 | inline fun Semanticdb.TextDocument.Builder.addSymbols(block: 37 | Semanticdb.SymbolInformation.Builder.() -> Unit): Semanticdb.TextDocument.Builder = 38 | this.addSymbols(Semanticdb.SymbolInformation.newBuilder().apply(block).build()) 39 | 40 | inline fun Semanticdb.TextDocument.Builder.addOccurrences(block: 41 | Semanticdb.SymbolOccurrence.Builder.() -> Unit): Semanticdb.TextDocument.Builder = 42 | this.addOccurrences(Semanticdb.SymbolOccurrence.newBuilder().apply(block).build()) 43 | 44 | inline fun Semanticdb.Range.copy(block: Semanticdb.Range.Builder.() -> Unit): Semanticdb.Range = 45 | this.toBuilder().apply(block).build() 46 | 47 | operator fun Semanticdb.Range.plus(other: Semanticdb.Range): Semanticdb.Range = 48 | this.toBuilder().mergeFrom(other).build() 49 | 50 | fun Semanticdb.Range?.orDefault(): Semanticdb.Range = this ?: Semanticdb.Range.getDefaultInstance() 51 | 52 | inline fun Semanticdb.Signature.copy(block: Semanticdb.Signature.Builder.() -> Unit): 53 | Semanticdb.Signature = this.toBuilder().apply(block).build() 54 | 55 | operator fun Semanticdb.Signature.plus(other: Semanticdb.Signature): Semanticdb.Signature = 56 | this.toBuilder().mergeFrom(other).build() 57 | 58 | fun Semanticdb.Signature?.orDefault(): Semanticdb.Signature = this ?: 59 | Semanticdb.Signature.getDefaultInstance() 60 | 61 | inline fun Semanticdb.Signature.Builder.classSignature(block: 62 | Semanticdb.ClassSignature.Builder.() -> Unit): Semanticdb.Signature.Builder = 63 | this.setClassSignature(Semanticdb.ClassSignature.newBuilder().apply(block).build()) 64 | 65 | inline fun Semanticdb.Signature.Builder.methodSignature(block: 66 | Semanticdb.MethodSignature.Builder.() -> Unit): Semanticdb.Signature.Builder = 67 | this.setMethodSignature(Semanticdb.MethodSignature.newBuilder().apply(block).build()) 68 | 69 | inline fun Semanticdb.Signature.Builder.typeSignature(block: Semanticdb.TypeSignature.Builder.() -> 70 | Unit): Semanticdb.Signature.Builder = 71 | this.setTypeSignature(Semanticdb.TypeSignature.newBuilder().apply(block).build()) 72 | 73 | inline fun Semanticdb.Signature.Builder.valueSignature(block: 74 | Semanticdb.ValueSignature.Builder.() -> Unit): Semanticdb.Signature.Builder = 75 | this.setValueSignature(Semanticdb.ValueSignature.newBuilder().apply(block).build()) 76 | 77 | inline fun Semanticdb.ClassSignature.copy(block: Semanticdb.ClassSignature.Builder.() -> Unit): 78 | Semanticdb.ClassSignature = this.toBuilder().apply(block).build() 79 | 80 | operator fun Semanticdb.ClassSignature.plus(other: Semanticdb.ClassSignature): 81 | Semanticdb.ClassSignature = this.toBuilder().mergeFrom(other).build() 82 | 83 | fun Semanticdb.ClassSignature?.orDefault(): Semanticdb.ClassSignature = this ?: 84 | Semanticdb.ClassSignature.getDefaultInstance() 85 | 86 | inline fun Semanticdb.ClassSignature.Builder.typeParameters(block: Semanticdb.Scope.Builder.() -> 87 | Unit): Semanticdb.ClassSignature.Builder = 88 | this.setTypeParameters(Semanticdb.Scope.newBuilder().apply(block).build()) 89 | 90 | inline fun Semanticdb.ClassSignature.Builder.addParents(block: Semanticdb.Type.Builder.() -> Unit): 91 | Semanticdb.ClassSignature.Builder = 92 | this.addParents(Semanticdb.Type.newBuilder().apply(block).build()) 93 | 94 | inline fun Semanticdb.ClassSignature.Builder.declarations(block: Semanticdb.Scope.Builder.() -> 95 | Unit): Semanticdb.ClassSignature.Builder = 96 | this.setDeclarations(Semanticdb.Scope.newBuilder().apply(block).build()) 97 | 98 | inline fun Semanticdb.MethodSignature.copy(block: Semanticdb.MethodSignature.Builder.() -> Unit): 99 | Semanticdb.MethodSignature = this.toBuilder().apply(block).build() 100 | 101 | operator fun Semanticdb.MethodSignature.plus(other: Semanticdb.MethodSignature): 102 | Semanticdb.MethodSignature = this.toBuilder().mergeFrom(other).build() 103 | 104 | fun Semanticdb.MethodSignature?.orDefault(): Semanticdb.MethodSignature = this ?: 105 | Semanticdb.MethodSignature.getDefaultInstance() 106 | 107 | inline fun Semanticdb.MethodSignature.Builder.typeParameters(block: Semanticdb.Scope.Builder.() -> 108 | Unit): Semanticdb.MethodSignature.Builder = 109 | this.setTypeParameters(Semanticdb.Scope.newBuilder().apply(block).build()) 110 | 111 | inline fun Semanticdb.MethodSignature.Builder.addParameterLists(block: 112 | Semanticdb.Scope.Builder.() -> Unit): Semanticdb.MethodSignature.Builder = 113 | this.addParameterLists(Semanticdb.Scope.newBuilder().apply(block).build()) 114 | 115 | inline fun Semanticdb.MethodSignature.Builder.returnType(block: Semanticdb.Type.Builder.() -> Unit): 116 | Semanticdb.MethodSignature.Builder = 117 | this.setReturnType(Semanticdb.Type.newBuilder().apply(block).build()) 118 | 119 | inline fun Semanticdb.TypeSignature.copy(block: Semanticdb.TypeSignature.Builder.() -> Unit): 120 | Semanticdb.TypeSignature = this.toBuilder().apply(block).build() 121 | 122 | operator fun Semanticdb.TypeSignature.plus(other: Semanticdb.TypeSignature): 123 | Semanticdb.TypeSignature = this.toBuilder().mergeFrom(other).build() 124 | 125 | fun Semanticdb.TypeSignature?.orDefault(): Semanticdb.TypeSignature = this ?: 126 | Semanticdb.TypeSignature.getDefaultInstance() 127 | 128 | inline fun Semanticdb.TypeSignature.Builder.typeParameters(block: Semanticdb.Scope.Builder.() -> 129 | Unit): Semanticdb.TypeSignature.Builder = 130 | this.setTypeParameters(Semanticdb.Scope.newBuilder().apply(block).build()) 131 | 132 | inline fun Semanticdb.TypeSignature.Builder.lowerBound(block: Semanticdb.Type.Builder.() -> Unit): 133 | Semanticdb.TypeSignature.Builder = 134 | this.setLowerBound(Semanticdb.Type.newBuilder().apply(block).build()) 135 | 136 | inline fun Semanticdb.TypeSignature.Builder.upperBound(block: Semanticdb.Type.Builder.() -> Unit): 137 | Semanticdb.TypeSignature.Builder = 138 | this.setUpperBound(Semanticdb.Type.newBuilder().apply(block).build()) 139 | 140 | inline fun Semanticdb.ValueSignature.copy(block: Semanticdb.ValueSignature.Builder.() -> Unit): 141 | Semanticdb.ValueSignature = this.toBuilder().apply(block).build() 142 | 143 | operator fun Semanticdb.ValueSignature.plus(other: Semanticdb.ValueSignature): 144 | Semanticdb.ValueSignature = this.toBuilder().mergeFrom(other).build() 145 | 146 | fun Semanticdb.ValueSignature?.orDefault(): Semanticdb.ValueSignature = this ?: 147 | Semanticdb.ValueSignature.getDefaultInstance() 148 | 149 | inline fun Semanticdb.ValueSignature.Builder.tpe(block: Semanticdb.Type.Builder.() -> Unit): 150 | Semanticdb.ValueSignature.Builder = 151 | this.setTpe(Semanticdb.Type.newBuilder().apply(block).build()) 152 | 153 | inline fun Semanticdb.SymbolInformation.copy(block: Semanticdb.SymbolInformation.Builder.() -> 154 | Unit): Semanticdb.SymbolInformation = this.toBuilder().apply(block).build() 155 | 156 | operator fun Semanticdb.SymbolInformation.plus(other: Semanticdb.SymbolInformation): 157 | Semanticdb.SymbolInformation = this.toBuilder().mergeFrom(other).build() 158 | 159 | fun Semanticdb.SymbolInformation?.orDefault(): Semanticdb.SymbolInformation = this ?: 160 | Semanticdb.SymbolInformation.getDefaultInstance() 161 | 162 | inline fun Semanticdb.SymbolInformation.Builder.signature(block: Semanticdb.Signature.Builder.() -> 163 | Unit): Semanticdb.SymbolInformation.Builder = 164 | this.setSignature(Semanticdb.Signature.newBuilder().apply(block).build()) 165 | 166 | inline fun Semanticdb.SymbolInformation.Builder.access(block: Semanticdb.Access.Builder.() -> Unit): 167 | Semanticdb.SymbolInformation.Builder = 168 | this.setAccess(Semanticdb.Access.newBuilder().apply(block).build()) 169 | 170 | inline fun Semanticdb.SymbolInformation.Builder.documentation(block: 171 | Semanticdb.Documentation.Builder.() -> Unit): Semanticdb.SymbolInformation.Builder = 172 | this.setDocumentation(Semanticdb.Documentation.newBuilder().apply(block).build()) 173 | 174 | inline fun Semanticdb.Access.copy(block: Semanticdb.Access.Builder.() -> Unit): Semanticdb.Access = 175 | this.toBuilder().apply(block).build() 176 | 177 | operator fun Semanticdb.Access.plus(other: Semanticdb.Access): Semanticdb.Access = 178 | this.toBuilder().mergeFrom(other).build() 179 | 180 | fun Semanticdb.Access?.orDefault(): Semanticdb.Access = this ?: 181 | Semanticdb.Access.getDefaultInstance() 182 | 183 | inline fun Semanticdb.Access.Builder.privateAccess(block: Semanticdb.PrivateAccess.Builder.() -> 184 | Unit): Semanticdb.Access.Builder = 185 | this.setPrivateAccess(Semanticdb.PrivateAccess.newBuilder().apply(block).build()) 186 | 187 | inline fun Semanticdb.Access.Builder.privateWithinAccess(block: 188 | Semanticdb.PrivateWithinAccess.Builder.() -> Unit): Semanticdb.Access.Builder = 189 | this.setPrivateWithinAccess(Semanticdb.PrivateWithinAccess.newBuilder().apply(block).build()) 190 | 191 | inline fun Semanticdb.Access.Builder.protectedAccess(block: Semanticdb.ProtectedAccess.Builder.() -> 192 | Unit): Semanticdb.Access.Builder = 193 | this.setProtectedAccess(Semanticdb.ProtectedAccess.newBuilder().apply(block).build()) 194 | 195 | inline fun Semanticdb.Access.Builder.publicAccess(block: Semanticdb.PublicAccess.Builder.() -> 196 | Unit): Semanticdb.Access.Builder = 197 | this.setPublicAccess(Semanticdb.PublicAccess.newBuilder().apply(block).build()) 198 | 199 | inline fun Semanticdb.PrivateAccess.copy(block: Semanticdb.PrivateAccess.Builder.() -> Unit): 200 | Semanticdb.PrivateAccess = this.toBuilder().apply(block).build() 201 | 202 | operator fun Semanticdb.PrivateAccess.plus(other: Semanticdb.PrivateAccess): 203 | Semanticdb.PrivateAccess = this.toBuilder().mergeFrom(other).build() 204 | 205 | fun Semanticdb.PrivateAccess?.orDefault(): Semanticdb.PrivateAccess = this ?: 206 | Semanticdb.PrivateAccess.getDefaultInstance() 207 | 208 | inline fun Semanticdb.PrivateWithinAccess.copy(block: Semanticdb.PrivateWithinAccess.Builder.() -> 209 | Unit): Semanticdb.PrivateWithinAccess = this.toBuilder().apply(block).build() 210 | 211 | operator fun Semanticdb.PrivateWithinAccess.plus(other: Semanticdb.PrivateWithinAccess): 212 | Semanticdb.PrivateWithinAccess = this.toBuilder().mergeFrom(other).build() 213 | 214 | fun Semanticdb.PrivateWithinAccess?.orDefault(): Semanticdb.PrivateWithinAccess = this ?: 215 | Semanticdb.PrivateWithinAccess.getDefaultInstance() 216 | 217 | inline fun Semanticdb.ProtectedAccess.copy(block: Semanticdb.ProtectedAccess.Builder.() -> Unit): 218 | Semanticdb.ProtectedAccess = this.toBuilder().apply(block).build() 219 | 220 | operator fun Semanticdb.ProtectedAccess.plus(other: Semanticdb.ProtectedAccess): 221 | Semanticdb.ProtectedAccess = this.toBuilder().mergeFrom(other).build() 222 | 223 | fun Semanticdb.ProtectedAccess?.orDefault(): Semanticdb.ProtectedAccess = this ?: 224 | Semanticdb.ProtectedAccess.getDefaultInstance() 225 | 226 | inline fun Semanticdb.PublicAccess.copy(block: Semanticdb.PublicAccess.Builder.() -> Unit): 227 | Semanticdb.PublicAccess = this.toBuilder().apply(block).build() 228 | 229 | operator fun Semanticdb.PublicAccess.plus(other: Semanticdb.PublicAccess): Semanticdb.PublicAccess = 230 | this.toBuilder().mergeFrom(other).build() 231 | 232 | fun Semanticdb.PublicAccess?.orDefault(): Semanticdb.PublicAccess = this ?: 233 | Semanticdb.PublicAccess.getDefaultInstance() 234 | 235 | inline fun Semanticdb.Documentation.copy(block: Semanticdb.Documentation.Builder.() -> Unit): 236 | Semanticdb.Documentation = this.toBuilder().apply(block).build() 237 | 238 | operator fun Semanticdb.Documentation.plus(other: Semanticdb.Documentation): 239 | Semanticdb.Documentation = this.toBuilder().mergeFrom(other).build() 240 | 241 | fun Semanticdb.Documentation?.orDefault(): Semanticdb.Documentation = this ?: 242 | Semanticdb.Documentation.getDefaultInstance() 243 | 244 | inline fun Semanticdb.SymbolOccurrence.copy(block: Semanticdb.SymbolOccurrence.Builder.() -> Unit): 245 | Semanticdb.SymbolOccurrence = this.toBuilder().apply(block).build() 246 | 247 | operator fun Semanticdb.SymbolOccurrence.plus(other: Semanticdb.SymbolOccurrence): 248 | Semanticdb.SymbolOccurrence = this.toBuilder().mergeFrom(other).build() 249 | 250 | fun Semanticdb.SymbolOccurrence?.orDefault(): Semanticdb.SymbolOccurrence = this ?: 251 | Semanticdb.SymbolOccurrence.getDefaultInstance() 252 | 253 | inline fun Semanticdb.SymbolOccurrence.Builder.range(block: Semanticdb.Range.Builder.() -> Unit): 254 | Semanticdb.SymbolOccurrence.Builder = 255 | this.setRange(Semanticdb.Range.newBuilder().apply(block).build()) 256 | 257 | inline fun Semanticdb.Scope.copy(block: Semanticdb.Scope.Builder.() -> Unit): Semanticdb.Scope = 258 | this.toBuilder().apply(block).build() 259 | 260 | operator fun Semanticdb.Scope.plus(other: Semanticdb.Scope): Semanticdb.Scope = 261 | this.toBuilder().mergeFrom(other).build() 262 | 263 | fun Semanticdb.Scope?.orDefault(): Semanticdb.Scope = this ?: Semanticdb.Scope.getDefaultInstance() 264 | 265 | inline fun Semanticdb.Scope.Builder.addHardlinks(block: Semanticdb.SymbolInformation.Builder.() -> 266 | Unit): Semanticdb.Scope.Builder = 267 | this.addHardlinks(Semanticdb.SymbolInformation.newBuilder().apply(block).build()) 268 | 269 | inline fun Semanticdb.Type.copy(block: Semanticdb.Type.Builder.() -> Unit): Semanticdb.Type = 270 | this.toBuilder().apply(block).build() 271 | 272 | operator fun Semanticdb.Type.plus(other: Semanticdb.Type): Semanticdb.Type = 273 | this.toBuilder().mergeFrom(other).build() 274 | 275 | fun Semanticdb.Type?.orDefault(): Semanticdb.Type = this ?: Semanticdb.Type.getDefaultInstance() 276 | 277 | inline fun Semanticdb.Type.Builder.typeRef(block: Semanticdb.TypeRef.Builder.() -> Unit): 278 | Semanticdb.Type.Builder = 279 | this.setTypeRef(Semanticdb.TypeRef.newBuilder().apply(block).build()) 280 | 281 | inline fun Semanticdb.Type.Builder.existentialType(block: Semanticdb.ExistentialType.Builder.() -> 282 | Unit): Semanticdb.Type.Builder = 283 | this.setExistentialType(Semanticdb.ExistentialType.newBuilder().apply(block).build()) 284 | 285 | inline fun Semanticdb.Type.Builder.intersectionType(block: Semanticdb.IntersectionType.Builder.() -> 286 | Unit): Semanticdb.Type.Builder = 287 | this.setIntersectionType(Semanticdb.IntersectionType.newBuilder().apply(block).build()) 288 | 289 | inline fun Semanticdb.TypeRef.copy(block: Semanticdb.TypeRef.Builder.() -> Unit): Semanticdb.TypeRef 290 | = this.toBuilder().apply(block).build() 291 | 292 | operator fun Semanticdb.TypeRef.plus(other: Semanticdb.TypeRef): Semanticdb.TypeRef = 293 | this.toBuilder().mergeFrom(other).build() 294 | 295 | fun Semanticdb.TypeRef?.orDefault(): Semanticdb.TypeRef = this ?: 296 | Semanticdb.TypeRef.getDefaultInstance() 297 | 298 | inline fun Semanticdb.TypeRef.Builder.addTypeArguments(block: Semanticdb.Type.Builder.() -> Unit): 299 | Semanticdb.TypeRef.Builder = 300 | this.addTypeArguments(Semanticdb.Type.newBuilder().apply(block).build()) 301 | 302 | inline fun Semanticdb.IntersectionType.copy(block: Semanticdb.IntersectionType.Builder.() -> Unit): 303 | Semanticdb.IntersectionType = this.toBuilder().apply(block).build() 304 | 305 | operator fun Semanticdb.IntersectionType.plus(other: Semanticdb.IntersectionType): 306 | Semanticdb.IntersectionType = this.toBuilder().mergeFrom(other).build() 307 | 308 | fun Semanticdb.IntersectionType?.orDefault(): Semanticdb.IntersectionType = this ?: 309 | Semanticdb.IntersectionType.getDefaultInstance() 310 | 311 | inline fun Semanticdb.IntersectionType.Builder.addTypes(block: Semanticdb.Type.Builder.() -> Unit): 312 | Semanticdb.IntersectionType.Builder = 313 | this.addTypes(Semanticdb.Type.newBuilder().apply(block).build()) 314 | 315 | inline fun Semanticdb.ExistentialType.copy(block: Semanticdb.ExistentialType.Builder.() -> Unit): 316 | Semanticdb.ExistentialType = this.toBuilder().apply(block).build() 317 | 318 | operator fun Semanticdb.ExistentialType.plus(other: Semanticdb.ExistentialType): 319 | Semanticdb.ExistentialType = this.toBuilder().mergeFrom(other).build() 320 | 321 | fun Semanticdb.ExistentialType?.orDefault(): Semanticdb.ExistentialType = this ?: 322 | Semanticdb.ExistentialType.getDefaultInstance() 323 | 324 | inline fun Semanticdb.ExistentialType.Builder.tpe(block: Semanticdb.Type.Builder.() -> Unit): 325 | Semanticdb.ExistentialType.Builder = 326 | this.setTpe(Semanticdb.Type.newBuilder().apply(block).build()) 327 | 328 | inline fun Semanticdb.ExistentialType.Builder.declarations(block: Semanticdb.Scope.Builder.() -> 329 | Unit): Semanticdb.ExistentialType.Builder = 330 | this.setDeclarations(Semanticdb.Scope.newBuilder().apply(block).build()) 331 | 332 | inline fun TextDocuments(block: Semanticdb.TextDocuments.Builder.() -> Unit): 333 | Semanticdb.TextDocuments = Semanticdb.TextDocuments.newBuilder().apply(block).build() 334 | 335 | inline fun TextDocument(block: Semanticdb.TextDocument.Builder.() -> Unit): Semanticdb.TextDocument 336 | = Semanticdb.TextDocument.newBuilder().apply(block).build() 337 | 338 | inline fun Range(block: Semanticdb.Range.Builder.() -> Unit): Semanticdb.Range = 339 | Semanticdb.Range.newBuilder().apply(block).build() 340 | 341 | inline fun Signature(block: Semanticdb.Signature.Builder.() -> Unit): Semanticdb.Signature = 342 | Semanticdb.Signature.newBuilder().apply(block).build() 343 | 344 | inline fun ClassSignature(block: Semanticdb.ClassSignature.Builder.() -> Unit): 345 | Semanticdb.ClassSignature = Semanticdb.ClassSignature.newBuilder().apply(block).build() 346 | 347 | inline fun MethodSignature(block: Semanticdb.MethodSignature.Builder.() -> Unit): 348 | Semanticdb.MethodSignature = Semanticdb.MethodSignature.newBuilder().apply(block).build() 349 | 350 | inline fun TypeSignature(block: Semanticdb.TypeSignature.Builder.() -> Unit): 351 | Semanticdb.TypeSignature = Semanticdb.TypeSignature.newBuilder().apply(block).build() 352 | 353 | inline fun ValueSignature(block: Semanticdb.ValueSignature.Builder.() -> Unit): 354 | Semanticdb.ValueSignature = Semanticdb.ValueSignature.newBuilder().apply(block).build() 355 | 356 | inline fun SymbolInformation(block: Semanticdb.SymbolInformation.Builder.() -> Unit): 357 | Semanticdb.SymbolInformation = 358 | Semanticdb.SymbolInformation.newBuilder().apply(block).build() 359 | 360 | inline fun Access(block: Semanticdb.Access.Builder.() -> Unit): Semanticdb.Access = 361 | Semanticdb.Access.newBuilder().apply(block).build() 362 | 363 | inline fun PrivateAccess(block: Semanticdb.PrivateAccess.Builder.() -> Unit): 364 | Semanticdb.PrivateAccess = Semanticdb.PrivateAccess.newBuilder().apply(block).build() 365 | 366 | inline fun PrivateWithinAccess(block: Semanticdb.PrivateWithinAccess.Builder.() -> Unit): 367 | Semanticdb.PrivateWithinAccess = 368 | Semanticdb.PrivateWithinAccess.newBuilder().apply(block).build() 369 | 370 | inline fun ProtectedAccess(block: Semanticdb.ProtectedAccess.Builder.() -> Unit): 371 | Semanticdb.ProtectedAccess = Semanticdb.ProtectedAccess.newBuilder().apply(block).build() 372 | 373 | inline fun PublicAccess(block: Semanticdb.PublicAccess.Builder.() -> Unit): Semanticdb.PublicAccess 374 | = Semanticdb.PublicAccess.newBuilder().apply(block).build() 375 | 376 | inline fun Documentation(block: Semanticdb.Documentation.Builder.() -> Unit): 377 | Semanticdb.Documentation = Semanticdb.Documentation.newBuilder().apply(block).build() 378 | 379 | inline fun SymbolOccurrence(block: Semanticdb.SymbolOccurrence.Builder.() -> Unit): 380 | Semanticdb.SymbolOccurrence = Semanticdb.SymbolOccurrence.newBuilder().apply(block).build() 381 | 382 | inline fun Scope(block: Semanticdb.Scope.Builder.() -> Unit): Semanticdb.Scope = 383 | Semanticdb.Scope.newBuilder().apply(block).build() 384 | 385 | inline fun Type(block: Semanticdb.Type.Builder.() -> Unit): Semanticdb.Type = 386 | Semanticdb.Type.newBuilder().apply(block).build() 387 | 388 | inline fun TypeRef(block: Semanticdb.TypeRef.Builder.() -> Unit): Semanticdb.TypeRef = 389 | Semanticdb.TypeRef.newBuilder().apply(block).build() 390 | 391 | inline fun IntersectionType(block: Semanticdb.IntersectionType.Builder.() -> Unit): 392 | Semanticdb.IntersectionType = Semanticdb.IntersectionType.newBuilder().apply(block).build() 393 | 394 | inline fun ExistentialType(block: Semanticdb.ExistentialType.Builder.() -> Unit): 395 | Semanticdb.ExistentialType = Semanticdb.ExistentialType.newBuilder().apply(block).build() 396 | 397 | @DslMarker 398 | @Target(AnnotationTarget.CLASS) 399 | @Retention(AnnotationRetention.BINARY) 400 | annotation class SemanticdbDslMarker 401 | 402 | @SemanticdbDslMarker 403 | interface SemanticdbDslBuilder 404 | -------------------------------------------------------------------------------- /semanticdb-kotlinc/src/test/kotlin/com/sourcegraph/semanticdb_kotlinc/test/SemanticdbSymbolsTest.kt: -------------------------------------------------------------------------------- 1 | package com.sourcegraph.semanticdb_kotlinc.test 2 | 3 | import com.sourcegraph.semanticdb_kotlinc.* 4 | import com.sourcegraph.semanticdb_kotlinc.Semanticdb.Documentation.Format 5 | import com.sourcegraph.semanticdb_kotlinc.Semanticdb.Language 6 | import com.sourcegraph.semanticdb_kotlinc.Semanticdb.SymbolOccurrence.Role 7 | import com.sourcegraph.semanticdb_kotlinc.test.ExpectedSymbols.SemanticdbData 8 | import com.sourcegraph.semanticdb_kotlinc.test.ExpectedSymbols.SymbolCacheData 9 | import com.tschuchort.compiletesting.SourceFile 10 | import kotlin.contracts.ExperimentalContracts 11 | import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi 12 | import org.junit.jupiter.api.TestFactory 13 | 14 | @ExperimentalCompilerApi 15 | @ExperimentalContracts 16 | class SemanticdbSymbolsTest { 17 | @TestFactory 18 | fun `method disambiguator`() = 19 | listOf( 20 | ExpectedSymbols( 21 | "Basic two methods", 22 | SourceFile.testKt( 23 | """ 24 | |class Test { 25 | | fun sample() {} 26 | | fun sample(x: Int) {} 27 | |} 28 | |""".trimMargin()), 29 | symbolsCacheData = 30 | SymbolCacheData( 31 | listOf("Test#sample().".symbol(), "Test#sample(+1).".symbol()), 32 | )), 33 | ExpectedSymbols( 34 | "Inline class constructor", 35 | SourceFile.testKt( 36 | """ 37 | |class Test(val x: Int) 38 | |""".trimMargin()), 39 | symbolsCacheData = SymbolCacheData(listOf("Test#``().(x)".symbol()))), 40 | ExpectedSymbols( 41 | "Inline + secondary class constructors", 42 | SourceFile.testKt( 43 | """ 44 | |class Test(val x: Int) { 45 | | constructor(y: Long): this(y.toInt()) 46 | | constructor(z: String): this(z.toInt()) 47 | |} 48 | |""".trimMargin()), 49 | symbolsCacheData = 50 | SymbolCacheData( 51 | listOf( 52 | "Test#``().(x)".symbol(), 53 | "Test#``(+1).(y)".symbol(), 54 | "Test#``(+2).(z)".symbol()))), 55 | ExpectedSymbols( 56 | "Disambiguator number is not affected by different named methods", 57 | SourceFile.testKt( 58 | """ 59 | |class Test { 60 | | fun sample() {} 61 | | fun test() {} 62 | | fun test(x: Int) {} 63 | |} 64 | |""".trimMargin()), 65 | symbolsCacheData = 66 | SymbolCacheData( 67 | listOf("Test#test().".symbol(), "Test#test(+1).".symbol()))), 68 | ExpectedSymbols( 69 | "Top level overloaded functions", 70 | SourceFile.testKt( 71 | """ 72 | |fun test() {} 73 | |fun test(x: Int) {} 74 | |""".trimMargin()), 75 | symbolsCacheData = 76 | SymbolCacheData(listOf("test().".symbol(), "test(+1).(x)".symbol()))), 77 | ExpectedSymbols( 78 | "Annotations incl annotation type alias", 79 | SourceFile.testKt( 80 | """ 81 | |import kotlin.contracts.ExperimentalContracts 82 | |import kotlin.test.Test 83 | | 84 | |@ExperimentalContracts 85 | |class Banaan { 86 | | @Test 87 | | fun test() {} 88 | |} 89 | |""".trimMargin()), 90 | symbolsCacheData = 91 | SymbolCacheData( 92 | listOf( 93 | "kotlin/contracts/ExperimentalContracts#".symbol(), 94 | "kotlin/test/Test#".symbol()))), 95 | // https://kotlinlang.slack.com/archives/C7L3JB43G/p1624995376114900 96 | /*ExpectedSymbols( 97 | "Method call with type parameters", 98 | SourceFile.testKt(""" 99 | import org.junit.jupiter.api.io.TempDir 100 | val burger = LinkedHashMap() 101 | """), 102 | symbolsCacheData = SymbolCacheData( 103 | listOf("kotlin/collection/TypeAliasesKt#LinkedHashMap#``().".symbol()) 104 | ) 105 | )*/ 106 | ) 107 | .mapCheckExpectedSymbols() 108 | 109 | 110 | @TestFactory 111 | fun `check package symbols`() = 112 | listOf( 113 | ExpectedSymbols( 114 | "single component package name", 115 | SourceFile.testKt( 116 | """ 117 | |package main 118 | | 119 | |class Test 120 | |""".trimMargin()), 121 | symbolsCacheData = SymbolCacheData(listOf("main/Test#".symbol()), 0)), 122 | ExpectedSymbols( 123 | "multi component package name", 124 | SourceFile.testKt( 125 | """ 126 | |package test.sample.main 127 | | 128 | |class Test 129 | |""".trimMargin()), 130 | symbolsCacheData = 131 | SymbolCacheData(listOf("test/sample/main/Test#".symbol()), 0)), 132 | ExpectedSymbols( 133 | "no package name", 134 | SourceFile.testKt( 135 | """ 136 | |class Test 137 | |""".trimMargin()), 138 | symbolsCacheData = SymbolCacheData(listOf("Test#".symbol()), 0))) 139 | .mapCheckExpectedSymbols() 140 | 141 | @TestFactory 142 | fun `check locals counts`() = 143 | listOf( 144 | ExpectedSymbols( 145 | "simple variables", 146 | SourceFile.testKt( 147 | """ 148 | |fun test() { 149 | | val x = "hello" 150 | | println(x) 151 | |} 152 | |""".trimMargin()), 153 | symbolsCacheData = SymbolCacheData(localsCount = 1))) 154 | .mapCheckExpectedSymbols() 155 | 156 | @TestFactory 157 | fun `builtin symbols`() = 158 | listOf( 159 | ExpectedSymbols( 160 | "types", 161 | SourceFile.testKt( 162 | """ 163 | |var x: Int = 1 164 | |lateinit var y: Unit 165 | |lateinit var z: Any 166 | |lateinit var w: Nothing 167 | |""".trimMargin()), 168 | symbolsCacheData = 169 | SymbolCacheData( 170 | listOf( 171 | "kotlin/Int#".symbol(), 172 | "kotlin/Unit#".symbol(), 173 | "kotlin/Any#".symbol(), 174 | "kotlin/Nothing#".symbol()))), 175 | ExpectedSymbols( 176 | "functions", 177 | SourceFile.testKt( 178 | """ 179 | |val x = mapOf() 180 | |fun main() { 181 | | println() 182 | |} 183 | |""".trimMargin()), 184 | symbolsCacheData = 185 | SymbolCacheData( 186 | listOf( 187 | "kotlin/collections/mapOf(+2).".symbol(), 188 | "kotlin/io/println(+10).".symbol())))) 189 | .mapCheckExpectedSymbols() 190 | 191 | @TestFactory 192 | fun `reference expressions`() = 193 | listOf( 194 | ExpectedSymbols( 195 | "dot qualified expression", 196 | SourceFile.testKt( 197 | """ 198 | |import java.lang.System 199 | | 200 | |fun main() { 201 | | System.err 202 | |} 203 | |""".trimMargin()), 204 | symbolsCacheData = SymbolCacheData(listOf("java/lang/System#err.".symbol())))) 205 | .mapCheckExpectedSymbols() 206 | 207 | @TestFactory 208 | fun `properties with getters-setters`() = 209 | listOf( 210 | ExpectedSymbols( 211 | "top level properties - implicit", 212 | SourceFile.testKt( 213 | """ 214 | |var x: Int = 5 215 | |""".trimMargin()), 216 | semanticdb = 217 | SemanticdbData( 218 | expectedOccurrences = 219 | listOf( 220 | SymbolOccurrence { 221 | role = Role.DEFINITION 222 | symbol = "x." 223 | range { 224 | startLine = 0 225 | startCharacter = 4 226 | endLine = 0 227 | endCharacter = 5 228 | } 229 | }, 230 | SymbolOccurrence { 231 | role = Role.DEFINITION 232 | symbol = "getX()." 233 | range { 234 | startLine = 0 235 | startCharacter = 4 236 | endLine = 0 237 | endCharacter = 5 238 | } 239 | }, 240 | SymbolOccurrence { 241 | role = Role.DEFINITION 242 | symbol = "setX()." 243 | range { 244 | startLine = 0 245 | startCharacter = 4 246 | endLine = 0 247 | endCharacter = 5 248 | } 249 | })), 250 | ), 251 | ExpectedSymbols( 252 | "top level properties - explicit getter", 253 | SourceFile.testKt( 254 | """ 255 | |var x: Int = 5 256 | | get() = field + 10 257 | |""".trimMargin()), 258 | semanticdb = 259 | SemanticdbData( 260 | expectedOccurrences = 261 | listOf( 262 | SymbolOccurrence { 263 | role = Role.DEFINITION 264 | symbol = "x." 265 | range { 266 | startLine = 0 267 | startCharacter = 4 268 | endLine = 0 269 | endCharacter = 5 270 | } 271 | }, 272 | SymbolOccurrence { 273 | role = Role.DEFINITION 274 | symbol = "setX()." 275 | range { 276 | startLine = 0 277 | startCharacter = 4 278 | endLine = 0 279 | endCharacter = 5 280 | } 281 | }, 282 | SymbolOccurrence { 283 | role = Role.DEFINITION 284 | symbol = "getX()." 285 | range { 286 | startLine = 1 287 | startCharacter = 4 288 | endLine = 1 289 | endCharacter = 7 290 | } 291 | })), 292 | ), 293 | ExpectedSymbols( 294 | "top level properties - explicit setter", 295 | SourceFile.testKt( 296 | """ 297 | |var x: Int = 5 298 | | set(value) { field = value + 5 } 299 | |""".trimMargin()), 300 | semanticdb = 301 | SemanticdbData( 302 | expectedOccurrences = 303 | listOf( 304 | SymbolOccurrence { 305 | role = Role.DEFINITION 306 | symbol = "x." 307 | range { 308 | startLine = 0 309 | startCharacter = 4 310 | endLine = 0 311 | endCharacter = 5 312 | } 313 | }, 314 | SymbolOccurrence { 315 | role = Role.DEFINITION 316 | symbol = "getX()." 317 | range { 318 | startLine = 0 319 | startCharacter = 4 320 | endLine = 0 321 | endCharacter = 5 322 | } 323 | }, 324 | SymbolOccurrence { 325 | role = Role.DEFINITION 326 | symbol = "setX()." 327 | range { 328 | startLine = 1 329 | startCharacter = 4 330 | endLine = 1 331 | endCharacter = 7 332 | } 333 | })), 334 | ), 335 | ExpectedSymbols( 336 | "top level properties - explicit getter & setter", 337 | SourceFile.testKt( 338 | """ 339 | |var x: Int = 5 340 | | get() = field + 10 341 | | set(value) { field = value + 10 } 342 | |""".trimMargin()), 343 | semanticdb = 344 | SemanticdbData( 345 | expectedOccurrences = 346 | listOf( 347 | SymbolOccurrence { 348 | role = Role.DEFINITION 349 | symbol = "x." 350 | range { 351 | startLine = 0 352 | startCharacter = 4 353 | endLine = 0 354 | endCharacter = 5 355 | } 356 | }, 357 | SymbolOccurrence { 358 | role = Role.DEFINITION 359 | symbol = "getX()." 360 | range { 361 | startLine = 1 362 | startCharacter = 4 363 | endLine = 1 364 | endCharacter = 7 365 | } 366 | }, 367 | SymbolOccurrence { 368 | role = Role.DEFINITION 369 | symbol = "setX()." 370 | range { 371 | startLine = 2 372 | startCharacter = 4 373 | endLine = 2 374 | endCharacter = 7 375 | } 376 | })), 377 | ), 378 | ExpectedSymbols( 379 | "class constructor properties", 380 | SourceFile.testKt( 381 | """ 382 | |class Test(var sample: Int, text: String): Throwable(sample.toString()) { 383 | | fun test() { 384 | | println(sample) 385 | | } 386 | |} 387 | |""".trimMargin()), 388 | semanticdb = 389 | SemanticdbData( 390 | expectedOccurrences = 391 | listOf( 392 | SymbolOccurrence { 393 | role = Role.DEFINITION 394 | symbol = "Test#``().(sample)" 395 | range { 396 | startLine = 0 397 | startCharacter = 15 398 | endLine = 0 399 | endCharacter = 21 400 | } 401 | }, 402 | SymbolOccurrence { 403 | role = Role.DEFINITION 404 | symbol = "Test#sample." 405 | range { 406 | startLine = 0 407 | startCharacter = 15 408 | endLine = 0 409 | endCharacter = 21 410 | } 411 | }, 412 | SymbolOccurrence { 413 | role = Role.DEFINITION 414 | symbol = "Test#getSample()." 415 | range { 416 | startLine = 0 417 | startCharacter = 15 418 | endLine = 0 419 | endCharacter = 21 420 | } 421 | }, 422 | SymbolOccurrence { 423 | role = Role.DEFINITION 424 | symbol = "Test#setSample()." 425 | range { 426 | startLine = 0 427 | startCharacter = 15 428 | endLine = 0 429 | endCharacter = 21 430 | } 431 | }, 432 | SymbolOccurrence { 433 | role = Role.REFERENCE 434 | symbol = "Test#``().(sample)" 435 | range { 436 | startLine = 0 437 | startCharacter = 53 438 | endLine = 0 439 | endCharacter = 59 440 | } 441 | }, 442 | SymbolOccurrence { 443 | role = Role.REFERENCE 444 | symbol = "Test#sample." 445 | range { 446 | startLine = 2 447 | startCharacter = 16 448 | endLine = 2 449 | endCharacter = 22 450 | } 451 | }, 452 | SymbolOccurrence { 453 | role = Role.REFERENCE 454 | symbol = "Test#getSample()." 455 | range { 456 | startLine = 2 457 | startCharacter = 16 458 | endLine = 2 459 | endCharacter = 22 460 | } 461 | }, 462 | )))) 463 | .mapCheckExpectedSymbols() 464 | 465 | @TestFactory 466 | fun `class constructors`() = 467 | listOf( 468 | ExpectedSymbols( 469 | "implicit primary constructor", 470 | SourceFile.testKt( 471 | """ 472 | |class Banana 473 | |""".trimMargin()), 474 | semanticdb = 475 | SemanticdbData( 476 | expectedOccurrences = 477 | listOf( 478 | SymbolOccurrence { 479 | role = Role.DEFINITION 480 | symbol = "Banana#" 481 | range { 482 | startLine = 0 483 | startCharacter = 6 484 | endLine = 0 485 | endCharacter = 12 486 | } 487 | }, 488 | SymbolOccurrence { 489 | role = Role.DEFINITION 490 | symbol = "Banana#``()." 491 | range { 492 | startLine = 0 493 | startCharacter = 6 494 | endLine = 0 495 | endCharacter = 12 496 | } 497 | }, 498 | ))), 499 | ExpectedSymbols( 500 | "explicit primary constructor without keyword", 501 | SourceFile.testKt( 502 | """ 503 | |class Banana(size: Int) 504 | |""".trimMargin()), 505 | semanticdb = 506 | SemanticdbData( 507 | expectedOccurrences = 508 | listOf( 509 | SymbolOccurrence { 510 | role = Role.DEFINITION 511 | symbol = "Banana#" 512 | range { 513 | startLine = 0 514 | startCharacter = 6 515 | endLine = 0 516 | endCharacter = 12 517 | } 518 | }, 519 | SymbolOccurrence { 520 | role = Role.DEFINITION 521 | symbol = "Banana#``()." 522 | range { 523 | startLine = 0 524 | startCharacter = 6 525 | endLine = 0 526 | endCharacter = 12 527 | } 528 | }, 529 | ))), 530 | ExpectedSymbols( 531 | "explicit primary constructor with keyword", 532 | SourceFile.testKt( 533 | """ 534 | |class Banana constructor(size: Int) 535 | |""".trimMargin()), 536 | semanticdb = 537 | SemanticdbData( 538 | expectedOccurrences = 539 | listOf( 540 | SymbolOccurrence { 541 | role = Role.DEFINITION 542 | symbol = "Banana#" 543 | range { 544 | startLine = 0 545 | startCharacter = 6 546 | endLine = 0 547 | endCharacter = 12 548 | } 549 | }, 550 | SymbolOccurrence { 551 | role = Role.DEFINITION 552 | symbol = "Banana#``()." 553 | range { 554 | startLine = 0 555 | startCharacter = 13 556 | endLine = 0 557 | endCharacter = 24 558 | } 559 | }, 560 | )))) 561 | .mapCheckExpectedSymbols() 562 | 563 | @TestFactory 564 | fun `Single Abstract Method interface`() = 565 | listOf( 566 | ExpectedSymbols( 567 | "basic java.lang.Runnable", 568 | SourceFile.testKt( 569 | """ 570 | |val x = Runnable { }.run() 571 | |""".trimMargin()), 572 | semanticdb = 573 | SemanticdbData( 574 | expectedOccurrences = 575 | listOf( 576 | SymbolOccurrence { 577 | role = Role.REFERENCE 578 | symbol = "java/lang/Runnable#" 579 | range { 580 | startLine = 0 581 | startCharacter = 8 582 | endLine = 0 583 | endCharacter = 16 584 | } 585 | }, 586 | SymbolOccurrence { 587 | role = Role.REFERENCE 588 | symbol = "java/lang/Runnable#run()." 589 | range { 590 | startLine = 0 591 | startCharacter = 21 592 | endLine = 0 593 | endCharacter = 24 594 | } 595 | })))) 596 | .mapCheckExpectedSymbols() 597 | 598 | @TestFactory 599 | fun kdoc() = 600 | listOf( 601 | ExpectedSymbols( 602 | "empty kdoc line", 603 | SourceFile.testKt( 604 | """ 605 | |/** 606 | | 607 | |hello world 608 | |* test content 609 | |*/ 610 | |val x = "" 611 | |""".trimMargin()), 612 | semanticdb = 613 | SemanticdbData( 614 | expectedSymbols = 615 | listOf( 616 | SymbolInformation { 617 | symbol = "x." 618 | displayName = "x" 619 | language = Language.KOTLIN 620 | documentation { 621 | message = 622 | "```kotlin\npublic final val x: String\n```\n\n----\n\n\nhello world\n test content\n" 623 | format = Format.MARKDOWN 624 | } 625 | }, 626 | SymbolInformation { 627 | symbol = "getX()." 628 | displayName = "x" 629 | language = Language.KOTLIN 630 | documentation { 631 | message = 632 | "```kotlin\npublic get(): String\n```\n\n----\n\n\nhello world\n test content\n" 633 | format = Format.MARKDOWN 634 | } 635 | })))) 636 | .mapCheckExpectedSymbols() 637 | } 638 | --------------------------------------------------------------------------------