Refactor: merge duplicated code for code fragment analysis
Introduce CodeFragmentAnalyzer
This commit is contained in:
@@ -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
|
||||
}
|
||||
+120
@@ -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
|
||||
}
|
||||
}
|
||||
+6
-19
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user