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()
|
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 {
|
public fun JetType?.safeType(expression: JetExpression): JetType {
|
||||||
if (this != null) return this
|
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.psi.util.PsiTreeUtil
|
||||||
import com.intellij.util.containers.SLRUCache
|
import com.intellij.util.containers.SLRUCache
|
||||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||||
import org.jetbrains.kotlin.analyzer.analyzeInContext
|
|
||||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||||
import org.jetbrains.kotlin.container.ComponentProvider
|
import org.jetbrains.kotlin.container.ComponentProvider
|
||||||
import org.jetbrains.kotlin.container.get
|
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.descriptors.ModuleDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
|
||||||
import org.jetbrains.kotlin.frontend.di.createContainerForLazyBodyResolve
|
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.idea.project.TargetPlatformDetector
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||||
import org.jetbrains.kotlin.resolve.*
|
import org.jetbrains.kotlin.resolve.*
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
|
import org.jetbrains.kotlin.resolve.lazy.ResolveSession
|
||||||
import org.jetbrains.kotlin.resolve.util.getScopeAndDataFlowForAnalyzeFragment
|
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
|
||||||
import java.util.HashMap
|
import java.util.HashMap
|
||||||
|
|
||||||
public interface CacheExtension<T> {
|
public interface CacheExtension<T> {
|
||||||
@@ -283,21 +279,12 @@ private object KotlinResolveDataProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun analyzeExpressionCodeFragment(componentProvider: ComponentProvider, codeFragment: JetCodeFragment): BindingContext {
|
private fun analyzeExpressionCodeFragment(componentProvider: ComponentProvider, codeFragment: JetCodeFragment): BindingContext {
|
||||||
val codeFragmentExpression = codeFragment.getContentElement()
|
val trace = BindingTraceContext()
|
||||||
if (codeFragmentExpression !is JetExpression) return BindingContext.EMPTY
|
componentProvider.get<CodeFragmentAnalyzer>().analyzeCodeFragment(
|
||||||
val resolveSession = componentProvider.get<ResolveSession>()
|
codeFragment,
|
||||||
|
trace,
|
||||||
val (scopeForContextElement, dataFlowInfo) = codeFragment.getScopeAndDataFlowForAnalyzeFragment(resolveSession) {
|
BodyResolveMode.PARTIAL_FOR_COMPLETION //TODO: discuss it
|
||||||
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()
|
|
||||||
)
|
)
|
||||||
|
return trace.bindingContext
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import com.intellij.psi.util.CachedValueProvider
|
|||||||
import com.intellij.psi.util.CachedValuesManager
|
import com.intellij.psi.util.CachedValuesManager
|
||||||
import com.intellij.psi.util.PsiModificationTracker
|
import com.intellij.psi.util.PsiModificationTracker
|
||||||
import com.intellij.util.containers.ContainerUtil
|
import com.intellij.util.containers.ContainerUtil
|
||||||
import org.jetbrains.kotlin.analyzer.computeTypeInContext
|
|
||||||
import org.jetbrains.kotlin.asJava.KotlinCodeBlockModificationListener
|
import org.jetbrains.kotlin.asJava.KotlinCodeBlockModificationListener
|
||||||
import org.jetbrains.kotlin.cfg.JetFlowInformationProvider
|
import org.jetbrains.kotlin.cfg.JetFlowInformationProvider
|
||||||
import org.jetbrains.kotlin.container.get
|
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.*
|
||||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||||
import org.jetbrains.kotlin.frontend.di.createContainerForBodyResolve
|
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.JetProbablyNothingFunctionShortNameIndex
|
||||||
import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingPropertyShortNameIndex
|
import org.jetbrains.kotlin.idea.stubindex.JetProbablyNothingPropertyShortNameIndex
|
||||||
import org.jetbrains.kotlin.name.FqName
|
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.LazyClassDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyPackageDescriptor
|
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyPackageDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
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.check
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
|
|
||||||
public class ResolveElementCache(
|
public class ResolveElementCache(
|
||||||
private val resolveSession: ResolveSession,
|
private val resolveSession: ResolveSession,
|
||||||
private val project: Project,
|
private val project: Project,
|
||||||
private val targetPlatform: TargetPlatform
|
private val targetPlatform: TargetPlatform,
|
||||||
|
private val codeFragmentAnalyzer: CodeFragmentAnalyzer
|
||||||
) : BodyResolveCache {
|
) : BodyResolveCache {
|
||||||
private class CachedFullResolve(val bindingContext: BindingContext, resolveElement: JetElement) {
|
private class CachedFullResolve(val bindingContext: BindingContext, resolveElement: JetElement) {
|
||||||
private val modificationStamp: Long? = modificationStamp(resolveElement)
|
private val modificationStamp: Long? = modificationStamp(resolveElement)
|
||||||
@@ -343,19 +342,11 @@ public class ResolveElementCache(
|
|||||||
private fun codeFragmentAdditionalResolve(resolveSession: ResolveSession, codeFragment: JetCodeFragment, bodyResolveMode: BodyResolveMode): BindingTrace {
|
private fun codeFragmentAdditionalResolve(resolveSession: ResolveSession, codeFragment: JetCodeFragment, bodyResolveMode: BodyResolveMode): BindingTrace {
|
||||||
val trace = createDelegatingTrace(codeFragment)
|
val trace = createDelegatingTrace(codeFragment)
|
||||||
|
|
||||||
val codeFragmentExpression = codeFragment.getContentElement() as? JetExpression ?: return trace
|
val contextResolveMode = if (bodyResolveMode == BodyResolveMode.PARTIAL)
|
||||||
|
BodyResolveMode.PARTIAL_FOR_COMPLETION
|
||||||
val (scopeForContextElement, dataFlowInfoForContextElement) = codeFragment.getScopeAndDataFlowForAnalyzeFragment(resolveSession) {
|
else
|
||||||
val contextResolveMode = if (bodyResolveMode == BodyResolveMode.PARTIAL)
|
bodyResolveMode
|
||||||
BodyResolveMode.PARTIAL_FOR_COMPLETION
|
codeFragmentAnalyzer.analyzeCodeFragment(codeFragment, trace, contextResolveMode)
|
||||||
else
|
|
||||||
bodyResolveMode
|
|
||||||
|
|
||||||
resolveToElement(it, contextResolveMode)
|
|
||||||
} ?: return trace
|
|
||||||
|
|
||||||
codeFragmentExpression.computeTypeInContext(scopeForContextElement, trace, dataFlowInfoForContextElement,
|
|
||||||
TypeUtils.NO_EXPECTED_TYPE, resolveSession.getModuleDescriptor())
|
|
||||||
|
|
||||||
return trace
|
return trace
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user