Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,15 @@ class DeepLinkProcessor(
private fun validClassElement(classElement: XTypeElement) = classElement.isActivity() || classElement.isHandler()

private fun verifyMethod(methodElement: XMethodElement) {
if (!methodElement.isStatic()) {
// XProcessing's isStatic() for KSP relies on annotation type resolution to detect
// @JvmStatic. This resolution can intermittently fail during incremental compilation
// with KSP2's Analysis API, causing isStatic() to return false for methods that are
// actually @JvmStatic inside a Kotlin object. As a workaround, we also accept methods
// whose enclosing type is a Kotlin object or companion object, since all methods in
// these types are effectively static in the JVM bytecode.
val enclosingElement = methodElement.enclosingElement
val isInKotlinObject = enclosingElement is XTypeElement && enclosingElement.isKotlinObject()
if (!methodElement.isStatic() && !isInKotlinObject) {
throw DeepLinkProcessorException(
element = methodElement,
errorMessage = "Only static methods can be annotated with @${DEEP_LINK_CLASS.simpleName}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1449,4 +1449,74 @@ class DeepLinkProcessorKspTest : BaseDeepLinkProcessorTest() {
)
}
}

/**
* Verifies that methods inside a Kotlin `object` with @JvmStatic compile successfully.
* This exercises the isInKotlinObject fallback in verifyMethod(), which works around
* intermittent KSP annotation resolution failures where isStatic() returns false
* for @JvmStatic methods in object declarations.
*/
@Test
fun testMethodInKotlinObjectWithJvmStaticCompiles() {
val kotlinObjectSource =
Source.KotlinSource(
"com/example/SampleDeepLinks.kt",
"""
package com.example
import android.content.Context
import android.content.Intent
import com.airbnb.deeplinkdispatch.DeepLink

object SampleDeepLinks {
@DeepLink("airbnb://example.com/deepLink")
@JvmStatic
fun intentForDeepLink(context: Context): Intent = Intent()
}
""",
)
val results =
listOf(
compileIncremental(
sourceFiles = listOf(kotlinObjectSource, module, fakeBaseDeeplinkDelegateJava),
useKsp = true,
),
)
results.forEach { result ->
assertThat(result.result.exitCode)
.isEqualTo(KotlinCompilation.ExitCode.OK)
}
}

/**
* Verifies that a non-static method (no @JvmStatic) in a plain class still fails.
*/
@Test
fun testNonStaticMethodInClassStillFails() {
val nonStaticSource =
Source.KotlinSource(
"com/example/SampleActivity.kt",
"""
package com.example
import android.content.Context
import android.content.Intent
import com.airbnb.deeplinkdispatch.DeepLink

class SampleActivity : android.app.Activity() {
@DeepLink("airbnb://example.com/deepLink")
fun intentFromNoStatic(context: Context): Intent = Intent()
}
""",
)
val results =
listOf(
compileIncremental(
sourceFiles = listOf(nonStaticSource, module, fakeBaseDeeplinkDelegateJava),
useKsp = true,
),
)
assertCompileError(
results = results,
errorMessage = "Only static methods can be annotated",
)
}
}