[Kotlin] Added additional tests for the settings of HasPlatformTypeInspection

KTIJ-22908

GitOrigin-RevId: ceaff50e361cad9e7117ceb1ee891215b56abcc6
This commit is contained in:
Frederik Haselmeier
2024-09-04 12:06:40 +00:00
committed by intellij-monorepo-bot
parent be8cdd32f8
commit 624b012c49
22 changed files with 176 additions and 27 deletions
@@ -1,6 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.k2.codeinsight.inspections
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInspection.IntentionWrapper
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.codeInspection.options.OptPane.checkbox
@@ -39,11 +40,10 @@ class HasPlatformTypeInspection(
}
}
context(KaSession)
private fun KaType.isFlexibleRecursive(): Boolean {
if (hasFlexibleNullability) return true
val classType = this as? KaClassType ?: return false
return classType.typeArguments.any { it !is KaStarTypeProjection && it.type?.isFlexibleRecursive() == true }
private fun KaSession.isFlexibleRecursive(type: KaType): Boolean {
if (type.hasFlexibleNullability) return true
val classType = type as? KaClassType ?: return false
return classType.typeArguments.any { arg -> arg !is KaStarTypeProjection && arg.type?.let { isFlexibleRecursive(it) } == true }
}
private val publicApiVisibilities = setOf(
@@ -51,9 +51,10 @@ class HasPlatformTypeInspection(
KaSymbolVisibility.PROTECTED,
)
context(KaSession)
private fun dangerousFlexibleTypeOrNull(
declaration: KtCallableDeclaration, publicAPIOnly: Boolean, reportPlatformArguments: Boolean
private fun KaSession.dangerousFlexibleTypeOrNull(
declaration: KtCallableDeclaration,
publicAPIOnly: Boolean,
reportPlatformArguments: Boolean
): KaType? {
when (declaration) {
is KtFunction -> if (declaration.isLocal || declaration.hasDeclaredReturnType()) return null
@@ -65,7 +66,7 @@ class HasPlatformTypeInspection(
val type = declaration.returnType
if (type is KaDynamicType) return null
if (reportPlatformArguments) {
if (!type.isFlexibleRecursive()) return null
if (!isFlexibleRecursive(type)) return null
} else {
if (!type.hasFlexibleNullability) return null
}
@@ -73,31 +74,27 @@ class HasPlatformTypeInspection(
return type
}
context(KaSession)
fun checkForPlatformType(element: KtCallableDeclaration, nameIdentifier: PsiElement, holder: ProblemsHolder) {
private fun KaSession.checkForPlatformType(element: KtCallableDeclaration, nameIdentifier: PsiElement, holder: ProblemsHolder) {
val dangerousFlexibleType = dangerousFlexibleTypeOrNull(element, publicAPIOnly, reportPlatformArguments) ?: return
val fixes = mutableListOf<IntentionAction>(
SpecifyExplicitTypeQuickFix(element, CallableReturnTypeUpdaterUtils.getTypeInfo(element))
)
if (dangerousFlexibleType.canBeNull) {
val nonNullableType = dangerousFlexibleType.withNullability(KaTypeNullability.NON_NULLABLE)
val expression = element.node.findChildByType(KtTokens.EQ)?.psi?.getNextSiblingIgnoringWhitespaceAndComments()
if (expression != null &&
(!reportPlatformArguments || !nonNullableType.isFlexibleRecursive())
) {
holder.registerProblem(
nameIdentifier,
KotlinBundle.message(
"declaration.has.type.inferred.from.a.platform.call.which.can.lead.to.unchecked.nullability.issues"
),
IntentionWrapper(AddExclExclCallFix(expression)),
IntentionWrapper(
SpecifyExplicitTypeQuickFix(
element,
CallableReturnTypeUpdaterUtils.getTypeInfo(element)
)
)
)
// Only add this fix if it can actually fully resolve the problem
if (expression != null && (!reportPlatformArguments || !isFlexibleRecursive(nonNullableType))) {
fixes.add(AddExclExclCallFix(expression))
}
}
holder.registerProblem(
nameIdentifier,
KotlinBundle.message("declaration.has.type.inferred.from.a.platform.call.which.can.lead.to.unchecked.nullability.issues"),
*fixes.map { action -> IntentionWrapper(action) }.toTypedArray()
)
}
override fun getOptionsPane() = pane(
@@ -7641,6 +7641,11 @@ public abstract class K2LocalInspectionTestGenerated extends AbstractK2LocalInsp
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("array.kt")
public void testArray() throws Exception {
runTest("testData/inspectionsLocal/hasPlatformType/array.kt");
}
@TestMetadata("extensionOnDynamic.kt")
public void testExtensionOnDynamic() throws Exception {
runTest("testData/inspectionsLocal/hasPlatformType/extensionOnDynamic.kt");
@@ -7656,6 +7661,11 @@ public abstract class K2LocalInspectionTestGenerated extends AbstractK2LocalInsp
runTest("testData/inspectionsLocal/hasPlatformType/functionExclExcl.kt");
}
@TestMetadata("internalFunction.kt")
public void testInternalFunction() throws Exception {
runTest("testData/inspectionsLocal/hasPlatformType/internalFunction.kt");
}
@TestMetadata("localClassFunction.kt")
public void testLocalClassFunction() throws Exception {
runTest("testData/inspectionsLocal/hasPlatformType/localClassFunction.kt");
@@ -7712,6 +7722,65 @@ public abstract class K2LocalInspectionTestGenerated extends AbstractK2LocalInsp
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/inspectionsLocal/hasPlatformTypeWithSettings")
public static class HasPlatformTypeWithSettings extends AbstractK2LocalInspectionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("array.kt")
public void testArray() throws Exception {
runTest("testData/inspectionsLocal/hasPlatformTypeWithSettings/array.kt");
}
@TestMetadata("internalFunction.kt")
public void testInternalFunction() throws Exception {
runTest("testData/inspectionsLocal/hasPlatformTypeWithSettings/internalFunction.kt");
}
@TestMetadata("localFunction.kt")
public void testLocalFunction() throws Exception {
runTest("testData/inspectionsLocal/hasPlatformTypeWithSettings/localFunction.kt");
}
@TestMetadata("nullableArrayNoExclExcl.kt")
public void testNullableArrayNoExclExcl() throws Exception {
runTest("testData/inspectionsLocal/hasPlatformTypeWithSettings/nullableArrayNoExclExcl.kt");
}
@TestMetadata("privateFunction.kt")
public void testPrivateFunction() throws Exception {
runTest("testData/inspectionsLocal/hasPlatformTypeWithSettings/privateFunction.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
runTest("testData/inspectionsLocal/hasPlatformTypeWithSettings/property.kt");
}
@TestMetadata("propertyExclExcl.kt")
public void testPropertyExclExcl() throws Exception {
runTest("testData/inspectionsLocal/hasPlatformTypeWithSettings/propertyExclExcl.kt");
}
@TestMetadata("protectedFunction.kt")
public void testProtectedFunction() throws Exception {
runTest("testData/inspectionsLocal/hasPlatformTypeWithSettings/protectedFunction.kt");
}
@TestMetadata("recursiveGeneric.kt")
public void testRecursiveGeneric() throws Exception {
runTest("testData/inspectionsLocal/hasPlatformTypeWithSettings/recursiveGeneric.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/inspectionsLocal/kotlinOptionsToCompilerOptions")
public static class KotlinOptionsToCompilerOptions extends AbstractK2LocalInspectionTest {
@@ -0,0 +1,4 @@
// WITH_STDLIB
// PROBLEM: none
val test<caret> = arrayOf<java.lang.String.valueOf(1)>
@@ -0,0 +1,4 @@
// WITH_STDLIB
// PROBLEM: none
internal fun foo<caret>() = java.lang.String.valueOf(1)
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.k2.codeinsight.inspections.HasPlatformTypeInspection
@@ -0,0 +1,3 @@
// WITH_STDLIB
val test<caret> = arrayOf(java.lang.String.valueOf(1))
@@ -0,0 +1,3 @@
// WITH_STDLIB
val test: Array<String?> = arrayOf(java.lang.String.valueOf(1))
@@ -0,0 +1,4 @@
// WITH_STDLIB
// FIX: Specify return type explicitly
internal fun foo<caret>() = java.lang.String.valueOf(1)
@@ -0,0 +1,4 @@
// WITH_STDLIB
// FIX: Specify return type explicitly
internal fun foo(): String = java.lang.String.valueOf(1)
@@ -0,0 +1,6 @@
// WITH_STDLIB
// PROBLEM: none
fun foo() {
fun bar<caret>() = java.lang.String.valueOf(2)
}
@@ -0,0 +1,3 @@
// WITH_STDLIB
val test<caret> = arrayOf(java.lang.String.valueOf(1)).takeIf { it.isEmpty() }
@@ -0,0 +1,3 @@
// WITH_STDLIB
val test: Array<String?>? = arrayOf(java.lang.String.valueOf(1)).takeIf { it.isEmpty() }
@@ -0,0 +1,4 @@
// WITH_STDLIB
// FIX: Specify return type explicitly
private fun foo<caret>() = java.lang.String.valueOf(1)
@@ -0,0 +1,4 @@
// WITH_STDLIB
// FIX: Specify return type explicitly
private fun foo(): String = java.lang.String.valueOf(1)
@@ -0,0 +1,4 @@
// WITH_STDLIB
// FIX: Specify type explicitly
val foo<caret> = java.lang.String.valueOf(1)
@@ -0,0 +1,4 @@
// WITH_STDLIB
// FIX: Specify type explicitly
val foo: String = java.lang.String.valueOf(1)
@@ -0,0 +1,4 @@
// WITH_STDLIB
// FIX: Add non-null asserted (java.lang.String.valueOf(1)!!) call
val foo<caret> = java.lang.String.valueOf(1)
@@ -0,0 +1,4 @@
// WITH_STDLIB
// FIX: Add non-null asserted (java.lang.String.valueOf(1)!!) call
val foo = java.lang.String.valueOf(1)!!
@@ -0,0 +1,6 @@
// WITH_STDLIB
// FIX: Specify return type explicitly
open class My {
protected fun foo<caret>() = java.lang.String.valueOf(3)
}
@@ -0,0 +1,6 @@
// WITH_STDLIB
// FIX: Specify return type explicitly
open class My {
protected fun foo(): String = java.lang.String.valueOf(3)
}
@@ -0,0 +1,8 @@
// WITH_STDLIB
// PROBLEM: none
interface Rec<R, out T: Rec<R, T>> {
fun t(): T
}
interface Super {
fun foo<caret>(p: Rec<*, *>) = p.t()
}
@@ -0,0 +1,4 @@
<inspection_tool class="HasPlatformTypeInspection">
<option name="publicAPIOnly" value="false" />
<option name="reportPlatformArguments" value="true" />
</inspection_tool>