[Analysis API] Add source shadowing for resolve extensions.

KtResolveExtensions are designed to handle IDE analysis use cases where
source might not be available at analysis time, because that source is
generated by an external source generator, such as an annotation
processor or resource compiler. The sources generated by those external
generators can appear in the analysis scope, and cause issues with
source clash - resolution may find the virtual source from the
KtResolveExtension, the on-disk generated source from the external
generator, or both. This can cause issues, because that on-disk
generated source may be stale, and may not have symbols that will exist
the next time the generator is run (or, conversely, may have symbols
that will disappear on the next build).

To solve this, add a `getShadowedScope(): GlobalSearchScope` to
`KtResolveExtension`. Any files in the module that are included in that
scope will be hidden from resolution, allowing the resolve extension to
cleanly replace those files.

^KT-58834 fixed
This commit is contained in:
Justin Paupore
2023-05-22 17:59:47 -07:00
committed by Ilya Kirillov
parent 7c87eb098d
commit f590e88bdd
30 changed files with 357 additions and 82 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.analysis.api.resolve.extensions
import com.intellij.openapi.util.ModificationTracker
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.name.FqName
/**
@@ -53,4 +54,17 @@ public abstract class KtResolveExtension {
* @see KtResolveExtension
*/
public abstract fun getContainedPackages(): Set<FqName>
/**
* Returns the scope of files that should be shadowed by the files provided by [getKtFiles].
*
* Any files in the module that are included in this scope will be removed from analysis results. This allows the files provided by
* [getKtFiles] to cleanly replace those files from the module.
*
* If this resolve extension is being used to generate declarations that would normally be provided by sources generated by an external
* build task, such as a resource compiler or annotation processor, the resolve extension should provide a scope here that covers those
* externally generated sources. This will prevent collisions between the definitions provided by [getKtFiles] and those provided by the
* (potentially stale) externally generated sources.
*/
public open fun getShadowedScope(): GlobalSearchScope = GlobalSearchScope.EMPTY_SCOPE
}
@@ -0,0 +1,15 @@
// UNRESOLVED_REFERENCE
// MODULE: extendedModule
// FILE: declarations.hidden.kt
package foo
fun bar() = "baz"
// MODULE: dependency2
// MODULE: main(extendedModule, dependency2)()()
// FILE: main.kt
fun main() {
val x = foo.<caret>bar()
}
@@ -0,0 +1,19 @@
// UNRESOLVED_REFERENCE
// MODULE: extendedModule
// FILE: TestClass.hidden.java
package foo;
public class TestClass {
public TestClass() {}
}
// MODULE: dependency2
// MODULE: main(extendedModule, dependency2)()()
// FILE: main.kt
package foo
fun main() {
val x = <caret>TestClass()
}
@@ -0,0 +1,15 @@
// MODULE: extendedModule
// FILE: generated.hidden.kt
package generated
fun String.generatedOverloadedExtensionFunction(): Int = TODO()
// MODULE: dependency2
// MODULE: main(extendedModule, dependency2)()()
// FILE: main.kt
import generated.*
fun main() {
"string".generatedOverloadedExtension<caret>Function()
}
@@ -0,0 +1,2 @@
Resolved to:
0: (in generated) fun kotlin.Any.generatedOverloadedExtensionFunction(): kotlin.Int
@@ -0,0 +1,11 @@
// UNRESOLVED_REFERENCE
// FILE: declarations.hidden.kt
package foo
fun bar() = "baz"
// FILE: main.kt
fun main() {
val x = foo.<caret>bar()
}
@@ -0,0 +1,15 @@
// UNRESOLVED_REFERENCE
// FILE: TestClass.hidden.java
package foo;
public class TestClass {
public TestClass() {}
}
// FILE: main.kt
package foo
fun main() {
val x = <caret>TestClass()
}
@@ -0,0 +1,11 @@
// FILE: generated.hidden.kt
package generated
fun String.generatedOverloadedExtensionFunction(): Int = TODO()
// FILE: main.kt
import generated.*
fun main() {
"string".generatedOverloadedExtension<caret>Function()
}
@@ -0,0 +1,2 @@
Resolved to:
0: (in generated) fun kotlin.Any.generatedOverloadedExtensionFunction(): kotlin.Int