mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[Kotlin] Modified KotlinCallProcessor to allow processing unresolved elements
KTIJ-29049 GitOrigin-RevId: bbadaf150875b69b444916052555bf156bdef578
This commit is contained in:
committed by
intellij-monorepo-bot
parent
b4c2eb5f19
commit
baafb50e56
+105
-35
@@ -6,6 +6,7 @@ import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.util.descendantsOfType
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.analysis.api.calls.*
|
||||
@@ -61,6 +62,34 @@ class FunctionCallTarget(
|
||||
get() = partiallyAppliedSymbol.symbol
|
||||
}
|
||||
|
||||
interface KotlinCallTargetProcessor {
|
||||
/**
|
||||
* Processes a successfully resolved [CallTarget].
|
||||
* If false is returned from this function, no further elements will be processed.
|
||||
*/
|
||||
fun KtAnalysisSession.processCallTarget(target: CallTarget): Boolean
|
||||
|
||||
/**
|
||||
* Processes a call that resolved as an error.
|
||||
* If false is returned from this function, no further elements will be processed.
|
||||
*/
|
||||
fun KtAnalysisSession.processUnresolvedCall(element: KtElement, callInfo: KtCallInfo?): Boolean
|
||||
}
|
||||
|
||||
private fun (KtAnalysisSession.(CallTarget) -> Unit).toCallTargetProcessor(): KotlinCallTargetProcessor {
|
||||
val processor = this
|
||||
return object : KotlinCallTargetProcessor {
|
||||
override fun KtAnalysisSession.processCallTarget(target: CallTarget): Boolean {
|
||||
processor(target)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun KtAnalysisSession.processUnresolvedCall(element: KtElement, callInfo: KtCallInfo?): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object KotlinCallProcessor {
|
||||
private val NAME_REFERENCE_IGNORED_PARENTS = arrayOf(
|
||||
KtUserType::class.java,
|
||||
@@ -72,7 +101,11 @@ object KotlinCallProcessor {
|
||||
)
|
||||
|
||||
fun process(element: PsiElement, processor: KtAnalysisSession.(CallTarget) -> Unit) {
|
||||
when (element) {
|
||||
process(element, processor.toCallTargetProcessor())
|
||||
}
|
||||
|
||||
fun process(element: PsiElement, processor: KotlinCallTargetProcessor): Boolean {
|
||||
return when (element) {
|
||||
is KtArrayAccessExpression -> handle(element, processor)
|
||||
is KtCallExpression -> handle(element, processor)
|
||||
is KtUnaryExpression -> handle(element, processor)
|
||||
@@ -83,8 +116,12 @@ object KotlinCallProcessor {
|
||||
is KtNameReferenceExpression -> {
|
||||
if (shouldHandleNameReference(element)) {
|
||||
handle(element, processor)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,57 +154,75 @@ object KotlinCallProcessor {
|
||||
return current
|
||||
}
|
||||
|
||||
private fun handle(element: KtElement, processor: KtAnalysisSession.(CallTarget) -> Unit) {
|
||||
private fun handle(element: KtElement, processor: KotlinCallTargetProcessor): Boolean {
|
||||
analyze(element) {
|
||||
fun handleSpecial(element: KtElement, filter: (KtSymbol) -> Boolean) {
|
||||
val symbols = element.mainReference?.resolveToSymbols() ?: return
|
||||
fun handleSpecial(element: KtElement, filter: (KtSymbol) -> Boolean): Boolean {
|
||||
val symbols = element.mainReference?.resolveToSymbols() ?: return true
|
||||
for (symbol in symbols) {
|
||||
if (!filter(symbol)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (symbol is KtFunctionLikeSymbol) {
|
||||
val signature = symbol.asSignature()
|
||||
val partiallyAppliedSymbol = KtPartiallyAppliedFunctionSymbol(signature, null, null)
|
||||
val call = KtSimpleFunctionCall(partiallyAppliedSymbol, linkedMapOf(), mapOf(), _isImplicitInvoke = false)
|
||||
processor(FunctionCallTarget(element, call, partiallyAppliedSymbol))
|
||||
} else if (symbol is KtVariableLikeSymbol) {
|
||||
val signature = symbol.asSignature()
|
||||
val partiallyAppliedSymbol = KtPartiallyAppliedVariableSymbol(signature, null, null)
|
||||
val call = KtSimpleVariableAccessCall(partiallyAppliedSymbol, linkedMapOf(), KtSimpleVariableAccess.Read)
|
||||
processor(VariableCallTarget(element, call, partiallyAppliedSymbol))
|
||||
val shouldContinue = with(processor) {
|
||||
when (symbol) {
|
||||
is KtFunctionLikeSymbol -> {
|
||||
val signature = symbol.asSignature()
|
||||
val partiallyAppliedSymbol = KtPartiallyAppliedFunctionSymbol(signature, null, null)
|
||||
val call = KtSimpleFunctionCall(partiallyAppliedSymbol, linkedMapOf(), mapOf(), _isImplicitInvoke = false)
|
||||
processCallTarget(FunctionCallTarget(element, call, partiallyAppliedSymbol))
|
||||
}
|
||||
|
||||
is KtVariableLikeSymbol -> {
|
||||
val signature = symbol.asSignature()
|
||||
val partiallyAppliedSymbol = KtPartiallyAppliedVariableSymbol(signature, null, null)
|
||||
val call = KtSimpleVariableAccessCall(partiallyAppliedSymbol, linkedMapOf(), KtSimpleVariableAccess.Read)
|
||||
processCallTarget(VariableCallTarget(element, call, partiallyAppliedSymbol))
|
||||
}
|
||||
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
if (!shouldContinue) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if (element is KtForExpression) {
|
||||
handleSpecial(element) { it is KtFunctionLikeSymbol }
|
||||
return
|
||||
return handleSpecial(element) { it is KtFunctionLikeSymbol }
|
||||
}
|
||||
|
||||
if (element is KtDestructuringDeclarationEntry) {
|
||||
handleSpecial(element) { !(it is KtLocalVariableSymbol && it.psi == element) }
|
||||
return
|
||||
return handleSpecial(element) { !(it is KtLocalVariableSymbol && it.psi == element) }
|
||||
}
|
||||
|
||||
val call = element.resolveCall()?.successfulCallOrNull<KtCall>()
|
||||
val callInfo = element.resolveCall()
|
||||
val call = callInfo?.successfulCallOrNull<KtCall>()
|
||||
|
||||
if (call != null) {
|
||||
when (call) {
|
||||
is KtDelegatedConstructorCall -> processor(FunctionCallTarget(element, call, call.partiallyAppliedSymbol))
|
||||
is KtSimpleFunctionCall -> processor(FunctionCallTarget(element, call, call.partiallyAppliedSymbol))
|
||||
is KtCompoundVariableAccessCall -> {
|
||||
processor(VariableCallTarget(element, call, call.partiallyAppliedSymbol))
|
||||
processor(FunctionCallTarget(element, call, call.compoundAccess.operationPartiallyAppliedSymbol))
|
||||
return with(processor) {
|
||||
if (call != null) {
|
||||
when (call) {
|
||||
is KtDelegatedConstructorCall -> processCallTarget(FunctionCallTarget(element, call, call.partiallyAppliedSymbol))
|
||||
is KtSimpleFunctionCall -> processCallTarget(FunctionCallTarget(element, call, call.partiallyAppliedSymbol))
|
||||
is KtCompoundVariableAccessCall -> {
|
||||
processCallTarget(VariableCallTarget(element, call, call.partiallyAppliedSymbol))
|
||||
processCallTarget(FunctionCallTarget(element, call, call.compoundAccess.operationPartiallyAppliedSymbol))
|
||||
}
|
||||
|
||||
is KtSimpleVariableAccessCall -> {
|
||||
processCallTarget(VariableCallTarget(element, call, call.partiallyAppliedSymbol))
|
||||
}
|
||||
|
||||
is KtCompoundArrayAccessCall -> {
|
||||
processCallTarget(FunctionCallTarget(element, call, call.getPartiallyAppliedSymbol))
|
||||
processCallTarget(FunctionCallTarget(element, call, call.setPartiallyAppliedSymbol))
|
||||
}
|
||||
|
||||
else -> true
|
||||
}
|
||||
is KtSimpleVariableAccessCall -> {
|
||||
processor(VariableCallTarget(element, call, call.partiallyAppliedSymbol))
|
||||
}
|
||||
is KtCompoundArrayAccessCall -> {
|
||||
processor(FunctionCallTarget(element, call, call.getPartiallyAppliedSymbol))
|
||||
processor(FunctionCallTarget(element, call, call.setPartiallyAppliedSymbol))
|
||||
}
|
||||
else -> {}
|
||||
} else {
|
||||
processUnresolvedCall(element, callInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,8 +230,23 @@ object KotlinCallProcessor {
|
||||
}
|
||||
|
||||
fun KotlinCallProcessor.process(elements: Collection<PsiElement>, processor: KtAnalysisSession.(CallTarget) -> Unit) {
|
||||
process(elements, processor.toCallTargetProcessor())
|
||||
}
|
||||
|
||||
fun KotlinCallProcessor.process(elements: Collection<PsiElement>, processor: KotlinCallTargetProcessor) {
|
||||
for (element in elements) {
|
||||
ProgressManager.checkCanceled()
|
||||
process(element, processor)
|
||||
if (!process(element, processor)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun KotlinCallProcessor.processExpressionsRecursively(element: PsiElement, processor: KtAnalysisSession.(CallTarget) -> Unit) {
|
||||
processExpressionsRecursively(element, processor.toCallTargetProcessor())
|
||||
}
|
||||
|
||||
fun KotlinCallProcessor.processExpressionsRecursively(element: PsiElement, processor: KotlinCallTargetProcessor) {
|
||||
val allExpressions = element.descendantsOfType<KtExpression>().toList()
|
||||
process(allExpressions, processor)
|
||||
}
|
||||
+27
-26
@@ -5,36 +5,31 @@ import com.intellij.codeInspection.IntentionWrapper
|
||||
import com.intellij.codeInspection.LocalInspectionToolSession
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.psi.util.descendantsOfType
|
||||
import org.jetbrains.kotlin.analysis.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.analysis.api.analyze
|
||||
import org.jetbrains.kotlin.analysis.api.calls.KtCallableMemberCall
|
||||
import org.jetbrains.kotlin.analysis.api.calls.successfulCallOrNull
|
||||
import org.jetbrains.kotlin.analysis.api.calls.KtCallInfo
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtFunctionSymbol
|
||||
import org.jetbrains.kotlin.analysis.api.symbols.KtKotlinPropertySymbol
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.idea.base.codeInsight.CallTarget
|
||||
import org.jetbrains.kotlin.idea.base.codeInsight.KotlinCallProcessor
|
||||
import org.jetbrains.kotlin.idea.base.codeInsight.process
|
||||
import org.jetbrains.kotlin.idea.base.projectStructure.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.base.codeInsight.KotlinCallTargetProcessor
|
||||
import org.jetbrains.kotlin.idea.base.codeInsight.processExpressionsRecursively
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.inspections.AbstractKotlinInspection
|
||||
import org.jetbrains.kotlin.idea.codeinsight.utils.getFqNameIfPackageOrNonLocal
|
||||
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFixBase
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.psi.namedFunctionVisitor
|
||||
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
|
||||
|
||||
internal class RedundantSuspendModifierInspection : AbstractKotlinInspection() {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||
return namedFunctionVisitor(fun(function) {
|
||||
if (!function.languageVersionSettings.supportsFeature(LanguageFeature.Coroutines)) return
|
||||
|
||||
val suspendModifier = function.modifierList?.getModifier(KtTokens.SUSPEND_KEYWORD) ?: return
|
||||
if (!function.hasBody()) return
|
||||
if (function.hasModifier(KtTokens.OVERRIDE_KEYWORD) || function.hasModifier(KtTokens.ACTUAL_KEYWORD)) return
|
||||
@@ -43,7 +38,6 @@ internal class RedundantSuspendModifierInspection : AbstractKotlinInspection() {
|
||||
val functionSymbol = function.getFunctionLikeSymbol() as? KtFunctionSymbol ?: return
|
||||
if (functionSymbol.modality == Modality.OPEN) return
|
||||
|
||||
if (function.hasUnresolvedCalls()) return
|
||||
if (function.hasSuspendOrUnresolvedCall(functionSymbol)) return
|
||||
|
||||
holder.registerProblem(
|
||||
@@ -66,24 +60,31 @@ internal class RedundantSuspendModifierInspection : AbstractKotlinInspection() {
|
||||
return this is KtFunctionSymbol && isSuspend
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
private fun KtNamedFunction.hasUnresolvedCalls(): Boolean {
|
||||
return anyDescendantOfType<KtExpression> { expression ->
|
||||
val resolvedCall = expression.resolveCall() ?: return@anyDescendantOfType false
|
||||
resolvedCall.successfulCallOrNull<KtCallableMemberCall<*, *>>() == null
|
||||
}
|
||||
}
|
||||
|
||||
context(KtAnalysisSession)
|
||||
private fun KtNamedFunction.hasSuspendOrUnresolvedCall(functionSymbol: KtFunctionSymbol): Boolean {
|
||||
val allExpressions = descendantsOfType<KtExpression>().toList()
|
||||
var hasSuspendCall = false
|
||||
var hasSuspendOrUnresolvedCall = false
|
||||
val selfCallableId = functionSymbol.callableIdIfNonLocal
|
||||
KotlinCallProcessor.process(allExpressions) { target ->
|
||||
if (target.symbol.isSuspendSymbol() && target.symbol.callableIdIfNonLocal != selfCallableId) {
|
||||
hasSuspendCall = true
|
||||
|
||||
KotlinCallProcessor.processExpressionsRecursively(this, object : KotlinCallTargetProcessor {
|
||||
override fun KtAnalysisSession.processCallTarget(target: CallTarget): Boolean {
|
||||
if (target.symbol.isSuspendSymbol() && target.symbol.callableIdIfNonLocal != selfCallableId) {
|
||||
hasSuspendOrUnresolvedCall = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return hasSuspendCall
|
||||
|
||||
override fun KtAnalysisSession.processUnresolvedCall(element: KtElement, callInfo: KtCallInfo?): Boolean {
|
||||
if (callInfo != null) {
|
||||
// A callInfo of null means that the element could not be resolved at all, so it is not even unresolved.
|
||||
// For example, if the element is not an expression, so we ignore those cases.
|
||||
hasSuspendOrUnresolvedCall = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
return hasSuspendOrUnresolvedCall
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user