Skip to content

Commit

Permalink
Scan Kotlin constants for JavaPsiFacade
Browse files Browse the repository at this point in the history
so that constants in light classes can be resolved by JavaPsiFacade.
  • Loading branch information
ting-yuan committed Oct 6, 2022
1 parent a634e55 commit 70f6be5
Show file tree
Hide file tree
Showing 11 changed files with 201 additions and 0 deletions.
Expand Up @@ -31,10 +31,16 @@ import com.google.devtools.ksp.processing.impl.NativePlatformInfoImpl
import com.google.devtools.ksp.processing.impl.ResolverImpl
import com.google.devtools.ksp.processing.impl.UnknownPlatformInfoImpl
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSDeclarationContainer
import com.google.devtools.ksp.symbol.KSFile
import com.google.devtools.ksp.symbol.KSNode
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.Modifier
import com.google.devtools.ksp.symbol.Origin
import com.google.devtools.ksp.symbol.Visibility
import com.google.devtools.ksp.symbol.impl.java.KSFileJavaImpl
import com.google.devtools.ksp.symbol.impl.kotlin.KSFileImpl
import com.google.devtools.ksp.visitor.KSDefaultVisitor
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFileManager
Expand Down Expand Up @@ -198,6 +204,40 @@ abstract class AbstractKotlinSymbolProcessingExtension(
newFiles, deferredSymbols, bindingTrace, project, componentProvider, incrementalContext, options
)

if (!initialized) {
// Visit constants so that JavaPsiFacade knows them.
// The annotation visitor in ResolverImpl covered newFiles already.
ksFiles.filterIsInstance<KSFileImpl>().filter { it !in dirtyFiles }.forEach {
try {
it.accept(
object : KSDefaultVisitor<Unit, Unit>() {
override fun defaultHandler(node: KSNode, data: Unit) = Unit

override fun visitDeclarationContainer(
declarationContainer: KSDeclarationContainer,
data: Unit
) {
declarationContainer.declarations.filterNot {
it.getVisibility() == Visibility.PRIVATE
}.forEach {
it.accept(this, Unit)
}
}

override fun visitPropertyDeclaration(property: KSPropertyDeclaration, data: Unit) {
if (property.modifiers.contains(Modifier.CONST)) {
property.getter // force resolution
}
}
},
Unit
)
} catch (_: Exception) {
// Do nothing.
}
}
}

val providers = loadProviders()
if (!initialized) {
codeGenerator = CodeGeneratorImpl(
Expand Down
@@ -0,0 +1,42 @@
package com.google.devtools.ksp.test

import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.TaskOutcome
import org.junit.Assert
import org.junit.Assume
import org.junit.Rule
import org.junit.Test
import java.io.File

class Playground3IT {
@Rule
@JvmField
val project: TemporaryTestProject = TemporaryTestProject("playground3")

private fun GradleRunner.buildAndCheck(vararg args: String, extraCheck: (BuildResult) -> Unit = {}) =
buildAndCheckOutcome(*args, outcome = TaskOutcome.SUCCESS, extraCheck = extraCheck)

private fun GradleRunner.buildAndCheckOutcome(
vararg args: String,
outcome: TaskOutcome,
extraCheck: (BuildResult) -> Unit = {}
) {
val result = this.withArguments(*args).build()

Assert.assertEquals(outcome, result.task(":workload:kspKotlin")?.outcome)

extraCheck(result)
}

@Test
fun testPlayground() {
// FIXME: `clean` fails to delete files on windows.
Assume.assumeFalse(System.getProperty("os.name").startsWith("Windows", ignoreCase = true))
val gradleRunner = GradleRunner.create().withProjectDir(project.root).withDebug(true)
gradleRunner.buildAndCheck(":workload:kspKotlin")

File(project.root, "workload/src/main/java/com/example/JavaClass.java").appendText("\n")
gradleRunner.buildAndCheck(":workload:kspKotlin")
}
}
@@ -0,0 +1,8 @@
plugins {
kotlin("jvm")
}

repositories {
mavenCentral()
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
}
@@ -0,0 +1,19 @@
pluginManagement {
val kotlinVersion: String by settings
val kspVersion: String by settings
val testRepo: String by settings
plugins {
id("com.google.devtools.ksp") version kspVersion
kotlin("jvm") version kotlinVersion
}
repositories {
maven(testRepo)
gradlePluginPortal()
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
}
}

rootProject.name = "playground"

include(":workload")
include(":test-processor")
@@ -0,0 +1,24 @@
val kspVersion: String by project
val testRepo: String by project

plugins {
kotlin("jvm")
}

group = "com.example"
version = "1.0-SNAPSHOT"

repositories {
maven(testRepo)
mavenCentral()
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
}

dependencies {
implementation(kotlin("stdlib"))
implementation("com.google.devtools.ksp:symbol-processing-api:$kspVersion")
}

sourceSets.main {
java.srcDirs("src/main/kotlin")
}
@@ -0,0 +1,27 @@
import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSFunctionDeclaration

class TestProcessor(
private val codeGenerator: CodeGenerator,
private val options: Map<String, String>,
private val logger: KSPLogger
) : SymbolProcessor {
override fun process(resolver: Resolver): List<KSAnnotated> {
resolver
.getSymbolsWithAnnotation("com.example.ann.MyAnn")
.filterIsInstance<KSFunctionDeclaration>()
.forEach { func ->
val arg = func.annotations.first().arguments.first().value.toString()
if (!arg.startsWith("REPLACE"))
throw IllegalStateException(arg)
}

return emptyList()
}
}

class TestProcessorProvider : SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor =
TestProcessor(environment.codeGenerator, environment.options, environment.logger)
}
@@ -0,0 +1 @@
TestProcessorProvider
@@ -0,0 +1,20 @@
val testRepo: String by project

plugins {
id("com.google.devtools.ksp")
kotlin("jvm")
}

version = "1.0-SNAPSHOT"

repositories {
maven(testRepo)
mavenCentral()
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap/")
}

dependencies {
implementation(kotlin("stdlib"))
implementation(project(":test-processor"))
ksp(project(":test-processor"))
}
@@ -0,0 +1,9 @@
package com.example;

import com.example.ann.MyAnn;

public class JavaClass {
@MyAnn(KotlinConsts.ACTION)
public void f() {
}
}
@@ -0,0 +1,8 @@
package com.example

class KotlinConsts {
companion object {
const val ACTION = "REPLACE"
const val ACTION2 = "REPLACE"
}
}
@@ -0,0 +1,3 @@
package com.example.ann

annotation class MyAnn(val value: String)

0 comments on commit 70f6be5

Please sign in to comment.