mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[Kotlin] convert the "redundant override" inspection to KotlinApplicableInspectionBase
KTIJ-16228 GitOrigin-RevId: 64604916f4061e55b67966487cfd142e329f4607
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9ebf164049
commit
8a8b2f3090
+85
-75
@@ -6,6 +6,7 @@ import com.intellij.codeInspection.CleanupLocalInspectionTool
|
||||
import com.intellij.codeInspection.LocalInspectionToolSession
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.codeInspection.util.InspectionMessage
|
||||
import com.intellij.modcommand.ModPsiUpdater
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.TextRange
|
||||
@@ -24,8 +25,9 @@ import org.jetbrains.kotlin.idea.base.analysis.api.utils.allOverriddenSymbolsWit
|
||||
import org.jetbrains.kotlin.idea.base.analysis.api.utils.isJavaSourceOrLibrary
|
||||
import org.jetbrains.kotlin.idea.base.psi.KotlinPsiHeuristics
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinApplicableInspectionBase
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.KotlinModCommandQuickFix
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicators.ApplicabilityRange
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens.MODIFIER_KEYWORDS_ARRAY
|
||||
import org.jetbrains.kotlin.lexer.KtTokens.OVERRIDE_KEYWORD
|
||||
@@ -39,79 +41,87 @@ import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.synthetic.canBePropertyAccessor
|
||||
|
||||
internal class KotlinRedundantOverrideInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): KtVisitorVoid =
|
||||
namedFunctionVisitor(fun(function) {
|
||||
val funKeyword = function.funKeyword ?: return
|
||||
val modifierList = function.modifierList ?: return
|
||||
val overrideKeyword = modifierList.getModifier(OVERRIDE_KEYWORD)
|
||||
if (overrideKeyword == null) return
|
||||
if (MODIFIER_EXCLUDE_OVERRIDE.any { modifierList.hasModifier(it) }) return
|
||||
if (KotlinPsiHeuristics.hasNonSuppressAnnotations(function)) return
|
||||
internal class KotlinRedundantOverrideInspection : KotlinApplicableInspectionBase.Simple<KtNamedFunction, Unit>(), CleanupLocalInspectionTool {
|
||||
override fun getProblemDescription(element: KtNamedFunction, context: Unit): @InspectionMessage String =
|
||||
KotlinBundle.message("redundant.overriding.method")
|
||||
|
||||
val qualifiedExpression = function.qualifiedExpression() ?: return
|
||||
val superExpression = qualifiedExpression.receiverExpression as? KtSuperExpression ?: return
|
||||
if (superExpression.superTypeQualifier != null) return
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): KtVisitor<*, *> =
|
||||
namedFunctionVisitor { function -> visitTargetElement(function, holder, isOnTheFly) }
|
||||
|
||||
val superCallElement = qualifiedExpression.selectorExpression as? KtCallElement ?: return
|
||||
if (!isSameFunctionName(superCallElement, function)) return
|
||||
if (!isSameArguments(superCallElement, function)) return
|
||||
override fun isApplicableByPsi(element: KtNamedFunction): Boolean {
|
||||
val modifierList = element.modifierList ?: return false
|
||||
val overrideKeyword = modifierList.getModifier(OVERRIDE_KEYWORD)
|
||||
if (overrideKeyword == null) return false
|
||||
if (MODIFIER_EXCLUDE_OVERRIDE.any { modifierList.hasModifier(it) }) return false
|
||||
if (KotlinPsiHeuristics.hasNonSuppressAnnotations(element)) return false
|
||||
|
||||
analyze(superCallElement) {
|
||||
val symbol = function.symbol
|
||||
val superCallInfo = superCallElement.resolveToCall() ?: return
|
||||
val superFunctionCallOrNull = superCallInfo.singleFunctionCallOrNull() ?: return
|
||||
val superFunctionSymbol = superFunctionCallOrNull.symbol
|
||||
val superFunctionIsAny = superFunctionSymbol.callableId in CALLABLE_IDS_OF_ANY
|
||||
val qualifiedExpression = element.qualifiedExpression() ?: return false
|
||||
val superExpression = qualifiedExpression.receiverExpression as? KtSuperExpression ?: return false
|
||||
if (superExpression.superTypeQualifier != null) return false
|
||||
|
||||
if (function.containingClassOrObject?.isData() == true) {
|
||||
if (superFunctionIsAny) return
|
||||
val allSuperOverriddenSymbols = superFunctionCallOrNull.symbol.allOverriddenSymbolsWithSelf
|
||||
if (allSuperOverriddenSymbols.any { it.callableId in CALLABLE_IDS_OF_ANY }) return
|
||||
val superCallElement = qualifiedExpression.selectorExpression as? KtCallElement ?: return false
|
||||
if (!isSameFunctionName(superCallElement, element)) return false
|
||||
if (!isSameArguments(superCallElement, element)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getApplicableRanges(element: KtNamedFunction): List<TextRange> {
|
||||
val funKeyword = element.funKeyword ?: return emptyList()
|
||||
val overrideKeyword = element.modifierList?.getModifier(OVERRIDE_KEYWORD) ?: return emptyList()
|
||||
return ApplicabilityRange.multiple(element) { listOf(funKeyword, overrideKeyword) }
|
||||
}
|
||||
|
||||
override fun createQuickFix(element: KtNamedFunction, context: Unit): KotlinModCommandQuickFix<KtNamedFunction> =
|
||||
RedundantOverrideFix
|
||||
|
||||
override fun KaSession.prepareContext(element: KtNamedFunction): Unit? {
|
||||
val symbol = element.symbol
|
||||
val qualifiedExpression = element.qualifiedExpression()
|
||||
val superCallElement = qualifiedExpression?.selectorExpression as? KtCallElement ?: return null
|
||||
val superCallInfo = superCallElement.resolveToCall() ?: return null
|
||||
val superFunctionCallOrNull = superCallInfo.singleFunctionCallOrNull() ?: return null
|
||||
val superFunctionSymbol = superFunctionCallOrNull.symbol
|
||||
val superFunctionIsAny = superFunctionSymbol.callableId in CALLABLE_IDS_OF_ANY
|
||||
|
||||
if (element.containingClassOrObject?.isData() == true) {
|
||||
if (superFunctionIsAny) return null
|
||||
val allSuperOverriddenSymbols = superFunctionCallOrNull.symbol.allOverriddenSymbolsWithSelf
|
||||
if (allSuperOverriddenSymbols.any { it.callableId in CALLABLE_IDS_OF_ANY }) return null
|
||||
}
|
||||
|
||||
if (element.hasDerivedProperty(symbol)) return null
|
||||
|
||||
val superCallValueParameters = superFunctionCallOrNull.partiallyAppliedSymbol.signature.valueParameters
|
||||
val functionValueParameters = symbol.valueParameters
|
||||
|
||||
if (functionValueParameters.size == superCallValueParameters.size &&
|
||||
functionValueParameters.zip(superCallValueParameters)
|
||||
.any {
|
||||
val functionParameterType = it.first.returnType
|
||||
val superParameterType = it.second.returnType
|
||||
val typesMatch = functionParameterType.semanticallyEquals(superParameterType)
|
||||
!typesMatch
|
||||
}
|
||||
) return null
|
||||
|
||||
if (function.hasDerivedProperty(symbol)) return
|
||||
val allFunctionOverriddenSymbols: Sequence<KaCallableSymbol> = symbol.allOverriddenSymbols
|
||||
// do nothing when the overridden function is from Any (e.g. `kotlin/Any.equals`)
|
||||
// and super function is abstract
|
||||
if (superFunctionIsAny && allFunctionOverriddenSymbols.any { it.modality == KaSymbolModality.ABSTRACT }) {
|
||||
return null
|
||||
}
|
||||
|
||||
val superCallValueParameters = superFunctionCallOrNull.partiallyAppliedSymbol.signature.valueParameters
|
||||
val functionValueParameters = symbol.valueParameters
|
||||
if (allFunctionOverriddenSymbols.any { it.isPackageVisibleNonJavaSymbol() }) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (functionValueParameters.size == superCallValueParameters.size &&
|
||||
functionValueParameters.zip(superCallValueParameters)
|
||||
.any {
|
||||
val functionParameterType = it.first.returnType
|
||||
val superParameterType = it.second.returnType
|
||||
val typesMatch = functionParameterType.semanticallyEquals(superParameterType)
|
||||
!typesMatch
|
||||
}
|
||||
) return
|
||||
if (element.isAmbiguouslyDerived(allFunctionOverriddenSymbols)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val allFunctionOverriddenSymbols: Sequence<KaCallableSymbol> = symbol.allOverriddenSymbols
|
||||
// do nothing when the overridden function is from Any (e.g. `kotlin/Any.equals`)
|
||||
// and super function is abstract
|
||||
if (superFunctionIsAny && allFunctionOverriddenSymbols.any { it.modality == KaSymbolModality.ABSTRACT }) {
|
||||
return
|
||||
}
|
||||
|
||||
if (allFunctionOverriddenSymbols.any { it.isPackageVisibleNonJavaSymbol() }) {
|
||||
return
|
||||
}
|
||||
|
||||
if (function.isAmbiguouslyDerived(allFunctionOverriddenSymbols)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val range = TextRange(overrideKeyword.startOffset, funKeyword.endOffset).shiftLeft(function.startOffset)
|
||||
val descriptor = holder.manager.createProblemDescriptor(
|
||||
function,
|
||||
range,
|
||||
KotlinBundle.message("redundant.overriding.method"),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
isOnTheFly,
|
||||
RedundantOverrideFix()
|
||||
)
|
||||
holder.registerProblem(descriptor)
|
||||
})
|
||||
return Unit
|
||||
}
|
||||
|
||||
private fun KaCallableSymbol.isPackageVisibleNonJavaSymbol(): Boolean {
|
||||
if (!origin.isJavaSourceOrLibrary()) return false
|
||||
@@ -204,19 +214,19 @@ internal class KotlinRedundantOverrideInspection : AbstractKotlinInspection(), C
|
||||
delegatedSuperDeclarationTypes.any { type.isSubtypeOf(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RedundantOverrideFix : KotlinModCommandQuickFix<KtNamedFunction>() {
|
||||
override fun getName(): String = KotlinBundle.message("redundant.override.fix.text")
|
||||
private object RedundantOverrideFix : KotlinModCommandQuickFix<KtNamedFunction>() {
|
||||
override fun getName(): String = KotlinBundle.message("redundant.override.fix.text")
|
||||
|
||||
override fun getFamilyName(): String = name
|
||||
override fun getFamilyName(): String = name
|
||||
|
||||
override fun applyFix(
|
||||
project: Project,
|
||||
element: KtNamedFunction,
|
||||
updater: ModPsiUpdater
|
||||
) {
|
||||
element.delete()
|
||||
}
|
||||
override fun applyFix(
|
||||
project: Project,
|
||||
element: KtNamedFunction,
|
||||
updater: ModPsiUpdater
|
||||
) {
|
||||
element.delete()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user