Create expect/actual: improve error hint message
#KT-33754 Fixed
This commit is contained in:
@@ -24,13 +24,10 @@ import org.jetbrains.kotlin.idea.core.overrideImplement.makeActual
|
|||||||
import org.jetbrains.kotlin.idea.core.overrideImplement.makeNotActual
|
import org.jetbrains.kotlin.idea.core.overrideImplement.makeNotActual
|
||||||
import org.jetbrains.kotlin.idea.core.toDescriptor
|
import org.jetbrains.kotlin.idea.core.toDescriptor
|
||||||
import org.jetbrains.kotlin.idea.core.util.DescriptorMemberChooserObject
|
import org.jetbrains.kotlin.idea.core.util.DescriptorMemberChooserObject
|
||||||
import org.jetbrains.kotlin.idea.inspections.findExistingEditor
|
|
||||||
import org.jetbrains.kotlin.idea.quickfix.KotlinIntentionActionsFactory
|
import org.jetbrains.kotlin.idea.quickfix.KotlinIntentionActionsFactory
|
||||||
import org.jetbrains.kotlin.idea.quickfix.TypeAccessibilityChecker
|
import org.jetbrains.kotlin.idea.quickfix.TypeAccessibilityChecker
|
||||||
import org.jetbrains.kotlin.idea.refactoring.getExpressionShortText
|
import org.jetbrains.kotlin.idea.refactoring.getExpressionShortText
|
||||||
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHint
|
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||||
import org.jetbrains.kotlin.idea.util.hasPrivateModifier
|
|
||||||
import org.jetbrains.kotlin.idea.util.liftToExpected
|
import org.jetbrains.kotlin.idea.util.liftToExpected
|
||||||
import org.jetbrains.kotlin.idea.util.module
|
import org.jetbrains.kotlin.idea.util.module
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
@@ -118,16 +115,13 @@ class CreateExpectedClassFix(
|
|||||||
) : CreateExpectedFix<KtClassOrObject>(klass, outerExpectedClass, commonModule, block@{ project, checker, element ->
|
) : CreateExpectedFix<KtClassOrObject>(klass, outerExpectedClass, commonModule, block@{ project, checker, element ->
|
||||||
val originalElements = element.collectDeclarations(withSelf = false).toList()
|
val originalElements = element.collectDeclarations(withSelf = false).toList()
|
||||||
val existingClasses = checker.findAndApplyExistingClasses(originalElements + klass)
|
val existingClasses = checker.findAndApplyExistingClasses(originalElements + klass)
|
||||||
if (!checker.isCorrectAndHaveNonPrivateModifier(element)) {
|
if (!checker.isCorrectAndHaveNonPrivateModifier(element, true)) return@block null
|
||||||
showUnknownTypesError(element)
|
|
||||||
return@block null
|
|
||||||
}
|
|
||||||
|
|
||||||
val (members, declarationsWithNonExistentClasses) = originalElements.partition {
|
val (members, declarationsWithNonExistentClasses) = originalElements.partition {
|
||||||
checker.isCorrectAndHaveNonPrivateModifier(it)
|
checker.isCorrectAndHaveNonPrivateModifier(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!showUnknownTypesDialog(project, declarationsWithNonExistentClasses)) return@block null
|
if (!showUnknownTypeInDeclarationDialog(project, declarationsWithNonExistentClasses)) return@block null
|
||||||
|
|
||||||
val membersForSelection = members.filter {
|
val membersForSelection = members.filter {
|
||||||
!it.isAlwaysActual() && if (it is KtParameter) it.hasValOrVar() else true
|
!it.isAlwaysActual() && if (it is KtParameter) it.hasValOrVar() else true
|
||||||
@@ -144,15 +138,12 @@ class CreateExpectedClassFix(
|
|||||||
|
|
||||||
val selectedClasses = checker.findAndApplyExistingClasses(selectedElements)
|
val selectedClasses = checker.findAndApplyExistingClasses(selectedElements)
|
||||||
val resultDeclarations = if (selectedClasses != existingClasses) {
|
val resultDeclarations = if (selectedClasses != existingClasses) {
|
||||||
if (!checker.isCorrectAndHaveNonPrivateModifier(element)) {
|
if (!checker.isCorrectAndHaveNonPrivateModifier(element, true)) return@block null
|
||||||
showUnknownTypesError(element)
|
|
||||||
return@block null
|
|
||||||
}
|
|
||||||
|
|
||||||
val (resultDeclarations, withErrors) = selectedElements.partition {
|
val (resultDeclarations, withErrors) = selectedElements.partition {
|
||||||
checker.isCorrectAndHaveNonPrivateModifier(it)
|
checker.isCorrectAndHaveNonPrivateModifier(it)
|
||||||
}
|
}
|
||||||
if (!showUnknownTypesDialog(project, withErrors)) return@block null
|
if (!showUnknownTypeInDeclarationDialog(project, withErrors)) return@block null
|
||||||
resultDeclarations
|
resultDeclarations
|
||||||
} else
|
} else
|
||||||
selectedElements
|
selectedElements
|
||||||
@@ -177,7 +168,10 @@ private fun TypeAccessibilityChecker.findAndApplyExistingClasses(elements: Colle
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showUnknownTypesDialog(project: Project, declarationsWithNonExistentClasses: Collection<KtNamedDeclaration>): Boolean {
|
private fun showUnknownTypeInDeclarationDialog(
|
||||||
|
project: Project,
|
||||||
|
declarationsWithNonExistentClasses: Collection<KtNamedDeclaration>
|
||||||
|
): Boolean {
|
||||||
if (declarationsWithNonExistentClasses.isEmpty()) return true
|
if (declarationsWithNonExistentClasses.isEmpty()) return true
|
||||||
val message = escapeXml(
|
val message = escapeXml(
|
||||||
declarationsWithNonExistentClasses.joinToString(
|
declarationsWithNonExistentClasses.joinToString(
|
||||||
@@ -195,17 +189,6 @@ private fun showUnknownTypesDialog(project: Project, declarationsWithNonExistent
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showUnknownTypesError(element: KtNamedDeclaration) {
|
|
||||||
element.findExistingEditor()?.let { editor ->
|
|
||||||
showErrorHint(
|
|
||||||
element.project,
|
|
||||||
editor,
|
|
||||||
"You cannot create the expect declaration from:\n${escapeXml(getExpressionShortText(element))}",
|
|
||||||
"Unknown types"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KtDeclaration.canAddActualModifier() = when (this) {
|
private fun KtDeclaration.canAddActualModifier() = when (this) {
|
||||||
is KtEnumEntry, is KtClassInitializer -> false
|
is KtEnumEntry, is KtClassInitializer -> false
|
||||||
is KtParameter -> hasValOrVar()
|
is KtParameter -> hasValOrVar()
|
||||||
@@ -300,10 +283,7 @@ class CreateExpectedCallableMemberFix(
|
|||||||
targetExpectedClass: KtClassOrObject?,
|
targetExpectedClass: KtClassOrObject?,
|
||||||
commonModule: Module
|
commonModule: Module
|
||||||
) : CreateExpectedFix<KtNamedDeclaration>(declaration, targetExpectedClass, commonModule, block@{ project, checker, element ->
|
) : CreateExpectedFix<KtNamedDeclaration>(declaration, targetExpectedClass, commonModule, block@{ project, checker, element ->
|
||||||
if (!checker.isCorrectAndHaveNonPrivateModifier(element)) {
|
if (!checker.isCorrectAndHaveNonPrivateModifier(element, true)) return@block null
|
||||||
showUnknownTypesError(element)
|
|
||||||
return@block null
|
|
||||||
}
|
|
||||||
val descriptor = element.toDescriptor() as? CallableMemberDescriptor
|
val descriptor = element.toDescriptor() as? CallableMemberDescriptor
|
||||||
checker.existingTypeNames = targetExpectedClass?.getSuperNames()?.toSet().orEmpty()
|
checker.existingTypeNames = targetExpectedClass?.getSuperNames()?.toSet().orEmpty()
|
||||||
descriptor?.let {
|
descriptor?.let {
|
||||||
@@ -317,6 +297,3 @@ class CreateExpectedCallableMemberFix(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
private fun TypeAccessibilityChecker.isCorrectAndHaveNonPrivateModifier(declaration: KtNamedDeclaration): Boolean =
|
|
||||||
!declaration.hasPrivateModifier() && checkAccessibility(declaration)
|
|
||||||
@@ -24,14 +24,13 @@ import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObj
|
|||||||
import org.jetbrains.kotlin.idea.core.overrideImplement.generateMember
|
import org.jetbrains.kotlin.idea.core.overrideImplement.generateMember
|
||||||
import org.jetbrains.kotlin.idea.core.overrideImplement.makeNotActual
|
import org.jetbrains.kotlin.idea.core.overrideImplement.makeNotActual
|
||||||
import org.jetbrains.kotlin.idea.core.toDescriptor
|
import org.jetbrains.kotlin.idea.core.toDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.inspections.findExistingEditor
|
||||||
import org.jetbrains.kotlin.idea.quickfix.TypeAccessibilityChecker
|
import org.jetbrains.kotlin.idea.quickfix.TypeAccessibilityChecker
|
||||||
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
|
import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
|
||||||
import org.jetbrains.kotlin.idea.refactoring.fqName.fqName
|
import org.jetbrains.kotlin.idea.refactoring.fqName.fqName
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
import org.jetbrains.kotlin.idea.refactoring.introduce.showErrorHint
|
||||||
|
import org.jetbrains.kotlin.idea.util.*
|
||||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.idea.util.hasInlineModifier
|
|
||||||
import org.jetbrains.kotlin.idea.util.isEffectivelyActual
|
|
||||||
import org.jetbrains.kotlin.idea.util.mustHaveValOrVar
|
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -379,3 +378,35 @@ class KotlinTypeInaccessibleException(fqNames: Collection<FqName?>) : Exception(
|
|||||||
|
|
||||||
fun KtNamedDeclaration.isAlwaysActual(): Boolean = safeAs<KtParameter>()?.parent?.parent?.safeAs<KtPrimaryConstructor>()
|
fun KtNamedDeclaration.isAlwaysActual(): Boolean = safeAs<KtParameter>()?.parent?.parent?.safeAs<KtPrimaryConstructor>()
|
||||||
?.mustHaveValOrVar() ?: false
|
?.mustHaveValOrVar() ?: false
|
||||||
|
|
||||||
|
|
||||||
|
fun TypeAccessibilityChecker.isCorrectAndHaveNonPrivateModifier(declaration: KtNamedDeclaration, showErrorHint: Boolean = false): Boolean {
|
||||||
|
if (declaration.hasPrivateModifier()) {
|
||||||
|
if (showErrorHint) showInaccessibleDeclarationError(declaration, "The declaration has a private modifier")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!showErrorHint) return checkAccessibility(declaration)
|
||||||
|
|
||||||
|
val types = incorrectTypes(declaration).ifEmpty { return true }
|
||||||
|
showInaccessibleDeclarationError(
|
||||||
|
declaration,
|
||||||
|
"Some types are not accessible from ${targetModule.name}:\n" + TypeAccessibilityChecker.typesToString(
|
||||||
|
types
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showInaccessibleDeclarationError(element: KtNamedDeclaration, message: String) {
|
||||||
|
element.findExistingEditor()?.let { editor ->
|
||||||
|
showErrorHint(element.project, editor, message, "Inaccessible declaration")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TypeAccessibilityChecker.Companion.typesToString(types: Collection<FqName?>, separator: CharSequence = "\n"): String {
|
||||||
|
return types.toSet().joinToString(separator = separator) {
|
||||||
|
it?.shortName()?.asString() ?: "Unknown type"
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// "Create expected class in common module testModule_Common" "true"
|
// "Create expected class in common module testModule_Common" "true"
|
||||||
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,class A
|
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some,A
|
||||||
// DISABLE-ERRORS
|
// DISABLE-ERRORS
|
||||||
|
|
||||||
interface Some
|
interface Some
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// "Create expected function in common module testModule_Common" "true"
|
// "Create expected function in common module testModule_Common" "true"
|
||||||
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,fun foo(some: Some){...}
|
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some
|
||||||
// DISABLE-ERRORS
|
// DISABLE-ERRORS
|
||||||
|
|
||||||
class Some
|
class Some
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// "Create expected function in common module testModule_Common" "true"
|
// "Create expected function in common module testModule_Common" "true"
|
||||||
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,fun foo(some: List<T>) = TODO()
|
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some
|
||||||
// DISABLE-ERRORS
|
// DISABLE-ERRORS
|
||||||
|
|
||||||
interface Some
|
interface Some
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// "Create expected function in common module testModule_Common" "true"
|
// "Create expected function in common module testModule_Common" "true"
|
||||||
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,fun foo(some: List<Some>){...}
|
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some
|
||||||
// DISABLE-ERRORS
|
// DISABLE-ERRORS
|
||||||
|
|
||||||
class Some
|
class Some
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// "Create expected function in common module testModule_Common" "true"
|
// "Create expected function in common module testModule_Common" "true"
|
||||||
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,fun foo(some: List<T>){...}
|
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some
|
||||||
// DISABLE-ERRORS
|
// DISABLE-ERRORS
|
||||||
|
|
||||||
interface Some
|
interface Some
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// "Create expected function in common module testModule_Common" "true"
|
// "Create expected function in common module testModule_Common" "true"
|
||||||
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,fun foo(some: List<T>){...}
|
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some
|
||||||
// DISABLE-ERRORS
|
// DISABLE-ERRORS
|
||||||
|
|
||||||
interface Some
|
interface Some
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// "Create expected property in common module testModule_Common" "true"
|
// "Create expected property in common module testModule_Common" "true"
|
||||||
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,actual val foo: Some = TODO()
|
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some
|
||||||
// DISABLE-ERRORS
|
// DISABLE-ERRORS
|
||||||
|
|
||||||
interface Some
|
interface Some
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// "Create expected property in common module testModule_Common" "true"
|
// "Create expected property in common module testModule_Common" "true"
|
||||||
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,actual val <T>Some<T>.foo: Some<T> get() = TODO()
|
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some
|
||||||
// DISABLE-ERRORS
|
// DISABLE-ERRORS
|
||||||
|
|
||||||
class Some<T>
|
class Some<T>
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// "Create expected property in common module testModule_Common" "true"
|
// "Create expected property in common module testModule_Common" "true"
|
||||||
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,actual val <T: A> Some<T>.foo: Some<T> get() = TODO()
|
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,A
|
||||||
// DISABLE-ERRORS
|
// DISABLE-ERRORS
|
||||||
interface A
|
interface A
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// "Create expected function in common module testModule_Common" "true"
|
// "Create expected function in common module testModule_Common" "true"
|
||||||
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,fun foo() = ""
|
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,SomeString
|
||||||
// DISABLE-ERRORS
|
// DISABLE-ERRORS
|
||||||
|
|
||||||
typealias SomeString = String
|
typealias SomeString = String
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// "Create expected function in common module proj_Common" "true"
|
// "Create expected function in common module proj_Common" "true"
|
||||||
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,fun createList() = ArrayList()
|
// SHOULD_FAIL_WITH: Some types are not accessible from proj_Common:,ArrayList
|
||||||
// DISABLE-ERRORS
|
// DISABLE-ERRORS
|
||||||
|
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// "Create expected function in common module proj_Common" "true"
|
// "Create expected function in common module proj_Common" "true"
|
||||||
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,fun createList() = ArrayList()
|
// SHOULD_FAIL_WITH: Some types are not accessible from proj_Common:,ArrayList
|
||||||
// DISABLE-ERRORS
|
// DISABLE-ERRORS
|
||||||
|
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
|
|||||||
Reference in New Issue
Block a user