blob: 91cf8dfde06e1d588d3e040d3119fc0d5f4562e1 [file] [log] [blame]
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.caches.resolve
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.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.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.utils.addImportingScopes
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
import org.jetbrains.kotlin.types.expressions.PreliminaryDeclarationVisitor
import javax.inject.Inject
class CodeFragmentAnalyzer(
private val resolveSession: ResolveSession,
private val qualifierResolver: QualifiedExpressionResolver,
private val expressionTypingServices: ExpressionTypingServices,
private val typeResolver: TypeResolver
) {
// component dependency cycle
var resolveElementCache: ResolveElementCache? = null
@Inject set
fun analyzeCodeFragment(codeFragment: KtCodeFragment, trace: BindingTrace, bodyResolveMode: BodyResolveMode) {
val codeFragmentElement = codeFragment.getContentElement()
val (scopeForContextElement, dataFlowInfo) = getScopeAndDataFlowForAnalyzeFragment(codeFragment) {
resolveElementCache!!.resolveToElements(listOf(it), bodyResolveMode)
} ?: return
when (codeFragmentElement) {
is KtExpression -> {
PreliminaryDeclarationVisitor.createForExpression(
codeFragmentElement, trace,
expressionTypingServices.languageVersionSettings
)
expressionTypingServices.getTypeInfo(
scopeForContextElement,
codeFragmentElement,
TypeUtils.NO_EXPECTED_TYPE,
dataFlowInfo,
trace,
false
)
}
is KtTypeReference -> {
val context = TypeResolutionContext(
scopeForContextElement,
trace,
true,
true,
codeFragment.suppressDiagnosticsInDebugMode()
).noBareTypes()
typeResolver.resolvePossiblyBareType(context, codeFragmentElement)
}
}
}
//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 getScopeAndDataFlowForAnalyzeFragment(
codeFragment: KtCodeFragment,
resolveToElement: (KtElement) -> BindingContext
): Pair<LexicalScope, DataFlowInfo>? {
val context = codeFragment.context
val scopeForContextElement: LexicalScope?
val dataFlowInfo: DataFlowInfo
fun getClassDescriptor(classOrObject: KtClassOrObject): ClassDescriptor? {
if (!KtPsiUtil.isLocal(classOrObject)) {
return resolveSession.getClassDescriptor(classOrObject, NoLookupLocation.FROM_IDE)
}
return resolveToElement(classOrObject)[BindingContext.DECLARATION_TO_DESCRIPTOR, classOrObject] as ClassDescriptor?
}
when (context) {
is KtPrimaryConstructor -> {
val descriptor =
(getClassDescriptor(context.getContainingClassOrObject()) as? ClassDescriptorWithResolutionScopes) ?: return null
scopeForContextElement = descriptor.scopeForInitializerResolution
dataFlowInfo = DataFlowInfo.EMPTY
}
is KtSecondaryConstructor -> {
val correctedContext = context.getDelegationCall().calleeExpression!!
val contextForElement = resolveToElement(correctedContext)
scopeForContextElement = contextForElement[BindingContext.LEXICAL_SCOPE, correctedContext]
dataFlowInfo = DataFlowInfo.EMPTY
}
is KtClassOrObject -> {
val descriptor = (getClassDescriptor(context) as? ClassDescriptorWithResolutionScopes) ?: return null
scopeForContextElement = descriptor.scopeForMemberDeclarationResolution
dataFlowInfo = DataFlowInfo.EMPTY
}
is KtFile -> {
scopeForContextElement = resolveSession.fileScopeProvider.getFileResolutionScope(context)
dataFlowInfo = DataFlowInfo.EMPTY
}
is KtElement -> {
val correctedContext = context.correctContextForElement()
val contextForElement = resolveToElement(correctedContext)
scopeForContextElement = contextForElement[BindingContext.LEXICAL_SCOPE, correctedContext]
dataFlowInfo = contextForElement.getDataFlowInfoAfter(correctedContext)
}
else -> return null
}
if (scopeForContextElement == null) return null
val importList = codeFragment.importsAsImportList()
if (importList == null || importList.imports.isEmpty()) {
return scopeForContextElement to dataFlowInfo
}
val importScopes = importList.imports.mapNotNull {
qualifierResolver.processImportReference(
it, resolveSession.moduleDescriptor, resolveSession.trace,
excludedImportNames = emptyList(), packageFragmentForVisibilityCheck = null
)
}
return scopeForContextElement.addImportingScopes(importScopes) to dataFlowInfo
}
}