Fix nullability quick-fixes with implicit receiver #KT-17726 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
9d2ae54d2c
commit
46aaee5d05
@@ -38,6 +38,10 @@ import org.jetbrains.kotlin.types.TypeUtils
|
|||||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import org.jetbrains.kotlin.util.isValidOperator
|
import org.jetbrains.kotlin.util.isValidOperator
|
||||||
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getImplicitReceiverValue
|
||||||
|
|
||||||
|
|
||||||
abstract class ExclExclCallFix(psiElement: PsiElement) : KotlinQuickFixAction<PsiElement>(psiElement) {
|
abstract class ExclExclCallFix(psiElement: PsiElement) : KotlinQuickFixAction<PsiElement>(psiElement) {
|
||||||
override fun getFamilyName(): String = text
|
override fun getFamilyName(): String = text
|
||||||
@@ -75,7 +79,9 @@ class RemoveExclExclCallFix(psiElement: PsiElement) : ExclExclCallFix(psiElement
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddExclExclCallFix(psiElement: PsiElement) : ExclExclCallFix(psiElement) {
|
class AddExclExclCallFix(psiElement: PsiElement, val checkImplicitReceivers: Boolean) : ExclExclCallFix(psiElement) {
|
||||||
|
constructor(psiElement: PsiElement) : this(psiElement, true)
|
||||||
|
|
||||||
override fun getText() = KotlinBundle.message("introduce.non.null.assertion")
|
override fun getText() = KotlinBundle.message("introduce.non.null.assertion")
|
||||||
|
|
||||||
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean
|
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean
|
||||||
@@ -85,40 +91,50 @@ class AddExclExclCallFix(psiElement: PsiElement) : ExclExclCallFix(psiElement) {
|
|||||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||||
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return
|
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return
|
||||||
|
|
||||||
val modifiedExpression = getExpressionForIntroduceCall() ?: return
|
val expr = getExpressionForIntroduceCall() ?: return
|
||||||
val exclExclExpression = KtPsiFactory(project).createExpressionByPattern("$0!!", modifiedExpression)
|
val modifiedExpression = expr.expression
|
||||||
|
val exclExclExpression = if (expr.implicitReceiver) {
|
||||||
|
KtPsiFactory(project).createExpressionByPattern("this!!.$0", modifiedExpression)
|
||||||
|
} else {
|
||||||
|
KtPsiFactory(project).createExpressionByPattern("$0!!", modifiedExpression)
|
||||||
|
}
|
||||||
modifiedExpression.replace(exclExclExpression)
|
modifiedExpression.replace(exclExclExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getExpressionForIntroduceCall(): KtExpression? {
|
private class ExpressionForCall(val expression: KtExpression, val implicitReceiver: Boolean)
|
||||||
|
|
||||||
|
private fun KtExpression?.expressionForCall(implicitReceiver: Boolean = false) = this?.let { ExpressionForCall(it, implicitReceiver) }
|
||||||
|
|
||||||
|
private fun getExpressionForIntroduceCall(): ExpressionForCall? {
|
||||||
val psiElement = element ?: return null
|
val psiElement = element ?: return null
|
||||||
if (psiElement is LeafPsiElement && psiElement.elementType == KtTokens.DOT) {
|
return if (psiElement is LeafPsiElement && psiElement.elementType == KtTokens.DOT) {
|
||||||
val sibling = psiElement.prevSibling
|
(psiElement.prevSibling as? KtExpression).expressionForCall()
|
||||||
if (sibling is KtExpression) {
|
|
||||||
return sibling
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (psiElement is KtArrayAccessExpression) {
|
else if (psiElement is KtArrayAccessExpression) {
|
||||||
return psiElement.arrayExpression
|
psiElement.arrayExpression.expressionForCall()
|
||||||
}
|
}
|
||||||
else if (psiElement is KtOperationReferenceExpression) {
|
else if (psiElement is KtOperationReferenceExpression) {
|
||||||
val parent = psiElement.parent
|
val parent = psiElement.parent
|
||||||
return when (parent) {
|
when (parent) {
|
||||||
is KtUnaryExpression -> parent.baseExpression
|
is KtUnaryExpression -> parent.baseExpression.expressionForCall()
|
||||||
is KtBinaryExpression -> parent.left
|
is KtBinaryExpression -> parent.left.expressionForCall()
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (psiElement is KtExpression) {
|
else if (psiElement is KtExpression) {
|
||||||
return psiElement
|
if (checkImplicitReceivers && psiElement.getResolvedCall(psiElement.analyze())?.getImplicitReceiverValue() != null) {
|
||||||
|
val expressionToReplace = psiElement.parent as? KtCallExpression ?: psiElement
|
||||||
|
expressionToReplace.expressionForCall(implicitReceiver = true)
|
||||||
|
}
|
||||||
|
else psiElement.expressionForCall()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object : KotlinSingleIntentionActionFactory() {
|
companion object : KotlinSingleIntentionActionFactory() {
|
||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction
|
override fun createAction(diagnostic: Diagnostic): IntentionAction = AddExclExclCallFix(diagnostic.psiElement)
|
||||||
= AddExclExclCallFix(diagnostic.psiElement)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +153,7 @@ object SmartCastImpossibleExclExclFixFactory: KotlinSingleIntentionActionFactory
|
|||||||
val nullableExpectedType = TypeUtils.makeNullable(expectedType)
|
val nullableExpectedType = TypeUtils.makeNullable(expectedType)
|
||||||
if (!type.isSubtypeOf(nullableExpectedType)) return null
|
if (!type.isSubtypeOf(nullableExpectedType)) return null
|
||||||
|
|
||||||
return AddExclExclCallFix(element)
|
return AddExclExclCallFix(element, checkImplicitReceivers = false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,11 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getImplicitReceiverValue
|
||||||
|
|
||||||
abstract class ReplaceCallFix(
|
abstract class ReplaceCallFix(
|
||||||
expression: KtQualifiedExpression,
|
expression: KtQualifiedExpression,
|
||||||
@@ -44,14 +47,36 @@ abstract class ReplaceCallFix(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ReplaceImplicitReceiverCallFix(expression: KtExpression) : KotlinQuickFixAction<KtExpression>(expression) {
|
||||||
|
override fun getFamilyName() = text
|
||||||
|
|
||||||
|
override fun getText() = "Replace with safe (this?.) call"
|
||||||
|
|
||||||
|
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||||
|
val element = element ?: return
|
||||||
|
val newExpression = KtPsiFactory(element).createExpressionByPattern("this?.$0", element)
|
||||||
|
element.replace(newExpression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ReplaceWithSafeCallFix(expression: KtDotQualifiedExpression): ReplaceCallFix(expression, "?.") {
|
class ReplaceWithSafeCallFix(expression: KtDotQualifiedExpression): ReplaceCallFix(expression, "?.") {
|
||||||
|
|
||||||
override fun getText() = "Replace with safe (?.) call"
|
override fun getText() = "Replace with safe (?.) call"
|
||||||
|
|
||||||
companion object : KotlinSingleIntentionActionFactory() {
|
companion object : KotlinSingleIntentionActionFactory() {
|
||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
val qualifiedExpression = diagnostic.psiElement.getParentOfType<KtDotQualifiedExpression>(strict = false) ?: return null
|
val psiElement = diagnostic.psiElement
|
||||||
return ReplaceWithSafeCallFix(qualifiedExpression)
|
val qualifiedExpression = psiElement.parent as? KtDotQualifiedExpression
|
||||||
|
if (qualifiedExpression != null) {
|
||||||
|
return ReplaceWithSafeCallFix(qualifiedExpression)
|
||||||
|
} else {
|
||||||
|
psiElement as? KtNameReferenceExpression ?: return null
|
||||||
|
if (psiElement.getResolvedCall(psiElement.analyze())?.getImplicitReceiverValue() != null) {
|
||||||
|
val expressionToReplace: KtExpression = psiElement.parent as? KtCallExpression ?: psiElement
|
||||||
|
return ReplaceImplicitReceiverCallFix(expressionToReplace)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention
|
|||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS
|
import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
|
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getImplicitReceiverValue
|
||||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isBoolean
|
|
||||||
|
|
||||||
class ReplaceInfixOrOperatorCallFix(element: KtExpression) : KotlinQuickFixAction<KtExpression>(element) {
|
class ReplaceInfixOrOperatorCallFix(element: KtExpression) : KotlinQuickFixAction<KtExpression>(element) {
|
||||||
|
|
||||||
@@ -99,6 +99,7 @@ class ReplaceInfixOrOperatorCallFix(element: KtExpression) : KotlinQuickFixActio
|
|||||||
is KtCallExpression -> {
|
is KtCallExpression -> {
|
||||||
if (parent.calleeExpression == null) null
|
if (parent.calleeExpression == null) null
|
||||||
else if (parent.parent is KtQualifiedExpression) null
|
else if (parent.parent is KtQualifiedExpression) null
|
||||||
|
else if (parent.getResolvedCall(parent.analyze())?.getImplicitReceiverValue() != null) null
|
||||||
else ReplaceInfixOrOperatorCallFix(parent)
|
else ReplaceInfixOrOperatorCallFix(parent)
|
||||||
}
|
}
|
||||||
else -> null
|
else -> null
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Add non-null asserted (!!) call" "true"
|
||||||
|
|
||||||
|
fun String?.foo() {
|
||||||
|
<caret>length
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Add non-null asserted (!!) call" "true"
|
||||||
|
|
||||||
|
fun String?.foo() {
|
||||||
|
this!!.length
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Add non-null asserted (!!) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun String?.foo() {
|
||||||
|
<caret>toLowerCase()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Add non-null asserted (!!) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun String?.foo() {
|
||||||
|
this!!.toLowerCase()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Add non-null asserted (!!) call" "true"
|
||||||
|
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a<caret>.length
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Add non-null asserted (!!) call" "true"
|
||||||
|
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a!!.length
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Add non-null asserted (!!) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a<caret>.toLowerCase()
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Add non-null asserted (!!) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a!!.toLowerCase()
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// "Replace with safe (?.) call" "false"
|
||||||
|
// ACTION: Add non-null asserted (!!) call
|
||||||
|
// ACTION: Convert to block body
|
||||||
|
// ACTION: Replace overloaded operator with function call
|
||||||
|
// ACTION: Replace with safe (this?.) call
|
||||||
|
// ACTION: Wrap with '?.let { ... }' call
|
||||||
|
// ERROR: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
|
||||||
|
|
||||||
|
fun String?.foo(exec: (String.() -> Unit)) = exec<caret>()
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo(array: Array<String>?) {
|
||||||
|
array<caret>[0]
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo(array: Array<String>?) {
|
||||||
|
array?.get(0)
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo(array: Array<String>?) {
|
||||||
|
array<caret>[0] = ""
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo(array: Array<String>?) {
|
||||||
|
array?.set(0, "")
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo(bar: Int?) {
|
||||||
|
bar +<caret> 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo(bar: Int?) {
|
||||||
|
bar?.plus(1)
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo() {}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
val fff: (() -> Unit)? = ::foo
|
||||||
|
<caret>fff()
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo() {}
|
||||||
|
|
||||||
|
fun bar() {
|
||||||
|
val fff: (() -> Unit)? = ::foo
|
||||||
|
fff?.invoke()
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo(list: List<String>?) {
|
||||||
|
list<caret>[0]
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo(list: List<String>?) {
|
||||||
|
list?.get(0)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Replace with dot call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String) {
|
||||||
|
a<caret>?.toLowerCase()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Replace with dot call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String) {
|
||||||
|
a.toLowerCase()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Replace with dot call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String) {
|
||||||
|
a<caret>?.length
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Replace with dot call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String) {
|
||||||
|
a.length
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a.apply {
|
||||||
|
this<caret>.length
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a.apply {
|
||||||
|
this?.length
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Replace with safe (this?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a.apply {
|
||||||
|
<caret>length
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Replace with safe (this?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a.apply {
|
||||||
|
this?.length
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Replace with safe (this?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a.apply {
|
||||||
|
<caret>toLowerCase()
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Replace with safe (this?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a.apply {
|
||||||
|
this?.toLowerCase()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Replace with safe (this?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun String?.foo() {
|
||||||
|
<caret>toLowerCase()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Replace with safe (this?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun String?.foo() {
|
||||||
|
this?.toLowerCase()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a<caret>.toLowerCase()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a?.toLowerCase()
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a.let {
|
||||||
|
it<caret>.length
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a.let {
|
||||||
|
it?.length
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a.let { b ->
|
||||||
|
b<caret>.length
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a.let { b ->
|
||||||
|
b?.length
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
// "Replace with safe (?.) call" "false"
|
||||||
|
// ACTION: Add non-null asserted (!!) call
|
||||||
|
// ACTION: Convert to expression body
|
||||||
|
// ACTION: Replace with safe (this?.) call
|
||||||
|
// ACTION: Wrap with '?.let { ... }' call
|
||||||
|
// ERROR: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type A?
|
||||||
|
|
||||||
|
class A {
|
||||||
|
fun foo() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun A?.bar() {
|
||||||
|
<caret>foo()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a<caret>.length
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Replace with safe (?.) call" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(a: String?) {
|
||||||
|
a?.length
|
||||||
|
}
|
||||||
@@ -234,6 +234,39 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/quickfix/addExclExclCall")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class AddExclExclCall extends AbstractQuickFixTest {
|
||||||
|
public void testAllFilesPresentInAddExclExclCall() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/addExclExclCall"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("implicit.kt")
|
||||||
|
public void testImplicit() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addExclExclCall/implicit.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("implicitFunctionCall.kt")
|
||||||
|
public void testImplicitFunctionCall() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addExclExclCall/implicitFunctionCall.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("normal.kt")
|
||||||
|
public void testNormal() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addExclExclCall/normal.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("normal2.kt")
|
||||||
|
public void testNormal2() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addExclExclCall/normal2.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/quickfix/addGenericUpperBound")
|
@TestMetadata("idea/testData/quickfix/addGenericUpperBound")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
@@ -7243,6 +7276,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("unsafeInvokeWithImplicitReceiver.kt")
|
||||||
|
public void testUnsafeInvokeWithImplicitReceiver() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInvokeWithImplicitReceiver.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("unsafeInvokeWithReceiver.kt")
|
@TestMetadata("unsafeInvokeWithReceiver.kt")
|
||||||
public void testUnsafeInvokeWithReceiver() throws Exception {
|
public void testUnsafeInvokeWithReceiver() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInvokeWithReceiver.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/nullables/unsafeInfixCall/unsafeInvokeWithReceiver.kt");
|
||||||
@@ -8427,6 +8466,45 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/quickfix/replaceInfixOrOperatorCall")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ReplaceInfixOrOperatorCall extends AbstractQuickFixTest {
|
||||||
|
public void testAllFilesPresentInReplaceInfixOrOperatorCall() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/replaceInfixOrOperatorCall"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("array.kt")
|
||||||
|
public void testArray() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceInfixOrOperatorCall/array.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("arraySet.kt")
|
||||||
|
public void testArraySet() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceInfixOrOperatorCall/arraySet.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("binaryOperator.kt")
|
||||||
|
public void testBinaryOperator() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceInfixOrOperatorCall/binaryOperator.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callExpression.kt")
|
||||||
|
public void testCallExpression() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceInfixOrOperatorCall/callExpression.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("list.kt")
|
||||||
|
public void testList() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceInfixOrOperatorCall/list.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/quickfix/replaceJvmFieldWithConst")
|
@TestMetadata("idea/testData/quickfix/replaceJvmFieldWithConst")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
@@ -8490,6 +8568,90 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/quickfix/replaceWithDotCall")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ReplaceWithDotCall extends AbstractQuickFixTest {
|
||||||
|
public void testAllFilesPresentInReplaceWithDotCall() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/replaceWithDotCall"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionCall.kt")
|
||||||
|
public void testFunctionCall() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceWithDotCall/functionCall.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("normal.kt")
|
||||||
|
public void testNormal() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceWithDotCall/normal.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/quickfix/replaceWithSafeCall")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class ReplaceWithSafeCall extends AbstractQuickFixTest {
|
||||||
|
public void testAllFilesPresentInReplaceWithSafeCall() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/replaceWithSafeCall"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("apply.kt")
|
||||||
|
public void testApply() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceWithSafeCall/apply.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("applyWithImplicitParameter.kt")
|
||||||
|
public void testApplyWithImplicitParameter() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceWithSafeCall/applyWithImplicitParameter.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("applyWithImplicitParameterFunctionCall.kt")
|
||||||
|
public void testApplyWithImplicitParameterFunctionCall() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceWithSafeCall/applyWithImplicitParameterFunctionCall.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("extFunction.kt")
|
||||||
|
public void testExtFunction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceWithSafeCall/extFunction.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("functionCall.kt")
|
||||||
|
public void testFunctionCall() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceWithSafeCall/functionCall.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("let.kt")
|
||||||
|
public void testLet() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceWithSafeCall/let.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("letWithParameter.kt")
|
||||||
|
public void testLetWithParameter() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceWithSafeCall/letWithParameter.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("noReplaceWithSafeCallForImplicitReceiver.kt")
|
||||||
|
public void testNoReplaceWithSafeCallForImplicitReceiver() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceWithSafeCall/noReplaceWithSafeCallForImplicitReceiver.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("normal.kt")
|
||||||
|
public void testNormal() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/replaceWithSafeCall/normal.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/quickfix/simplifyComparison")
|
@TestMetadata("idea/testData/quickfix/simplifyComparison")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user