New evaluator that doesn't depend on the 'extract function' refactoring (KT-28192, KT-25220, KT-25222, KT-21650)

This commit is contained in:
Yan Zhulanow
2019-01-30 02:35:10 +03:00
parent 3e849294f0
commit 0657a3d399
69 changed files with 2300 additions and 1497 deletions
@@ -16,18 +16,23 @@
package org.jetbrains.kotlin.idea.caches.resolve
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
import org.jetbrains.kotlin.idea.project.ResolveElementCache
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoAfter
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.scopes.ExplicitImportsScope
import org.jetbrains.kotlin.resolve.scopes.ImportingScope
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.utils.ErrorLexicalScope
import org.jetbrains.kotlin.resolve.scopes.utils.addImportingScopes
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
@@ -43,132 +48,154 @@ class CodeFragmentAnalyzer(
@set:Inject // component dependency cycle
lateinit var resolveElementCache: ResolveElementCache
fun analyzeCodeFragment(codeFragment: KtCodeFragment, trace: BindingTrace, bodyResolveMode: BodyResolveMode): BindingTrace {
val codeFragmentElement = codeFragment.getContentElement()
fun analyzeCodeFragment(codeFragment: KtCodeFragment, bodyResolveMode: BodyResolveMode): BindingTrace {
val contextAnalysisResult = analyzeCodeFragmentContext(codeFragment, bodyResolveMode)
return doAnalyzeCodeFragment(codeFragment, contextAnalysisResult)
}
val (scopeForContextElement, dataFlowInfo, newBindingContext) = doAnalyzeCoreFragment(codeFragment) {
resolveElementCache!!.resolveToElements(listOf(it), bodyResolveMode)
} ?: return trace
private fun doAnalyzeCodeFragment(codeFragment: KtCodeFragment, contextAnalysisResult: ContextAnalysisResult): BindingTrace {
val (bindingContext, scope, dataFlowInfo) = contextAnalysisResult
val bindingTrace = DelegatingBindingTrace(bindingContext, "For code fragment analysis")
val newBindingTrace = DelegatingBindingTrace(newBindingContext, "For code fragment analysis")
when (codeFragmentElement) {
when (val contentElement = codeFragment.getContentElement()) {
is KtExpression -> {
PreliminaryDeclarationVisitor.createForExpression(
codeFragmentElement, newBindingTrace,
contentElement, bindingTrace,
expressionTypingServices.languageVersionSettings
)
expressionTypingServices.getTypeInfo(
scopeForContextElement,
codeFragmentElement,
TypeUtils.NO_EXPECTED_TYPE,
dataFlowInfo,
newBindingTrace,
false
scope, contentElement, TypeUtils.NO_EXPECTED_TYPE,
dataFlowInfo, bindingTrace, false
)
}
is KtTypeReference -> {
val context = TypeResolutionContext(
scopeForContextElement,
newBindingTrace,
true,
true,
codeFragment.suppressDiagnosticsInDebugMode()
scope, bindingTrace,
true, true, codeFragment.suppressDiagnosticsInDebugMode()
).noBareTypes()
typeResolver.resolvePossiblyBareType(context, codeFragmentElement)
typeResolver.resolvePossiblyBareType(context, contentElement)
}
}
return newBindingTrace
return bindingTrace
}
//TODO: this code should be moved into debugger which should set correct context for its code fragment
private fun KtElement.correctContextForElement(): KtElement {
return when (this) {
is KtProperty -> this.delegateExpressionOrInitializer
is KtFunctionLiteral -> this.bodyExpression?.statements?.lastOrNull()
is KtDeclarationWithBody -> this.bodyExpression
is KtBlockExpression -> this.statements.lastOrNull()
else -> null
} ?: this
}
private fun doAnalyzeCoreFragment(
codeFragment: KtCodeFragment,
resolveToElement: (KtElement) -> BindingContext
): Triple<LexicalScope, DataFlowInfo, BindingContext>? {
val context = codeFragment.context
val scopeForContextElement: LexicalScope?
private data class ContextAnalysisResult(
val bindingContext: BindingContext,
val scope: LexicalScope,
val dataFlowInfo: DataFlowInfo
)
fun getClassDescriptor(classOrObject: KtClassOrObject): Pair<BindingContext, ClassDescriptorWithResolutionScopes>? {
val bindingContext: BindingContext
val classDescriptor: ClassDescriptor?
if (!KtPsiUtil.isLocal(classOrObject)) {
bindingContext = resolveSession.bindingContext
classDescriptor = resolveSession.getClassDescriptor(classOrObject, NoLookupLocation.FROM_IDE)
} else {
bindingContext = resolveToElement(classOrObject)
classDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, classOrObject] as ClassDescriptor?
}
return (classDescriptor as? ClassDescriptorWithResolutionScopes)?.let { Pair(bindingContext, it) }
private fun analyzeCodeFragmentContext(codeFragment: KtCodeFragment, bodyResolveMode: BodyResolveMode): ContextAnalysisResult {
fun resolutionFactory(element: KtElement): BindingContext {
return resolveElementCache.resolveToElements(listOf(element), bodyResolveMode)
}
val bindingContextForContext: BindingContext
val context = refineContextElement(codeFragment.context)
var bindingContext: BindingContext = BindingContext.EMPTY
var dataFlowInfo: DataFlowInfo = DataFlowInfo.EMPTY
var scope: LexicalScope? = null
when (context) {
is KtPrimaryConstructor -> {
val (bindingContext, classDescriptor) = getClassDescriptor(context.getContainingClassOrObject()) ?: return null
scopeForContextElement = classDescriptor.scopeForInitializerResolution
dataFlowInfo = DataFlowInfo.EMPTY
bindingContextForContext = bindingContext
val containingClass = context.getContainingClassOrObject()
val resolutionResult = getClassDescriptor(containingClass, ::resolutionFactory)
if (resolutionResult != null) {
bindingContext = resolutionResult.bindingContext
scope = resolutionResult.descriptor.scopeForInitializerResolution
}
}
is KtSecondaryConstructor -> {
val correctedContext = context.getDelegationCall().calleeExpression!!
bindingContextForContext = resolveToElement(correctedContext)
scopeForContextElement = bindingContextForContext[BindingContext.LEXICAL_SCOPE, correctedContext]
dataFlowInfo = DataFlowInfo.EMPTY
val expression = context.bodyExpression ?: context.getDelegationCall().calleeExpression
if (expression != null) {
bindingContext = resolutionFactory(expression)
scope = bindingContext[BindingContext.LEXICAL_SCOPE, expression]
}
}
is KtClassOrObject -> {
val (bindingContext, classDescriptor) = getClassDescriptor(context) ?: return null
scopeForContextElement = classDescriptor.scopeForMemberDeclarationResolution
dataFlowInfo = DataFlowInfo.EMPTY
bindingContextForContext = bindingContext
val resolutionResult = getClassDescriptor(context, ::resolutionFactory)
if (resolutionResult != null) {
bindingContext = resolutionResult.bindingContext
scope = resolutionResult.descriptor.scopeForMemberDeclarationResolution
}
}
is KtFile -> {
scopeForContextElement = resolveSession.fileScopeProvider.getFileResolutionScope(context)
dataFlowInfo = DataFlowInfo.EMPTY
bindingContextForContext = BindingContext.EMPTY
bindingContext = resolveSession.bindingContext
scope = resolveSession.fileScopeProvider.getFileResolutionScope(context)
}
is KtElement -> {
val correctedContext = context.correctContextForElement()
bindingContextForContext = resolveToElement(correctedContext)
scopeForContextElement = bindingContextForContext[BindingContext.LEXICAL_SCOPE, correctedContext]
dataFlowInfo = bindingContextForContext.getDataFlowInfoAfter(correctedContext)
bindingContext = resolutionFactory(context)
scope = bindingContext[BindingContext.LEXICAL_SCOPE, context]
dataFlowInfo = bindingContext.getDataFlowInfoAfter(context)
}
else -> return null
}
if (scopeForContextElement == null) return null
val scopeWithImports = enrichScopeWithImports(scope ?: ErrorLexicalScope(), codeFragment)
return ContextAnalysisResult(bindingContext, scopeWithImports, dataFlowInfo)
}
private data class ClassResolutionResult(val bindingContext: BindingContext, val descriptor: ClassDescriptorWithResolutionScopes)
private fun getClassDescriptor(
classOrObject: KtClassOrObject,
resolutionFactory: (KtElement) -> BindingContext
): ClassResolutionResult? {
val bindingContext: BindingContext
val classDescriptor: ClassDescriptor?
if (!KtPsiUtil.isLocal(classOrObject)) {
bindingContext = resolveSession.bindingContext
classDescriptor = resolveSession.getClassDescriptor(classOrObject, NoLookupLocation.FROM_IDE)
} else {
bindingContext = resolutionFactory(classOrObject)
classDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, classOrObject] as ClassDescriptor?
}
return (classDescriptor as? ClassDescriptorWithResolutionScopes)?.let { ClassResolutionResult(bindingContext, it) }
}
private fun refineContextElement(context: PsiElement?): KtElement? {
return when (context) {
is KtParameter -> context.getParentOfType<KtFunction>(true)
is KtProperty -> context.delegateExpressionOrInitializer
is KtConstructor<*> -> context
is KtFunctionLiteral -> context.bodyExpression?.statements?.lastOrNull()
is KtDeclarationWithBody -> context.bodyExpression
is KtBlockExpression -> context.statements.lastOrNull()
else -> null
} ?: context as? KtElement
}
private fun enrichScopeWithImports(scope: LexicalScope, codeFragment: KtCodeFragment): LexicalScope {
val additionalImportingScopes = mutableListOf<ImportingScope>()
val externalDescriptors = codeFragment.externalDescriptors ?: emptyList()
if (externalDescriptors.isNotEmpty()) {
additionalImportingScopes += ExplicitImportsScope(externalDescriptors)
}
val importList = codeFragment.importsAsImportList()
if (importList == null || importList.imports.isEmpty()) {
return Triple(scopeForContextElement, dataFlowInfo, bindingContextForContext)
if (importList != null && importList.imports.isNotEmpty()) {
additionalImportingScopes += createImportScopes(importList)
}
val importScopes = importList.imports.mapNotNull {
if (additionalImportingScopes.isNotEmpty()) {
return scope.addImportingScopes(additionalImportingScopes)
}
return scope
}
private fun createImportScopes(importList: KtImportList): List<ImportingScope> {
return importList.imports.mapNotNull {
qualifierResolver.processImportReference(
it, resolveSession.moduleDescriptor, resolveSession.trace,
excludedImportNames = emptyList(), packageFragmentForVisibilityCheck = null
)
}
return Triple(scopeForContextElement.addImportingScopes(importScopes), dataFlowInfo, bindingContextForContext)
}
}
@@ -176,7 +176,9 @@ private object KotlinResolveDataProvider {
): AnalysisResult {
try {
if (analyzableElement is KtCodeFragment) {
return AnalysisResult.success(analyzeExpressionCodeFragment(codeFragmentAnalyzer, analyzableElement), moduleDescriptor)
val bodyResolveMode = BodyResolveMode.PARTIAL_FOR_COMPLETION
val bindingContext = codeFragmentAnalyzer.analyzeCodeFragment(analyzableElement, bodyResolveMode).bindingContext
return AnalysisResult.success(bindingContext, moduleDescriptor)
}
val trace = DelegatingBindingTrace(
@@ -217,19 +219,4 @@ private object KotlinResolveDataProvider {
return AnalysisResult.internalError(BindingContext.EMPTY, e)
}
}
private fun analyzeExpressionCodeFragment(codeFragmentAnalyzer: CodeFragmentAnalyzer, codeFragment: KtCodeFragment): BindingContext {
val contextElement = codeFragment.getContentElement()
val trace = if (contextElement != null) {
DelegatingBindingTrace(contextElement.analyze(BodyResolveMode.PARTIAL_FOR_COMPLETION), "Trace for code fragment resolution")
} else {
BindingTraceContext()
}
return codeFragmentAnalyzer.analyzeCodeFragment(
codeFragment,
trace,
BodyResolveMode.PARTIAL_FOR_COMPLETION //TODO: discuss it
).bindingContext
}
}
@@ -457,14 +457,12 @@ class ResolveElementCache(
}
private fun codeFragmentAdditionalResolve(codeFragment: KtCodeFragment, bodyResolveMode: BodyResolveMode): BindingTrace {
val trace = createDelegatingTrace(codeFragment, bodyResolveMode.bindingTraceFilter)
val contextResolveMode = if (bodyResolveMode == BodyResolveMode.PARTIAL)
BodyResolveMode.PARTIAL_FOR_COMPLETION
else
bodyResolveMode
return codeFragmentAnalyzer.analyzeCodeFragment(codeFragment, trace, contextResolveMode)
return codeFragmentAnalyzer.analyzeCodeFragment(codeFragment, contextResolveMode)
}
private fun annotationAdditionalResolve(resolveSession: ResolveSession, ktAnnotationEntry: KtAnnotationEntry): BindingTrace {