FIR IDE: Ensure all fixes provided by HLDiagnosticFixFactory are of type

QuickFixActionBase.

This allows us to verify in the quickfix tests that the action comes
from a diagnostic-based quickfix, and not an available intention. We
are adding APIs that wrap IntentionActions as QuickFixActionBase.
This commit is contained in:
Mark Punzalan
2021-06-10 00:24:58 +00:00
committed by TeamCityServer
parent 43a8299f48
commit 783e5972d0
9 changed files with 86 additions and 62 deletions
@@ -12,7 +12,7 @@ import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.core.overrideImplement.KtImplementMembersHandler.Companion.getUnimplementedMembers import org.jetbrains.kotlin.idea.core.overrideImplement.KtImplementMembersHandler.Companion.getUnimplementedMembers
import org.jetbrains.kotlin.idea.core.util.KotlinIdeaCoreBundle import org.jetbrains.kotlin.idea.core.util.KotlinIdeaCoreBundle
import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactory import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactoryFromIntentionActions
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.analyse import org.jetbrains.kotlin.idea.frontend.api.analyse
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
@@ -120,21 +120,25 @@ internal class KtImplementAsConstructorParameterQuickfix(private val members: Co
object MemberNotImplementedQuickfixFactories { object MemberNotImplementedQuickfixFactories {
val abstractMemberNotImplemented = diagnosticFixFactory(KtFirDiagnostic.AbstractMemberNotImplemented::class) { diagnostic -> val abstractMemberNotImplemented =
getUnimplementedMemberFixes(diagnostic.psi) diagnosticFixFactoryFromIntentionActions(KtFirDiagnostic.AbstractMemberNotImplemented::class) { diagnostic ->
} getUnimplementedMemberFixes(diagnostic.psi)
}
val abstractClassMemberNotImplemented = diagnosticFixFactory(KtFirDiagnostic.AbstractClassMemberNotImplemented::class) { diagnostic -> val abstractClassMemberNotImplemented =
getUnimplementedMemberFixes(diagnostic.psi) diagnosticFixFactoryFromIntentionActions(KtFirDiagnostic.AbstractClassMemberNotImplemented::class) { diagnostic ->
} getUnimplementedMemberFixes(diagnostic.psi)
}
val manyInterfacesMemberNotImplemented = diagnosticFixFactory(KtFirDiagnostic.ManyInterfacesMemberNotImplemented::class) { diagnostic -> val manyInterfacesMemberNotImplemented =
getUnimplementedMemberFixes(diagnostic.psi) diagnosticFixFactoryFromIntentionActions(KtFirDiagnostic.ManyInterfacesMemberNotImplemented::class) { diagnostic ->
} getUnimplementedMemberFixes(diagnostic.psi)
}
val manyImplMemberNotImplemented = diagnosticFixFactory(KtFirDiagnostic.ManyImplMemberNotImplemented::class) { diagnostic -> val manyImplMemberNotImplemented =
getUnimplementedMemberFixes(diagnostic.psi, false) diagnosticFixFactoryFromIntentionActions(KtFirDiagnostic.ManyImplMemberNotImplemented::class) { diagnostic ->
} getUnimplementedMemberFixes(diagnostic.psi, false)
}
@OptIn(ExperimentalStdlibApi::class) @OptIn(ExperimentalStdlibApi::class)
private fun KtAnalysisSession.getUnimplementedMemberFixes( private fun KtAnalysisSession.getUnimplementedMemberFixes(
@@ -20,7 +20,8 @@ import kotlin.reflect.KClass
abstract class AbstractHLIntention<PSI : KtElement, INPUT : HLApplicatorInput>( abstract class AbstractHLIntention<PSI : KtElement, INPUT : HLApplicatorInput>(
elementType: KClass<PSI>, elementType: KClass<PSI>,
) : SelfTargetingIntention<PSI>(elementType.java, { "" }, { "" }) { val applicator: HLApplicator<PSI, INPUT>
) : SelfTargetingIntention<PSI>(elementType.java, applicator::getFamilyName) {
final override fun isApplicableTo(element: PSI, caretOffset: Int): Boolean { final override fun isApplicableTo(element: PSI, caretOffset: Int): Boolean {
val project = element.project// TODO expensive operation, may require traversing the tree up to containing PsiFile val project = element.project// TODO expensive operation, may require traversing the tree up to containing PsiFile
if (!applicator.isApplicableByPsi(element, project)) return false if (!applicator.isApplicableByPsi(element, project)) return false
@@ -61,6 +62,5 @@ abstract class AbstractHLIntention<PSI : KtElement, INPUT : HLApplicatorInput>(
} }
abstract val applicabilityRange: HLApplicabilityRange<PSI> abstract val applicabilityRange: HLApplicabilityRange<PSI>
abstract val applicator: HLApplicator<PSI, INPUT>
abstract val inputProvider: HLApplicatorInputProvider<PSI, INPUT> abstract val inputProvider: HLApplicatorInputProvider<PSI, INPUT>
} }
@@ -6,15 +6,19 @@
package org.jetbrains.kotlin.idea.fir.api.fixes package org.jetbrains.kotlin.idea.fir.api.fixes
import com.intellij.codeInsight.intention.IntentionAction import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.idea.api.applicator.HLApplicator import org.jetbrains.kotlin.idea.api.applicator.HLApplicator
import org.jetbrains.kotlin.idea.api.applicator.HLApplicatorInput import org.jetbrains.kotlin.idea.api.applicator.HLApplicatorInput
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtDiagnosticWithPsi import org.jetbrains.kotlin.idea.frontend.api.diagnostics.KtDiagnosticWithPsi
import org.jetbrains.kotlin.idea.quickfix.QuickFixActionBase
import kotlin.reflect.KClass import kotlin.reflect.KClass
sealed class HLDiagnosticFixFactory<DIAGNOSTIC : KtDiagnosticWithPsi<*>> { sealed class HLDiagnosticFixFactory<DIAGNOSTIC : KtDiagnosticWithPsi<*>> {
abstract fun KtAnalysisSession.createQuickFixes(diagnostic: DIAGNOSTIC): List<IntentionAction> abstract fun KtAnalysisSession.createQuickFixes(diagnostic: DIAGNOSTIC): List<QuickFixActionBase<*>>
abstract val diagnosticClass: KClass<DIAGNOSTIC> abstract val diagnosticClass: KClass<DIAGNOSTIC>
} }
@@ -27,11 +31,11 @@ private class HLDiagnosticFixFactoryWithFixedApplicator<DIAGNOSTIC : KtDiagnosti
createTargets.invoke(this, diagnostic).map { (target, input) -> HLQuickFix(target, input, applicator) } createTargets.invoke(this, diagnostic).map { (target, input) -> HLQuickFix(target, input, applicator) }
} }
private class HLDiagnosticFixFactoryWithVariableApplicator<DIAGNOSTIC : KtDiagnosticWithPsi<*>>( private class HLDiagnosticFixFactoryUsingQuickFixActionBase<DIAGNOSTIC : KtDiagnosticWithPsi<*>>(
override val diagnosticClass: KClass<DIAGNOSTIC>, override val diagnosticClass: KClass<DIAGNOSTIC>,
private val createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List<IntentionAction> private val createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List<QuickFixActionBase<*>>
) : HLDiagnosticFixFactory<DIAGNOSTIC>() { ) : HLDiagnosticFixFactory<DIAGNOSTIC>() {
override fun KtAnalysisSession.createQuickFixes(diagnostic: DIAGNOSTIC): List<IntentionAction> = override fun KtAnalysisSession.createQuickFixes(diagnostic: DIAGNOSTIC): List<QuickFixActionBase<*>> =
createQuickFixes.invoke(this, diagnostic) createQuickFixes.invoke(this, diagnostic)
} }
@@ -52,19 +56,42 @@ fun <DIAGNOSTIC : KtDiagnosticWithPsi<*>, TARGET_PSI : PsiElement, INPUT : HLApp
HLDiagnosticFixFactoryWithFixedApplicator(diagnosticClass, applicator, createTargets) HLDiagnosticFixFactoryWithFixedApplicator(diagnosticClass, applicator, createTargets)
/** /**
* Returns a [HLDiagnosticFixFactory] that creates [HLQuickFix]es from a diagnostic. * Returns a [HLDiagnosticFixFactory] that creates [QuickFixActionBase]s from a diagnostic.
*/ */
fun <DIAGNOSTIC : KtDiagnosticWithPsi<*>> diagnosticFixFactory( fun <DIAGNOSTIC : KtDiagnosticWithPsi<*>> diagnosticFixFactory(
diagnosticClass: KClass<DIAGNOSTIC>, diagnosticClass: KClass<DIAGNOSTIC>,
createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List<IntentionAction> createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List<QuickFixActionBase<*>>
): HLDiagnosticFixFactory<DIAGNOSTIC> = ): HLDiagnosticFixFactory<DIAGNOSTIC> =
HLDiagnosticFixFactoryWithVariableApplicator(diagnosticClass, createQuickFixes) HLDiagnosticFixFactoryUsingQuickFixActionBase(diagnosticClass, createQuickFixes)
/** /**
* Returns a [Collection] of [HLDiagnosticFixFactory] that creates [HLQuickFix]es from diagnostics that have the same type of [PsiElement]. * Returns a [Collection] of [HLDiagnosticFixFactory] that creates [QuickFixActionBase]s from diagnostics that have the same type of
* [PsiElement].
*/ */
fun <DIAGNOSTIC_PSI : PsiElement, DIAGNOSTIC : KtDiagnosticWithPsi<DIAGNOSTIC_PSI>> diagnosticFixFactories( fun <DIAGNOSTIC_PSI : PsiElement, DIAGNOSTIC : KtDiagnosticWithPsi<DIAGNOSTIC_PSI>> diagnosticFixFactories(
vararg diagnosticClasses: KClass<out DIAGNOSTIC>, vararg diagnosticClasses: KClass<out DIAGNOSTIC>,
createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List<IntentionAction> createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List<QuickFixActionBase<*>>
): Collection<HLDiagnosticFixFactory<out DIAGNOSTIC>> = ): Collection<HLDiagnosticFixFactory<out DIAGNOSTIC>> =
diagnosticClasses.map { HLDiagnosticFixFactoryWithVariableApplicator(it, createQuickFixes) } diagnosticClasses.map { HLDiagnosticFixFactoryUsingQuickFixActionBase(it, createQuickFixes) }
/**
* Returns a [HLDiagnosticFixFactory] that creates [IntentionAction]s from a diagnostic.
*/
fun <DIAGNOSTIC : KtDiagnosticWithPsi<*>> diagnosticFixFactoryFromIntentionActions(
diagnosticClass: KClass<DIAGNOSTIC>,
createIntentionActions: KtAnalysisSession.(DIAGNOSTIC) -> List<IntentionAction>
): HLDiagnosticFixFactory<DIAGNOSTIC> {
// Wrap the IntentionActions as QuickFixActionBase. This ensures all fixes are of type QuickFixActionBase.
val createQuickFixes: KtAnalysisSession.(DIAGNOSTIC) -> List<QuickFixActionBase<*>> = { diagnostic ->
val intentionActions = createIntentionActions.invoke(this, diagnostic)
intentionActions.map { IntentionActionAsQuickFixWrapper(it, diagnostic.psi) }
}
return HLDiagnosticFixFactoryUsingQuickFixActionBase(diagnosticClass, createQuickFixes)
}
private class IntentionActionAsQuickFixWrapper<T : PsiElement>(val intentionAction: IntentionAction, element: T) :
QuickFixActionBase<T>(element) {
override fun getText(): String = intentionAction.text
override fun getFamilyName(): String = intentionAction.familyName
override fun invoke(project: Project, editor: Editor?, file: PsiFile?) = intentionAction.invoke(project, editor, file)
}
@@ -25,18 +25,11 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
abstract class HLAddAccessorIntention(private val addGetter: Boolean, private val addSetter: Boolean) : abstract class HLAddAccessorIntention(private val addGetter: Boolean, private val addSetter: Boolean) :
AbstractHLIntention<KtProperty, HLApplicatorInput.Empty>(KtProperty::class) { AbstractHLIntention<KtProperty, HLApplicatorInput.Empty>(KtProperty::class, applicator(addGetter, addSetter)) {
override val applicabilityRange: HLApplicabilityRange<KtProperty> = applicabilityTarget { ktProperty -> override val applicabilityRange: HLApplicabilityRange<KtProperty> = applicabilityTarget { ktProperty ->
if (ktProperty.hasInitializer()) ktProperty.nameIdentifier else ktProperty if (ktProperty.hasInitializer()) ktProperty.nameIdentifier else ktProperty
} }
override val applicator: HLApplicator<KtProperty, HLApplicatorInput.Empty> = applicator {
applyTo { ktProperty, _, _, editor ->
AbstractAddAccessorsIntention.applyTo(ktProperty, editor, addGetter, addSetter)
}
familyAndActionName(AbstractAddAccessorsIntention.createFamilyName(addGetter, addSetter))
}
override val inputProvider: HLApplicatorInputProvider<KtProperty, HLApplicatorInput.Empty> = inputProvider { ktProperty -> override val inputProvider: HLApplicatorInputProvider<KtProperty, HLApplicatorInput.Empty> = inputProvider { ktProperty ->
val symbol = ktProperty.getVariableSymbol() as? KtPropertySymbol ?: return@inputProvider null val symbol = ktProperty.getVariableSymbol() as? KtPropertySymbol ?: return@inputProvider null
if (symbol.containsAnnotation(JVM_FIELD_CLASS_ID)) return@inputProvider null if (symbol.containsAnnotation(JVM_FIELD_CLASS_ID)) return@inputProvider null
@@ -6,8 +6,8 @@
package org.jetbrains.kotlin.idea.fir.intentions.declarations package org.jetbrains.kotlin.idea.fir.intentions.declarations
import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.fir.api.*
import org.jetbrains.kotlin.idea.api.applicator.with import org.jetbrains.kotlin.idea.api.applicator.with
import org.jetbrains.kotlin.idea.fir.api.*
import org.jetbrains.kotlin.idea.fir.api.applicator.HLApplicabilityRange import org.jetbrains.kotlin.idea.fir.api.applicator.HLApplicabilityRange
import org.jetbrains.kotlin.idea.fir.api.applicator.inputProvider import org.jetbrains.kotlin.idea.fir.api.applicator.inputProvider
import org.jetbrains.kotlin.idea.fir.applicators.ApplicabilityRanges import org.jetbrains.kotlin.idea.fir.applicators.ApplicabilityRanges
@@ -15,16 +15,7 @@ import org.jetbrains.kotlin.idea.fir.applicators.CallableReturnTypeUpdaterApplic
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
class HLSpecifyExplicitTypeForCallableDeclarationIntention : class HLSpecifyExplicitTypeForCallableDeclarationIntention :
AbstractHLIntention<KtCallableDeclaration, CallableReturnTypeUpdaterApplicator.Type>( AbstractHLIntention<KtCallableDeclaration, CallableReturnTypeUpdaterApplicator.Type>(KtCallableDeclaration::class, applicator) {
KtCallableDeclaration::class
) {
override val applicator = CallableReturnTypeUpdaterApplicator.applicator.with {
isApplicableByPsi { declaration: KtCallableDeclaration ->
if (declaration is KtConstructor<*> || declaration is KtFunctionLiteral) return@isApplicableByPsi false
declaration.typeReference == null && (declaration as? KtNamedFunction)?.hasBlockBody() != true
}
familyAndActionName(KotlinBundle.lazyMessage("specify.return.type.explicitly"))
}
override val applicabilityRange: HLApplicabilityRange<KtCallableDeclaration> = ApplicabilityRanges.SELF override val applicabilityRange: HLApplicabilityRange<KtCallableDeclaration> = ApplicabilityRanges.SELF
override val inputProvider = inputProvider<KtCallableDeclaration, CallableReturnTypeUpdaterApplicator.Type> { declaration -> override val inputProvider = inputProvider<KtCallableDeclaration, CallableReturnTypeUpdaterApplicator.Type> { declaration ->
@@ -32,4 +23,14 @@ class HLSpecifyExplicitTypeForCallableDeclarationIntention :
val denotableType = returnType.approximateToSuperPublicDenotable() ?: returnType val denotableType = returnType.approximateToSuperPublicDenotable() ?: returnType
with(CallableReturnTypeUpdaterApplicator.Type) { createByKtType(denotableType) } with(CallableReturnTypeUpdaterApplicator.Type) { createByKtType(denotableType) }
} }
companion object {
private val applicator = CallableReturnTypeUpdaterApplicator.applicator.with {
isApplicableByPsi { declaration: KtCallableDeclaration ->
if (declaration is KtConstructor<*> || declaration is KtFunctionLiteral) return@isApplicableByPsi false
declaration.typeReference == null && (declaration as? KtNamedFunction)?.hasBlockBody() != true
}
familyAndActionName(KotlinBundle.lazyMessage("specify.return.type.explicitly"))
}
}
} }
@@ -5,7 +5,7 @@
package org.jetbrains.kotlin.idea.quickfix.fixes package org.jetbrains.kotlin.idea.quickfix.fixes
import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactory import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactoryFromIntentionActions
import org.jetbrains.kotlin.idea.fir.intentions.HLAddGetterAndSetterIntention import org.jetbrains.kotlin.idea.fir.intentions.HLAddGetterAndSetterIntention
import org.jetbrains.kotlin.idea.fir.intentions.HLAddGetterIntention import org.jetbrains.kotlin.idea.fir.intentions.HLAddGetterIntention
import org.jetbrains.kotlin.idea.fir.intentions.HLAddSetterIntention import org.jetbrains.kotlin.idea.fir.intentions.HLAddSetterIntention
@@ -13,15 +13,16 @@ import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
import org.jetbrains.kotlin.psi.KtProperty import org.jetbrains.kotlin.psi.KtProperty
object AddAccessorsFactories { object AddAccessorsFactories {
val addAccessorsToUninitializedProperty = diagnosticFixFactory(KtFirDiagnostic.MustBeInitializedOrBeAbstract::class) { diagnostic -> val addAccessorsToUninitializedProperty =
val property: KtProperty = diagnostic.psi diagnosticFixFactoryFromIntentionActions(KtFirDiagnostic.MustBeInitializedOrBeAbstract::class) { diagnostic ->
val addGetter = property.getter == null val property: KtProperty = diagnostic.psi
val addSetter = property.isVar && property.setter == null val addGetter = property.getter == null
when { val addSetter = property.isVar && property.setter == null
addGetter && addSetter -> listOf(HLAddGetterAndSetterIntention()) when {
addGetter -> listOf(HLAddGetterIntention()) addGetter && addSetter -> listOf(HLAddGetterAndSetterIntention())
addSetter -> listOf(HLAddSetterIntention()) addGetter -> listOf(HLAddGetterIntention())
else -> emptyList() addSetter -> listOf(HLAddSetterIntention())
else -> emptyList()
}
} }
}
} }
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.idea.quickfix.fixes package org.jetbrains.kotlin.idea.quickfix.fixes
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactory import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactory
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
@@ -30,7 +29,7 @@ object AddExclExclCallFixFactories {
getFixForUnsafeCall(diagnostic.psi) getFixForUnsafeCall(diagnostic.psi)
} }
private fun KtAnalysisSession.getFixForUnsafeCall(psi: PsiElement): List<IntentionAction> { private fun KtAnalysisSession.getFixForUnsafeCall(psi: PsiElement): List<AddExclExclCallFix> {
val (target, hasImplicitReceiver) = when (val unwrapped = psi.unwrapParenthesesLabelsAndAnnotations()) { val (target, hasImplicitReceiver) = when (val unwrapped = psi.unwrapParenthesesLabelsAndAnnotations()) {
// `foo.bar` -> `foo!!.bar` // `foo.bar` -> `foo!!.bar`
is KtDotQualifiedExpression -> unwrapped.receiverExpression to false is KtDotQualifiedExpression -> unwrapped.receiverExpression to false
@@ -5,13 +5,13 @@
package org.jetbrains.kotlin.idea.quickfix.fixes package org.jetbrains.kotlin.idea.quickfix.fixes
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactory import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactory
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
import org.jetbrains.kotlin.idea.frontend.api.types.KtType import org.jetbrains.kotlin.idea.frontend.api.types.KtType
import org.jetbrains.kotlin.idea.frontend.api.types.KtTypeNullability import org.jetbrains.kotlin.idea.frontend.api.types.KtTypeNullability
import org.jetbrains.kotlin.idea.quickfix.AddExclExclCallFix
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -36,7 +36,7 @@ object TypeMismatchFactories {
psi: PsiElement, psi: PsiElement,
expectedType: KtType, expectedType: KtType,
actualType: KtType actualType: KtType
): List<IntentionAction> { ): List<AddExclExclCallFix> {
// TODO: Add more fixes than just AddExclExclCallFix when available. // TODO: Add more fixes than just AddExclExclCallFix when available.
if (!expectedType.canBeNull && actualType.canBeNull) { if (!expectedType.canBeNull && actualType.canBeNull) {
// We don't want to offer AddExclExclCallFix if we know the expression is definitely null, e.g.: // We don't want to offer AddExclExclCallFix if we know the expression is definitely null, e.g.:
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.idea.quickfix.fixes package org.jetbrains.kotlin.idea.quickfix.fixes
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.SmartPsiElementPointer import com.intellij.psi.SmartPsiElementPointer
@@ -180,7 +179,7 @@ object WrapWithSafeLetCallFixFactories {
private fun KtAnalysisSession.createWrapWithSafeLetCallInputForNullableExpressionIfMoreThanImmediateParentIsWrapped( private fun KtAnalysisSession.createWrapWithSafeLetCallInputForNullableExpressionIfMoreThanImmediateParentIsWrapped(
nullableExpression: KtExpression?, nullableExpression: KtExpression?,
isImplicitInvokeCallToMemberProperty: Boolean = false, isImplicitInvokeCallToMemberProperty: Boolean = false,
): List<IntentionAction> { ): List<HLQuickFix<KtExpression, Input>> {
val surroundingExpression = nullableExpression?.surroundingExpression val surroundingExpression = nullableExpression?.surroundingExpression
if ( if (
surroundingExpression == null || surroundingExpression == null ||
@@ -205,7 +204,7 @@ object WrapWithSafeLetCallFixFactories {
isImplicitInvokeCallToMemberProperty: Boolean = false, isImplicitInvokeCallToMemberProperty: Boolean = false,
surroundingExpression: KtExpression? = findParentExpressionAtNullablePosition(nullableExpression) surroundingExpression: KtExpression? = findParentExpressionAtNullablePosition(nullableExpression)
?: nullableExpression?.surroundingExpression ?: nullableExpression?.surroundingExpression
): List<IntentionAction> { ): List<HLQuickFix<KtExpression, Input>> {
if (nullableExpression == null || surroundingExpression == null) return emptyList() if (nullableExpression == null || surroundingExpression == null) return emptyList()
val existingNames = val existingNames =
nullableExpression.containingKtFile.getScopeContextForPosition(nullableExpression).scopes.getPossibleCallableNames() nullableExpression.containingKtFile.getScopeContextForPosition(nullableExpression).scopes.getPossibleCallableNames()