New evaluator that doesn't depend on the 'extract function' refactoring (KT-28192, KT-25220, KT-25222, KT-21650)
This commit is contained in:
+110
-83
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+3
-16
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user