Create expect/actual: improve error hint message

#KT-33754 Fixed
This commit is contained in:
Dmitry Gridin
2019-09-06 19:45:19 +07:00
parent 773e4764b1
commit a552f06526
14 changed files with 56 additions and 48 deletions
@@ -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.toDescriptor
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.TypeAccessibilityChecker
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.hasPrivateModifier
import org.jetbrains.kotlin.idea.util.liftToExpected
import org.jetbrains.kotlin.idea.util.module
import org.jetbrains.kotlin.lexer.KtTokens
@@ -118,16 +115,13 @@ class CreateExpectedClassFix(
) : CreateExpectedFix<KtClassOrObject>(klass, outerExpectedClass, commonModule, block@{ project, checker, element ->
val originalElements = element.collectDeclarations(withSelf = false).toList()
val existingClasses = checker.findAndApplyExistingClasses(originalElements + klass)
if (!checker.isCorrectAndHaveNonPrivateModifier(element)) {
showUnknownTypesError(element)
return@block null
}
if (!checker.isCorrectAndHaveNonPrivateModifier(element, true)) return@block null
val (members, declarationsWithNonExistentClasses) = originalElements.partition {
checker.isCorrectAndHaveNonPrivateModifier(it)
}
if (!showUnknownTypesDialog(project, declarationsWithNonExistentClasses)) return@block null
if (!showUnknownTypeInDeclarationDialog(project, declarationsWithNonExistentClasses)) return@block null
val membersForSelection = members.filter {
!it.isAlwaysActual() && if (it is KtParameter) it.hasValOrVar() else true
@@ -144,15 +138,12 @@ class CreateExpectedClassFix(
val selectedClasses = checker.findAndApplyExistingClasses(selectedElements)
val resultDeclarations = if (selectedClasses != existingClasses) {
if (!checker.isCorrectAndHaveNonPrivateModifier(element)) {
showUnknownTypesError(element)
return@block null
}
if (!checker.isCorrectAndHaveNonPrivateModifier(element, true)) return@block null
val (resultDeclarations, withErrors) = selectedElements.partition {
checker.isCorrectAndHaveNonPrivateModifier(it)
}
if (!showUnknownTypesDialog(project, withErrors)) return@block null
if (!showUnknownTypeInDeclarationDialog(project, withErrors)) return@block null
resultDeclarations
} else
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
val message = escapeXml(
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) {
is KtEnumEntry, is KtClassInitializer -> false
is KtParameter -> hasValOrVar()
@@ -300,10 +283,7 @@ class CreateExpectedCallableMemberFix(
targetExpectedClass: KtClassOrObject?,
commonModule: Module
) : CreateExpectedFix<KtNamedDeclaration>(declaration, targetExpectedClass, commonModule, block@{ project, checker, element ->
if (!checker.isCorrectAndHaveNonPrivateModifier(element)) {
showUnknownTypesError(element)
return@block null
}
if (!checker.isCorrectAndHaveNonPrivateModifier(element, true)) return@block null
val descriptor = element.toDescriptor() as? CallableMemberDescriptor
checker.existingTypeNames = targetExpectedClass?.getSuperNames()?.toSet().orEmpty()
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.makeNotActual
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.refactoring.createKotlinFile
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.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.name.FqName
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>()
?.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,5 +1,5 @@
// "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
interface Some
@@ -1,5 +1,5 @@
// "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
class Some
@@ -1,5 +1,5 @@
// "Create expected function in common module testModule_Common" "true"
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,fun foo(some: List&lt;T&gt;) = TODO()
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some
// DISABLE-ERRORS
interface Some
@@ -1,5 +1,5 @@
// "Create expected function in common module testModule_Common" "true"
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,fun foo(some: List&lt;Some&gt;){...}
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some
// DISABLE-ERRORS
class Some
@@ -1,5 +1,5 @@
// "Create expected function in common module testModule_Common" "true"
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,fun foo(some: List&lt;T&gt;){...}
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some
// DISABLE-ERRORS
interface Some
@@ -1,5 +1,5 @@
// "Create expected function in common module testModule_Common" "true"
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,fun foo(some: List&lt;T&gt;){...}
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some
// DISABLE-ERRORS
interface Some
@@ -1,5 +1,5 @@
// "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
interface Some
@@ -1,5 +1,5 @@
// "Create expected property in common module testModule_Common" "true"
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,actual val &lt;T&gt;Some&lt;T&gt;.foo: Some&lt;T&gt; get() = TODO()
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,Some
// DISABLE-ERRORS
class Some<T>
@@ -1,5 +1,5 @@
// "Create expected property in common module testModule_Common" "true"
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,actual val &lt;T: A&gt; Some&lt;T&gt;.foo: Some&lt;T&gt; get() = TODO()
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,A
// DISABLE-ERRORS
interface A
@@ -1,5 +1,5 @@
// "Create expected function in common module testModule_Common" "true"
// SHOULD_FAIL_WITH: You cannot create the expect declaration from:,fun foo() = &quot;&quot;
// SHOULD_FAIL_WITH: Some types are not accessible from testModule_Common:,SomeString
// DISABLE-ERRORS
typealias SomeString = String
@@ -1,5 +1,5 @@
// "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
import java.util.ArrayList
@@ -1,5 +1,5 @@
// "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
import java.util.ArrayList