diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt index 48e49a5c7f2..250ad17d245 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt @@ -354,12 +354,10 @@ fun KtDeclaration.isOverridable(): Boolean { } } -fun KtDeclaration.getModalityFromDescriptor(): KtModifierKeywordToken? { - val descriptor = this.resolveToDescriptorIfAny() +fun KtDeclaration.getModalityFromDescriptor(descriptor: DeclarationDescriptor? = resolveToDescriptorIfAny()): KtModifierKeywordToken? { if (descriptor is MemberDescriptor) { return mapModality(descriptor.modality) } - return null } diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ImplicitNullableNothingTypeInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ImplicitNullableNothingTypeInspection.kt index 18deda12f06..acbcd6ae52e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/ImplicitNullableNothingTypeInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ImplicitNullableNothingTypeInspection.kt @@ -16,21 +16,33 @@ package org.jetbrains.kotlin.idea.inspections +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.core.getModalityFromDescriptor import org.jetbrains.kotlin.idea.intentions.SpecifyTypeExplicitlyIntention +import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.types.typeUtil.isNullableNothing class ImplicitNullableNothingTypeInspection : IntentionBasedInspection( - intention = SpecifyTypeExplicitlyIntention::class, - additionalChecker = { declaration -> - SpecifyTypeExplicitlyIntention.getTypeForDeclaration(declaration).isNullableNothing() && - (declaration.getModalityFromDescriptor() == KtTokens.OPEN_KEYWORD || - declaration is KtProperty && declaration.isVar) - }, - problemText = "Implicit `Nothing?` type" + intention = SpecifyTypeExplicitlyIntention::class, + additionalChecker = { declaration -> declaration.check() }, + problemText = "Implicit `Nothing?` type" ) { override fun inspectionTarget(element: KtCallableDeclaration) = element.nameIdentifier } + +private fun KtCallableDeclaration.check(): Boolean { + if (!SpecifyTypeExplicitlyIntention.getTypeForDeclaration(this).isNullableNothing()) return false + val descriptor = this.resolveToDescriptorIfAny() + return (getModalityFromDescriptor(descriptor) == KtTokens.OPEN_KEYWORD || this is KtProperty && this.isVar) && + !isOverridingNullableNothing(descriptor) +} + +private fun KtCallableDeclaration.isOverridingNullableNothing(descriptor: DeclarationDescriptor?): Boolean { + return hasModifier(KtTokens.OVERRIDE_KEYWORD) && + (descriptor as? CallableMemberDescriptor)?.overriddenDescriptors?.any { it.returnType?.isNullableNothing() == true } == true +} diff --git a/idea/testData/inspectionsLocal/ImplicitNullableNothingType/.inspection b/idea/testData/inspectionsLocal/implicitNullableNothingType/.inspection similarity index 100% rename from idea/testData/inspectionsLocal/ImplicitNullableNothingType/.inspection rename to idea/testData/inspectionsLocal/implicitNullableNothingType/.inspection diff --git a/idea/testData/inspectionsLocal/ImplicitNullableNothingType/final.kt b/idea/testData/inspectionsLocal/implicitNullableNothingType/final.kt similarity index 100% rename from idea/testData/inspectionsLocal/ImplicitNullableNothingType/final.kt rename to idea/testData/inspectionsLocal/implicitNullableNothingType/final.kt diff --git a/idea/testData/inspectionsLocal/ImplicitNullableNothingType/function.kt b/idea/testData/inspectionsLocal/implicitNullableNothingType/function.kt similarity index 100% rename from idea/testData/inspectionsLocal/ImplicitNullableNothingType/function.kt rename to idea/testData/inspectionsLocal/implicitNullableNothingType/function.kt diff --git a/idea/testData/inspectionsLocal/ImplicitNullableNothingType/function.kt.after b/idea/testData/inspectionsLocal/implicitNullableNothingType/function.kt.after similarity index 100% rename from idea/testData/inspectionsLocal/ImplicitNullableNothingType/function.kt.after rename to idea/testData/inspectionsLocal/implicitNullableNothingType/function.kt.after diff --git a/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideFunction.kt b/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideFunction.kt new file mode 100644 index 00000000000..9bc4753d332 --- /dev/null +++ b/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideFunction.kt @@ -0,0 +1,7 @@ +abstract class Parent { + protected abstract fun foo(): String? +} + +class Child : Parent() { + override fun foo() = null +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideFunction.kt.after b/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideFunction.kt.after new file mode 100644 index 00000000000..c441f498dbb --- /dev/null +++ b/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideFunction.kt.after @@ -0,0 +1,7 @@ +abstract class Parent { + protected abstract fun foo(): String? +} + +class Child : Parent() { + override fun foo(): Nothing? = null +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideNullableNothingFunction.kt b/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideNullableNothingFunction.kt new file mode 100644 index 00000000000..1a465c1fcc0 --- /dev/null +++ b/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideNullableNothingFunction.kt @@ -0,0 +1,9 @@ +// PROBLEM: none + +abstract class Parent { + protected abstract fun foo(): Nothing? +} + +class Child : Parent() { + override fun foo() = null +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideNullableNothingProperty.kt b/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideNullableNothingProperty.kt new file mode 100644 index 00000000000..09593f9c51c --- /dev/null +++ b/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideNullableNothingProperty.kt @@ -0,0 +1,9 @@ +// PROBLEM: none + +abstract class Parent { + abstract val foo: Nothing? +} + +class Child : Parent() { + override val foo = null +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideProperty.kt b/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideProperty.kt new file mode 100644 index 00000000000..451148eb1ef --- /dev/null +++ b/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideProperty.kt @@ -0,0 +1,7 @@ +abstract class Parent { + abstract val foo: Int? +} + +class Child : Parent() { + override val foo = null +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideProperty.kt.after b/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideProperty.kt.after new file mode 100644 index 00000000000..7ff97baed14 --- /dev/null +++ b/idea/testData/inspectionsLocal/implicitNullableNothingType/overrideProperty.kt.after @@ -0,0 +1,7 @@ +abstract class Parent { + abstract val foo: Int? +} + +class Child : Parent() { + override val foo: Nothing? = null +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/ImplicitNullableNothingType/top.kt b/idea/testData/inspectionsLocal/implicitNullableNothingType/top.kt similarity index 100% rename from idea/testData/inspectionsLocal/ImplicitNullableNothingType/top.kt rename to idea/testData/inspectionsLocal/implicitNullableNothingType/top.kt diff --git a/idea/testData/inspectionsLocal/ImplicitNullableNothingType/val.kt b/idea/testData/inspectionsLocal/implicitNullableNothingType/val.kt similarity index 100% rename from idea/testData/inspectionsLocal/ImplicitNullableNothingType/val.kt rename to idea/testData/inspectionsLocal/implicitNullableNothingType/val.kt diff --git a/idea/testData/inspectionsLocal/ImplicitNullableNothingType/variable.kt b/idea/testData/inspectionsLocal/implicitNullableNothingType/variable.kt similarity index 100% rename from idea/testData/inspectionsLocal/ImplicitNullableNothingType/variable.kt rename to idea/testData/inspectionsLocal/implicitNullableNothingType/variable.kt diff --git a/idea/testData/inspectionsLocal/ImplicitNullableNothingType/variable.kt.after b/idea/testData/inspectionsLocal/implicitNullableNothingType/variable.kt.after similarity index 100% rename from idea/testData/inspectionsLocal/ImplicitNullableNothingType/variable.kt.after rename to idea/testData/inspectionsLocal/implicitNullableNothingType/variable.kt.after diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index ee975e9721c..af6ecfad32c 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -3271,7 +3271,7 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { } } - @TestMetadata("idea/testData/inspectionsLocal/ImplicitNullableNothingType") + @TestMetadata("idea/testData/inspectionsLocal/implicitNullableNothingType") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class ImplicitNullableNothingType extends AbstractLocalInspectionTest { @@ -3280,32 +3280,52 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { } public void testAllFilesPresentInImplicitNullableNothingType() throws Exception { - KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/ImplicitNullableNothingType"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/implicitNullableNothingType"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); } @TestMetadata("final.kt") public void testFinal() throws Exception { - runTest("idea/testData/inspectionsLocal/ImplicitNullableNothingType/final.kt"); + runTest("idea/testData/inspectionsLocal/implicitNullableNothingType/final.kt"); } @TestMetadata("function.kt") public void testFunction() throws Exception { - runTest("idea/testData/inspectionsLocal/ImplicitNullableNothingType/function.kt"); + runTest("idea/testData/inspectionsLocal/implicitNullableNothingType/function.kt"); + } + + @TestMetadata("overrideFunction.kt") + public void testOverrideFunction() throws Exception { + runTest("idea/testData/inspectionsLocal/implicitNullableNothingType/overrideFunction.kt"); + } + + @TestMetadata("overrideNullableNothingFunction.kt") + public void testOverrideNullableNothingFunction() throws Exception { + runTest("idea/testData/inspectionsLocal/implicitNullableNothingType/overrideNullableNothingFunction.kt"); + } + + @TestMetadata("overrideNullableNothingProperty.kt") + public void testOverrideNullableNothingProperty() throws Exception { + runTest("idea/testData/inspectionsLocal/implicitNullableNothingType/overrideNullableNothingProperty.kt"); + } + + @TestMetadata("overrideProperty.kt") + public void testOverrideProperty() throws Exception { + runTest("idea/testData/inspectionsLocal/implicitNullableNothingType/overrideProperty.kt"); } @TestMetadata("top.kt") public void testTop() throws Exception { - runTest("idea/testData/inspectionsLocal/ImplicitNullableNothingType/top.kt"); + runTest("idea/testData/inspectionsLocal/implicitNullableNothingType/top.kt"); } @TestMetadata("val.kt") public void testVal() throws Exception { - runTest("idea/testData/inspectionsLocal/ImplicitNullableNothingType/val.kt"); + runTest("idea/testData/inspectionsLocal/implicitNullableNothingType/val.kt"); } @TestMetadata("variable.kt") public void testVariable() throws Exception { - runTest("idea/testData/inspectionsLocal/ImplicitNullableNothingType/variable.kt"); + runTest("idea/testData/inspectionsLocal/implicitNullableNothingType/variable.kt"); } }