Flexible types supported in Extract Function when used from debugger/evaluate expression

This commit is contained in:
Andrey Breslav
2014-10-17 14:07:13 +04:00
committed by Pavel V. Talanov
parent 925177f3d8
commit d4d909de5f
6 changed files with 56 additions and 33 deletions
@@ -42,8 +42,8 @@ public fun JetElement.getLazyResolveSession(): ResolveSessionForBodies {
return KotlinCacheService.getInstance(getProject()).getLazyResolveSession(this)
}
public fun JetElement.getAnalysisResults(): AnalyzeExhaust {
return KotlinCacheService.getInstance(getProject()).getAnalysisResults(listOf(this))
public fun JetElement.getAnalysisResults(vararg extraFiles: JetFile): AnalyzeExhaust {
return KotlinCacheService.getInstance(getProject()).getAnalysisResults(listOf(this) + extraFiles.toList())
}
public fun JetElement.getBindingContext(): BindingContext {
@@ -105,10 +105,11 @@ public class KotlinCacheService(val project: Project) {
private fun getGlobalCache(platform: TargetPlatform) = globalCachesPerPlatform[platform]!!.modulesCache
private fun getGlobalLibrariesCache(platform: TargetPlatform) = globalCachesPerPlatform[platform]!!.librariesCache
private val syntheticFileCaches = object : SLRUCache<JetFile, KotlinResolveCache>(2, 3) {
override fun createValue(file: JetFile?): KotlinResolveCache {
val targetPlatform = TargetPlatformDetector.getPlatform(file!!)
val syntheticFileModule = file.getModuleInfo()
private val syntheticFileCaches = object : SLRUCache<Set<JetFile>, KotlinResolveCache>(2, 3) {
override fun createValue(files: Set<JetFile>): KotlinResolveCache {
// we assume that all files come from the same module
val targetPlatform = files.map { TargetPlatformDetector.getPlatform(it) }.toSet().single()
val syntheticFileModule = files.map { it.getModuleInfo() }.toSet().single()
return when {
syntheticFileModule is ModuleSourceInfo -> {
val dependentModules = syntheticFileModule.getDependentModules()
@@ -116,7 +117,7 @@ public class KotlinCacheService(val project: Project) {
project,
globalResolveSessionProvider(
targetPlatform,
syntheticFiles = listOf(file),
syntheticFiles = files,
reuseDataFromCache = getGlobalCache(targetPlatform),
moduleFilter = { it in dependentModules },
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
@@ -129,7 +130,7 @@ public class KotlinCacheService(val project: Project) {
project,
globalResolveSessionProvider(
targetPlatform,
syntheticFiles = listOf(file),
syntheticFiles = files,
reuseDataFromCache = getGlobalLibrariesCache(targetPlatform),
moduleFilter = { it == syntheticFileModule },
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
@@ -141,12 +142,12 @@ public class KotlinCacheService(val project: Project) {
//NOTE: this code should not be called for sdk or library classes
// currently the only known scenario is when we cannot determine that file is a library source
// (file under both classes and sources root)
LOG.warn("Creating cache with synthetic file ($file) in classes of library $syntheticFileModule")
LOG.warn("Creating cache with synthetic files ($files) in classes of library $syntheticFileModule")
KotlinResolveCache(
project,
globalResolveSessionProvider(
targetPlatform,
syntheticFiles = listOf(file),
syntheticFiles = files,
moduleFilter = { true },
dependencies = listOf(PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
)
@@ -158,9 +159,9 @@ public class KotlinCacheService(val project: Project) {
}
}
private fun getCacheForSyntheticFile(file: JetFile): KotlinResolveCache {
private fun getCacheForSyntheticFile(files: Set<JetFile>): KotlinResolveCache {
return synchronized(syntheticFileCaches) {
syntheticFileCaches[file]
syntheticFileCaches[files]
}
}
@@ -171,7 +172,7 @@ public class KotlinCacheService(val project: Project) {
public fun getLazyResolveSession(element: JetElement): ResolveSessionForBodies {
val file = element.getContainingJetFile()
if (!ProjectRootsUtil.isInProjectSource(file)) {
return getCacheForSyntheticFile(file).getLazyResolveSession(file)
return getCacheForSyntheticFile(setOf(file)).getLazyResolveSession(file)
}
return getGlobalLazyResolveSession(file, TargetPlatformDetector.getPlatform(file))
@@ -180,11 +181,15 @@ public class KotlinCacheService(val project: Project) {
public fun getAnalysisResults(elements: Collection<JetElement>): AnalyzeExhaust {
if (elements.isEmpty()) return AnalyzeExhaust.EMPTY
val firstFile = elements.first().getContainingJetFile()
if (elements.size == 1 && (!ProjectRootsUtil.isInProjectSource(firstFile) && firstFile !is JetCodeFragment)) {
return getCacheForSyntheticFile(firstFile).getAnalysisResultsForElements(elements)
val files = elements.map { it.getContainingJetFile() }.toSet()
if (files.all { !ProjectRootsUtil.isInProjectSource(it) }
// TODO: decide what to do with this
// && files.all { it !is JetCodeFragment }
) {
return getCacheForSyntheticFile(files).getAnalysisResultsForElements(elements)
}
val firstFile = elements.first().getContainingJetFile()
return getGlobalCache(TargetPlatformDetector.getPlatform(firstFile)).getAnalysisResultsForElements(elements)
}
@@ -71,7 +71,7 @@ import org.jetbrains.jet.lang.resolve.java.structure.impl.JavaClassImpl
import com.intellij.openapi.project.Project
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.jet.codegen.StackValue
import org.jetbrains.jet.analyzer.AnalyzeExhaust
import org.jetbrains.jet.lang.types.Flexibility
private val RECEIVER_NAME = "\$receiver"
private val THIS_NAME = "this"
@@ -232,9 +232,8 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
return runReadAction {
val file = createFileForDebugger(codeFragment, extractedFunction)
file.checkForErrors()
val analyzeExhaust = file.checkForErrors()
val analyzeExhaust = file.getAnalysisResults()
val state = GenerationState(
file.getProject(),
ClassBuilderFactories.BINARIES,
@@ -259,7 +258,7 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
throw EvaluateExceptionUtil.createEvaluateException(e)
}
private fun JetFile.checkForErrors() {
private fun JetFile.checkForErrors() =
runReadAction {
try {
AnalyzingUtils.checkForSyntacticErrors(this)
@@ -268,7 +267,7 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
throw EvaluateExceptionUtil.createEvaluateException(e.getMessage())
}
val analyzeExhaust = this.getAnalysisResults()
val analyzeExhaust = this.getAnalysisResults(createFlexibleTypesFile())
if (analyzeExhaust.isError()) {
throw EvaluateExceptionUtil.createEvaluateException(analyzeExhaust.getError())
}
@@ -277,8 +276,9 @@ class KotlinEvaluator(val codeFragment: JetCodeFragment,
bindingContext.getDiagnostics().firstOrNull { it.getSeverity() == Severity.ERROR }?.let {
throw EvaluateExceptionUtil.createEvaluateException(DefaultErrorMessages.RENDERER.render(it))
}
analyzeExhaust
}
}
}
}
@@ -304,12 +304,28 @@ private fun createFileForDebugger(codeFragment: JetCodeFragment,
assert(extractedFunctionText != null, "Text of extracted function shouldn't be null")
fileText = fileText.replace("!FUNCTION!", extractedFunction.getText()!!)
val virtualFile = LightVirtualFile("debugFile.kt", JetLanguage.INSTANCE, fileText)
virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET)
val jetFile = (PsiFileFactory.getInstance(codeFragment.getProject()) as PsiFileFactoryImpl)
.trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false) as JetFile
val jetFile = codeFragment.createJetFile("debugFile.kt", fileText)
jetFile.skipVisibilityCheck = true
jetFile.analysisContext = codeFragment
return jetFile
}
private fun PsiElement.createFlexibleTypesFile(): JetFile {
return createJetFile(
"FLEXIBLE_TYPES.kt",
"""
package ${Flexibility.FLEXIBLE_TYPE_CLASSIFIER.getPackageFqName()}
public class ${Flexibility.FLEXIBLE_TYPE_CLASSIFIER.getRelativeClassName()}<L, U>
"""
)
}
private fun PsiElement.createJetFile(fileName: String, fileText: String): JetFile {
// Not using JetPsiFactory because we need a virtual file attached to the JetFile
val virtualFile = LightVirtualFile(fileName, JetLanguage.INSTANCE, fileText)
virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET)
val jetFile = (PsiFileFactory.getInstance(getProject()) as PsiFileFactoryImpl)
.trySetupPsiForFile(virtualFile, JetLanguage.INSTANCE, true, false) as JetFile
jetFile.analysisContext = this
return jetFile
}
@@ -107,7 +107,7 @@ fun getFunctionForExtractedFragment(
}
return validationResult.descriptor
.generateDeclaration(ExtractionGeneratorOptions(inTempFile = true))
.generateDeclaration(ExtractionGeneratorOptions(inTempFile = true, flexibleTypesAllowed = true))
.declaration as JetNamedFunction
}
@@ -318,7 +318,8 @@ data class ExtractableCodeDescriptor(
data class ExtractionGeneratorOptions(
val inTempFile: Boolean = false,
val extractAsProperty: Boolean = false
val extractAsProperty: Boolean = false,
val flexibleTypesAllowed: Boolean = false
) {
class object {
val DEFAULT = ExtractionGeneratorOptions()
@@ -56,7 +56,6 @@ import org.jetbrains.jet.plugin.util.psi.patternMatching.JetPsiRange
import org.jetbrains.jet.plugin.util.psi.patternMatching.JetPsiRange.Match
import org.jetbrains.jet.plugin.util.psi.patternMatching.UnificationResult.WeaklyMatched
import org.jetbrains.jet.plugin.util.psi.patternMatching.UnificationResult.StronglyMatched
import org.jetbrains.jet.lang.psi.JetDeclarationWithBody
import org.jetbrains.jet.plugin.util.IdeDescriptorRenderers
import org.jetbrains.jet.lang.psi.psiUtil.parents
import java.util.ArrayList
@@ -64,7 +63,9 @@ import java.util.ArrayList
fun ExtractableCodeDescriptor.getDeclarationText(
options: ExtractionGeneratorOptions = ExtractionGeneratorOptions.DEFAULT,
withBody: Boolean = true,
descriptorRenderer: DescriptorRenderer = IdeDescriptorRenderers.SOURCE_CODE
descriptorRenderer: DescriptorRenderer = if (options.flexibleTypesAllowed)
DescriptorRenderer.FLEXIBLE_TYPES_FOR_CODE
else IdeDescriptorRenderers.SOURCE_CODE
): String {
if (!canGenerateProperty() && options.extractAsProperty) {
throw IllegalArgumentException("Can't generate property: ${extractionData.getCodeFragmentText()}")
@@ -344,7 +345,7 @@ fun ExtractableCodeDescriptor.generateDeclaration(options: ExtractionGeneratorOp
fun createDeclaration(): JetNamedDeclaration {
return with(extractionData) {
if (options.inTempFile) {
createTemporaryDeclaration("${getDeclarationText()}\n")
createTemporaryDeclaration("${getDeclarationText(options)}\n")
}
else {
psiFactory.createDeclaration(getDeclarationText(options))
@@ -276,6 +276,6 @@ public class KotlinExtractFunctionDialog extends DialogWrapper {
@NotNull
public ExtractionGeneratorOptions getGeneratorOptions() {
return new ExtractionGeneratorOptions(false, propertyCheckBox.isSelected());
return new ExtractionGeneratorOptions(false, propertyCheckBox.isSelected(), false);
}
}