From d4d909de5f2f95407af422b31a3fb2c954bd1d07 Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Fri, 17 Oct 2014 14:07:13 +0400 Subject: [PATCH] Flexible types supported in Extract Function when used from debugger/evaluate expression --- .../caches/resolve/KotlinCacheService.kt | 37 ++++++++++-------- .../evaluate/KotlinEvaluationBuilder.kt | 38 +++++++++++++------ .../extractFunctionForDebuggerUtil.kt | 2 +- .../ExtractableCodeDescriptor.kt | 3 +- .../extractFunction/extractorUtil.kt | 7 ++-- .../ui/KotlinExtractFunctionDialog.java | 2 +- 6 files changed, 56 insertions(+), 33 deletions(-) diff --git a/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/KotlinCacheService.kt b/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/KotlinCacheService.kt index d85d97f2170..06f5a962183 100644 --- a/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/KotlinCacheService.kt +++ b/idea/idea-analysis/src/org/jetbrains/jet/plugin/caches/resolve/KotlinCacheService.kt @@ -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(2, 3) { - override fun createValue(file: JetFile?): KotlinResolveCache { - val targetPlatform = TargetPlatformDetector.getPlatform(file!!) - val syntheticFileModule = file.getModuleInfo() + private val syntheticFileCaches = object : SLRUCache, KotlinResolveCache>(2, 3) { + override fun createValue(files: Set): 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): 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): 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) } diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluationBuilder.kt b/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluationBuilder.kt index 6ab23427f3f..98c32a1b559 100644 --- a/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluationBuilder.kt +++ b/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/KotlinEvaluationBuilder.kt @@ -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()} + """ + ) +} + +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 } diff --git a/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/extractFunctionForDebuggerUtil.kt b/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/extractFunctionForDebuggerUtil.kt index 1f75b3cb08b..13a35d4b1e1 100644 --- a/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/extractFunctionForDebuggerUtil.kt +++ b/idea/src/org/jetbrains/jet/plugin/debugger/evaluate/extractFunctionForDebuggerUtil.kt @@ -107,7 +107,7 @@ fun getFunctionForExtractedFragment( } return validationResult.descriptor - .generateDeclaration(ExtractionGeneratorOptions(inTempFile = true)) + .generateDeclaration(ExtractionGeneratorOptions(inTempFile = true, flexibleTypesAllowed = true)) .declaration as JetNamedFunction } diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/ExtractableCodeDescriptor.kt b/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/ExtractableCodeDescriptor.kt index d7a9fa6356f..a3b4bb3b984 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/ExtractableCodeDescriptor.kt +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/ExtractableCodeDescriptor.kt @@ -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() diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractorUtil.kt b/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractorUtil.kt index cf635e3d241..ea145cf5454 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractorUtil.kt +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/extractorUtil.kt @@ -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)) diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/ui/KotlinExtractFunctionDialog.java b/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/ui/KotlinExtractFunctionDialog.java index 9245e601aa6..0bf8cb421c8 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/ui/KotlinExtractFunctionDialog.java +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/extractFunction/ui/KotlinExtractFunctionDialog.java @@ -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); } }