From 3f52503ac3b486c19c80a64fe81a531558c51bc8 Mon Sep 17 00:00:00 2001 From: Dmitry Timofeev Date: Tue, 25 Jan 2022 01:31:52 +0900 Subject: [PATCH] [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 --- .../idea/quickfix/AddExclExclCallFix.kt | 1 + .../idea/quickfix/AddFunctionParametersFix.kt | 1 + .../idea/quickfix/AddGenericUpperBoundFix.kt | 4 + .../idea/quickfix/ChangeToLabeledReturnFix.kt | 1 + .../idea/quickfix/ChangeVariableTypeFix.kt | 10 +- .../QuickFixFactoryForTypeMismatchError.kt | 21 +- .../kotlin/idea/quickfix/QuickFixRegistrar.kt | 25 +++ .../ReplaceInfixOrOperatorCallFixFactory.kt | 13 +- .../idea/quickfix/SurroundWithLambdaFix.kt | 5 + .../idea/quickfix/SurroundWithNullCheckFix.kt | 28 ++- .../idea/quickfix/WrapWithSafeLetCallFix.kt | 44 +++- .../CreateCallableFromCallActionFactory.kt | 3 +- .../CreateGetSetFunctionActionFactory.kt | 2 +- .../QuickFixMultiFileTestGenerated.java | 196 ++++++++++++++++++ ...llabilityMismatchWithImplicitReceiver.test | 50 +++++ .../javaReceiverNullability1.test | 37 ++++ .../javaReceiverNullability2.test | 46 ++++ .../javaReceiverNullabilityInvoke.test | 46 ++++ .../javaReceiverNullabilityProperty.test | 49 +++++ ...vaReceiverNullabilityWithThisReceiver.test | 50 +++++ .../javaUpperBoundViolated.test | 35 ++++ .../makeArgumentNonNullable.test | 59 ++++++ .../makeArgumentNullable.test | 59 ++++++ .../makeReturnValueNonNullable.test | 59 ++++++ ...llabilityMismatchWithImplicitReceiver.test | 50 +++++ .../javaReceiverNullability1.test | 37 ++++ .../javaReceiverNullability2.test | 46 ++++ .../javaReceiverNullabilityArrayGet.test | 38 ++++ .../javaReceiverNullabilityArraySet.test | 39 ++++ .../javaReceiverNullabilityInfixPlus.test | 40 ++++ .../javaReceiverNullabilityInvoke.test | 40 ++++ .../javaReceiverNullabilityProperty.test | 49 +++++ ...vaReceiverNullabilityWithThisReceiver.test | 50 +++++ .../javaReceiverNullabilityAlso.test | 44 ++++ ...avaReceiverNullabilityRunExplicitThis.test | 44 ++++ .../javaNullabilityArgument.test | 52 +++++ .../javaReceiverNullability.test | 48 +++++ .../javaReceiverNullabilityInvoke.test | 48 +++++ .../javaNullabilityMismatchArgument.test | 48 +++++ .../javaNullabilityMismatchArgument2.test | 50 +++++ .../javaReceiverNullabilityInvoke.test | 46 ++++ .../javaReceiverNullabilityInvoke2.test | 44 ++++ 42 files changed, 1640 insertions(+), 17 deletions(-) create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaNullabilityMismatchWithImplicitReceiver.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullability1.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullability2.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullabilityInvoke.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullabilityProperty.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullabilityWithThisReceiver.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/addGenericUpperBound/javaUpperBoundViolated.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/override/wrongNullabilityForJavaOverride/makeArgumentNonNullable.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/override/wrongNullabilityForJavaOverride/makeArgumentNullable.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/override/wrongNullabilityForJavaOverride/makeReturnValueNonNullable.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaNullabilityMismatchWithImplicitReceiver.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullability1.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullability2.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityArrayGet.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityArraySet.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityInfixPlus.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityInvoke.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityProperty.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityWithThisReceiver.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCallForScopeFunction/javaReceiverNullabilityAlso.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCallForScopeFunction/javaReceiverNullabilityRunExplicitThis.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/javaNullabilityArgument.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/javaReceiverNullability.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/javaReceiverNullabilityInvoke.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaNullabilityMismatchArgument.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaNullabilityMismatchArgument2.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaReceiverNullabilityInvoke.test create mode 100644 plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaReceiverNullabilityInvoke2.test diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/AddExclExclCallFix.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/AddExclExclCallFix.kt index af621f12a2ed..d24e41a59f21 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/AddExclExclCallFix.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/AddExclExclCallFix.kt @@ -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) diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/AddFunctionParametersFix.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/AddFunctionParametersFix.kt index bc54f59264c2..6f62ed4c3fa4 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/AddFunctionParametersFix.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/AddFunctionParametersFix.kt @@ -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 diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/AddGenericUpperBoundFix.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/AddGenericUpperBoundFix.kt index 7f0ef63585b3..fd55b0ce7e7b 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/AddGenericUpperBoundFix.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/AddGenericUpperBoundFix.kt @@ -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() } } diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToLabeledReturnFix.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToLabeledReturnFix.kt index 2c8b1bb7685c..efd1f1ebde93 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToLabeledReturnFix.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeToLabeledReturnFix.kt @@ -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) diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableTypeFix.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableTypeFix.kt index 97b93a8f9a71..a3cf10017517 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableTypeFix.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeVariableTypeFix.kt @@ -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()) } } } diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt index 51e942e10a36..8728a9e1d331 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixFactoryForTypeMismatchError.kt @@ -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()?.calleeExpression == targetExpression + getAddExclExclCallFix(targetExpression, checkCalleeExpression)?.let { actions.add(it) } if (expectedType.isBoolean()) { actions.add(AddEqEqTrueFix(targetExpression)) } diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index 89c6ab442376..a4eb4aa29b19 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -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) } } diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixOrOperatorCallFixFactory.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixOrOperatorCallFixFactory.kt index f9a6102e2a50..8a645088e031 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixOrOperatorCallFixFactory.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceInfixOrOperatorCallFixFactory.kt @@ -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() ?: expression.parent?.safeAs() + } + 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) { diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/SurroundWithLambdaFix.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/SurroundWithLambdaFix.kt index 333160ac8b79..9dbd621942f3 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/SurroundWithLambdaFix.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/SurroundWithLambdaFix.kt @@ -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) diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/SurroundWithNullCheckFix.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/SurroundWithNullCheckFix.kt index cac8b4358738..d488f42ad1df 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/SurroundWithNullCheckFix.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/SurroundWithNullCheckFix.kt @@ -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(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) } diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/WrapWithSafeLetCallFix.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/WrapWithSafeLetCallFix.kt index 47b10abf3458..5b89aed32573 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/WrapWithSafeLetCallFix.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/WrapWithSafeLetCallFix.kt @@ -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(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() ?: call, typeMismatch.psiElement) + if (!isNullabilityMismatch(expected = expectedType, actual = actualType)) return null + + return WrapWithSafeLetCallFix(call.getLastParentOfTypeInRow() ?: 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(strict = true) ?: return null + + if (!isNullabilityMismatch(expected = nullabilityMismatch.a, actual = nullabilityMismatch.b)) return null + + return WrapWithSafeLetCallFix(call.getLastParentOfTypeInRow() ?: call, element) } } } \ No newline at end of file diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt index 288996a2de5e..6c01eaa07c72 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt @@ -67,7 +67,8 @@ sealed class CreateCallableFromCallActionFactory( Errors.NONE_APPLICABLE -> if (diagElement is KtOperationReferenceExpression) null else diagElement.getNonStrictParentOfType() - Errors.TYPE_MISMATCH -> (diagElement.parent as? KtValueArgument)?.getStrictParentOfType() + Errors.TYPE_MISMATCH, + Errors.TYPE_MISMATCH_WARNING -> (diagElement.parent as? KtValueArgument)?.getStrictParentOfType() else -> throw AssertionError("Unexpected diagnostic: ${diagnostic.factory}") } as? KtExpression diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetSetFunctionActionFactory.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetSetFunctionActionFactory.kt index 0b16b3e78817..0bdc9f356944 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetSetFunctionActionFactory.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateGetSetFunctionActionFactory.kt @@ -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 diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 9d5586a9132e..bde510fdd090 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -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"); + } + } } diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaNullabilityMismatchWithImplicitReceiver.test b/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaNullabilityMismatchWithImplicitReceiver.test new file mode 100644 index 000000000000..4f1fdd47a60e --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaNullabilityMismatchWithImplicitReceiver.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.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!!.bar() + } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullability1.test b/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullability1.test new file mode 100644 index 000000000000..0770f5e86162 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullability1.test @@ -0,0 +1,37 @@ +// FILE: test.before.kt +// "Add non-null asserted (!!) call" "true" +package p; + +fun test(x: Foo) { + x.foo().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()!!.length +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullability2.test b/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullability2.test new file mode 100644 index 000000000000..4e011924d4f0 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullability2.test @@ -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.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!!.bar() +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullabilityInvoke.test b/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullabilityInvoke.test new file mode 100644 index 000000000000..f336c84439e0 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullabilityInvoke.test @@ -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() +} + +// 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() + (y!!)() +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullabilityProperty.test b/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullabilityProperty.test new file mode 100644 index 000000000000..a4673e64d9b9 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullabilityProperty.test @@ -0,0 +1,49 @@ +// FILE: test.before.kt +// "Add non-null asserted (!!) call" "true" +package p; + +fun test(x: Foo) { + x.value.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!!.length +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullabilityWithThisReceiver.test b/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullabilityWithThisReceiver.test new file mode 100644 index 000000000000..c5f5aed59479 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/addExclExclCall/javaReceiverNullabilityWithThisReceiver.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()) { + 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!!.bar() + } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/addGenericUpperBound/javaUpperBoundViolated.test b/plugins/kotlin/idea/tests/testData/quickfix/addGenericUpperBound/javaUpperBoundViolated.test new file mode 100644 index 000000000000..c118e1a2dfba --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/addGenericUpperBound/javaUpperBoundViolated.test @@ -0,0 +1,35 @@ +// FILE: test.before.kt +// "Add 'kotlin.Any' as upper bound for T" "true" +package p; + +class Derived : Base<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 {} + +// FILE: test.after.kt +// "Add 'kotlin.Any' as upper bound for T" "true" +package p; + +class Derived : Base<T>() {} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/override/wrongNullabilityForJavaOverride/makeArgumentNonNullable.test b/plugins/kotlin/idea/tests/testData/quickfix/override/wrongNullabilityForJavaOverride/makeArgumentNonNullable.test new file mode 100644 index 000000000000..bee1417016e7 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/override/wrongNullabilityForJavaOverride/makeArgumentNonNullable.test @@ -0,0 +1,59 @@ +// FILE: test.before.kt +// "Change function signature to 'fun foo(x: String): String'" "true" +package p; + +class Bar : Foo() { + 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() { + override fun foo(x: String): String { + return "bar" + } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/override/wrongNullabilityForJavaOverride/makeArgumentNullable.test b/plugins/kotlin/idea/tests/testData/quickfix/override/wrongNullabilityForJavaOverride/makeArgumentNullable.test new file mode 100644 index 000000000000..d98135524703 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/override/wrongNullabilityForJavaOverride/makeArgumentNullable.test @@ -0,0 +1,59 @@ +// FILE: test.before.kt +// "Change function signature to 'fun foo(x: String?): String'" "true" +package p; + +class Bar : Foo() { + 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() { + override fun foo(x: String?): String { + return "bar" + } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/override/wrongNullabilityForJavaOverride/makeReturnValueNonNullable.test b/plugins/kotlin/idea/tests/testData/quickfix/override/wrongNullabilityForJavaOverride/makeReturnValueNonNullable.test new file mode 100644 index 000000000000..f72f8cb444c3 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/override/wrongNullabilityForJavaOverride/makeReturnValueNonNullable.test @@ -0,0 +1,59 @@ +// FILE: test.before.kt +// "Change function signature to 'fun foo(x: String): String'" "true" +package p; + +class Bar : Foo() { + 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() { + override fun foo(x: String): String { + return "bar" + } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaNullabilityMismatchWithImplicitReceiver.test b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaNullabilityMismatchWithImplicitReceiver.test new file mode 100644 index 000000000000..f32e62eeff55 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaNullabilityMismatchWithImplicitReceiver.test @@ -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()) { + 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()) { + this?.bar() + } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullability1.test b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullability1.test new file mode 100644 index 000000000000..f73398088dcd --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullability1.test @@ -0,0 +1,37 @@ +// FILE: test.before.kt +// "Replace with safe (?.) call" "true" +package p; + +fun test(x: Foo) { + x.foo().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()?.length +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullability2.test b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullability2.test new file mode 100644 index 000000000000..a97b6630058d --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullability2.test @@ -0,0 +1,46 @@ +// FILE: test.before.kt +// "Replace with safe (?.) call" "true" +package p; + +fun test(x: Foo) { + val y = x.foo() + y.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?.bar() +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityArrayGet.test b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityArrayGet.test new file mode 100644 index 000000000000..a437b7d8d23c --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityArrayGet.test @@ -0,0 +1,38 @@ +// FILE: test.before.kt +// "Replace with safe (?.) call" "true" +package p; + +fun test(x: Foo) { + x.findAvailable()[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()?.get(1) +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityArraySet.test b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityArraySet.test new file mode 100644 index 000000000000..12648f016573 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityArraySet.test @@ -0,0 +1,39 @@ +// FILE: test.before.kt +// "Replace with safe (?.) call" "true" +package p; + +fun test(x: Foo) { + x.findAvailable()[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()?.set(1, 2) +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityInfixPlus.test b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityInfixPlus.test new file mode 100644 index 000000000000..33bbb986872f --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityInfixPlus.test @@ -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 + 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?.plus(y) +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityInvoke.test b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityInvoke.test new file mode 100644 index 000000000000..55e282ad4d3f --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityInvoke.test @@ -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() +} + +// 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?.invoke() +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityProperty.test b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityProperty.test new file mode 100644 index 000000000000..b58beb038e11 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityProperty.test @@ -0,0 +1,49 @@ +// FILE: test.before.kt +// "Replace with safe (?.) call" "true" +package p; + +fun test(x: Foo) { + x.value.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?.length +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityWithThisReceiver.test b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityWithThisReceiver.test new file mode 100644 index 000000000000..b997cb6d9983 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCall/javaReceiverNullabilityWithThisReceiver.test @@ -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.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?.bar() + } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCallForScopeFunction/javaReceiverNullabilityAlso.test b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCallForScopeFunction/javaReceiverNullabilityAlso.test new file mode 100644 index 000000000000..106096d42e6c --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCallForScopeFunction/javaReceiverNullabilityAlso.test @@ -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.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.foo() + } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCallForScopeFunction/javaReceiverNullabilityRunExplicitThis.test b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCallForScopeFunction/javaReceiverNullabilityRunExplicitThis.test new file mode 100644 index 000000000000..9d17cbde4afc --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/replaceWithSafeCallForScopeFunction/javaReceiverNullabilityRunExplicitThis.test @@ -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.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.foo() + } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/javaNullabilityArgument.test b/plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/javaNullabilityArgument.test new file mode 100644 index 000000000000..108b4ab730ad --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/javaNullabilityArgument.test @@ -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) +} + +// 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 (y != null) { + f(y) + } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/javaReceiverNullability.test b/plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/javaReceiverNullability.test new file mode 100644 index 000000000000..1da0cece58db --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/javaReceiverNullability.test @@ -0,0 +1,48 @@ +// FILE: test.before.kt +// "Surround with null check" "true" +package p; + +fun test(x: Foo) { + val y = x.foo() + y.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() + if (y != null) { + y.bar() + } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/javaReceiverNullabilityInvoke.test b/plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/javaReceiverNullabilityInvoke.test new file mode 100644 index 000000000000..ff35d21066c3 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/surroundWithNullCheck/javaReceiverNullabilityInvoke.test @@ -0,0 +1,48 @@ +// FILE: test.before.kt +// "Surround with null check" "true" +package p; + +fun test(x: Foo) { + val y = x.foo() + 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 +// "Surround with null check" "true" +package p; + +fun test(x: Foo) { + val y = x.foo() + if (y != null) { + y() + } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaNullabilityMismatchArgument.test b/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaNullabilityMismatchArgument.test new file mode 100644 index 000000000000..bc20e21b1302 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaNullabilityMismatchArgument.test @@ -0,0 +1,48 @@ +// FILE: test.before.kt +// "Wrap with '?.let { ... }' call" "true" +package p; + +fun f(x: A) {} + +fun test(x: Foo) { + f(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.foo()?.let { f(it) } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaNullabilityMismatchArgument2.test b/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaNullabilityMismatchArgument2.test new file mode 100644 index 000000000000..2050ea042a3b --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaNullabilityMismatchArgument2.test @@ -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(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?.let { f(it) } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaReceiverNullabilityInvoke.test b/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaReceiverNullabilityInvoke.test new file mode 100644 index 000000000000..8d0901d1dab9 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaReceiverNullabilityInvoke.test @@ -0,0 +1,46 @@ +// FILE: test.before.kt +// "Wrap with '?.let { ... }' call" "true" +package p; + +fun test(x: Foo) { + val y = x.foo() + 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 +// "Wrap with '?.let { ... }' call" "true" +package p; + +fun test(x: Foo) { + val y = x.foo() + y?.let { it() } +} diff --git a/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaReceiverNullabilityInvoke2.test b/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaReceiverNullabilityInvoke2.test new file mode 100644 index 000000000000..76e3c1543e24 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/quickfix/wrapWithSafeLetCall/javaReceiverNullabilityInvoke2.test @@ -0,0 +1,44 @@ +// FILE: test.before.kt +// "Wrap with '?.let { ... }' call" "true" +package p; + +fun test(x: Foo) { + 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) { + x.foo()?.let { it() } +}