diff --git a/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/ActualWithoutExpectFactory.kt b/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/ActualWithoutExpectFactory.kt index f1b681373d13..3101529ceff7 100644 --- a/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/ActualWithoutExpectFactory.kt +++ b/plugins/kotlin/code-insight/fixes-k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/fixes/ActualWithoutExpectFactory.kt @@ -9,12 +9,14 @@ import com.intellij.openapi.util.NlsContexts import com.intellij.openapi.util.text.StringUtil import com.intellij.psi.PsiElement import com.intellij.psi.createSmartPointer +import com.intellij.util.containers.addIfNotNull import org.jetbrains.kotlin.analysis.api.KaExperimentalApi import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.fir.diagnostics.KaFirDiagnostic import org.jetbrains.kotlin.analysis.api.symbols.KaCallableSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol import org.jetbrains.kotlin.analysis.api.symbols.KaValueParameterSymbol +import org.jetbrains.kotlin.analysis.api.types.KaClassType import org.jetbrains.kotlin.analysis.api.types.KaType import org.jetbrains.kotlin.idea.base.analysis.api.utils.analyzeInModalWindow import org.jetbrains.kotlin.idea.base.analysis.api.utils.shortenReferences @@ -28,6 +30,8 @@ import org.jetbrains.kotlin.idea.core.createFileForDeclaration import org.jetbrains.kotlin.idea.core.overrideImplement.MemberGenerateMode import org.jetbrains.kotlin.idea.core.overrideImplement.generateClassWithMembers import org.jetbrains.kotlin.idea.core.overrideImplement.generateMember +import org.jetbrains.kotlin.idea.k2.refactoring.introduce.extractionEngine.getUnResolvableInScope +import org.jetbrains.kotlin.idea.k2.refactoring.introduce.extractionEngine.isResolvableInScope import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateClassUtil.getTypeDescription import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHint import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass @@ -39,7 +43,6 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier import org.jetbrains.kotlin.psi.psiUtil.startOffset -import kotlin.collections.plus internal object ActualWithoutExpectFactory { @@ -143,7 +146,7 @@ sealed class CreateExpectedFix( val targetExpectedClass = targetExpectedClassPointer?.element val expectedFile = targetExpectedClass?.containingKtFile ?: getOrCreateImplementationFile() ?: return val declaration = element ?: return - val expectPrototype = generate(project, targetExpectedClass, declaration) ?: return + val expectPrototype = generate(project, targetExpectedClass, expectedFile, declaration) ?: return val target = project.executeWriteCommand(familyName, null) { val initial = (when { targetExpectedClass != null && expectPrototype is KtPrimaryConstructor -> targetExpectedClass.add(expectPrototype) @@ -160,7 +163,7 @@ sealed class CreateExpectedFix( ) } - abstract fun generate(project: Project, targetExpectedClass: KtClassOrObject?, declaration: D): D? + abstract fun generate(project: Project, targetExpectedClass: KtClassOrObject?, expectedFile: KtFile, declaration: D): D? fun showInaccessibleDeclarationError( element: PsiElement, @@ -172,38 +175,80 @@ sealed class CreateExpectedFix( } } + @OptIn(KaExperimentalApi::class) protected fun isCorrectAndHaveAccessibleModifiers( declaration: KtNamedDeclaration, - showErrorHint: Boolean = false, + expectedFile: KtFile, ): Boolean { val inaccessibleModifier = INACCESSIBLE_MODIFIERS.find { declaration.hasModifier(it) } if (inaccessibleModifier != null) { - if (showErrorHint) showInaccessibleDeclarationError( + showInaccessibleDeclarationError( declaration, KotlinBundle.message("the.declaration.has.0.modifier", inaccessibleModifier) ) return false } if (declaration is KtFunction && declaration.hasBody() && declaration.containingClassOrObject?.isInterfaceClass() == true) { - if (showErrorHint) showInaccessibleDeclarationError( + showInaccessibleDeclarationError( declaration, KotlinBundle.message("the.function.declaration.shouldn.t.have.a.default.implementation") ) return false } - //todo check accessibility of types in the common module - //if (!showErrorHint) return checkAccessibility(declaration) - // - //val types = incorrectTypes(declaration).ifEmpty { return true } - //showInaccessibleDeclarationError( - // declaration, - // KotlinBundle.message( - // "some.types.are.not.accessible.from.0.1", - // targetModule.name, - // TypeAccessibilityChecker.typesToString(types) - // ) - //) + val unresolvedTypes = analyzeInModalWindow(declaration, KotlinBundle.message("fix.change.signature.prepare")) { + fun MutableList.processTypeParametersOwner(owner: KtTypeParameterListOwner) { + owner.typeParameters.forEach { addIfNotNull(it.extendsBound?.type) } + owner.typeConstraints.forEach { addIfNotNull(it.boundTypeReference?.type) } + owner.annotationEntries.forEach { addIfNotNull(it.typeReference?.type) } + } + + val usedTypes = when (declaration) { + is KtConstructor<*> -> declaration.valueParameters.mapNotNull { it.typeReference?.type } + is KtFunction -> buildList { + addIfNotNull(declaration.receiverTypeReference?.type) + addIfNotNull(declaration.returnType) + for (parameter in declaration.valueParameters) { + addIfNotNull(parameter.returnType) + } + processTypeParametersOwner(declaration) + } + + is KtProperty -> buildList { + addIfNotNull(declaration.receiverTypeReference?.type) + addIfNotNull(declaration.returnType) + processTypeParametersOwner(declaration) + + } + is KtClass -> buildList { + if (declaration.isInline()) { + declaration.primaryConstructor?.valueParameters?.forEach { addIfNotNull(it.returnType) } + } + processTypeParametersOwner(declaration) + } + + else -> emptyList() + }.map { it.abbreviation ?: it }.distinct() + + val expectedVirtualFile = expectedFile.virtualFile + usedTypes.mapNotNull { + getUnResolvableInScope(it, expectedFile, mutableSetOf()) { classSymbol -> + val psi = (classSymbol.getExpectsForActual().firstOrNull() ?: classSymbol).psi + val useScope = psi?.useScope + psi == declaration || useScope != null && expectedVirtualFile != null && useScope.contains(expectedVirtualFile) + } + }.mapNotNull { (it as? KaClassType)?.classId?.shortClassName?.asString() } + } + if (unresolvedTypes.isNotEmpty()) { + showInaccessibleDeclarationError( + declaration, + KotlinBundle.message( + "some.types.are.not.accessible.from.0.1", + module.name, + unresolvedTypes.joinToString() + ) + ) + } return true } @@ -224,9 +269,10 @@ internal class CreateExpectedClassFix( override fun generate( project: Project, targetExpectedClass: KtClassOrObject?, + expectedFile: KtFile, declaration: KtNamedDeclaration, ): KtNamedDeclaration? { - if (!isCorrectAndHaveAccessibleModifiers(declaration, true)) return null + if (!isCorrectAndHaveAccessibleModifiers(declaration, expectedFile)) return null return analyzeInModalWindow(declaration, KotlinBundle.message("fix.change.signature.prepare")) { val classSymbol = declaration.symbol as? KaClassSymbol ?: return@analyzeInModalWindow null @@ -252,9 +298,10 @@ internal class CreateExpectedCallableMemberFix( override fun generate( project: Project, targetExpectedClass: KtClassOrObject?, + expectedFile: KtFile, declaration: KtNamedDeclaration, ): KtNamedDeclaration? { - if (!isCorrectAndHaveAccessibleModifiers(declaration, true)) return null + if (!isCorrectAndHaveAccessibleModifiers(declaration, expectedFile)) return null return analyzeInModalWindow(declaration, KotlinBundle.message("fix.change.signature.prepare")) { val callableSymbol = declaration.symbol as? KaCallableSymbol ?: return@analyzeInModalWindow null diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/annotationOnClass/jvm/one/two/Platform.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/annotationOnClass/jvm/one/two/Platform.kt index bfce2975cf13..dfdb10dd36b7 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/annotationOnClass/jvm/one/two/Platform.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/annotationOnClass/jvm/one/two/Platform.kt @@ -1,7 +1,7 @@ // "Create expected class in common module testModule_Common" "true" // SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common: // SHOULD_FAIL_WITH: JvmAnnotationClass -// IGNORE_K2 + package one.two diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/classUpperBounds/jvm/Utils.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/classUpperBounds/jvm/Utils.kt index b1b3a9977697..8d7e53e2c169 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/classUpperBounds/jvm/Utils.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/classUpperBounds/jvm/Utils.kt @@ -1,7 +1,7 @@ // "Create expected class in common module testModule_Common" "true" // SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some // DISABLE_ERRORS -// IGNORE_K2 + interface Some diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/errorType/common/foo.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/errorType/common/foo.kt index 1ca33147520e..d1a6d778ec0b 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/errorType/common/foo.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/errorType/common/foo.kt @@ -1,4 +1,6 @@ // "Add missing actual declarations" "true" +// K2_ACTION: "Create actual in 'testModule_JVM'" "true" +// K2_TOOL: org.jetbrains.kotlin.idea.k2.codeinsight.inspections.KotlinNoActualForExpectInspection // SHOULD_FAIL_WITH: Cannot generate class: Type <Unknown> is not accessible from target module // DISABLE_ERRORS // IGNORE_K2 diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunParameter/jvm/Utils.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunParameter/jvm/Utils.kt index 6be8cbde18fc..e1ded1a84713 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunParameter/jvm/Utils.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunParameter/jvm/Utils.kt @@ -1,7 +1,7 @@ // "Create expected function in common module testModule_Common" "true" // SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some // DISABLE_ERRORS -// IGNORE_K2 + class Some actual fun foo(some: Some) {} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunReturnType/jvm/Utils.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunReturnType/jvm/Utils.kt index 55240107ead5..16b2e3b11b05 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunReturnType/jvm/Utils.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunReturnType/jvm/Utils.kt @@ -1,7 +1,7 @@ // "Create expected function in common module testModule_Common" "true" // SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some // DISABLE_ERRORS -// IGNORE_K2 + interface Some diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunTypeParameter/jvm/Utils.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunTypeParameter/jvm/Utils.kt index a29ed4fa0cc2..aed4ba09d8d6 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunTypeParameter/jvm/Utils.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunTypeParameter/jvm/Utils.kt @@ -1,7 +1,7 @@ // "Create expected function in common module testModule_Common" "true" // SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some // DISABLE_ERRORS -// IGNORE_K2 + class Some actual fun foo(some: List) {} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunUpperBounds/jvm/Utils.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunUpperBounds/jvm/Utils.kt index a59724f80a04..5f4462668309 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunUpperBounds/jvm/Utils.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunUpperBounds/jvm/Utils.kt @@ -1,7 +1,7 @@ // "Create expected function in common module testModule_Common" "true" // SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some // DISABLE_ERRORS -// IGNORE_K2 + interface Some actual fun foo(some: List) {} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunUpperBounds2/jvm/Utils.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunUpperBounds2/jvm/Utils.kt index 1d11cb1796c4..e3db8cc8bb35 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunUpperBounds2/jvm/Utils.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelFunUpperBounds2/jvm/Utils.kt @@ -1,7 +1,7 @@ // "Create expected function in common module testModule_Common" "true" // SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some // DISABLE_ERRORS -// IGNORE_K2 + interface Some actual fun foo(some: List) {} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelProperty/jvm/Utils.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelProperty/jvm/Utils.kt index 7234dcc9dc3b..e9d714f71cfd 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelProperty/jvm/Utils.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelProperty/jvm/Utils.kt @@ -1,7 +1,7 @@ // "Create expected property in common module testModule_Common" "true" // SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some // DISABLE_ERRORS -// IGNORE_K2 + interface Some actual val foo: Some = TODO() \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelPropertyTypeParam2/jvm/Utils.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelPropertyTypeParam2/jvm/Utils.kt index 5948720106c4..85a6ed875766 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelPropertyTypeParam2/jvm/Utils.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelPropertyTypeParam2/jvm/Utils.kt @@ -1,7 +1,7 @@ // "Create expected property in common module testModule_Common" "true" // SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some // DISABLE_ERRORS -// IGNORE_K2 + class Some actual val Some.foo: Some get() = TODO() \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelPropertyTypeParamBound/jvm/Utils.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelPropertyTypeParamBound/jvm/Utils.kt index ecfecba587ca..ed2c40fda285 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelPropertyTypeParamBound/jvm/Utils.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/accessibilityChecker/topLevelPropertyTypeParamBound/jvm/Utils.kt @@ -1,7 +1,7 @@ // "Create expected property in common module testModule_Common" "true" // SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,A // DISABLE_ERRORS -// IGNORE_K2 + interface A actual val Some.foo: Some get() = TODO() \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/funWithAccessibleExpansion/jvm/Utils.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/funWithAccessibleExpansion/jvm/Utils.kt index 21ee659b8052..ac09426fbd9f 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/funWithAccessibleExpansion/jvm/Utils.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/funWithAccessibleExpansion/jvm/Utils.kt @@ -1,7 +1,6 @@ // "Create expected function in common module testModule_Common" "true" // SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,SomeString // DISABLE_ERRORS -// IGNORE_K2 typealias SomeString = String diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/funWithJdk/proj_jvm_dep(fulljdk)/Utils.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/funWithJdk/proj_jvm_dep(fulljdk)/Utils.kt index a7b2e70b7774..4231be4bae08 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/funWithJdk/proj_jvm_dep(fulljdk)/Utils.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/funWithJdk/proj_jvm_dep(fulljdk)/Utils.kt @@ -1,7 +1,6 @@ // "Create expected function in common module proj_Common" "true" // SHOULD_FAIL_WITH: Some types are not accessible from proj_Common:,ArrayList // DISABLE_ERRORS -// IGNORE_K2 import java.util.ArrayList diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/funWithJdk/proj_jvm_dep(fulljdk)/Utils.kt.after b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/funWithJdk/proj_jvm_dep(fulljdk)/Utils.kt.after index 4b5b0fb46b02..523f949ccf7b 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/funWithJdk/proj_jvm_dep(fulljdk)/Utils.kt.after +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/funWithJdk/proj_jvm_dep(fulljdk)/Utils.kt.after @@ -1,7 +1,6 @@ // "Create expected function in common module proj_Common" "true" // SHOULD_FAIL_WITH: Some types are not accessible from proj_Common:,ArrayList // DISABLE_ERRORS -// IGNORE_K2 import java.util.ArrayList diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/stdlibWithJavaAlias/proj_jvm_dep(stdlib)_dep(fulljdk)/My.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/stdlibWithJavaAlias/proj_jvm_dep(stdlib)_dep(fulljdk)/My.kt index a9179f940ea8..e5cc90e21769 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/stdlibWithJavaAlias/proj_jvm_dep(stdlib)_dep(fulljdk)/My.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/stdlibWithJavaAlias/proj_jvm_dep(stdlib)_dep(fulljdk)/My.kt @@ -1,5 +1,5 @@ // "Create expected property in common module proj_Common" "true" // DISABLE_ERRORS - +// IGNORE_K2 actual val some: HashMap = TODO() \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/stdlibWithJavaAlias/proj_jvm_dep(stdlib)_dep(fulljdk)/My.kt.after b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/stdlibWithJavaAlias/proj_jvm_dep(stdlib)_dep(fulljdk)/My.kt.after index 714a4107f138..ef3b03bb1403 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/stdlibWithJavaAlias/proj_jvm_dep(stdlib)_dep(fulljdk)/My.kt.after +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/createExpect/stdlibWithJavaAlias/proj_jvm_dep(stdlib)_dep(fulljdk)/My.kt.after @@ -1,5 +1,5 @@ // "Create expected property in common module proj_Common" "true" // DISABLE_ERRORS - +// IGNORE_K2 actual val some: HashMap = TODO() \ No newline at end of file diff --git a/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/introduce/extractionEngine/KotlinTypeDescriptor.kt b/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/introduce/extractionEngine/KotlinTypeDescriptor.kt index 97fcb247ab26..0f195463887a 100644 --- a/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/introduce/extractionEngine/KotlinTypeDescriptor.kt +++ b/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/introduce/extractionEngine/KotlinTypeDescriptor.kt @@ -3,13 +3,14 @@ package org.jetbrains.kotlin.idea.k2.refactoring.introduce.extractionEngine import com.intellij.psi.PsiElement import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.PsiUtil import com.intellij.psi.util.parentOfType import org.jetbrains.kotlin.analysis.api.KaExperimentalApi import org.jetbrains.kotlin.analysis.api.KaSession import org.jetbrains.kotlin.analysis.api.analyze import org.jetbrains.kotlin.analysis.api.renderer.declarations.impl.KaDeclarationRendererForSource import org.jetbrains.kotlin.analysis.api.symbols.KaAnonymousObjectSymbol -import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol +import org.jetbrains.kotlin.analysis.api.symbols.KaClassLikeSymbol import org.jetbrains.kotlin.analysis.api.symbols.typeParameters import org.jetbrains.kotlin.analysis.api.types.* import org.jetbrains.kotlin.idea.k2.refactoring.extractFunction.Parameter @@ -121,47 +122,80 @@ class KotlinTypeDescriptor(private val data: IExtractionData) : TypeDescriptor): Boolean { +fun isResolvableInScope( + typeToCheck: KaType, + scope: PsiElement, + typeParameters: MutableSet, +): Boolean { + return getUnResolvableInScope(typeToCheck, scope, typeParameters) == null +} + +context(KaSession) +@OptIn(KaExperimentalApi::class) +fun getUnResolvableInScope( + typeToCheck: KaType, + scope: PsiElement, + typeParameters: MutableSet, + classAccessibilityChecker: (KaClassLikeSymbol) -> Boolean = { true } +): KaType? { require(scope.containingFile is KtFile) ((typeToCheck as? KaTypeParameterType)?.symbol?.psi as? KtTypeParameter)?.let { typeParameter -> val typeParameterListOwner = typeParameter.parentOfType() if (typeParameterListOwner == null || !PsiTreeUtil.isAncestor(typeParameterListOwner, scope, true)) { typeParameters.add(TypeParameter(typeParameter, typeParameter.collectRelevantConstraints())) } - return true + return null } if (typeToCheck is KaClassType) { val classSymbol = typeToCheck.symbol - if ((classSymbol as? KaAnonymousObjectSymbol)?.superTypes?.all { isResolvableInScope(it, scope, typeParameters) } == true) { - return true + val unresolvedInSuperType = (classSymbol as? KaAnonymousObjectSymbol)?.superTypes?.firstNotNullOfOrNull { + getUnResolvableInScope( + it, + scope, + typeParameters, + classAccessibilityChecker + ) + } + if (unresolvedInSuperType != null) { + return unresolvedInSuperType } - if ((classSymbol as? KaClassSymbol)?.classId == null) { + if (classSymbol.classId == null) { //because org.jetbrains.kotlin.fir.FirVisibilityChecker.Default always return true for local classes, //let's be pessimistic here and prohibit local classes completely - return false + return typeToCheck + } + + if (!classAccessibilityChecker(classSymbol)) { + return typeToCheck } val fileSymbol = (scope.containingFile as KtFile).symbol if (!createUseSiteVisibilityChecker(fileSymbol, receiverExpression = null, scope).isVisible(classSymbol)) { - return false + return typeToCheck } - typeToCheck.typeArguments.mapNotNull { it.type }.forEach { - if (!isResolvableInScope(it, scope, typeParameters)) return false + val unresolvedInTypeArguments = typeToCheck.typeArguments.mapNotNull { it.type }.firstNotNullOfOrNull { + getUnResolvableInScope(it, scope, typeParameters, classAccessibilityChecker) + } + if (unresolvedInTypeArguments != null) { + return unresolvedInTypeArguments } } if (typeToCheck is KaErrorType) { - return false + return typeToCheck } if (typeToCheck is KaIntersectionType) { - return false + return typeToCheck } if (typeToCheck is KaDefinitelyNotNullType) { - if (!isResolvableInScope(typeToCheck.original, scope, typeParameters)) return false + val unresolvedOriginal = getUnResolvableInScope(typeToCheck.original, scope, typeParameters, classAccessibilityChecker) + if (unresolvedOriginal != null) { + return unresolvedOriginal + } } - return true + return null }