mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[kotlin] mpp: "create expect" should warn about inaccessible annotations
^KTIJ-20493 Fixed GitOrigin-RevId: c1fbbe47db3dbe955545e1ba9169f419bba55f4a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
97ad13d9df
commit
a43fedd524
+3
-3
@@ -20,7 +20,7 @@ interface TypeAccessibilityChecker {
|
||||
* For example, you want to move `open class A<T : A>` class to another module.
|
||||
* In this case, you should add [FqName] of class `A` in [existingTypeNames].
|
||||
*/
|
||||
var existingTypeNames: Collection<String>
|
||||
var existingTypeNames: Set<String>
|
||||
|
||||
fun incorrectTypes(declaration: KtNamedDeclaration): Collection<FqName?>
|
||||
fun incorrectTypes(descriptor: DeclarationDescriptor): Collection<FqName?>
|
||||
@@ -30,13 +30,13 @@ interface TypeAccessibilityChecker {
|
||||
fun checkAccessibility(descriptor: DeclarationDescriptor): Boolean
|
||||
fun checkAccessibility(type: KotlinType): Boolean
|
||||
|
||||
fun <R> runInContext(fqNames: Collection<String>, block: TypeAccessibilityChecker.() -> R): R
|
||||
fun <R> runInContext(fqNames: Set<String>, block: TypeAccessibilityChecker.() -> R): R
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
project: Project,
|
||||
targetModule: Module,
|
||||
existingFqNames: Collection<String> = emptyList()
|
||||
existingFqNames: Set<String> = emptySet(),
|
||||
): TypeAccessibilityChecker = TypeAccessibilityCheckerImpl(project, targetModule, existingFqNames)
|
||||
|
||||
@get:TestOnly
|
||||
|
||||
+40
-21
@@ -6,6 +6,7 @@ import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.project.isTestModule
|
||||
import org.jetbrains.kotlin.idea.caches.project.toDescriptor
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.fqName
|
||||
@@ -27,7 +28,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
class TypeAccessibilityCheckerImpl(
|
||||
override val project: Project,
|
||||
override val targetModule: Module,
|
||||
override var existingTypeNames: Collection<String> = emptyList()
|
||||
override var existingTypeNames: Set<String> = emptySet()
|
||||
) : TypeAccessibilityChecker {
|
||||
private val scope by lazy { GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(targetModule, targetModule.isTestModule) }
|
||||
private var builtInsModule: ModuleDescriptor? = targetModule.toDescriptor()
|
||||
@@ -52,7 +53,7 @@ class TypeAccessibilityCheckerImpl(
|
||||
|
||||
override fun checkAccessibility(type: KotlinType): Boolean = incorrectTypesInSequence(type.collectAllTypes(), true).isEmpty()
|
||||
|
||||
override fun <R> runInContext(fqNames: Collection<String>, block: TypeAccessibilityChecker.() -> R): R {
|
||||
override fun <R> runInContext(fqNames: Set<String>, block: TypeAccessibilityChecker.() -> R): R {
|
||||
val oldValue = existingTypeNames
|
||||
existingTypeNames = fqNames
|
||||
return block().also { existingTypeNames = oldValue }
|
||||
@@ -62,10 +63,12 @@ class TypeAccessibilityCheckerImpl(
|
||||
sequence: Sequence<FqName?>,
|
||||
lazy: Boolean = true
|
||||
): List<FqName?> {
|
||||
return if (lazy) {
|
||||
for (fqName in sequence) if (!fqName.canFindClassInModule()) return listOf(fqName)
|
||||
emptyList()
|
||||
} else sequence.filter { !it.canFindClassInModule() }.toList()
|
||||
val uniqueSequence = sequence.distinct().filter { !it.canFindClassInModule() }
|
||||
return when {
|
||||
uniqueSequence.none() -> emptyList()
|
||||
lazy -> listOf(uniqueSequence.first())
|
||||
else -> uniqueSequence.toList()
|
||||
}
|
||||
}
|
||||
|
||||
private fun incorrectTypesInDescriptor(descriptor: DeclarationDescriptor, lazy: Boolean) =
|
||||
@@ -81,33 +84,44 @@ class TypeAccessibilityCheckerImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private tailrec fun DeclarationDescriptor.additionalClasses(existingClasses: Collection<String> = emptySet()): Collection<String> =
|
||||
private tailrec fun DeclarationDescriptor.additionalClasses(existingClasses: Set<String> = emptySet()): Set<String> =
|
||||
when (this) {
|
||||
is ClassifierDescriptorWithTypeParameters -> {
|
||||
val myParameters = existingClasses + declaredTypeParameters.map { it.fqNameOrNull()?.asString() ?: return emptySet() }
|
||||
val containingDeclaration = containingDeclaration
|
||||
if (isInner) containingDeclaration.additionalClasses(myParameters) else myParameters
|
||||
}
|
||||
is CallableDescriptor -> containingDeclaration.additionalClasses(existingClasses + typeParameters.map {
|
||||
it.fqNameOrNull()?.asString() ?: return emptySet()
|
||||
})
|
||||
|
||||
is CallableDescriptor -> containingDeclaration.additionalClasses(
|
||||
existingClasses = existingClasses + typeParameters.map { it.fqNameOrNull()?.asString() ?: return emptySet() }
|
||||
)
|
||||
|
||||
else ->
|
||||
existingClasses
|
||||
}
|
||||
|
||||
private fun DeclarationDescriptor.collectAllTypes(): Sequence<FqName?> {
|
||||
return when (this) {
|
||||
is ClassConstructorDescriptor -> valueParameters.asSequence().map(ValueParameterDescriptor::getType)
|
||||
.flatMap(KotlinType::collectAllTypes)
|
||||
is ClassDescriptor -> if (isInlineClass()) unsubstitutedPrimaryConstructor?.collectAllTypes().orEmpty() else {
|
||||
emptySequence()
|
||||
} + declaredTypeParameters.asSequence().flatMap(DeclarationDescriptor::collectAllTypes) + sequenceOf(fqNameOrNull())
|
||||
val annotations = annotations.asSequence().map(AnnotationDescriptor::type).flatMap(KotlinType::collectAllTypes)
|
||||
return annotations + when (this) {
|
||||
is ClassConstructorDescriptor -> valueParameters.asSequence().flatMap(DeclarationDescriptor::collectAllTypes)
|
||||
is ClassDescriptor -> {
|
||||
val primaryConstructorTypes = if (isInlineClass())
|
||||
unsubstitutedPrimaryConstructor?.collectAllTypes().orEmpty()
|
||||
else
|
||||
emptySequence()
|
||||
|
||||
primaryConstructorTypes +
|
||||
declaredTypeParameters.asSequence().flatMap(DeclarationDescriptor::collectAllTypes) +
|
||||
sequenceOf(fqNameOrNull())
|
||||
}
|
||||
|
||||
is CallableDescriptor -> {
|
||||
val returnType = returnType ?: return sequenceOf(null)
|
||||
returnType.collectAllTypes() +
|
||||
explicitParameters.map(ParameterDescriptor::getType).flatMap(KotlinType::collectAllTypes) +
|
||||
explicitParameters.asSequence().flatMap(DeclarationDescriptor::collectAllTypes) +
|
||||
typeParameters.asSequence().flatMap(DeclarationDescriptor::collectAllTypes)
|
||||
}
|
||||
|
||||
is TypeParameterDescriptor -> {
|
||||
val upperBounds = upperBounds
|
||||
val singleUpperBound = upperBounds.singleOrNull()
|
||||
@@ -118,6 +132,7 @@ private fun DeclarationDescriptor.collectAllTypes(): Sequence<FqName?> {
|
||||
if (extendBoundText == null || extendBoundText == "Any?") sequenceOf(singleUpperBound.fqName)
|
||||
else sequenceOf(null)
|
||||
}
|
||||
|
||||
upperBounds.isEmpty() -> sequenceOf(fqNameOrNull())
|
||||
else -> upperBounds.asSequence().flatMap(KotlinType::collectAllTypes)
|
||||
}
|
||||
@@ -126,10 +141,14 @@ private fun DeclarationDescriptor.collectAllTypes(): Sequence<FqName?> {
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinType.collectAllTypes(): Sequence<FqName?> = if (isError) sequenceOf(null)
|
||||
else sequenceOf(fqName) + arguments.asSequence()
|
||||
.map(TypeProjection::getType)
|
||||
.flatMap(KotlinType::collectAllTypes)
|
||||
private fun KotlinType.collectAllTypes(): Sequence<FqName?> =
|
||||
if (isError) {
|
||||
sequenceOf(null)
|
||||
} else {
|
||||
sequenceOf(fqName) +
|
||||
arguments.asSequence().map(TypeProjection::getType).flatMap(KotlinType::collectAllTypes) +
|
||||
annotations.asSequence().map(AnnotationDescriptor::type).flatMap(KotlinType::collectAllTypes)
|
||||
}
|
||||
|
||||
private val CallableDescriptor.explicitParameters: Sequence<ParameterDescriptor>
|
||||
get() = valueParameters.asSequence() + dispatchReceiverParameter?.let {
|
||||
|
||||
+4
-2
@@ -439,9 +439,9 @@ fun TypeAccessibilityChecker.Companion.typesToString(types: Collection<FqName?>,
|
||||
}
|
||||
}
|
||||
|
||||
fun TypeAccessibilityChecker.findAndApplyExistingClasses(elements: Collection<KtNamedDeclaration>): HashSet<String> {
|
||||
fun TypeAccessibilityChecker.findAndApplyExistingClasses(elements: Collection<KtNamedDeclaration>): Set<String> {
|
||||
var classes = elements.filterIsInstance<KtClassOrObject>()
|
||||
while (true) {
|
||||
while (classes.isNotEmpty()) {
|
||||
val existingNames = classes.mapNotNull { it.fqName?.asString() }.toHashSet()
|
||||
existingTypeNames = existingNames
|
||||
|
||||
@@ -450,4 +450,6 @@ fun TypeAccessibilityChecker.findAndApplyExistingClasses(elements: Collection<Kt
|
||||
|
||||
classes = newExistingClasses
|
||||
}
|
||||
|
||||
return existingTypeNames
|
||||
}
|
||||
|
||||
+20
-10
@@ -26,6 +26,11 @@ public abstract class QuickFixMultiModuleTestGenerated extends AbstractQuickFixM
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("annotationOnClass")
|
||||
public void testAnnotationOnClass() throws Exception {
|
||||
runTest("testData/multiModuleQuickFix/accessibilityChecker/annotationOnClass/");
|
||||
}
|
||||
|
||||
@TestMetadata("classPrimaryConstructor")
|
||||
public void testClassPrimaryConstructor() throws Exception {
|
||||
runTest("testData/multiModuleQuickFix/accessibilityChecker/classPrimaryConstructor/");
|
||||
@@ -537,6 +542,11 @@ public abstract class QuickFixMultiModuleTestGenerated extends AbstractQuickFixM
|
||||
runTest("testData/multiModuleQuickFix/createExpect/class/");
|
||||
}
|
||||
|
||||
@TestMetadata("classWithAnnotation")
|
||||
public void testClassWithAnnotation() throws Exception {
|
||||
runTest("testData/multiModuleQuickFix/createExpect/classWithAnnotation/");
|
||||
}
|
||||
|
||||
@TestMetadata("classWithSuperClassAndTypeParameter")
|
||||
public void testClassWithSuperClassAndTypeParameter() throws Exception {
|
||||
runTest("testData/multiModuleQuickFix/createExpect/classWithSuperClassAndTypeParameter/");
|
||||
@@ -616,11 +626,6 @@ public abstract class QuickFixMultiModuleTestGenerated extends AbstractQuickFixM
|
||||
public void testFunction() throws Exception {
|
||||
runTest("testData/multiModuleQuickFix/createExpect/function/");
|
||||
}
|
||||
|
||||
@TestMetadata("function2")
|
||||
public void testFunction2() throws Exception {
|
||||
runTest("testData/multiModuleQuickFix/createExpect/function2/");
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@@ -630,6 +635,11 @@ public abstract class QuickFixMultiModuleTestGenerated extends AbstractQuickFixM
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("function2")
|
||||
public void testFunction2() throws Exception {
|
||||
runTest("testData/multiModuleQuickFix/createExpect/function2/");
|
||||
}
|
||||
|
||||
@TestMetadata("functionInInterface")
|
||||
public void testFunctionInInterface() throws Exception {
|
||||
runTest("testData/multiModuleQuickFix/createExpect/functionInInterface/");
|
||||
@@ -724,11 +734,6 @@ public abstract class QuickFixMultiModuleTestGenerated extends AbstractQuickFixM
|
||||
public void testPropertyWithLateinitModifier() throws Exception {
|
||||
runTest("testData/multiModuleQuickFix/createExpect/propertyWithLateinitModifier/");
|
||||
}
|
||||
|
||||
@TestMetadata("propertyWithPrivateModifier")
|
||||
public void testPropertyWithPrivateModifier() throws Exception {
|
||||
runTest("testData/multiModuleQuickFix/createExpect/propertyWithPrivateModifier/");
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@@ -738,6 +743,11 @@ public abstract class QuickFixMultiModuleTestGenerated extends AbstractQuickFixM
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("propertyWithPrivateModifier")
|
||||
public void testPropertyWithPrivateModifier() throws Exception {
|
||||
runTest("testData/multiModuleQuickFix/createExpect/propertyWithPrivateModifier/");
|
||||
}
|
||||
|
||||
@TestMetadata("sealedClass")
|
||||
public void testSealedClass() throws Exception {
|
||||
runTest("testData/multiModuleQuickFix/createExpect/sealedClass/");
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Create expected class in common module testModule_Common" "true"
|
||||
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:
|
||||
// SHOULD_FAIL_WITH: JvmAnnotationClass
|
||||
|
||||
package one.two
|
||||
|
||||
annotation class JvmAnnotationClass
|
||||
|
||||
@JvmAnnotationClass
|
||||
actual class Plat<caret>form
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// "Create expected class in common module testModule_Common" "true"
|
||||
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some,A
|
||||
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some
|
||||
// DISABLE-ERRORS
|
||||
|
||||
interface Some
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
// To be implemented
|
||||
expect class A<T> {
|
||||
}
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
// "Create expected class in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
// ERROR: Unresolved reference: T
|
||||
// ERROR: Unresolved reference: TODO
|
||||
|
||||
actual class A<T> {
|
||||
class B {
|
||||
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
// "Create expected class in common module testModule_Common" "true"
|
||||
// DISABLE-ERRORS
|
||||
|
||||
actual class A<T> {
|
||||
class B {
|
||||
fun a(): T = TODO()
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package one.two
|
||||
|
||||
annotation class CommonAnnotationClass
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package one.two
|
||||
|
||||
@CommonAnnotationClass
|
||||
expect class <selection><caret></selection>Platform
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// "Create expected class in common module testModule_Common" "true"
|
||||
|
||||
package one.two
|
||||
|
||||
@CommonAnnotationClass
|
||||
actual class Plat<caret>form
|
||||
-2
@@ -3,8 +3,6 @@
|
||||
|
||||
annotation class CommonAnnotation
|
||||
expect class My {
|
||||
tailrec fun foo(arg: Int): Int
|
||||
|
||||
@CommonAnnotation
|
||||
fun initialize()
|
||||
|
||||
|
||||
+1
-1
@@ -17,4 +17,4 @@ actual class <caret>My {
|
||||
actual fun initialize() {
|
||||
some = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -5,7 +5,7 @@ annotation class PlatformAnnotation
|
||||
|
||||
actual class <caret>My {
|
||||
@PlatformAnnotation
|
||||
actual tailrec fun foo(arg: Int): Int {
|
||||
tailrec fun foo(arg: Int): Int {
|
||||
if (arg <= 1) return 1
|
||||
return foo(arg - 1)
|
||||
}
|
||||
@@ -17,4 +17,4 @@ actual class <caret>My {
|
||||
actual fun initialize() {
|
||||
some = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -1,2 +1,3 @@
|
||||
These declarations cannot be transformed:
|
||||
fun foo(arg: Int){...}
|
||||
actual lateinit var some: Boolean
|
||||
|
||||
Reference in New Issue
Block a user