Support contracts in PartialBodyResolveFilter

Previously, PartialBodyResolveFilter didn't know about contracts and was
filtering out calls of such functions, leading to unstable completion in
cases like that:

fun test(x: Any?) {
    require(x is String)
    x.<caret>
}

However, PartialBodyResolveFilter works by pure PSI, while to determine
if function has a contract we have to resolve it.

To solve it, we do something very similar to what has been done with
Nothin-returning functions: introduce
KotlinProbablyContractedFunctionShortNameIndex, which collects all
function which *may* have a contract during indexing.

^KT-25275 Fixed
This commit is contained in:
Dmitry Savvinov
2018-07-20 19:35:45 +03:00
parent 5ab79a111d
commit f90b29c2e3
23 changed files with 293 additions and 2 deletions
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.resolve.lazy
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiRecursiveElementVisitor
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
@@ -38,6 +39,7 @@ class PartialBodyResolveFilter(
private val statementMarks = StatementMarks()
private val globalProbablyNothingCallableNames = ProbablyNothingCallableNames.getInstance(declaration.project)
private val globalProbablyContractedCallableNames = ProbablyContractedCallableNames.getInstance(declaration.project)
private val contextNothingFunctionNames = HashSet<String>()
private val contextNothingVariableNames = HashSet<String>()
@@ -169,6 +171,19 @@ class PartialBodyResolveFilter(
}
}
override fun visitCallExpression(expression: KtCallExpression) {
super.visitCallExpression(expression)
val nameReference = expression.calleeExpression as? KtNameReferenceExpression ?: return
if (!globalProbablyContractedCallableNames.isProbablyContractedCallableName(nameReference.getReferencedName())) return
val mentionedSmartCastName = expression.findMentionedName(filter)
if (mentionedSmartCastName != null) {
addPlace(mentionedSmartCastName, expression)
}
}
override fun visitBinaryWithTypeRHSExpression(expression: KtBinaryExpressionWithTypeRHS) {
expression.acceptChildren(this)
@@ -259,6 +274,25 @@ class PartialBodyResolveFilter(
return map
}
private fun KtExpression.findMentionedName(filter: (SmartCastName) -> Boolean): SmartCastName? {
var foundMentionedName: SmartCastName? = null
val visitor = object : PsiRecursiveElementVisitor() {
override fun visitElement(element: PsiElement) {
if (foundMentionedName != null) return
if (element !is KtSimpleNameExpression) super.visitElement(element)
if (element !is KtExpression) return
element.smartCastExpressionName()?.takeIf(filter)?.let { foundMentionedName = it }
}
}
accept(visitor)
return foundMentionedName
}
/**
* Returns names of expressions that would possibly be smart cast
* in then (first component) and else (second component)
@@ -0,0 +1,18 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve.lazy
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
interface ProbablyContractedCallableNames {
fun isProbablyContractedCallableName(name: String): Boolean
companion object {
fun getInstance(project: Project): ProbablyContractedCallableNames =
ServiceManager.getService(project, ProbablyContractedCallableNames::class.java)!!
}
}