diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceAssertBooleanWithAssertEqualityInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceAssertBooleanWithAssertEqualityInspection.kt index da93b7e26d2..e4ae91a8b6c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceAssertBooleanWithAssertEqualityInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceAssertBooleanWithAssertEqualityInspection.kt @@ -9,16 +9,23 @@ import com.intellij.codeInsight.actions.OptimizeImportsProcessor import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.core.replaced -import org.jetbrains.kotlin.idea.intentions.getCallableDescriptor import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf -class ReplaceAssertBooleanWithAssertEqualityInspection : AbstractApplicabilityBasedInspection(KtCallExpression::class.java) { - +class ReplaceAssertBooleanWithAssertEqualityInspection : AbstractApplicabilityBasedInspection( + KtCallExpression::class.java +) { override fun inspectionText(element: KtCallExpression) = "Replace assert boolean with assert equality" override val defaultFixText = "Replace assert boolean with assert equality" @@ -58,16 +65,28 @@ class ReplaceAssertBooleanWithAssertEqualityInspection : AbstractApplicabilityBa return null } - if (getCallableDescriptor()?.containingDeclaration?.fqNameSafe != FqName(kotlinTestPackage)) { + val context = analyze(BodyResolveMode.PARTIAL) + if (descriptor(context)?.containingDeclaration?.fqNameSafe != FqName(kotlinTestPackage)) { return null } if (valueArguments.size != 1 && valueArguments.size != 2) return null val binaryExpression = valueArguments.first().getArgumentExpression() as? KtBinaryExpression ?: return null + val leftType = binaryExpression.left?.type(context) ?: return null + val rightType = binaryExpression.right?.type(context) ?: return null + if (!leftType.isSubtypeOf(rightType) && !rightType.isSubtypeOf(leftType)) return null val operationToken = binaryExpression.operationToken return assertionMap[Pair(referencedName, operationToken)] } + + private fun KtExpression.descriptor(context: BindingContext): CallableDescriptor? { + return getResolvedCall(context)?.resultingDescriptor + } + + private fun KtExpression.type(context: BindingContext): KotlinType? { + return descriptor(context)?.returnType + } companion object { private const val kotlinTestPackage = "kotlin.test" diff --git a/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/notSubtype.kt b/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/notSubtype.kt new file mode 100644 index 00000000000..216f2dc5c54 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/notSubtype.kt @@ -0,0 +1,12 @@ +// RUNTIME_WITH_KOTLIN_TEST +// PROBLEM: none + +import kotlin.test.assertTrue + +interface Parent + +interface Child : Parent + +fun test(p: Parent, c: Child?) { + assertTrue(p == c) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/notSubtype2.kt b/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/notSubtype2.kt new file mode 100644 index 00000000000..437a225be22 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/notSubtype2.kt @@ -0,0 +1,12 @@ +// RUNTIME_WITH_KOTLIN_TEST +// PROBLEM: none + +import kotlin.test.assertTrue + +interface Parent + +interface Child : Parent + +fun test(p: Parent, c: Child?) { + assertTrue(c === p) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/subtype.kt b/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/subtype.kt new file mode 100644 index 00000000000..2120fd42e90 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/subtype.kt @@ -0,0 +1,11 @@ +// RUNTIME_WITH_KOTLIN_TEST + +import kotlin.test.assertTrue + +interface Parent + +interface Child : Parent + +fun test(p: Parent?, c: Child) { + assertTrue(p == c) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/subtype.kt.after b/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/subtype.kt.after new file mode 100644 index 00000000000..411daefc694 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/subtype.kt.after @@ -0,0 +1,11 @@ +// RUNTIME_WITH_KOTLIN_TEST + +import kotlin.test.assertEquals + +interface Parent + +interface Child : Parent + +fun test(p: Parent?, c: Child) { + assertEquals(p, c) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/subtype2.kt b/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/subtype2.kt new file mode 100644 index 00000000000..6418b2fce54 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/subtype2.kt @@ -0,0 +1,11 @@ +// RUNTIME_WITH_KOTLIN_TEST + +import kotlin.test.assertTrue + +interface Parent + +interface Child : Parent + +fun test(p: Parent?, c: Child) { + assertTrue(c === p) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/subtype2.kt.after b/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/subtype2.kt.after new file mode 100644 index 00000000000..82d48e0bff5 --- /dev/null +++ b/idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/subtype2.kt.after @@ -0,0 +1,11 @@ +// RUNTIME_WITH_KOTLIN_TEST + +import kotlin.test.assertSame + +interface Parent + +interface Child : Parent + +fun test(p: Parent?, c: Child) { + assertSame(c, p) +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 31db75ce9ca..ddf3fc1b84a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -6845,6 +6845,26 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { public void testAssertTrueWithMessage() throws Exception { runTest("idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/assertTrueWithMessage.kt"); } + + @TestMetadata("notSubtype.kt") + public void testNotSubtype() throws Exception { + runTest("idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/notSubtype.kt"); + } + + @TestMetadata("notSubtype2.kt") + public void testNotSubtype2() throws Exception { + runTest("idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/notSubtype2.kt"); + } + + @TestMetadata("subtype.kt") + public void testSubtype() throws Exception { + runTest("idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/subtype.kt"); + } + + @TestMetadata("subtype2.kt") + public void testSubtype2() throws Exception { + runTest("idea/testData/inspectionsLocal/replaceAssertBooleanWithAssertEquality/subtype2.kt"); + } } @TestMetadata("idea/testData/inspectionsLocal/replaceAssociateFunction")