Refactor: merge duplicated code for code fragment analysis

Introduce CodeFragmentAnalyzer
This commit is contained in:
Pavel V. Talanov
2015-07-30 17:49:32 +03:00
parent 7130e9f881
commit 6802973743
5 changed files with 134 additions and 132 deletions
@@ -54,16 +54,6 @@ public fun JetExpression.analyzeInContext(
return trace.getBindingContext()
}
public fun JetExpression.computeTypeInContext(
scope: JetScope,
trace: BindingTrace = BindingTraceContext(),
dataFlowInfo: DataFlowInfo = DataFlowInfo.EMPTY,
expectedType: JetType = TypeUtils.NO_EXPECTED_TYPE,
module: ModuleDescriptor = scope.getModule()
): JetType {
return computeTypeInfoInContext(scope, trace, dataFlowInfo, expectedType, module).type.safeType(this)
}
public fun JetType?.safeType(expression: JetExpression): JetType {
if (this != null) return this
@@ -1,86 +0,0 @@
/*
* 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.resolve.util
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
import org.jetbrains.kotlin.resolve.scopes.ChainedScope
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
//TODO: this code should be moved into debugger which should set correct context for its code fragment
private fun JetExpression.correctContextForExpression(): JetExpression {
return when (this) {
is JetProperty -> this.getDelegateExpressionOrInitializer()
is JetFunctionLiteral -> this.getBodyExpression()?.getStatements()?.lastOrNull()
is JetDeclarationWithBody -> this.getBodyExpression()
is JetBlockExpression -> this.getStatements().lastOrNull()
else -> {
this.siblings(forward = false, withItself = false).firstIsInstanceOrNull<JetExpression>()
?: this.parents.firstIsInstanceOrNull<JetExpression>()
}
} ?: this
}
public fun JetCodeFragment.getScopeAndDataFlowForAnalyzeFragment(
resolveSession: KotlinCodeAnalyzer,
resolveToElement: (JetElement) -> BindingContext
): Pair<JetScope, DataFlowInfo>? {
val context = getContext()
if (context !is JetExpression) return null
val scopeForContextElement: JetScope?
val dataFlowInfo: DataFlowInfo
when (context) {
is JetClassOrObject -> {
val descriptor = resolveSession.getClassDescriptor(context) as ClassDescriptorWithResolutionScopes
scopeForContextElement = descriptor.getScopeForMemberDeclarationResolution()
dataFlowInfo = DataFlowInfo.EMPTY
}
is JetExpression -> {
val correctedContext = context.correctContextForExpression()
val contextForElement = resolveToElement(correctedContext)
scopeForContextElement = contextForElement[BindingContext.RESOLUTION_SCOPE, correctedContext]
dataFlowInfo = contextForElement.getDataFlowInfo(correctedContext)
}
is JetFile -> {
scopeForContextElement = resolveSession.getFileScopeProvider().getFileScope(context)
dataFlowInfo = DataFlowInfo.EMPTY
}
else -> return null
}
if (scopeForContextElement == null) return null
val codeFragmentScope = resolveSession.getFileScopeProvider().getFileScope(this)
val chainedScope = ChainedScope(
scopeForContextElement.getContainingDeclaration(),
"Scope for resolve code fragment",
scopeForContextElement, codeFragmentScope)
return chainedScope to dataFlowInfo
}
@@ -0,0 +1,120 @@
/*
* 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.ClassDescriptorWithResolutionScopes
import org.jetbrains.kotlin.idea.project.ResolveElementCache
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.siblings
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
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.ChainedScope
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import javax.inject.Inject
import kotlin.properties.Delegates
public class CodeFragmentAnalyzer(
private val resolveSession: ResolveSession,
private val expressionTypingServices: ExpressionTypingServices
) {
// component dependency cycle
public var resolveElementCache: ResolveElementCache? = null
@Inject set
public fun analyzeCodeFragment(codeFragment: JetCodeFragment, trace: BindingTrace, bodyResolveMode: BodyResolveMode) {
val codeFragmentExpression = codeFragment.getContentElement()
if (codeFragmentExpression !is JetExpression) return
val (scopeForContextElement, dataFlowInfo) = getScopeAndDataFlowForAnalyzeFragment(codeFragment) {
resolveElementCache!!.resolveToElement(it, bodyResolveMode)
} ?: return
expressionTypingServices.getTypeInfo(
scopeForContextElement,
codeFragmentExpression,
TypeUtils.NO_EXPECTED_TYPE,
dataFlowInfo,
trace
)
}
//TODO: this code should be moved into debugger which should set correct context for its code fragment
private fun JetExpression.correctContextForExpression(): JetExpression {
return when (this) {
is JetProperty -> this.getDelegateExpressionOrInitializer()
is JetFunctionLiteral -> this.getBodyExpression()?.getStatements()?.lastOrNull()
is JetDeclarationWithBody -> this.getBodyExpression()
is JetBlockExpression -> this.getStatements().lastOrNull()
else -> {
this.siblings(forward = false, withItself = false).firstIsInstanceOrNull<JetExpression>()
?: this.parents.firstIsInstanceOrNull<JetExpression>()
}
} ?: this
}
private fun getScopeAndDataFlowForAnalyzeFragment(
codeFragment: JetCodeFragment,
resolveToElement: (JetElement) -> BindingContext
): Pair<JetScope, DataFlowInfo>? {
val context = codeFragment.getContext()
if (context !is JetExpression) return null
val scopeForContextElement: JetScope?
val dataFlowInfo: DataFlowInfo
when (context) {
is JetClassOrObject -> {
val descriptor = resolveSession.getClassDescriptor(context) as ClassDescriptorWithResolutionScopes
scopeForContextElement = descriptor.getScopeForMemberDeclarationResolution()
dataFlowInfo = DataFlowInfo.EMPTY
}
is JetExpression -> {
val correctedContext = context.correctContextForExpression()
val contextForElement = resolveToElement(correctedContext)
scopeForContextElement = contextForElement[BindingContext.RESOLUTION_SCOPE, correctedContext]
dataFlowInfo = contextForElement.getDataFlowInfo(correctedContext)
}
is JetFile -> {
scopeForContextElement = resolveSession.getFileScopeProvider().getFileScope(context)
dataFlowInfo = DataFlowInfo.EMPTY
}
else -> return null
}
if (scopeForContextElement == null) return null
val codeFragmentScope = resolveSession.getFileScopeProvider().getFileScope(codeFragment)
val chainedScope = ChainedScope(
scopeForContextElement.getContainingDeclaration(),
"Scope for resolve code fragment",
scopeForContextElement, codeFragmentScope)
return chainedScope to dataFlowInfo
}
}
@@ -27,7 +27,6 @@ import com.intellij.psi.util.PsiModificationTracker
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.containers.SLRUCache
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.analyzer.analyzeInContext
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.container.ComponentProvider
import org.jetbrains.kotlin.container.get
@@ -37,15 +36,12 @@ import org.jetbrains.kotlin.context.withProject
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.frontend.di.createContainerForLazyBodyResolve
import org.jetbrains.kotlin.idea.project.ResolveElementCache
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.*
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.util.getScopeAndDataFlowForAnalyzeFragment
import org.jetbrains.kotlin.types.TypeUtils
import java.util.HashMap
public interface CacheExtension<T> {
@@ -283,21 +279,12 @@ private object KotlinResolveDataProvider {
}
private fun analyzeExpressionCodeFragment(componentProvider: ComponentProvider, codeFragment: JetCodeFragment): BindingContext {
val codeFragmentExpression = codeFragment.getContentElement()
if (codeFragmentExpression !is JetExpression) return BindingContext.EMPTY
val resolveSession = componentProvider.get<ResolveSession>()
val (scopeForContextElement, dataFlowInfo) = codeFragment.getScopeAndDataFlowForAnalyzeFragment(resolveSession) {
componentProvider.get<ResolveElementCache>().resolveToElement(it, BodyResolveMode.PARTIAL_FOR_COMPLETION) //TODO: discuss it
} ?: return BindingContext.EMPTY
return codeFragmentExpression.analyzeInContext(
scopeForContextElement,
BindingTraceContext(),
dataFlowInfo,
TypeUtils.NO_EXPECTED_TYPE,
resolveSession.getModuleDescriptor()
val trace = BindingTraceContext()
componentProvider.get<CodeFragmentAnalyzer>().analyzeCodeFragment(
codeFragment,
trace,
BodyResolveMode.PARTIAL_FOR_COMPLETION //TODO: discuss it
)
return trace.bindingContext
}
}
@@ -22,7 +22,6 @@ import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.analyzer.computeTypeInContext
import org.jetbrains.kotlin.asJava.KotlinCodeBlockModificationListener
import org.jetbrains.kotlin.cfg.JetFlowInformationProvider
import org.jetbrains.kotlin.container.get
@@ -32,6 +31,7 @@ import org.jetbrains.kotlin.context.withProject
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.frontend.di.createContainerForBodyResolve
import org.jetbrains.kotlin.idea.caches.resolve.CodeFragmentAnalyzer
import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingFunctionShortNameIndex
import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingPropertyShortNameIndex
import org.jetbrains.kotlin.name.FqName
@@ -45,15 +45,14 @@ import org.jetbrains.kotlin.resolve.lazy.*
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyPackageDescriptor
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.resolve.util.getScopeAndDataFlowForAnalyzeFragment
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.utils.addToStdlib.check
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
public class ResolveElementCache(
private val resolveSession: ResolveSession,
private val project: Project,
private val targetPlatform: TargetPlatform
private val targetPlatform: TargetPlatform,
private val codeFragmentAnalyzer: CodeFragmentAnalyzer
) : BodyResolveCache {
private class CachedFullResolve(val bindingContext: BindingContext, resolveElement: JetElement) {
private val modificationStamp: Long? = modificationStamp(resolveElement)
@@ -343,19 +342,11 @@ public class ResolveElementCache(
private fun codeFragmentAdditionalResolve(resolveSession: ResolveSession, codeFragment: JetCodeFragment, bodyResolveMode: BodyResolveMode): BindingTrace {
val trace = createDelegatingTrace(codeFragment)
val codeFragmentExpression = codeFragment.getContentElement() as? JetExpression ?: return trace
val (scopeForContextElement, dataFlowInfoForContextElement) = codeFragment.getScopeAndDataFlowForAnalyzeFragment(resolveSession) {
val contextResolveMode = if (bodyResolveMode == BodyResolveMode.PARTIAL)
BodyResolveMode.PARTIAL_FOR_COMPLETION
else
bodyResolveMode
resolveToElement(it, contextResolveMode)
} ?: return trace
codeFragmentExpression.computeTypeInContext(scopeForContextElement, trace, dataFlowInfoForContextElement,
TypeUtils.NO_EXPECTED_TYPE, resolveSession.getModuleDescriptor())
val contextResolveMode = if (bodyResolveMode == BodyResolveMode.PARTIAL)
BodyResolveMode.PARTIAL_FOR_COMPLETION
else
bodyResolveMode
codeFragmentAnalyzer.analyzeCodeFragment(codeFragment, trace, contextResolveMode)
return trace
}