[kotlin] Added quick fixes for warnings introduced in 1.6.20 or earlier

Registered quick fixes for warnings that are mostly related to Java
nullability mismatch.

^KTIJ-20827 Fixed

(cherry picked from commit 7d5bbcd3aa9b16a6a60bab0f1e3f7978622b7c49)

GitOrigin-RevId: c79a1e985970792a932371e7c1b57354fa2575d5
This commit is contained in:
Dmitry Timofeev
2022-02-08 08:52:56 +00:00
committed by intellij-monorepo-bot
parent 5e91e7fb01
commit 3f52503ac3
42 changed files with 1640 additions and 17 deletions
@@ -55,6 +55,7 @@ fun getAddExclExclCallFix(element: PsiElement?, checkImplicitReceivers: Boolean
is KtExpression -> {
val parent = psiElement.parent
val context = psiElement.analyze()
if (checkImplicitReceivers && psiElement.getResolvedCall(context)?.getImplicitReceiverValue() is ExtensionReceiver) {
val expressionToReplace = parent as? KtCallExpression ?: parent as? KtCallableReferenceExpression ?: psiElement
expressionToReplace.asFix(implicitReceiver = true)
@@ -247,6 +247,7 @@ class AddFunctionParametersFix(
val element = DiagnosticFactory.cast(
this,
Errors.TYPE_MISMATCH,
Errors.TYPE_MISMATCH_WARNING,
Errors.CONSTANT_EXPECTED_TYPE_MISMATCH,
Errors.NULL_FOR_NONNULL_TYPE,
).psiElement
@@ -73,6 +73,10 @@ class AddGenericUpperBoundFix(
val inferenceData = Errors.TYPE_INFERENCE_UPPER_BOUND_VIOLATED.cast(diagnostic).a
createActionsByInferenceData(inferenceData)
}
ErrorsJvm.UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS -> {
val upperBoundViolated = ErrorsJvm.UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS.cast(diagnostic)
listOfNotNull(createAction(upperBoundViolated.b, upperBoundViolated.a))
}
else -> emptyList()
}
}
@@ -69,6 +69,7 @@ class ChangeToLabeledReturnFix(
Errors.RETURN_NOT_ALLOWED ->
diagnostic.psiElement as? KtReturnExpression
Errors.TYPE_MISMATCH,
Errors.TYPE_MISMATCH_WARNING,
Errors.CONSTANT_EXPECTED_TYPE_MISMATCH,
Errors.NULL_FOR_NONNULL_TYPE ->
getLambdaReturnExpression(diagnostic.psiElement, context)
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.ErrorUtils
import org.jetbrains.kotlin.types.KotlinType
@@ -166,7 +167,14 @@ open class ChangeVariableTypeFix(element: KtCallableDeclaration, type: KotlinTyp
if (binaryExpression.operationToken != KtTokens.EQ) return null
val property = left.mainReference?.resolve() as? KtProperty ?: return null
if (!property.isVar || property.typeReference != null || !property.initializer.isNullExpression()) return null
return ChangeVariableTypeFix(property, Errors.TYPE_MISMATCH.cast(diagnostic).b.makeNullable())
val actualType = when (diagnostic.factory) {
Errors.TYPE_MISMATCH -> Errors.TYPE_MISMATCH.cast(diagnostic).b
Errors.TYPE_MISMATCH_WARNING -> Errors.TYPE_MISMATCH_WARNING.cast(diagnostic).b
ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS ->
ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.cast(diagnostic).b
else -> null
} ?: return null
return ChangeVariableTypeFix(property, actualType.makeNullable())
}
}
}
@@ -5,12 +5,10 @@ package org.jetbrains.kotlin.idea.quickfix
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.diagnostic.Logger
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.backend.jvm.ir.psiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.isKFunctionType
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
@@ -37,6 +35,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.constants.IntegerValueTypeConstant
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.types.CommonSupertypes
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
@@ -69,6 +68,11 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
expectedType = diagnosticWithParameters.a
expressionType = diagnosticWithParameters.b
}
Errors.TYPE_MISMATCH_WARNING -> {
val diagnosticWithParameters = Errors.TYPE_MISMATCH_WARNING.cast(diagnostic)
expectedType = diagnosticWithParameters.a
expressionType = diagnosticWithParameters.b
}
Errors.NULL_FOR_NONNULL_TYPE -> {
val diagnosticWithParameters = Errors.NULL_FOR_NONNULL_TYPE.cast(diagnostic)
expectedType = diagnosticWithParameters.a
@@ -96,6 +100,11 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
expressionType = signedConstantValue.getType(NO_EXPECTED_TYPE)
} else return emptyList()
}
ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS -> {
val diagnosticWithParameters = ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.cast(diagnostic)
expectedType = diagnosticWithParameters.a
expressionType = diagnosticWithParameters.b
}
else -> {
LOG.error("Unexpected diagnostic: " + DefaultErrorMessages.render(diagnostic))
return emptyList()
@@ -165,7 +174,13 @@ class QuickFixFactoryForTypeMismatchError : KotlinIntentionActionsFactory() {
val nullableExpected = expectedType.makeNullable()
if (expressionType.isSubtypeOf(nullableExpected)) {
val targetExpression = diagnosticElement.getTopMostQualifiedForSelectorIfAny()
getAddExclExclCallFix(targetExpression)?.let { actions.add(it) }
// With implicit receivers (e.g., inside a scope function),
// NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS is reported on the callee, so we need
// to explicitly check for nullable implicit receiver
val checkCalleeExpression =
diagnostic.factory == ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS &&
targetExpression.parent?.safeAs<KtCallExpression>()?.calleeExpression == targetExpression
getAddExclExclCallFix(targetExpression, checkCalleeExpression)?.let { actions.add(it) }
if (expectedType.isBoolean()) {
actions.add(AddEqEqTrueFix(targetExpression))
}
@@ -163,6 +163,7 @@ class QuickFixRegistrar : QuickFixContributor {
TOO_MANY_ARGUMENTS.registerFactory(ImportForMismatchingArgumentsFix)
NO_VALUE_FOR_PARAMETER.registerFactory(ImportForMismatchingArgumentsFix)
TYPE_MISMATCH.registerFactory(ImportForMismatchingArgumentsFix)
TYPE_MISMATCH_WARNING.registerFactory(ImportForMismatchingArgumentsFix)
CONSTANT_EXPECTED_TYPE_MISMATCH.registerFactory(ImportForMismatchingArgumentsFix)
NAMED_PARAMETER_NOT_FOUND.registerFactory(ImportForMismatchingArgumentsFix)
NONE_APPLICABLE.registerFactory(ImportForMismatchingArgumentsFix)
@@ -245,6 +246,8 @@ class QuickFixRegistrar : QuickFixContributor {
UNNECESSARY_SAFE_CALL.registerFactory(Fe10ReplaceWithDotCallFixFactory)
UNSAFE_CALL.registerFactory(ReplaceWithSafeCallFixFactory)
SAFE_CALL_WILL_CHANGE_NULLABILITY.registerFactory(Fe10ReplaceWithDotCallFixFactory)
RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.registerFactory(ReplaceWithSafeCallFixFactory)
NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.registerFactory(ReplaceWithSafeCallFixFactory)
UNSAFE_CALL.registerFactory(SurroundWithNullCheckFix)
UNSAFE_IMPLICIT_INVOKE_CALL.registerFactory(SurroundWithNullCheckFix)
@@ -252,12 +255,18 @@ class QuickFixRegistrar : QuickFixContributor {
UNSAFE_OPERATOR_CALL.registerFactory(SurroundWithNullCheckFix)
ITERATOR_ON_NULLABLE.registerFactory(SurroundWithNullCheckFix.IteratorOnNullableFactory)
TYPE_MISMATCH.registerFactory(SurroundWithNullCheckFix.TypeMismatchFactory)
TYPE_MISMATCH_WARNING.registerFactory(SurroundWithNullCheckFix.TypeMismatchFactory)
RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.registerFactory(SurroundWithNullCheckFix)
NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.registerFactory(SurroundWithNullCheckFix.TypeMismatchFactory)
UNSAFE_CALL.registerFactory(WrapWithSafeLetCallFix.UnsafeFactory)
UNSAFE_IMPLICIT_INVOKE_CALL.registerFactory(WrapWithSafeLetCallFix.UnsafeFactory)
UNSAFE_INFIX_CALL.registerFactory(WrapWithSafeLetCallFix.UnsafeFactory)
UNSAFE_OPERATOR_CALL.registerFactory(WrapWithSafeLetCallFix.UnsafeFactory)
TYPE_MISMATCH.registerFactory(WrapWithSafeLetCallFix.TypeMismatchFactory)
TYPE_MISMATCH_WARNING.registerFactory(WrapWithSafeLetCallFix.TypeMismatchFactory)
RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.registerFactory(WrapWithSafeLetCallFix.UnsafeFactory)
NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.registerFactory(WrapWithSafeLetCallFix.TypeMismatchFactory)
UNSAFE_CALL.registerFactory(UnsafeCallExclExclFixFactory)
UNSAFE_INFIX_CALL.registerFactory(UnsafeCallExclExclFixFactory)
@@ -268,6 +277,9 @@ class QuickFixRegistrar : QuickFixContributor {
UNSAFE_CALL.registerFactory(ReplaceInfixOrOperatorCallFixFactory) // [] only
UNSAFE_IMPLICIT_INVOKE_CALL.registerFactory(ReplaceInfixOrOperatorCallFixFactory)
UNSAFE_CALL.registerFactory(ReplaceWithSafeCallForScopeFunctionFixFactory)
RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.registerFactory(UnsafeCallExclExclFixFactory)
RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.registerFactory(ReplaceInfixOrOperatorCallFixFactory)
RECEIVER_NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.registerFactory(ReplaceWithSafeCallForScopeFunctionFixFactory)
AMBIGUOUS_ANONYMOUS_TYPE_INFERRED.registerActions(SpecifyTypeExplicitlyFix())
PROPERTY_WITH_NO_TYPE_NO_INITIALIZER.registerActions(SpecifyTypeExplicitlyFix())
@@ -315,6 +327,7 @@ class QuickFixRegistrar : QuickFixContributor {
VAR_TYPE_MISMATCH_ON_OVERRIDE.registerFactory(changeVariableTypeFix)
COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH.registerFactory(ChangeVariableTypeFix.ComponentFunctionReturnTypeMismatchFactory)
TYPE_MISMATCH.registerFactory(ChangeVariableTypeFix.VariableInitializedWithNullFactory)
TYPE_MISMATCH_WARNING.registerFactory(ChangeVariableTypeFix.VariableInitializedWithNullFactory)
val changeFunctionReturnTypeFix = ChangeCallableReturnTypeFix.ChangingReturnTypeToUnitFactory
RETURN_TYPE_MISMATCH.registerFactory(changeFunctionReturnTypeFix)
@@ -334,6 +347,7 @@ class QuickFixRegistrar : QuickFixContributor {
NO_VALUE_FOR_PARAMETER.registerFactory(ChangeFunctionSignatureFix)
TYPE_MISMATCH.registerFactory(AddFunctionParametersFix)
TYPE_MISMATCH_WARNING.registerFactory(AddFunctionParametersFix)
CONSTANT_EXPECTED_TYPE_MISMATCH.registerFactory(AddFunctionParametersFix)
NULL_FOR_NONNULL_TYPE.registerFactory(AddFunctionParametersFix)
@@ -359,6 +373,7 @@ class QuickFixRegistrar : QuickFixContributor {
NO_VALUE_FOR_PARAMETER.registerFactory(CreateBinaryOperationActionFactory)
TOO_MANY_ARGUMENTS.registerFactory(CreateBinaryOperationActionFactory)
TYPE_MISMATCH_ERRORS.forEach { it.registerFactory(CreateBinaryOperationActionFactory) }
TYPE_MISMATCH_WARNING.registerFactory(CreateBinaryOperationActionFactory)
UNRESOLVED_REFERENCE_WRONG_RECEIVER.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES)
UNRESOLVED_REFERENCE.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES)
@@ -368,6 +383,7 @@ class QuickFixRegistrar : QuickFixContributor {
EXPRESSION_EXPECTED_PACKAGE_FOUND.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES)
NONE_APPLICABLE.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES)
TYPE_MISMATCH.registerFactory(*CreateCallableFromCallActionFactory.FUNCTIONS)
TYPE_MISMATCH_WARNING.registerFactory(*CreateCallableFromCallActionFactory.FUNCTIONS)
NO_VALUE_FOR_PARAMETER.registerFactory(CreateConstructorFromDelegationCallActionFactory)
TOO_MANY_ARGUMENTS.registerFactory(CreateConstructorFromDelegationCallActionFactory)
@@ -393,10 +409,12 @@ class QuickFixRegistrar : QuickFixContributor {
val factoryForTypeMismatchError = QuickFixFactoryForTypeMismatchError()
TYPE_MISMATCH.registerFactory(factoryForTypeMismatchError)
TYPE_MISMATCH_WARNING.registerFactory(factoryForTypeMismatchError)
NULL_FOR_NONNULL_TYPE.registerFactory(factoryForTypeMismatchError)
CONSTANT_EXPECTED_TYPE_MISMATCH.registerFactory(factoryForTypeMismatchError)
TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH.registerFactory(factoryForTypeMismatchError)
SIGNED_CONSTANT_CONVERTED_TO_UNSIGNED.registerFactory(factoryForTypeMismatchError)
NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.registerFactory(factoryForTypeMismatchError)
SMARTCAST_IMPOSSIBLE.registerFactory(SmartCastImpossibleExclExclFixFactory)
SMARTCAST_IMPOSSIBLE.registerFactory(CastExpressionFix.SmartCastImpossibleFactory)
@@ -408,8 +426,10 @@ class QuickFixRegistrar : QuickFixContributor {
NO_GET_METHOD.registerFactory(CreateGetFunctionActionFactory)
TYPE_MISMATCH_ERRORS.forEach { it.registerFactory(CreateGetFunctionActionFactory) }
TYPE_MISMATCH_WARNING.registerFactory(CreateGetFunctionActionFactory)
NO_SET_METHOD.registerFactory(CreateSetFunctionActionFactory)
TYPE_MISMATCH_ERRORS.forEach { it.registerFactory(CreateSetFunctionActionFactory) }
TYPE_MISMATCH_WARNING.registerFactory(CreateSetFunctionActionFactory)
HAS_NEXT_MISSING.registerFactory(CreateHasNextFunctionActionFactory)
HAS_NEXT_FUNCTION_NONE_APPLICABLE.registerFactory(CreateHasNextFunctionActionFactory)
NEXT_MISSING.registerFactory(CreateNextFunctionActionFactory)
@@ -476,6 +496,7 @@ class QuickFixRegistrar : QuickFixContributor {
UPPER_BOUND_VIOLATED.registerFactory(AddGenericUpperBoundFix.Factory)
TYPE_INFERENCE_UPPER_BOUND_VIOLATED.registerFactory(AddGenericUpperBoundFix.Factory)
UPPER_BOUND_VIOLATED_BASED_ON_JAVA_ANNOTATIONS.registerFactory(AddGenericUpperBoundFix.Factory)
TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH.registerFactory(ConvertClassToKClassFix)
@@ -583,6 +604,7 @@ class QuickFixRegistrar : QuickFixContributor {
RETURN_NOT_ALLOWED.registerFactory(ChangeToLabeledReturnFix)
TYPE_MISMATCH.registerFactory(ChangeToLabeledReturnFix)
TYPE_MISMATCH_WARNING.registerFactory(ChangeToLabeledReturnFix)
CONSTANT_EXPECTED_TYPE_MISMATCH.registerFactory(ChangeToLabeledReturnFix)
NULL_FOR_NONNULL_TYPE.registerFactory(ChangeToLabeledReturnFix)
@@ -637,6 +659,7 @@ class QuickFixRegistrar : QuickFixContributor {
ASSIGN_OPERATOR_AMBIGUITY.registerFactory(AssignOperatorAmbiguityFactory)
TYPE_MISMATCH.registerFactory(SurroundWithLambdaFix)
TYPE_MISMATCH_WARNING.registerFactory(SurroundWithLambdaFix)
CONSTANT_EXPECTED_TYPE_MISMATCH.registerFactory(SurroundWithLambdaFix)
NO_SET_METHOD.registerFactory(ChangeToMutableCollectionFix)
@@ -708,5 +731,7 @@ class QuickFixRegistrar : QuickFixContributor {
WRONG_NULLABILITY_FOR_JAVA_OVERRIDE.registerFactory(MakeUpperBoundNonNullableFix)
TYPE_MISMATCH.registerFactory(MakeUpperBoundNonNullableFix)
NOTHING_TO_OVERRIDE.registerFactory(MakeUpperBoundNonNullableFix)
WRONG_NULLABILITY_FOR_JAVA_OVERRIDE.registerFactory(ChangeMemberFunctionSignatureFix)
}
}
@@ -3,6 +3,7 @@
package org.jetbrains.kotlin.idea.quickfix
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
@@ -15,13 +16,19 @@ import org.jetbrains.kotlin.psi.KtQualifiedExpression
import org.jetbrains.kotlin.resolve.calls.util.getImplicitReceiverValue
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
object ReplaceInfixOrOperatorCallFixFactory : KotlinSingleIntentionActionFactory() {
private fun findArrayAccessExpression(expression: PsiElement): KtArrayAccessExpression? {
return expression.safeAs<KtArrayAccessExpression>() ?: expression.parent?.safeAs<KtArrayAccessExpression>()
}
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val expression = diagnostic.psiElement
if (expression is KtArrayAccessExpression && diagnostic.factory != Errors.UNSAFE_IMPLICIT_INVOKE_CALL) {
if (expression.arrayExpression == null) return null
return ReplaceInfixOrOperatorCallFix(expression, expression.shouldHaveNotNullType())
val arrayAccessExpression = findArrayAccessExpression(expression)
if (arrayAccessExpression != null && diagnostic.factory != Errors.UNSAFE_IMPLICIT_INVOKE_CALL) {
if (arrayAccessExpression.arrayExpression == null) return null
return ReplaceInfixOrOperatorCallFix(arrayAccessExpression, arrayAccessExpression.shouldHaveNotNullType())
}
when (val parent = expression.parent) {
@@ -51,6 +51,11 @@ class SurroundWithLambdaFix(
expectedType = diagnosticWithParameters.a
expressionType = diagnosticWithParameters.b
}
Errors.TYPE_MISMATCH_WARNING -> {
val diagnosticWithParameters = Errors.TYPE_MISMATCH_WARNING.cast(diagnostic)
expectedType = diagnosticWithParameters.a
expressionType = diagnosticWithParameters.b
}
Errors.CONSTANT_EXPECTED_TYPE_MISMATCH -> {
val context = (diagnostic.psiFile as KtFile).analyzeWithContent()
val diagnosticWithParameters = Errors.CONSTANT_EXPECTED_TYPE_MISMATCH.cast(diagnostic)
@@ -22,8 +22,10 @@ import org.jetbrains.kotlin.psi.psiUtil.getLastParentOfTypeInRow
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypesAndPredicate
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.utils.findVariable
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isNullabilityMismatch
class SurroundWithNullCheckFix(
@@ -98,10 +100,28 @@ class SurroundWithNullCheckFix(
}
object TypeMismatchFactory : KotlinSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val typeMismatch = Errors.TYPE_MISMATCH.cast(diagnostic)
val nullableExpression = typeMismatch.psiElement as? KtReferenceExpression ?: return null
val nullableExpression = diagnostic.psiElement as? KtReferenceExpression ?: return null
val expectedType: KotlinType
val actualType: KotlinType
when (diagnostic.factory) {
Errors.TYPE_MISMATCH -> {
val diagnosticWithParameters = Errors.TYPE_MISMATCH.cast(diagnostic)
expectedType = diagnosticWithParameters.a
actualType = diagnosticWithParameters.b
}
Errors.TYPE_MISMATCH_WARNING -> {
val diagnosticWithParameters = Errors.TYPE_MISMATCH_WARNING.cast(diagnostic)
expectedType = diagnosticWithParameters.a
actualType = diagnosticWithParameters.b
}
ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS -> {
val diagnosticWithParameters = ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.cast(diagnostic)
expectedType = diagnosticWithParameters.a
actualType = diagnosticWithParameters.b
}
else -> return null
}
val root = when (val parent = nullableExpression.parent) {
is KtValueArgument -> {
val call = parent.getParentOfType<KtCallExpression>(true) ?: return null
@@ -114,7 +134,7 @@ class SurroundWithNullCheckFix(
else -> return null
}
if (root.parent !is KtBlockExpression) return null
if (!isNullabilityMismatch(expected = typeMismatch.a, actual = typeMismatch.b)) return null
if (!isNullabilityMismatch(expected = expectedType, actual = actualType)) return null
if (!nullableExpression.isStableSimpleExpression()) return null
return SurroundWithNullCheckFix(root, nullableExpression)
}
@@ -16,6 +16,8 @@ import org.jetbrains.kotlin.idea.intentions.canBeReplacedWithInvokeCall
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.calls.util.getParameterForArgument
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isNullable
import org.jetbrains.kotlin.types.typeUtil.isNullabilityMismatch
@@ -87,13 +89,47 @@ class WrapWithSafeLetCallFix(
object TypeMismatchFactory : KotlinSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val typeMismatch = Errors.TYPE_MISMATCH.cast(diagnostic)
val argument = typeMismatch.psiElement.parent as? KtValueArgument ?: return null
val element = diagnostic.psiElement as? KtExpression ?: return null
val argument = element.parent as? KtValueArgument ?: return null
val call = argument.getParentOfType<KtCallExpression>(true) ?: return null
if (!isNullabilityMismatch(expected = typeMismatch.a, actual = typeMismatch.b)) return null
val expectedType: KotlinType
val actualType: KotlinType
when (diagnostic.factory) {
Errors.TYPE_MISMATCH -> {
val diagnosticWithParameters = Errors.TYPE_MISMATCH.cast(diagnostic)
expectedType = diagnosticWithParameters.a
actualType = diagnosticWithParameters.b
}
Errors.TYPE_MISMATCH_WARNING -> {
val diagnosticWithParameters = Errors.TYPE_MISMATCH_WARNING.cast(diagnostic)
expectedType = diagnosticWithParameters.a
actualType = diagnosticWithParameters.b
}
ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS -> {
val diagnosticWithParameters = ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.cast(diagnostic)
expectedType = diagnosticWithParameters.a
actualType = diagnosticWithParameters.b
}
else -> return null
}
return WrapWithSafeLetCallFix(call.getLastParentOfTypeInRow<KtQualifiedExpression>() ?: call, typeMismatch.psiElement)
if (!isNullabilityMismatch(expected = expectedType, actual = actualType)) return null
return WrapWithSafeLetCallFix(call.getLastParentOfTypeInRow<KtQualifiedExpression>() ?: call, element)
}
}
object NullabilityMismatchBasedOnJavaAnnotationsFactory : KotlinSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val nullabilityMismatch = ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS.cast(diagnostic)
val element = nullabilityMismatch.psiElement as? KtExpression ?: return null
val argument = element.parent as? KtValueArgument ?: return null
val call = argument.getParentOfType<KtCallExpression>(strict = true) ?: return null
if (!isNullabilityMismatch(expected = nullabilityMismatch.a, actual = nullabilityMismatch.b)) return null
return WrapWithSafeLetCallFix(call.getLastParentOfTypeInRow<KtQualifiedExpression>() ?: call, element)
}
}
}
@@ -67,7 +67,8 @@ sealed class CreateCallableFromCallActionFactory<E : KtExpression>(
Errors.NONE_APPLICABLE ->
if (diagElement is KtOperationReferenceExpression) null else diagElement.getNonStrictParentOfType<KtCallExpression>()
Errors.TYPE_MISMATCH -> (diagElement.parent as? KtValueArgument)?.getStrictParentOfType<KtCallExpression>()
Errors.TYPE_MISMATCH,
Errors.TYPE_MISMATCH_WARNING -> (diagElement.parent as? KtValueArgument)?.getStrictParentOfType<KtCallExpression>()
else -> throw AssertionError("Unexpected diagnostic: ${diagnostic.factory}")
} as? KtExpression
@@ -16,7 +16,7 @@ abstract class CreateGetSetFunctionActionFactory(private val isGet: Boolean) :
return when (diagnostic.factory) {
Errors.NO_GET_METHOD -> if (isGet) Errors.NO_GET_METHOD.cast(diagnostic).psiElement else null
Errors.NO_SET_METHOD -> if (!isGet) Errors.NO_SET_METHOD.cast(diagnostic).psiElement else null
in Errors.TYPE_MISMATCH_ERRORS -> {
in Errors.TYPE_MISMATCH_ERRORS, Errors.TYPE_MISMATCH_WARNING -> {
val indicesNode = diagnostic.psiElement.parent as? KtContainerNode ?: return null
if (indicesNode.node.elementType != KtNodeTypes.INDICES) return null
val arrayAccess = indicesNode.parent as? KtArrayAccessExpression ?: return null
@@ -32,6 +32,57 @@ public abstract class QuickFixMultiFileTestGenerated extends AbstractQuickFixMul
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/quickfix/addExclExclCall")
public static class AddExclExclCall extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, this, testDataFilePath);
}
@TestMetadata("javaNullabilityMismatchWithImplicitReceiver.test")
public void testJavaNullabilityMismatchWithImplicitReceiver() throws Exception {
runTest("testData/quickfix/addExclExclCall/javaNullabilityMismatchWithImplicitReceiver.test");
}
@TestMetadata("javaReceiverNullability1.test")
public void testJavaReceiverNullability1() throws Exception {
runTest("testData/quickfix/addExclExclCall/javaReceiverNullability1.test");
}
@TestMetadata("javaReceiverNullability2.test")
public void testJavaReceiverNullability2() throws Exception {
runTest("testData/quickfix/addExclExclCall/javaReceiverNullability2.test");
}
@TestMetadata("javaReceiverNullabilityInvoke.test")
public void testJavaReceiverNullabilityInvoke() throws Exception {
runTest("testData/quickfix/addExclExclCall/javaReceiverNullabilityInvoke.test");
}
@TestMetadata("javaReceiverNullabilityProperty.test")
public void testJavaReceiverNullabilityProperty() throws Exception {
runTest("testData/quickfix/addExclExclCall/javaReceiverNullabilityProperty.test");
}
@TestMetadata("javaReceiverNullabilityWithThisReceiver.test")
public void testJavaReceiverNullabilityWithThisReceiver() throws Exception {
runTest("testData/quickfix/addExclExclCall/javaReceiverNullabilityWithThisReceiver.test");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/quickfix/addGenericUpperBound")
public static class AddGenericUpperBound extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, this, testDataFilePath);
}
@TestMetadata("javaUpperBoundViolated.test")
public void testJavaUpperBoundViolated() throws Exception {
runTest("testData/quickfix/addGenericUpperBound/javaUpperBoundViolated.test");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/quickfix/addJvmDefault")
public static class AddJvmDefault extends AbstractQuickFixMultiFileTest {
@@ -1992,6 +2043,29 @@ public abstract class QuickFixMultiFileTestGenerated extends AbstractQuickFixMul
runTest("testData/quickfix/override/nothingToOverride/twoPackages.before.Main.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/quickfix/override/wrongNullabilityForJavaOverride")
public static class WrongNullabilityForJavaOverride extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, this, testDataFilePath);
}
@TestMetadata("makeArgumentNonNullable.test")
public void testMakeArgumentNonNullable() throws Exception {
runTest("testData/quickfix/override/wrongNullabilityForJavaOverride/makeArgumentNonNullable.test");
}
@TestMetadata("makeArgumentNullable.test")
public void testMakeArgumentNullable() throws Exception {
runTest("testData/quickfix/override/wrongNullabilityForJavaOverride/makeArgumentNullable.test");
}
@TestMetadata("makeReturnValueNonNullable.test")
public void testMakeReturnValueNonNullable() throws Exception {
runTest("testData/quickfix/override/wrongNullabilityForJavaOverride/makeReturnValueNonNullable.test");
}
}
}
@RunWith(JUnit3RunnerWithInners.class)
@@ -2042,6 +2116,100 @@ public abstract class QuickFixMultiFileTestGenerated extends AbstractQuickFixMul
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/quickfix/replaceWithSafeCall")
public static class ReplaceWithSafeCall extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, this, testDataFilePath);
}
@TestMetadata("javaNullabilityMismatchWithImplicitReceiver.test")
public void testJavaNullabilityMismatchWithImplicitReceiver() throws Exception {
runTest("testData/quickfix/replaceWithSafeCall/javaNullabilityMismatchWithImplicitReceiver.test");
}
@TestMetadata("javaReceiverNullability1.test")
public void testJavaReceiverNullability1() throws Exception {
runTest("testData/quickfix/replaceWithSafeCall/javaReceiverNullability1.test");
}
@TestMetadata("javaReceiverNullability2.test")
public void testJavaReceiverNullability2() throws Exception {
runTest("testData/quickfix/replaceWithSafeCall/javaReceiverNullability2.test");
}
@TestMetadata("javaReceiverNullabilityArrayGet.test")
public void testJavaReceiverNullabilityArrayGet() throws Exception {
runTest("testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityArrayGet.test");
}
@TestMetadata("javaReceiverNullabilityArraySet.test")
public void testJavaReceiverNullabilityArraySet() throws Exception {
runTest("testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityArraySet.test");
}
@TestMetadata("javaReceiverNullabilityInfixPlus.test")
public void testJavaReceiverNullabilityInfixPlus() throws Exception {
runTest("testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityInfixPlus.test");
}
@TestMetadata("javaReceiverNullabilityInvoke.test")
public void testJavaReceiverNullabilityInvoke() throws Exception {
runTest("testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityInvoke.test");
}
@TestMetadata("javaReceiverNullabilityProperty.test")
public void testJavaReceiverNullabilityProperty() throws Exception {
runTest("testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityProperty.test");
}
@TestMetadata("javaReceiverNullabilityWithThisReceiver.test")
public void testJavaReceiverNullabilityWithThisReceiver() throws Exception {
runTest("testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityWithThisReceiver.test");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/quickfix/replaceWithSafeCallForScopeFunction")
public static class ReplaceWithSafeCallForScopeFunction extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, this, testDataFilePath);
}
@TestMetadata("javaReceiverNullabilityAlso.test")
public void testJavaReceiverNullabilityAlso() throws Exception {
runTest("testData/quickfix/replaceWithSafeCallForScopeFunction/javaReceiverNullabilityAlso.test");
}
@TestMetadata("javaReceiverNullabilityRunExplicitThis.test")
public void testJavaReceiverNullabilityRunExplicitThis() throws Exception {
runTest("testData/quickfix/replaceWithSafeCallForScopeFunction/javaReceiverNullabilityRunExplicitThis.test");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/quickfix/surroundWithNullCheck")
public static class SurroundWithNullCheck extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, this, testDataFilePath);
}
@TestMetadata("javaNullabilityArgument.test")
public void testJavaNullabilityArgument() throws Exception {
runTest("testData/quickfix/surroundWithNullCheck/javaNullabilityArgument.test");
}
@TestMetadata("javaReceiverNullability.test")
public void testJavaReceiverNullability() throws Exception {
runTest("testData/quickfix/surroundWithNullCheck/javaReceiverNullability.test");
}
@TestMetadata("javaReceiverNullabilityInvoke.test")
public void testJavaReceiverNullabilityInvoke() throws Exception {
runTest("testData/quickfix/surroundWithNullCheck/javaReceiverNullabilityInvoke.test");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/quickfix/typeImports")
public static class TypeImports extends AbstractQuickFixMultiFileTest {
@@ -2107,4 +2275,32 @@ public abstract class QuickFixMultiFileTestGenerated extends AbstractQuickFixMul
runTest("testData/quickfix/when/addRemainingBranchesAnotherPackageAll.before.Main.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/quickfix/wrapWithSafeLetCall")
public static class WrapWithSafeLetCall extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, this, testDataFilePath);
}
@TestMetadata("javaNullabilityMismatchArgument.test")
public void testJavaNullabilityMismatchArgument() throws Exception {
runTest("testData/quickfix/wrapWithSafeLetCall/javaNullabilityMismatchArgument.test");
}
@TestMetadata("javaNullabilityMismatchArgument2.test")
public void testJavaNullabilityMismatchArgument2() throws Exception {
runTest("testData/quickfix/wrapWithSafeLetCall/javaNullabilityMismatchArgument2.test");
}
@TestMetadata("javaReceiverNullabilityInvoke.test")
public void testJavaReceiverNullabilityInvoke() throws Exception {
runTest("testData/quickfix/wrapWithSafeLetCall/javaReceiverNullabilityInvoke.test");
}
@TestMetadata("javaReceiverNullabilityInvoke2.test")
public void testJavaReceiverNullabilityInvoke2() throws Exception {
runTest("testData/quickfix/wrapWithSafeLetCall/javaReceiverNullabilityInvoke2.test");
}
}
}
@@ -0,0 +1,50 @@
// FILE: test.before.kt
// "Add non-null asserted (!!) call" "true"
// WITH_STDLIB
package p;
fun test(x: Foo) {
with (x.foo()) {
this<caret>.bar()
}
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/A.java
package p;
public class A {
public void bar() {}
}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable A foo() { return null; }
}
// FILE: test.after.kt
// "Add non-null asserted (!!) call" "true"
// WITH_STDLIB
package p;
fun test(x: Foo) {
with (x.foo()) {
this<caret>!!.bar()
}
}
@@ -0,0 +1,37 @@
// FILE: test.before.kt
// "Add non-null asserted (!!) call" "true"
package p;
fun test(x: Foo) {
x.foo()<caret>.length
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable String foo() { return null; }
}
// FILE: test.after.kt
// "Add non-null asserted (!!) call" "true"
package p;
fun test(x: Foo) {
x.foo()<caret>!!.length
}
@@ -0,0 +1,46 @@
// FILE: test.before.kt
// "Add non-null asserted (!!) call" "true"
package p;
fun test(x: Foo) {
val y = x.foo()
y<caret>.bar()
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/A.java
package p;
public class A {
public void bar() {}
}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable A foo() { return null; }
}
// FILE: test.after.kt
// "Add non-null asserted (!!) call" "true"
package p;
fun test(x: Foo) {
val y = x.foo()
y<caret>!!.bar()
}
@@ -0,0 +1,46 @@
// FILE: test.before.kt
// "Add non-null asserted (!!) call" "true"
package p;
fun test(x: Foo) {
val y = x.foo()
<caret>y()
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/A.java
package p;
public class A {
public void invoke();
}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable A foo() { return null; }
}
// FILE: test.after.kt
// "Add non-null asserted (!!) call" "true"
package p;
fun test(x: Foo) {
val y = x.foo()
<caret>(y!!)()
}
@@ -0,0 +1,49 @@
// FILE: test.before.kt
// "Add non-null asserted (!!) call" "true"
package p;
fun test(x: Foo) {
x.value<caret>.length
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
private @Nullable String value;
public Foo(@Nullable String value) {
this.value = value;
}
public @Nullable String getValue() {
return this.value;
}
public void setValue(@Nullable String value) {
this.value = value;
}
}
// FILE: test.after.kt
// "Add non-null asserted (!!) call" "true"
package p;
fun test(x: Foo) {
x.value<caret>!!.length
}
@@ -0,0 +1,50 @@
// FILE: test.before.kt
// "Add non-null asserted (!!) call" "true"
// WITH_STDLIB
package p;
fun test(x: Foo) {
with (x.foo()) {
<caret>bar()
}
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/A.java
package p;
public class A {
public void bar() {}
}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable A foo() { return null; }
}
// FILE: test.after.kt
// "Add non-null asserted (!!) call" "true"
// WITH_STDLIB
package p;
fun test(x: Foo) {
with (x.foo()) {
<caret>this!!.bar()
}
}
@@ -0,0 +1,35 @@
// FILE: test.before.kt
// "Add 'kotlin.Any' as upper bound for T" "true"
package p;
class Derived<T> : Base<<caret>T>() {}
// FILE: org/jspecify/nullness/NullMarked.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.MODULE;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target({TYPE, PACKAGE, MODULE})
@Retention(RUNTIME)
public @interface NullMarked {}
// FILE: p/Base.java
package p;
import org.jspecify.nullness.NullMarked;
@NullMarked
public class Base<T> {}
// FILE: test.after.kt
// "Add 'kotlin.Any' as upper bound for T" "true"
package p;
class Derived<T : Any> : Base<<caret>T>() {}
@@ -0,0 +1,59 @@
// FILE: test.before.kt
// "Change function signature to 'fun foo(x: String): String'" "true"
package p;
class Bar : Foo() {
<caret>override fun foo(x: String?): String {
return "bar"
}
}
// FILE: org/jspecify/nullness/NullMarked.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.MODULE;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target({TYPE, PACKAGE, MODULE})
@Retention(RUNTIME)
public @interface NullMarked {}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.NullMarked;
import org.jspecify.nullness.Nullable;
@NullMarked
public class Foo {
public String foo(String x) { return x; }
}
// FILE: test.after.kt
// "Change function signature to 'fun foo(x: String): String'" "true"
package p;
class Bar : Foo() {
<caret>override fun foo(x: String): String {
return "bar"
}
}
@@ -0,0 +1,59 @@
// FILE: test.before.kt
// "Change function signature to 'fun foo(x: String?): String'" "true"
package p;
class Bar : Foo() {
<caret>override fun foo(x: String): String {
return "bar"
}
}
// FILE: org/jspecify/nullness/NullMarked.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.MODULE;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target({TYPE, PACKAGE, MODULE})
@Retention(RUNTIME)
public @interface NullMarked {}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.NullMarked;
import org.jspecify.nullness.Nullable;
@NullMarked
public class Foo {
public String foo(@Nullable String x) { return x; }
}
// FILE: test.after.kt
// "Change function signature to 'fun foo(x: String?): String'" "true"
package p;
class Bar : Foo() {
<caret>override fun foo(x: String?): String {
return "bar"
}
}
@@ -0,0 +1,59 @@
// FILE: test.before.kt
// "Change function signature to 'fun foo(x: String): String'" "true"
package p;
class Bar : Foo() {
<caret>override fun foo(x: String): String? {
return "bar"
}
}
// FILE: org/jspecify/nullness/NullMarked.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.MODULE;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target({TYPE, PACKAGE, MODULE})
@Retention(RUNTIME)
public @interface NullMarked {}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.NullMarked;
import org.jspecify.nullness.Nullable;
@NullMarked
public class Foo {
public String foo(String x) { return x; }
}
// FILE: test.after.kt
// "Change function signature to 'fun foo(x: String): String'" "true"
package p;
class Bar : Foo() {
<caret>override fun foo(x: String): String {
return "bar"
}
}
@@ -0,0 +1,50 @@
// FILE: test.before.kt
// "Replace with safe (this?.) call" "true"
// WITH_STDLIB
package p;
fun test(x: Foo) {
with (x.foo()) {
<caret>bar()
}
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/A.java
package p;
public class A {
public void bar() {}
}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable A foo() { return null; }
}
// FILE: test.after.kt
// "Replace with safe (this?.) call" "true"
// WITH_STDLIB
package p;
fun test(x: Foo) {
with (x.foo()) {
<caret>this?.bar()
}
}
@@ -0,0 +1,37 @@
// FILE: test.before.kt
// "Replace with safe (?.) call" "true"
package p;
fun test(x: Foo) {
x.foo()<caret>.length
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable String foo() { return null; }
}
// FILE: test.after.kt
// "Replace with safe (?.) call" "true"
package p;
fun test(x: Foo) {
x.foo()<caret>?.length
}
@@ -0,0 +1,46 @@
// FILE: test.before.kt
// "Replace with safe (?.) call" "true"
package p;
fun test(x: Foo) {
val y = x.foo()
y<caret>.bar()
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/A.java
package p;
public class A {
public void bar() {}
}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable A foo() { return null; }
}
// FILE: test.after.kt
// "Replace with safe (?.) call" "true"
package p;
fun test(x: Foo) {
val y = x.foo()
y<caret>?.bar()
}
@@ -0,0 +1,38 @@
// FILE: test.before.kt
// "Replace with safe (?.) call" "true"
package p;
fun test(x: Foo) {
x.findAvailable()<caret>[1]
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public int get(int index) { return 1; }
public @Nullable Foo findAvailable() { return this; }
}
// FILE: test.after.kt
// "Replace with safe (?.) call" "true"
package p;
fun test(x: Foo) {
x.findAvailable()<caret>?.get(1)
}
@@ -0,0 +1,39 @@
// FILE: test.before.kt
// "Replace with safe (?.) call" "true"
package p;
fun test(x: Foo) {
x.findAvailable()<caret>[1] = 2
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public int get(int index) { return 1; }
public void set(int index, int value) {}
public @Nullable Foo findAvailable() { return this; }
}
// FILE: test.after.kt
// "Replace with safe (?.) call" "true"
package p;
fun test(x: Foo) {
x.findAvailable()<caret>?.set(1, 2)
}
@@ -0,0 +1,40 @@
// FILE: test.before.kt
// "Replace with safe (?.) call" "true"
package p;
fun test(x: Foo) {
val y = x.findAvailable()
val z = y<caret> + y
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable Foo plus(Foo other) { return this; }
public @Nullable Foo findAvailable() { return this; }
}
// FILE: test.after.kt
// "Replace with safe (?.) call" "true"
package p;
fun test(x: Foo) {
val y = x.findAvailable()
val z = y<caret>?.plus(y)
}
@@ -0,0 +1,40 @@
// FILE: test.before.kt
// "Replace with safe (?.) call" "true"
package p;
fun test(x: Foo) {
val y = x.findAvailable()
val z = y<caret>()
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public void invoke() {}
public @Nullable Foo findAvailable() { return this; }
}
// FILE: test.after.kt
// "Replace with safe (?.) call" "true"
package p;
fun test(x: Foo) {
val y = x.findAvailable()
val z = y<caret>?.invoke()
}
@@ -0,0 +1,49 @@
// FILE: test.before.kt
// "Replace with safe (?.) call" "true"
package p;
fun test(x: Foo) {
x.value<caret>.length
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
private @Nullable String value;
public Foo(@Nullable String value) {
this.value = value;
}
public @Nullable String getValue() {
return this.value;
}
public void setValue(@Nullable String value) {
this.value = value;
}
}
// FILE: test.after.kt
// "Replace with safe (?.) call" "true"
package p;
fun test(x: Foo) {
x.value<caret>?.length
}
@@ -0,0 +1,50 @@
// FILE: test.before.kt
// "Replace with safe (?.) call" "true"
// WITH_STDLIB
package p;
fun test(x: Foo) {
with (x.foo()) {
this<caret>.bar()
}
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/A.java
package p;
public class A {
public void bar() {}
}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable A foo() { return null; }
}
// FILE: test.after.kt
// "Replace with safe (?.) call" "true"
// WITH_STDLIB
package p;
fun test(x: Foo) {
with (x.foo()) {
this<caret>?.bar()
}
}
@@ -0,0 +1,44 @@
// FILE: test.before.kt
// "Replace scope function with safe (?.) call" "true"
// WITH_STDLIB
package p;
fun test(x: Foo) {
x.findAvailable().also {
it<caret>.foo()
}
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public void foo() {}
public @Nullable Foo findAvailable() { return this; }
}
// FILE: test.after.kt
// "Replace scope function with safe (?.) call" "true"
// WITH_STDLIB
package p;
fun test(x: Foo) {
x.findAvailable()?.also {
it<caret>.foo()
}
}
@@ -0,0 +1,44 @@
// FILE: test.before.kt
// "Replace scope function with safe (?.) call" "true"
// WITH_STDLIB
package p;
fun test(x: Foo) {
x.findAvailable().run {
this<caret>.foo()
}
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public void foo() {}
public @Nullable Foo findAvailable() { return this; }
}
// FILE: test.after.kt
// "Replace scope function with safe (?.) call" "true"
// WITH_STDLIB
package p;
fun test(x: Foo) {
x.findAvailable()?.run {
this<caret>.foo()
}
}
@@ -0,0 +1,52 @@
// FILE: test.before.kt
// "Surround with null check" "true"
package p;
fun f(x: A) {}
fun test(x: Foo) {
val y = x.foo()
f(y<caret>)
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/A.java
package p;
public class A {
public void bar() {}
}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable A foo() { return null; }
}
// FILE: test.after.kt
// "Surround with null check" "true"
package p;
fun f(x: A) {}
fun test(x: Foo) {
val y = x.foo()
if <caret>(y != null) {
f(y)
}
}
@@ -0,0 +1,48 @@
// FILE: test.before.kt
// "Surround with null check" "true"
package p;
fun test(x: Foo) {
val y = x.foo()
y<caret>.bar()
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/A.java
package p;
public class A {
public void bar() {}
}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable A foo() { return null; }
}
// FILE: test.after.kt
// "Surround with null check" "true"
package p;
fun test(x: Foo) {
val y = x.foo()
i<caret>f (y != null) {
y.bar()
}
}
@@ -0,0 +1,48 @@
// FILE: test.before.kt
// "Surround with null check" "true"
package p;
fun test(x: Foo) {
val y = x.foo()
y<caret>()
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/A.java
package p;
public class A {
public void invoke();
}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable A foo() { return null; }
}
// FILE: test.after.kt
// "Surround with null check" "true"
package p;
fun test(x: Foo) {
val y = x.foo()
i<caret>f (y != null) {
y()
}
}
@@ -0,0 +1,48 @@
// FILE: test.before.kt
// "Wrap with '?.let { ... }' call" "true"
package p;
fun f(x: A) {}
fun test(x: Foo) {
f(<caret>x.foo())
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/A.java
package p;
public class A {
public void bar();
}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable A foo() { return null; }
}
// FILE: test.after.kt
// "Wrap with '?.let { ... }' call" "true"
package p;
fun f(x: A) {}
fun test(x: Foo) {
x.<caret>foo()?.let { f(it) }
}
@@ -0,0 +1,50 @@
// FILE: test.before.kt
// "Wrap with '?.let { ... }' call" "true"
package p;
fun f(x: A) {}
fun test(x: Foo) {
val a = x.foo()
f(<caret>a)
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/A.java
package p;
public class A {
public void bar();
}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable A foo() { return null; }
}
// FILE: test.after.kt
// "Wrap with '?.let { ... }' call" "true"
package p;
fun f(x: A) {}
fun test(x: Foo) {
val a = x.foo()
a<caret>?.let { f(it) }
}
@@ -0,0 +1,46 @@
// FILE: test.before.kt
// "Wrap with '?.let { ... }' call" "true"
package p;
fun test(x: Foo) {
val y = x.foo()
y<caret>()
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/A.java
package p;
public class A {
public void invoke();
}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable A foo() { return null; }
}
// FILE: test.after.kt
// "Wrap with '?.let { ... }' call" "true"
package p;
fun test(x: Foo) {
val y = x.foo()
y<caret>?.let { it() }
}
@@ -0,0 +1,44 @@
// FILE: test.before.kt
// "Wrap with '?.let { ... }' call" "true"
package p;
fun test(x: Foo) {
<caret>x.foo()()
}
// FILE: org/jspecify/nullness/Nullable.java
package org.jspecify.nullness;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Target(TYPE_USE)
@Retention(RUNTIME)
public @interface Nullable {}
// FILE: p/A.java
package p;
public class A {
public void invoke();
}
// FILE: p/Foo.java
package p;
import org.jspecify.nullness.Nullable;
public class Foo {
public @Nullable A foo() { return null; }
}
// FILE: test.after.kt
// "Wrap with '?.let { ... }' call" "true"
package p;
fun test(x: Foo) {
<caret>x.foo()?.let { it() }
}