Minor: fix warnings
This commit is contained in:
@@ -40,7 +40,6 @@ import com.intellij.util.ExceptionUtil
|
|||||||
import com.sun.jdi.InvocationException
|
import com.sun.jdi.InvocationException
|
||||||
import com.sun.jdi.ObjectReference
|
import com.sun.jdi.ObjectReference
|
||||||
import com.sun.jdi.VMDisconnectedException
|
import com.sun.jdi.VMDisconnectedException
|
||||||
import com.sun.jdi.VirtualMachine
|
|
||||||
import com.sun.jdi.request.EventRequest
|
import com.sun.jdi.request.EventRequest
|
||||||
import org.jetbrains.eval4j.*
|
import org.jetbrains.eval4j.*
|
||||||
import org.jetbrains.eval4j.jdi.JDIEval
|
import org.jetbrains.eval4j.jdi.JDIEval
|
||||||
@@ -66,8 +65,6 @@ import org.jetbrains.kotlin.idea.util.DebuggerUtils
|
|||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.idea.util.attachment.attachmentByPsiFile
|
import org.jetbrains.kotlin.idea.util.attachment.attachmentByPsiFile
|
||||||
import org.jetbrains.kotlin.idea.util.attachment.mergeAttachments
|
import org.jetbrains.kotlin.idea.util.attachment.mergeAttachments
|
||||||
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.codeFragmentUtil.debugTypeInfo
|
import org.jetbrains.kotlin.psi.codeFragmentUtil.debugTypeInfo
|
||||||
@@ -84,6 +81,7 @@ import java.util.*
|
|||||||
|
|
||||||
internal val RECEIVER_NAME = "\$receiver"
|
internal val RECEIVER_NAME = "\$receiver"
|
||||||
internal val THIS_NAME = "this"
|
internal val THIS_NAME = "this"
|
||||||
|
val logger = Logger.getInstance(KotlinEvaluator::class.java)
|
||||||
|
|
||||||
object KotlinEvaluationBuilder: EvaluatorBuilder {
|
object KotlinEvaluationBuilder: EvaluatorBuilder {
|
||||||
override fun build(codeFragment: PsiElement, position: SourcePosition?): ExpressionEvaluator {
|
override fun build(codeFragment: PsiElement, position: SourcePosition?): ExpressionEvaluator {
|
||||||
@@ -120,14 +118,10 @@ object KotlinEvaluationBuilder: EvaluatorBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val logger = Logger.getInstance(javaClass<KotlinEvaluator>())
|
class KotlinEvaluator(val codeFragment: KtCodeFragment, val sourcePosition: SourcePosition): Evaluator {
|
||||||
|
|
||||||
class KotlinEvaluator(val codeFragment: KtCodeFragment,
|
|
||||||
val sourcePosition: SourcePosition
|
|
||||||
) : Evaluator {
|
|
||||||
override fun evaluate(context: EvaluationContextImpl): Any? {
|
override fun evaluate(context: EvaluationContextImpl): Any? {
|
||||||
if (codeFragment.text.isEmpty()) {
|
if (codeFragment.text.isEmpty()) {
|
||||||
return context.debugProcess.virtualMachineProxy.mirrorOf()
|
return context.debugProcess.virtualMachineProxy.mirrorOfVoid()
|
||||||
}
|
}
|
||||||
|
|
||||||
var isCompiledDataFromCache = true
|
var isCompiledDataFromCache = true
|
||||||
@@ -164,7 +158,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment,
|
|||||||
ExceptionUtil.getThrowableText(e),
|
ExceptionUtil.getThrowableText(e),
|
||||||
mergeAttachments(*attachments)))
|
mergeAttachments(*attachments)))
|
||||||
|
|
||||||
val cause = if (e.getMessage() != null) ": ${e.getMessage()}" else ""
|
val cause = if (e.message != null) ": ${e.message}" else ""
|
||||||
exception("An exception occurs during Evaluate Expression Action $cause")
|
exception("An exception occurs during Evaluate Expression Action $cause")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -178,24 +172,20 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment,
|
|||||||
codeFragment.checkForErrors(false)
|
codeFragment.checkForErrors(false)
|
||||||
|
|
||||||
val extractionResult = getFunctionForExtractedFragment(codeFragment, sourcePosition.file, sourcePosition.line)
|
val extractionResult = getFunctionForExtractedFragment(codeFragment, sourcePosition.file, sourcePosition.line)
|
||||||
if (extractionResult == null) {
|
?: throw IllegalStateException("Code fragment cannot be extracted to function")
|
||||||
throw IllegalStateException("Code fragment cannot be extracted to function")
|
|
||||||
}
|
|
||||||
val parametersDescriptor = extractionResult.getParametersForDebugger(codeFragment)
|
val parametersDescriptor = extractionResult.getParametersForDebugger(codeFragment)
|
||||||
val extractedFunction = extractionResult.declaration as KtNamedFunction
|
val extractedFunction = extractionResult.declaration as KtNamedFunction
|
||||||
|
|
||||||
val classFileFactory = createClassFileFactory(codeFragment, extractedFunction, context, parametersDescriptor)
|
val classFileFactory = createClassFileFactory(codeFragment, extractedFunction, context, parametersDescriptor)
|
||||||
|
|
||||||
val outputFiles = classFileFactory.asList().filterClassFiles()
|
val outputFiles = classFileFactory.asList().filterClassFiles()
|
||||||
.sortedBy { it.relativePath.length() }
|
.sortedBy { it.relativePath.length }
|
||||||
|
|
||||||
val funName = runReadAction { extractedFunction.name }
|
val funName = runReadAction { extractedFunction.name }
|
||||||
if (funName == null) {
|
?: throw IllegalStateException("Extracted function should have a name: ${extractedFunction.text}")
|
||||||
throw IllegalStateException("Extracted function should have a name: ${extractedFunction.text}")
|
|
||||||
}
|
|
||||||
|
|
||||||
val additionalFiles = if (outputFiles.size() < 2) emptyList()
|
val additionalFiles = if (outputFiles.size < 2) emptyList()
|
||||||
else outputFiles.subList(1, outputFiles.size()).map { getClassName(it.relativePath) to it.asByteArray() }
|
else outputFiles.subList(1, outputFiles.size).map { getClassName(it.relativePath) to it.asByteArray() }
|
||||||
|
|
||||||
return CompiledDataDescriptor(
|
return CompiledDataDescriptor(
|
||||||
outputFiles.first().asByteArray(),
|
outputFiles.first().asByteArray(),
|
||||||
@@ -353,7 +343,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment,
|
|||||||
val (bindingContext, moduleDescriptor, files) = jetFile.checkForErrors(true)
|
val (bindingContext, moduleDescriptor, files) = jetFile.checkForErrors(true)
|
||||||
|
|
||||||
val generateClassFilter = object : GenerationState.GenerateClassFilter() {
|
val generateClassFilter = object : GenerationState.GenerateClassFilter() {
|
||||||
override fun shouldGeneratePackagePart(file: KtFile) = file == jetFile
|
override fun shouldGeneratePackagePart(jetFile: KtFile) = jetFile == jetFile
|
||||||
override fun shouldAnnotateClass(classOrObject: KtClassOrObject) = true
|
override fun shouldAnnotateClass(classOrObject: KtClassOrObject) = true
|
||||||
override fun shouldGenerateClass(classOrObject: KtClassOrObject) = classOrObject.getContainingJetFile() == jetFile
|
override fun shouldGenerateClass(classOrObject: KtClassOrObject) = classOrObject.getContainingJetFile() == jetFile
|
||||||
override fun shouldGenerateScript(script: KtScript) = false
|
override fun shouldGenerateScript(script: KtScript) = false
|
||||||
@@ -405,9 +395,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment,
|
|||||||
val declarationDescriptor = paramAnonymousType.constructor.declarationDescriptor
|
val declarationDescriptor = paramAnonymousType.constructor.declarationDescriptor
|
||||||
if (declarationDescriptor is ClassDescriptor) {
|
if (declarationDescriptor is ClassDescriptor) {
|
||||||
val localVariable = visitor.findValue(localVariableName, asmType = null, checkType = false, failIfNotFound = false)
|
val localVariable = visitor.findValue(localVariableName, asmType = null, checkType = false, failIfNotFound = false)
|
||||||
if (localVariable == null) {
|
?: exception("Couldn't find local variable this in current frame to get classType for anonymous type $paramAnonymousType}")
|
||||||
exception("Couldn't find local variable this in current frame to get classType for anonymous type ${paramAnonymousType}}")
|
|
||||||
}
|
|
||||||
record(CodegenBinding.ASM_TYPE, declarationDescriptor, localVariable.asmType)
|
record(CodegenBinding.ASM_TYPE, declarationDescriptor, localVariable.asmType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -416,7 +404,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment,
|
|||||||
private fun exception(msg: String) = throw EvaluateExceptionUtil.createEvaluateException(msg)
|
private fun exception(msg: String) = throw EvaluateExceptionUtil.createEvaluateException(msg)
|
||||||
|
|
||||||
private fun exception(e: Throwable): Nothing {
|
private fun exception(e: Throwable): Nothing {
|
||||||
val message = e.getMessage()
|
val message = e.message
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
throw EvaluateExceptionUtil.createEvaluateException(message, e)
|
throw EvaluateExceptionUtil.createEvaluateException(message, e)
|
||||||
}
|
}
|
||||||
@@ -429,7 +417,7 @@ class KotlinEvaluator(val codeFragment: KtCodeFragment,
|
|||||||
AnalyzingUtils.checkForSyntacticErrors(this)
|
AnalyzingUtils.checkForSyntacticErrors(this)
|
||||||
}
|
}
|
||||||
catch (e: IllegalArgumentException) {
|
catch (e: IllegalArgumentException) {
|
||||||
throw EvaluateExceptionUtil.createEvaluateException(e.getMessage())
|
throw EvaluateExceptionUtil.createEvaluateException(e.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
val resolutionFacade = KotlinCacheService.getInstance(project).getResolutionFacade(listOf(this, createFlexibleTypesFile()))
|
val resolutionFacade = KotlinCacheService.getInstance(project).getResolutionFacade(listOf(this, createFlexibleTypesFile()))
|
||||||
@@ -489,7 +477,7 @@ private fun createFileForDebugger(codeFragment: KtCodeFragment,
|
|||||||
jetFile.suppressDiagnosticsInDebugMode = true
|
jetFile.suppressDiagnosticsInDebugMode = true
|
||||||
|
|
||||||
val list = jetFile.declarations
|
val list = jetFile.declarations
|
||||||
val function = list.get(0) as KtNamedFunction
|
val function = list[0] as KtNamedFunction
|
||||||
|
|
||||||
function.receiverTypeReference?.debugTypeInfo = extractedFunction.receiverTypeReference?.debugTypeInfo
|
function.receiverTypeReference?.debugTypeInfo = extractedFunction.receiverTypeReference?.debugTypeInfo
|
||||||
|
|
||||||
|
|||||||
+50
-57
@@ -44,17 +44,17 @@ fun getFunctionForExtractedFragment(
|
|||||||
fun getErrorMessageForExtractFunctionResult(analysisResult: AnalysisResult, tmpFile: KtFile): String {
|
fun getErrorMessageForExtractFunctionResult(analysisResult: AnalysisResult, tmpFile: KtFile): String {
|
||||||
if (KotlinInternalMode.enabled) {
|
if (KotlinInternalMode.enabled) {
|
||||||
logger.error("Couldn't extract function for debugger:\n" +
|
logger.error("Couldn't extract function for debugger:\n" +
|
||||||
"FILE NAME: ${breakpointFile.getName()}\n" +
|
"FILE NAME: ${breakpointFile.name}\n" +
|
||||||
"BREAKPOINT LINE: ${breakpointLine}\n" +
|
"BREAKPOINT LINE: $breakpointLine\n" +
|
||||||
"CODE FRAGMENT:\n${codeFragment.getText()}\n" +
|
"CODE FRAGMENT:\n${codeFragment.text}\n" +
|
||||||
"ERRORS:\n${analysisResult.messages.map { "$it: ${it.renderMessage()}" }.joinToString("\n")}\n" +
|
"ERRORS:\n${analysisResult.messages.map { "$it: ${it.renderMessage()}" }.joinToString("\n")}\n" +
|
||||||
"TMPFILE_TEXT:\n${tmpFile.text}\n" +
|
"TMPFILE_TEXT:\n${tmpFile.text}\n" +
|
||||||
"FILE TEXT: \n${breakpointFile.getText()}\n")
|
"FILE TEXT: \n${breakpointFile.text}\n")
|
||||||
}
|
}
|
||||||
return analysisResult.messages.map { errorMessage ->
|
return analysisResult.messages.map { errorMessage ->
|
||||||
val message = when(errorMessage) {
|
val message = when(errorMessage) {
|
||||||
ErrorMessage.NO_EXPRESSION -> "Cannot perform an action without an expression"
|
ErrorMessage.NO_EXPRESSION -> "Cannot perform an action without an expression"
|
||||||
ErrorMessage.NO_CONTAINER -> "Cannot perform an action at this breakpoint ${breakpointFile.getName()}:${breakpointLine}"
|
ErrorMessage.NO_CONTAINER -> "Cannot perform an action at this breakpoint ${breakpointFile.name}:$breakpointLine"
|
||||||
ErrorMessage.SUPER_CALL -> "Cannot perform an action for expression with super call"
|
ErrorMessage.SUPER_CALL -> "Cannot perform an action for expression with super call"
|
||||||
ErrorMessage.DENOTABLE_TYPES -> "Cannot perform an action because following types are unavailable from debugger scope"
|
ErrorMessage.DENOTABLE_TYPES -> "Cannot perform an action because following types are unavailable from debugger scope"
|
||||||
ErrorMessage.ERROR_TYPES -> "Cannot perform an action because this code fragment contains erroneous types"
|
ErrorMessage.ERROR_TYPES -> "Cannot perform an action because this code fragment contains erroneous types"
|
||||||
@@ -74,16 +74,14 @@ fun getFunctionForExtractedFragment(
|
|||||||
val tmpFile = originalFile.createTempCopy { it }
|
val tmpFile = originalFile.createTempCopy { it }
|
||||||
tmpFile.suppressDiagnosticsInDebugMode = true
|
tmpFile.suppressDiagnosticsInDebugMode = true
|
||||||
|
|
||||||
val contextElement = getExpressionToAddDebugExpressionBefore(tmpFile, codeFragment.getContext(), breakpointLine)
|
val contextElement = getExpressionToAddDebugExpressionBefore(tmpFile, codeFragment.context, breakpointLine) ?: return null
|
||||||
if (contextElement == null) return null
|
|
||||||
|
|
||||||
addImportsToFile(codeFragment.importsAsImportList(), tmpFile)
|
addImportsToFile(codeFragment.importsAsImportList(), tmpFile)
|
||||||
|
|
||||||
val newDebugExpressions = addDebugExpressionBeforeContextElement(codeFragment, contextElement)
|
val newDebugExpressions = addDebugExpressionBeforeContextElement(codeFragment, contextElement)
|
||||||
if (newDebugExpressions.isEmpty()) return null
|
if (newDebugExpressions.isEmpty()) return null
|
||||||
|
|
||||||
val targetSibling = tmpFile.getDeclarations().firstOrNull()
|
val targetSibling = tmpFile.declarations.firstOrNull() ?: return null
|
||||||
if (targetSibling == null) return null
|
|
||||||
|
|
||||||
val options = ExtractionOptions(inferUnitTypeForUnusedValues = false,
|
val options = ExtractionOptions(inferUnitTypeForUnusedValues = false,
|
||||||
enableListBoxing = true,
|
enableListBoxing = true,
|
||||||
@@ -95,8 +93,8 @@ fun getFunctionForExtractedFragment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val validationResult = analysisResult.descriptor!!.validate()
|
val validationResult = analysisResult.descriptor!!.validate()
|
||||||
if (!validationResult.conflicts.isEmpty()) {
|
if (!validationResult.conflicts.isEmpty) {
|
||||||
throw EvaluateExceptionUtil.createEvaluateException("Following declarations are unavailable in debug scope: ${validationResult.conflicts.keySet().map { it.getText() }.joinToString(",")}")
|
throw EvaluateExceptionUtil.createEvaluateException("Following declarations are unavailable in debug scope: ${validationResult.conflicts.keySet().map { it.text }.joinToString(",")}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val generatorOptions = ExtractionGeneratorOptions(inTempFile = true,
|
val generatorOptions = ExtractionGeneratorOptions(inTempFile = true,
|
||||||
@@ -111,12 +109,12 @@ fun getFunctionForExtractedFragment(
|
|||||||
|
|
||||||
private fun addImportsToFile(newImportList: KtImportList?, tmpFile: KtFile) {
|
private fun addImportsToFile(newImportList: KtImportList?, tmpFile: KtFile) {
|
||||||
if (newImportList != null) {
|
if (newImportList != null) {
|
||||||
val tmpFileImportList = tmpFile.getImportList()
|
val tmpFileImportList = tmpFile.importList
|
||||||
val packageDirective = tmpFile.getPackageDirective()
|
val packageDirective = tmpFile.packageDirective
|
||||||
val psiFactory = KtPsiFactory(tmpFile)
|
val psiFactory = KtPsiFactory(tmpFile)
|
||||||
if (tmpFileImportList == null) {
|
if (tmpFileImportList == null) {
|
||||||
tmpFile.addAfter(psiFactory.createNewLine(), packageDirective)
|
tmpFile.addAfter(psiFactory.createNewLine(), packageDirective)
|
||||||
tmpFile.addAfter(newImportList, tmpFile.getPackageDirective())
|
tmpFile.addAfter(newImportList, tmpFile.packageDirective)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val tmpFileImports = tmpFileImportList.imports
|
val tmpFileImports = tmpFileImportList.imports
|
||||||
@@ -136,51 +134,46 @@ private fun addImportsToFile(newImportList: KtImportList?, tmpFile: KtFile) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun KtFile.getElementInCopy(e: PsiElement): PsiElement? {
|
private fun KtFile.getElementInCopy(e: PsiElement): PsiElement? {
|
||||||
val offset = e.getTextRange()?.getStartOffset()
|
val offset = e.textRange?.startOffset ?: return null
|
||||||
if (offset == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
var elementAt = this.findElementAt(offset)
|
var elementAt = this.findElementAt(offset)
|
||||||
while (elementAt == null || elementAt.getTextRange()?.getEndOffset() != e.getTextRange()?.getEndOffset()) {
|
while (elementAt == null || elementAt.textRange?.endOffset != e.textRange?.endOffset) {
|
||||||
elementAt = elementAt?.getParent()
|
elementAt = elementAt?.parent
|
||||||
}
|
}
|
||||||
return elementAt
|
return elementAt
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getExpressionToAddDebugExpressionBefore(tmpFile: KtFile, contextElement: PsiElement?, line: Int): PsiElement? {
|
private fun getExpressionToAddDebugExpressionBefore(tmpFile: KtFile, contextElement: PsiElement?, line: Int): PsiElement? {
|
||||||
if (contextElement == null) {
|
if (contextElement == null) {
|
||||||
val lineStart = CodeInsightUtils.getStartLineOffset(tmpFile, line)
|
val lineStart = CodeInsightUtils.getStartLineOffset(tmpFile, line) ?: return null
|
||||||
if (lineStart == null) return null
|
|
||||||
|
|
||||||
val elementAtOffset = tmpFile.findElementAt(lineStart)
|
val elementAtOffset = tmpFile.findElementAt(lineStart) ?: return null
|
||||||
if (elementAtOffset == null) return null
|
|
||||||
|
|
||||||
return CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, lineStart) ?: elementAtOffset
|
return CodeInsightUtils.getTopmostElementAtOffset(elementAtOffset, lineStart) ?: elementAtOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
val containingFile = contextElement.getContainingFile()
|
val containingFile = contextElement.containingFile
|
||||||
if (containingFile is KtCodeFragment) {
|
if (containingFile is KtCodeFragment) {
|
||||||
return getExpressionToAddDebugExpressionBefore(tmpFile, containingFile.getContext(), line)
|
return getExpressionToAddDebugExpressionBefore(tmpFile, containingFile.context, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun shouldStop(el: PsiElement?, p: PsiElement?) = p is KtBlockExpression || el is KtDeclaration || el is KtFile
|
fun shouldStop(el: PsiElement?, p: PsiElement?) = p is KtBlockExpression || el is KtDeclaration || el is KtFile
|
||||||
|
|
||||||
var elementAt = tmpFile.getElementInCopy(contextElement)
|
var elementAt = tmpFile.getElementInCopy(contextElement)
|
||||||
|
|
||||||
var parent = elementAt?.getParent()
|
var parent = elementAt?.parent
|
||||||
if (shouldStop(elementAt, parent)) {
|
if (shouldStop(elementAt, parent)) {
|
||||||
return elementAt
|
return elementAt
|
||||||
}
|
}
|
||||||
|
|
||||||
var parentOfParent = parent?.getParent()
|
var parentOfParent = parent?.parent
|
||||||
|
|
||||||
while (parent != null && parentOfParent != null) {
|
while (parent != null && parentOfParent != null) {
|
||||||
if (shouldStop(parent, parentOfParent)) {
|
if (shouldStop(parent, parentOfParent)) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
parent = parent.getParent()
|
parent = parent.parent
|
||||||
parentOfParent = parent?.getParent()
|
parentOfParent = parent?.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent
|
return parent
|
||||||
@@ -191,9 +184,9 @@ private fun addDebugExpressionBeforeContextElement(codeFragment: KtCodeFragment,
|
|||||||
|
|
||||||
fun insertNewInitializer(classBody: KtClassBody): PsiElement? {
|
fun insertNewInitializer(classBody: KtClassBody): PsiElement? {
|
||||||
val initializer = psiFactory.createAnonymousInitializer()
|
val initializer = psiFactory.createAnonymousInitializer()
|
||||||
val newInitializer = (classBody.addAfter(initializer, classBody.getFirstChild()) as KtClassInitializer)
|
val newInitializer = (classBody.addAfter(initializer, classBody.firstChild) as KtClassInitializer)
|
||||||
val block = newInitializer.getBody() as KtBlockExpression?
|
val block = newInitializer.body as KtBlockExpression?
|
||||||
return block?.getLastChild()
|
return block?.lastChild
|
||||||
}
|
}
|
||||||
|
|
||||||
val elementBefore = when {
|
val elementBefore = when {
|
||||||
@@ -201,20 +194,20 @@ private fun addDebugExpressionBeforeContextElement(codeFragment: KtCodeFragment,
|
|||||||
val fakeFunction = psiFactory.createFunction("fun _debug_fun_() {}")
|
val fakeFunction = psiFactory.createFunction("fun _debug_fun_() {}")
|
||||||
contextElement.add(psiFactory.createNewLine())
|
contextElement.add(psiFactory.createNewLine())
|
||||||
val newFakeFun = contextElement.add(fakeFunction) as KtNamedFunction
|
val newFakeFun = contextElement.add(fakeFunction) as KtNamedFunction
|
||||||
newFakeFun.getBodyExpression()!!.getLastChild()
|
newFakeFun.bodyExpression!!.lastChild
|
||||||
}
|
}
|
||||||
contextElement is KtProperty && !contextElement.isLocal() -> {
|
contextElement is KtProperty && !contextElement.isLocal -> {
|
||||||
val delegateExpressionOrInitializer = contextElement.getDelegateExpressionOrInitializer()
|
val delegateExpressionOrInitializer = contextElement.delegateExpressionOrInitializer
|
||||||
if (delegateExpressionOrInitializer != null) {
|
if (delegateExpressionOrInitializer != null) {
|
||||||
wrapInRunFun(delegateExpressionOrInitializer)
|
wrapInRunFun(delegateExpressionOrInitializer)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
val getter = contextElement.getGetter()!!
|
val getter = contextElement.getter!!
|
||||||
if (!getter.hasBlockBody()) {
|
if (!getter.hasBlockBody()) {
|
||||||
wrapInRunFun(getter.getBodyExpression()!!)
|
wrapInRunFun(getter.bodyExpression!!)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
(getter.getBodyExpression() as KtBlockExpression).getStatements().first()
|
(getter.bodyExpression as KtBlockExpression).statements.first()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,29 +219,29 @@ private fun addDebugExpressionBeforeContextElement(codeFragment: KtCodeFragment,
|
|||||||
insertNewInitializer(contextElement.getBody()!!)
|
insertNewInitializer(contextElement.getBody()!!)
|
||||||
}
|
}
|
||||||
contextElement is KtFunctionLiteral -> {
|
contextElement is KtFunctionLiteral -> {
|
||||||
val block = contextElement.getBodyExpression()!!
|
val block = contextElement.bodyExpression!!
|
||||||
block.getStatements().firstOrNull() ?: block.getLastChild()
|
block.statements.firstOrNull() ?: block.lastChild
|
||||||
}
|
}
|
||||||
contextElement is KtDeclarationWithBody && !contextElement.hasBody()-> {
|
contextElement is KtDeclarationWithBody && !contextElement.hasBody()-> {
|
||||||
val block = psiFactory.createBlock("")
|
val block = psiFactory.createBlock("")
|
||||||
val newBlock = contextElement.add(block) as KtBlockExpression
|
val newBlock = contextElement.add(block) as KtBlockExpression
|
||||||
newBlock.getRBrace()
|
newBlock.rBrace
|
||||||
}
|
}
|
||||||
contextElement is KtDeclarationWithBody && !contextElement.hasBlockBody()-> {
|
contextElement is KtDeclarationWithBody && !contextElement.hasBlockBody()-> {
|
||||||
wrapInRunFun(contextElement.getBodyExpression()!!)
|
wrapInRunFun(contextElement.bodyExpression!!)
|
||||||
}
|
}
|
||||||
contextElement is KtDeclarationWithBody && contextElement.hasBlockBody()-> {
|
contextElement is KtDeclarationWithBody && contextElement.hasBlockBody()-> {
|
||||||
val block = contextElement.getBodyExpression() as KtBlockExpression
|
val block = contextElement.bodyExpression as KtBlockExpression
|
||||||
val last = block.getStatements().lastOrNull()
|
val last = block.statements.lastOrNull()
|
||||||
if (last is KtReturnExpression)
|
if (last is KtReturnExpression)
|
||||||
last
|
last
|
||||||
else
|
else
|
||||||
block.getRBrace()
|
block.rBrace
|
||||||
}
|
}
|
||||||
contextElement is KtWhenEntry -> {
|
contextElement is KtWhenEntry -> {
|
||||||
val entryExpression = contextElement.getExpression()
|
val entryExpression = contextElement.expression
|
||||||
if (entryExpression is KtBlockExpression) {
|
if (entryExpression is KtBlockExpression) {
|
||||||
entryExpression.getStatements().firstOrNull() ?: entryExpression.getLastChild()
|
entryExpression.statements.firstOrNull() ?: entryExpression.lastChild
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
wrapInRunFun(entryExpression!!)
|
wrapInRunFun(entryExpression!!)
|
||||||
@@ -259,18 +252,18 @@ private fun addDebugExpressionBeforeContextElement(codeFragment: KtCodeFragment,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val parent = elementBefore?.getParent()
|
val parent = elementBefore?.parent
|
||||||
if (parent == null || elementBefore == null) return emptyList()
|
if (parent == null || elementBefore == null) return emptyList()
|
||||||
|
|
||||||
parent.addBefore(psiFactory.createNewLine(), elementBefore)
|
parent.addBefore(psiFactory.createNewLine(), elementBefore)
|
||||||
|
|
||||||
fun insertExpression(expr: KtElement?): List<KtExpression> {
|
fun insertExpression(expr: KtElement?): List<KtExpression> {
|
||||||
when (expr) {
|
when (expr) {
|
||||||
is KtBlockExpression -> return expr.getStatements().flatMap { insertExpression(it) }
|
is KtBlockExpression -> return expr.statements.flatMap { insertExpression(it) }
|
||||||
is KtExpression -> {
|
is KtExpression -> {
|
||||||
val newDebugExpression = parent.addBefore(expr, elementBefore)
|
val newDebugExpression = parent.addBefore(expr, elementBefore)
|
||||||
if (newDebugExpression == null) {
|
if (newDebugExpression == null) {
|
||||||
logger.error("Couldn't insert debug expression ${expr.getText()} to context file before ${elementBefore.getText()}")
|
logger.error("Couldn't insert debug expression ${expr.text} to context file before ${elementBefore.text}")
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
parent.addBefore(psiFactory.createNewLine(), elementBefore)
|
parent.addBefore(psiFactory.createNewLine(), elementBefore)
|
||||||
@@ -280,7 +273,7 @@ private fun addDebugExpressionBeforeContextElement(codeFragment: KtCodeFragment,
|
|||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
val containingFile = codeFragment.getContext()?.getContainingFile()
|
val containingFile = codeFragment.context?.containingFile
|
||||||
if (containingFile is KtCodeFragment) {
|
if (containingFile is KtCodeFragment) {
|
||||||
insertExpression(containingFile.getContentElement() as? KtExpression)
|
insertExpression(containingFile.getContentElement() as? KtExpression)
|
||||||
}
|
}
|
||||||
@@ -290,11 +283,11 @@ private fun addDebugExpressionBeforeContextElement(codeFragment: KtCodeFragment,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun replaceByRunFunction(expression: KtExpression): KtCallExpression {
|
private fun replaceByRunFunction(expression: KtExpression): KtCallExpression {
|
||||||
val callExpression = KtPsiFactory(expression).createExpression("run { \n${expression.getText()} \n}") as KtCallExpression
|
val callExpression = KtPsiFactory(expression).createExpression("run { \n${expression.text} \n}") as KtCallExpression
|
||||||
val replaced = expression.replaced(callExpression)
|
val replaced = expression.replaced(callExpression)
|
||||||
val typeArguments = InsertExplicitTypeArgumentsIntention.createTypeArguments(replaced, replaced.analyze())
|
val typeArguments = InsertExplicitTypeArgumentsIntention.createTypeArguments(replaced, replaced.analyze())
|
||||||
if (typeArguments?.getArguments()?.isNotEmpty() ?: false) {
|
if (typeArguments?.arguments?.isNotEmpty() ?: false) {
|
||||||
val calleeExpression = replaced.getCalleeExpression()
|
val calleeExpression = replaced.calleeExpression
|
||||||
replaced.addAfter(typeArguments!!, calleeExpression)
|
replaced.addAfter(typeArguments!!, calleeExpression)
|
||||||
}
|
}
|
||||||
return replaced
|
return replaced
|
||||||
@@ -304,7 +297,7 @@ private fun wrapInRunFun(expression: KtExpression): PsiElement? {
|
|||||||
val replacedBody = replaceByRunFunction(expression)
|
val replacedBody = replaceByRunFunction(expression)
|
||||||
|
|
||||||
// Increment modification tracker to clear ResolveCache after changes in function body
|
// Increment modification tracker to clear ResolveCache after changes in function body
|
||||||
(PsiManager.getInstance(expression.getProject()).getModificationTracker() as PsiModificationTrackerImpl).incCounter()
|
(PsiManager.getInstance(expression.project).modificationTracker as PsiModificationTrackerImpl).incCounter()
|
||||||
|
|
||||||
return replacedBody.getFunctionLiteralArguments().first().getFunctionLiteral().getBodyExpression()?.getFirstChild()
|
return replacedBody.functionLiteralArguments.first().getFunctionLiteral().bodyExpression?.firstChild
|
||||||
}
|
}
|
||||||
|
|||||||
+105
-108
@@ -124,7 +124,7 @@ private fun List<Instruction>.getVarDescriptorsAccessedAfterwards(bindingContext
|
|||||||
PseudocodeUtil.extractVariableDescriptorIfAny(it, false, bindingContext)?.let { accessedAfterwards.add(it) }
|
PseudocodeUtil.extractVariableDescriptorIfAny(it, false, bindingContext)?.let { accessedAfterwards.add(it) }
|
||||||
|
|
||||||
it is LocalFunctionDeclarationInstruction ->
|
it is LocalFunctionDeclarationInstruction ->
|
||||||
doTraversal(it.body.getEnterInstruction())
|
doTraversal(it.body.enterInstruction)
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
@@ -155,14 +155,13 @@ private fun List<Instruction>.getResultTypeAndExpressions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun instructionToType(instruction: Instruction): KotlinType? {
|
fun instructionToType(instruction: Instruction): KotlinType? {
|
||||||
val expression = instructionToExpression(instruction, true)
|
val expression = instructionToExpression(instruction, true) ?: return null
|
||||||
|
|
||||||
if (expression == null) return null
|
|
||||||
if (options.inferUnitTypeForUnusedValues && expression.isUsedAsStatement(bindingContext)) return null
|
if (options.inferUnitTypeForUnusedValues && expression.isUsedAsStatement(bindingContext)) return null
|
||||||
|
|
||||||
return bindingContext.getType(expression)
|
return bindingContext.getType(expression)
|
||||||
?: (expression as? KtReferenceExpression)?.let {
|
?: (expression as? KtReferenceExpression)?.let {
|
||||||
(bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.getReturnType()
|
(bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.returnType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,7 +204,7 @@ private fun getCommonNonTrivialSuccessorIfAny(instructions: List<Instruction>):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (instructions.flatMap { it.nextInstructions }.any { !it.accept(singleSuccessorCheckingVisitor) }) return null
|
if (instructions.flatMap { it.nextInstructions }.any { !it.accept(singleSuccessorCheckingVisitor) }) return null
|
||||||
return singleSuccessorCheckingVisitor.target ?: instructions.firstOrNull()?.owner?.getSinkInstruction()
|
return singleSuccessorCheckingVisitor.target ?: instructions.firstOrNull()?.owner?.sinkInstruction
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KotlinType.isMeaningful(): Boolean {
|
private fun KotlinType.isMeaningful(): Boolean {
|
||||||
@@ -228,7 +227,7 @@ private fun ExtractionData.getLocalDeclarationsWithNonLocalUsages(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return declarations.sortedBy { it.getTextRange()!!.getStartOffset() }
|
return declarations.sortedBy { it.textRange!!.startOffset }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ExtractionData.analyzeControlFlow(
|
private fun ExtractionData.analyzeControlFlow(
|
||||||
@@ -248,7 +247,7 @@ private fun ExtractionData.analyzeControlFlow(
|
|||||||
val jumpExits = ArrayList<AbstractJumpInstruction>()
|
val jumpExits = ArrayList<AbstractJumpInstruction>()
|
||||||
exitPoints.forEach {
|
exitPoints.forEach {
|
||||||
val e = (it as? UnconditionalJumpInstruction)?.element
|
val e = (it as? UnconditionalJumpInstruction)?.element
|
||||||
val insn =
|
val inst =
|
||||||
when {
|
when {
|
||||||
it !is ReturnValueInstruction && it !is ReturnNoValueInstruction && it.owner != pseudocode ->
|
it !is ReturnValueInstruction && it !is ReturnNoValueInstruction && it.owner != pseudocode ->
|
||||||
null
|
null
|
||||||
@@ -258,38 +257,38 @@ private fun ExtractionData.analyzeControlFlow(
|
|||||||
it
|
it
|
||||||
}
|
}
|
||||||
|
|
||||||
when (insn) {
|
when (inst) {
|
||||||
is ReturnValueInstruction -> {
|
is ReturnValueInstruction -> {
|
||||||
if (insn.owner == pseudocode) {
|
if (inst.owner == pseudocode) {
|
||||||
if (insn.returnExpressionIfAny == null) {
|
if (inst.returnExpressionIfAny == null) {
|
||||||
defaultExits.add(insn)
|
defaultExits.add(inst)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
valuedReturnExits.add(insn)
|
valuedReturnExits.add(inst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is AbstractJumpInstruction -> {
|
is AbstractJumpInstruction -> {
|
||||||
val element = insn.element
|
val element = inst.element
|
||||||
if ((element is KtReturnExpression && insn.owner == pseudocode)
|
if ((element is KtReturnExpression && inst.owner == pseudocode)
|
||||||
|| element is KtBreakExpression
|
|| element is KtBreakExpression
|
||||||
|| element is KtContinueExpression) {
|
|| element is KtContinueExpression) {
|
||||||
jumpExits.add(insn)
|
jumpExits.add(inst)
|
||||||
}
|
}
|
||||||
else if (element !is KtThrowExpression) {
|
else if (element !is KtThrowExpression) {
|
||||||
defaultExits.add(insn)
|
defaultExits.add(inst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> if (insn != null && insn !is LocalFunctionDeclarationInstruction) {
|
else -> if (inst != null && inst !is LocalFunctionDeclarationInstruction) {
|
||||||
defaultExits.add(insn)
|
defaultExits.add(inst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val nonLocallyUsedDeclarations = getLocalDeclarationsWithNonLocalUsages(pseudocode, localInstructions, bindingContext)
|
val nonLocallyUsedDeclarations = getLocalDeclarationsWithNonLocalUsages(pseudocode, localInstructions, bindingContext)
|
||||||
val (declarationsToCopy, declarationsToReport) = nonLocallyUsedDeclarations.partition { it is KtProperty && it.isLocal() }
|
val (declarationsToCopy, declarationsToReport) = nonLocallyUsedDeclarations.partition { it is KtProperty && it.isLocal }
|
||||||
|
|
||||||
val (typeOfDefaultFlow, defaultResultExpressions) = defaultExits.getResultTypeAndExpressions(bindingContext, targetScope, options, module)
|
val (typeOfDefaultFlow, defaultResultExpressions) = defaultExits.getResultTypeAndExpressions(bindingContext, targetScope, options, module)
|
||||||
val (returnValueType, valuedReturnExpressions) = valuedReturnExits.getResultTypeAndExpressions(bindingContext, targetScope, options, module)
|
val (returnValueType, valuedReturnExpressions) = valuedReturnExits.getResultTypeAndExpressions(bindingContext, targetScope, options, module)
|
||||||
@@ -298,7 +297,7 @@ private fun ExtractionData.analyzeControlFlow(
|
|||||||
ControlFlow(Collections.emptyList(), { OutputValueBoxer.AsTuple(it, module) }, declarationsToCopy)
|
ControlFlow(Collections.emptyList(), { OutputValueBoxer.AsTuple(it, module) }, declarationsToCopy)
|
||||||
|
|
||||||
val defaultReturnType = if (returnValueType.isMeaningful()) returnValueType else typeOfDefaultFlow
|
val defaultReturnType = if (returnValueType.isMeaningful()) returnValueType else typeOfDefaultFlow
|
||||||
if (defaultReturnType.isError()) return emptyControlFlow to ErrorMessage.ERROR_TYPES
|
if (defaultReturnType.isError) return emptyControlFlow to ErrorMessage.ERROR_TYPES
|
||||||
|
|
||||||
val controlFlow = if (defaultReturnType.isMeaningful()) {
|
val controlFlow = if (defaultReturnType.isMeaningful()) {
|
||||||
emptyControlFlow.copy(outputValues = Collections.singletonList(ExpressionValue(false, defaultResultExpressions, defaultReturnType)))
|
emptyControlFlow.copy(outputValues = Collections.singletonList(ExpressionValue(false, defaultResultExpressions, defaultReturnType)))
|
||||||
@@ -313,10 +312,10 @@ private fun ExtractionData.analyzeControlFlow(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val outParameters =
|
val outParameters =
|
||||||
parameters.filter { it.mirrorVarName != null && modifiedVarDescriptors[it.originalDescriptor] != null }.sortedBy { it.nameForRef }
|
parameters.filter { it.mirrorVarName != null && modifiedVarDescriptors.getRaw(it.originalDescriptor) != null }.sortedBy { it.nameForRef }
|
||||||
val outDeclarations =
|
val outDeclarations =
|
||||||
declarationsToCopy.filter { modifiedVarDescriptors[bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it]] != null }
|
declarationsToCopy.filter { modifiedVarDescriptors.getRaw(bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it]) != null }
|
||||||
val modifiedValueCount = outParameters.size() + outDeclarations.size()
|
val modifiedValueCount = outParameters.size + outDeclarations.size
|
||||||
|
|
||||||
val outputValues = ArrayList<OutputValue>()
|
val outputValues = ArrayList<OutputValue>()
|
||||||
|
|
||||||
@@ -333,7 +332,7 @@ private fun ExtractionData.analyzeControlFlow(
|
|||||||
|
|
||||||
if (defaultExits.isNotEmpty()) {
|
if (defaultExits.isNotEmpty()) {
|
||||||
if (modifiedValueCount != 0) return outputAndExitsError
|
if (modifiedValueCount != 0) return outputAndExitsError
|
||||||
if (valuedReturnExits.size() != 1) return multipleExitsError
|
if (valuedReturnExits.size != 1) return multipleExitsError
|
||||||
|
|
||||||
val element = valuedReturnExits.first().element as KtExpression
|
val element = valuedReturnExits.first().element as KtExpression
|
||||||
return controlFlow.copy(outputValues = Collections.singletonList(Jump(listOf(element), element, true, module.builtIns))) to null
|
return controlFlow.copy(outputValues = Collections.singletonList(Jump(listOf(element), element, true, module.builtIns))) to null
|
||||||
@@ -345,15 +344,15 @@ private fun ExtractionData.analyzeControlFlow(
|
|||||||
|
|
||||||
outDeclarations.mapTo(outputValues) {
|
outDeclarations.mapTo(outputValues) {
|
||||||
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it] as? CallableDescriptor
|
val descriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it] as? CallableDescriptor
|
||||||
Initializer(it as KtProperty, descriptor?.getReturnType() ?: module.builtIns.defaultParameterType)
|
Initializer(it as KtProperty, descriptor?.returnType ?: module.builtIns.defaultParameterType)
|
||||||
}
|
}
|
||||||
outParameters.mapTo(outputValues) { ParameterUpdate(it, modifiedVarDescriptors[it.originalDescriptor]!!) }
|
outParameters.mapTo(outputValues) { ParameterUpdate(it, modifiedVarDescriptors.getRaw(it.originalDescriptor)!!) }
|
||||||
|
|
||||||
if (outputValues.isNotEmpty()) {
|
if (outputValues.isNotEmpty()) {
|
||||||
if (jumpExits.isNotEmpty()) return outputAndExitsError
|
if (jumpExits.isNotEmpty()) return outputAndExitsError
|
||||||
|
|
||||||
val boxerFactory: (List<OutputValue>) -> OutputValueBoxer = when {
|
val boxerFactory: (List<OutputValue>) -> OutputValueBoxer = when {
|
||||||
outputValues.size() > 3 -> {
|
outputValues.size > 3 -> {
|
||||||
if (!options.enableListBoxing) {
|
if (!options.enableListBoxing) {
|
||||||
val outValuesStr =
|
val outValuesStr =
|
||||||
(outParameters.map { it.originalDescriptor.renderForMessage() }
|
(outParameters.map { it.originalDescriptor.renderForMessage() }
|
||||||
@@ -370,8 +369,7 @@ private fun ExtractionData.analyzeControlFlow(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (jumpExits.isNotEmpty()) {
|
if (jumpExits.isNotEmpty()) {
|
||||||
val jumpTarget = getCommonNonTrivialSuccessorIfAny(jumpExits)
|
val jumpTarget = getCommonNonTrivialSuccessorIfAny(jumpExits) ?: return multipleExitsError
|
||||||
if (jumpTarget == null) return multipleExitsError
|
|
||||||
|
|
||||||
val singleExit = getCommonNonTrivialSuccessorIfAny(defaultExits) == jumpTarget
|
val singleExit = getCommonNonTrivialSuccessorIfAny(defaultExits) == jumpTarget
|
||||||
val conditional = !singleExit && defaultExits.isNotEmpty()
|
val conditional = !singleExit && defaultExits.isNotEmpty()
|
||||||
@@ -384,18 +382,18 @@ private fun ExtractionData.analyzeControlFlow(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun ExtractionData.createTemporaryDeclaration(functionText: String): KtNamedDeclaration {
|
fun ExtractionData.createTemporaryDeclaration(functionText: String): KtNamedDeclaration {
|
||||||
val textRange = targetSibling.getTextRange()!!
|
val textRange = targetSibling.textRange!!
|
||||||
|
|
||||||
val insertText: String
|
val insertText: String
|
||||||
val insertPosition: Int
|
val insertPosition: Int
|
||||||
val lookupPosition: Int
|
val lookupPosition: Int
|
||||||
if (insertBefore) {
|
if (insertBefore) {
|
||||||
insertPosition = textRange.getStartOffset()
|
insertPosition = textRange.startOffset
|
||||||
lookupPosition = insertPosition
|
lookupPosition = insertPosition
|
||||||
insertText = functionText
|
insertText = functionText
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
insertPosition = textRange.getEndOffset()
|
insertPosition = textRange.endOffset
|
||||||
lookupPosition = insertPosition + 1
|
lookupPosition = insertPosition + 1
|
||||||
insertText = "\n$functionText"
|
insertText = "\n$functionText"
|
||||||
}
|
}
|
||||||
@@ -407,14 +405,14 @@ fun ExtractionData.createTemporaryDeclaration(functionText: String): KtNamedDecl
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun ExtractionData.createTemporaryCodeBlock(): KtBlockExpression =
|
private fun ExtractionData.createTemporaryCodeBlock(): KtBlockExpression =
|
||||||
(createTemporaryDeclaration("fun() {\n$codeFragmentText\n}\n") as KtNamedFunction).getBodyExpression() as KtBlockExpression
|
(createTemporaryDeclaration("fun() {\n$codeFragmentText\n}\n") as KtNamedFunction).bodyExpression as KtBlockExpression
|
||||||
|
|
||||||
private fun KotlinType.collectReferencedTypes(processTypeArguments: Boolean): List<KotlinType> {
|
private fun KotlinType.collectReferencedTypes(processTypeArguments: Boolean): List<KotlinType> {
|
||||||
if (!processTypeArguments) return Collections.singletonList(this)
|
if (!processTypeArguments) return Collections.singletonList(this)
|
||||||
return DFS.dfsFromNode(
|
return DFS.dfsFromNode(
|
||||||
this,
|
this,
|
||||||
object: Neighbors<KotlinType> {
|
object: Neighbors<KotlinType> {
|
||||||
override fun getNeighbors(current: KotlinType): Iterable<KotlinType> = current.getArguments().map { it.getType() }
|
override fun getNeighbors(current: KotlinType): Iterable<KotlinType> = current.arguments.map { it.type }
|
||||||
},
|
},
|
||||||
VisitedWithSet(),
|
VisitedWithSet(),
|
||||||
object: CollectingNodeHandler<KotlinType, KotlinType, ArrayList<KotlinType>>(ArrayList()) {
|
object: CollectingNodeHandler<KotlinType, KotlinType, ArrayList<KotlinType>>(ArrayList()) {
|
||||||
@@ -426,16 +424,15 @@ private fun KotlinType.collectReferencedTypes(processTypeArguments: Boolean): Li
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun KtTypeParameter.collectRelevantConstraints(): List<KtTypeConstraint> {
|
fun KtTypeParameter.collectRelevantConstraints(): List<KtTypeConstraint> {
|
||||||
val typeConstraints = getNonStrictParentOfType<KtTypeParameterListOwner>()?.getTypeConstraints()
|
val typeConstraints = getNonStrictParentOfType<KtTypeParameterListOwner>()?.typeConstraints ?: return Collections.emptyList()
|
||||||
if (typeConstraints == null) return Collections.emptyList()
|
return typeConstraints.filter { it.subjectTypeParameterName?.mainReference?.resolve() == this}
|
||||||
return typeConstraints.filter { it.getSubjectTypeParameterName()?.mainReference?.resolve() == this}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun TypeParameter.collectReferencedTypes(bindingContext: BindingContext): List<KotlinType> {
|
fun TypeParameter.collectReferencedTypes(bindingContext: BindingContext): List<KotlinType> {
|
||||||
val typeRefs = ArrayList<KtTypeReference>()
|
val typeRefs = ArrayList<KtTypeReference>()
|
||||||
originalDeclaration.getExtendsBound()?.let { typeRefs.add(it) }
|
originalDeclaration.extendsBound?.let { typeRefs.add(it) }
|
||||||
originalConstraints
|
originalConstraints
|
||||||
.map { it.getBoundTypeReference() }
|
.map { it.boundTypeReference }
|
||||||
.filterNotNullTo(typeRefs)
|
.filterNotNullTo(typeRefs)
|
||||||
|
|
||||||
return typeRefs
|
return typeRefs
|
||||||
@@ -445,7 +442,7 @@ fun TypeParameter.collectReferencedTypes(bindingContext: BindingContext): List<K
|
|||||||
|
|
||||||
private fun KotlinType.isExtractable(targetScope: LexicalScope?): Boolean {
|
private fun KotlinType.isExtractable(targetScope: LexicalScope?): Boolean {
|
||||||
return collectReferencedTypes(true).fold(true) { extractable, typeToCheck ->
|
return collectReferencedTypes(true).fold(true) { extractable, typeToCheck ->
|
||||||
val parameterTypeDescriptor = typeToCheck.getConstructor().getDeclarationDescriptor() as? TypeParameterDescriptor
|
val parameterTypeDescriptor = typeToCheck.constructor.declarationDescriptor as? TypeParameterDescriptor
|
||||||
val typeParameter = parameterTypeDescriptor?.let {
|
val typeParameter = parameterTypeDescriptor?.let {
|
||||||
DescriptorToSourceUtils.descriptorToDeclaration(it)
|
DescriptorToSourceUtils.descriptorToDeclaration(it)
|
||||||
} as? KtTypeParameter
|
} as? KtTypeParameter
|
||||||
@@ -462,7 +459,7 @@ private fun KotlinType.processTypeIfExtractable(
|
|||||||
processTypeArguments: Boolean = true
|
processTypeArguments: Boolean = true
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return collectReferencedTypes(processTypeArguments).fold(true) { extractable, typeToCheck ->
|
return collectReferencedTypes(processTypeArguments).fold(true) { extractable, typeToCheck ->
|
||||||
val parameterTypeDescriptor = typeToCheck.getConstructor().getDeclarationDescriptor() as? TypeParameterDescriptor
|
val parameterTypeDescriptor = typeToCheck.constructor.declarationDescriptor as? TypeParameterDescriptor
|
||||||
val typeParameter = parameterTypeDescriptor?.let {
|
val typeParameter = parameterTypeDescriptor?.let {
|
||||||
DescriptorToSourceUtils.descriptorToDeclaration(it)
|
DescriptorToSourceUtils.descriptorToDeclaration(it)
|
||||||
} as? KtTypeParameter
|
} as? KtTypeParameter
|
||||||
@@ -479,7 +476,7 @@ private fun KotlinType.processTypeIfExtractable(
|
|||||||
options.allowSpecialClassNames && typeToCheck.isSpecial() ->
|
options.allowSpecialClassNames && typeToCheck.isSpecial() ->
|
||||||
extractable
|
extractable
|
||||||
|
|
||||||
typeToCheck.isError() ->
|
typeToCheck.isError ->
|
||||||
false
|
false
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
@@ -537,7 +534,7 @@ private class MutableParameter(
|
|||||||
val typePredicate = and(typePredicates)
|
val typePredicate = and(typePredicates)
|
||||||
|
|
||||||
val typeSet = if (defaultType.isFlexible()) {
|
val typeSet = if (defaultType.isFlexible()) {
|
||||||
val bounds = defaultType.getCapability(javaClass<Flexibility>())!!
|
val bounds = defaultType.getCapability(Flexibility::class.java)!!
|
||||||
LinkedHashSet<KotlinType>().apply {
|
LinkedHashSet<KotlinType>().apply {
|
||||||
if (typePredicate(bounds.upperBound)) add(bounds.upperBound)
|
if (typePredicate(bounds.upperBound)) add(bounds.upperBound)
|
||||||
if (typePredicate(bounds.lowerBound)) add(bounds.lowerBound)
|
if (typePredicate(bounds.lowerBound)) add(bounds.lowerBound)
|
||||||
@@ -545,7 +542,7 @@ private class MutableParameter(
|
|||||||
}
|
}
|
||||||
else linkedSetOf(defaultType)
|
else linkedSetOf(defaultType)
|
||||||
|
|
||||||
val addNullableTypes = defaultType.isNullabilityFlexible() && typeSet.size() > 1
|
val addNullableTypes = defaultType.isNullabilityFlexible() && typeSet.size > 1
|
||||||
val superTypes = TypeUtils.getAllSupertypes(defaultType).filter(typePredicate)
|
val superTypes = TypeUtils.getAllSupertypes(defaultType).filter(typePredicate)
|
||||||
|
|
||||||
for (superType in superTypes) {
|
for (superType in superTypes) {
|
||||||
@@ -615,28 +612,28 @@ private fun ExtractionData.inferParametersInfo(
|
|||||||
extractFunctionRef -> {
|
extractFunctionRef -> {
|
||||||
originalDescriptor as FunctionDescriptor
|
originalDescriptor as FunctionDescriptor
|
||||||
builtIns.getFunctionType(Annotations.EMPTY,
|
builtIns.getFunctionType(Annotations.EMPTY,
|
||||||
originalDescriptor.getExtensionReceiverParameter()?.getType(),
|
originalDescriptor.extensionReceiverParameter?.type,
|
||||||
originalDescriptor.getValueParameters().map { it.getType() },
|
originalDescriptor.valueParameters.map { it.type },
|
||||||
originalDescriptor.getReturnType() ?: builtIns.defaultReturnType)
|
originalDescriptor.returnType ?: builtIns.defaultReturnType)
|
||||||
}
|
}
|
||||||
parameterExpression != null ->
|
parameterExpression != null ->
|
||||||
(if (useSmartCastsIfPossible) bindingContext[BindingContext.SMARTCAST, parameterExpression] else null)
|
(if (useSmartCastsIfPossible) bindingContext[BindingContext.SMARTCAST, parameterExpression] else null)
|
||||||
?: bindingContext.getType(parameterExpression)
|
?: bindingContext.getType(parameterExpression)
|
||||||
?: (parameterExpression as? KtReferenceExpression)?.let {
|
?: (parameterExpression as? KtReferenceExpression)?.let {
|
||||||
(bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.getReturnType()
|
(bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.returnType
|
||||||
}
|
}
|
||||||
?: if (receiverToExtract.exists()) receiverToExtract.getType() else null
|
?: if (receiverToExtract.exists()) receiverToExtract.type else null
|
||||||
receiverToExtract is ThisReceiver -> {
|
receiverToExtract is ThisReceiver -> {
|
||||||
val calleeExpression = resolvedCall!!.getCall().getCalleeExpression()
|
val calleeExpression = resolvedCall!!.call.calleeExpression
|
||||||
val typeByDataFlowInfo = if (useSmartCastsIfPossible) {
|
val typeByDataFlowInfo = if (useSmartCastsIfPossible) {
|
||||||
bindingContext[BindingContext.EXPRESSION_TYPE_INFO, calleeExpression]?.dataFlowInfo?.let { dataFlowInfo ->
|
bindingContext[BindingContext.EXPRESSION_TYPE_INFO, calleeExpression]?.dataFlowInfo?.let { dataFlowInfo ->
|
||||||
val possibleTypes = dataFlowInfo.getPossibleTypes(DataFlowValueFactory.createDataFlowValue(receiverToExtract))
|
val possibleTypes = dataFlowInfo.getPossibleTypes(DataFlowValueFactory.createDataFlowValue(receiverToExtract))
|
||||||
if (possibleTypes.isNotEmpty()) CommonSupertypes.commonSupertype(possibleTypes) else null
|
if (possibleTypes.isNotEmpty()) CommonSupertypes.commonSupertype(possibleTypes) else null
|
||||||
}
|
}
|
||||||
} else null
|
} else null
|
||||||
typeByDataFlowInfo ?: receiverToExtract.getType()
|
typeByDataFlowInfo ?: receiverToExtract.type
|
||||||
}
|
}
|
||||||
receiverToExtract.exists() -> receiverToExtract.getType()
|
receiverToExtract.exists() -> receiverToExtract.type
|
||||||
else -> null
|
else -> null
|
||||||
} ?: builtIns.defaultParameterType
|
} ?: builtIns.defaultParameterType
|
||||||
}
|
}
|
||||||
@@ -645,23 +642,23 @@ private fun ExtractionData.inferParametersInfo(
|
|||||||
val (originalRef, originalDeclaration, originalDescriptor, resolvedCall) = refInfo.resolveResult
|
val (originalRef, originalDeclaration, originalDescriptor, resolvedCall) = refInfo.resolveResult
|
||||||
val ref = refInfo.refExpr
|
val ref = refInfo.refExpr
|
||||||
|
|
||||||
val selector = (ref.getParent() as? KtCallExpression) ?: ref
|
val selector = (ref.parent as? KtCallExpression) ?: ref
|
||||||
val superExpr = (selector.getParent() as? KtQualifiedExpression)?.getReceiverExpression() as? KtSuperExpression
|
val superExpr = (selector.parent as? KtQualifiedExpression)?.receiverExpression as? KtSuperExpression
|
||||||
if (superExpr != null) {
|
if (superExpr != null) {
|
||||||
info.errorMessage = ErrorMessage.SUPER_CALL
|
info.errorMessage = ErrorMessage.SUPER_CALL
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
val extensionReceiver = resolvedCall?.getExtensionReceiver()
|
val extensionReceiver = resolvedCall?.extensionReceiver
|
||||||
val receiverToExtract = when {
|
val receiverToExtract = when {
|
||||||
extensionReceiver == ReceiverValue.NO_RECEIVER,
|
extensionReceiver == ReceiverValue.NO_RECEIVER,
|
||||||
isSynthesizedInvoke(originalDescriptor) -> resolvedCall?.getDispatchReceiver()
|
isSynthesizedInvoke(originalDescriptor) -> resolvedCall?.dispatchReceiver
|
||||||
else -> extensionReceiver
|
else -> extensionReceiver
|
||||||
} ?: ReceiverValue.NO_RECEIVER
|
} ?: ReceiverValue.NO_RECEIVER
|
||||||
|
|
||||||
val thisDescriptor = (receiverToExtract as? ThisReceiver)?.getDeclarationDescriptor()
|
val thisDescriptor = (receiverToExtract as? ThisReceiver)?.declarationDescriptor
|
||||||
val hasThisReceiver = thisDescriptor != null
|
val hasThisReceiver = thisDescriptor != null
|
||||||
val thisExpr = ref.getParent() as? KtThisExpression
|
val thisExpr = ref.parent as? KtThisExpression
|
||||||
|
|
||||||
if (hasThisReceiver
|
if (hasThisReceiver
|
||||||
&& DescriptorToSourceUtilsIde.getAllDeclarations(project, thisDescriptor!!).all { it.isInsideOf(originalElements) }) {
|
&& DescriptorToSourceUtilsIde.getAllDeclarations(project, thisDescriptor!!).all { it.isInsideOf(originalElements) }) {
|
||||||
@@ -671,22 +668,22 @@ private fun ExtractionData.inferParametersInfo(
|
|||||||
val referencedClassifierDescriptor: ClassifierDescriptor? = (thisDescriptor ?: originalDescriptor).let {
|
val referencedClassifierDescriptor: ClassifierDescriptor? = (thisDescriptor ?: originalDescriptor).let {
|
||||||
when (it) {
|
when (it) {
|
||||||
is ClassDescriptor ->
|
is ClassDescriptor ->
|
||||||
when(it.getKind()) {
|
when(it.kind) {
|
||||||
ClassKind.OBJECT, ClassKind.ENUM_CLASS -> it as ClassifierDescriptor
|
ClassKind.OBJECT, ClassKind.ENUM_CLASS -> it
|
||||||
ClassKind.ENUM_ENTRY -> it.getContainingDeclaration() as? ClassDescriptor
|
ClassKind.ENUM_ENTRY -> it.containingDeclaration as? ClassDescriptor
|
||||||
else -> if (ref.getNonStrictParentOfType<KtTypeReference>() != null) it as ClassifierDescriptor else null
|
else -> if (ref.getNonStrictParentOfType<KtTypeReference>() != null) it else null
|
||||||
}
|
}
|
||||||
|
|
||||||
is TypeParameterDescriptor -> it as ClassifierDescriptor
|
is TypeParameterDescriptor -> it
|
||||||
|
|
||||||
is ConstructorDescriptor -> it.getContainingDeclaration()
|
is ConstructorDescriptor -> it.containingDeclaration
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
} as? ClassifierDescriptor
|
||||||
}
|
}
|
||||||
|
|
||||||
if (referencedClassifierDescriptor != null) {
|
if (referencedClassifierDescriptor != null) {
|
||||||
if (!referencedClassifierDescriptor.getDefaultType().processTypeIfExtractable(
|
if (!referencedClassifierDescriptor.defaultType.processTypeIfExtractable(
|
||||||
info.typeParameters, info.nonDenotableTypes, options, targetScope, referencedClassifierDescriptor is TypeParameterDescriptor
|
info.typeParameters, info.nonDenotableTypes, options, targetScope, referencedClassifierDescriptor is TypeParameterDescriptor
|
||||||
)) continue
|
)) continue
|
||||||
|
|
||||||
@@ -703,8 +700,8 @@ private fun ExtractionData.inferParametersInfo(
|
|||||||
|
|
||||||
val extractFunctionRef =
|
val extractFunctionRef =
|
||||||
options.captureLocalFunctions
|
options.captureLocalFunctions
|
||||||
&& originalRef.getReferencedName() == originalDescriptor.getName().asString() // to forbid calls by convention
|
&& originalRef.getReferencedName() == originalDescriptor.name.asString() // to forbid calls by convention
|
||||||
&& originalDeclaration is KtNamedFunction && originalDeclaration.isLocal()
|
&& originalDeclaration is KtNamedFunction && originalDeclaration.isLocal
|
||||||
&& targetScope.findFunction(originalDescriptor.name, NoLookupLocation.FROM_IDE) { it == originalDescriptor } == null
|
&& targetScope.findFunction(originalDescriptor.name, NoLookupLocation.FROM_IDE) { it == originalDescriptor } == null
|
||||||
|
|
||||||
val descriptorToExtract = (if (extractThis) thisDescriptor else null) ?: originalDescriptor
|
val descriptorToExtract = (if (extractThis) thisDescriptor else null) ?: originalDescriptor
|
||||||
@@ -713,12 +710,12 @@ private fun ExtractionData.inferParametersInfo(
|
|||||||
if (extractParameter) {
|
if (extractParameter) {
|
||||||
val parameterExpression = when {
|
val parameterExpression = when {
|
||||||
receiverToExtract is ExpressionReceiver -> {
|
receiverToExtract is ExpressionReceiver -> {
|
||||||
val receiverExpression = receiverToExtract.getExpression()
|
val receiverExpression = receiverToExtract.expression
|
||||||
// If p.q has a smart-cast, then extract entire qualified expression
|
// If p.q has a smart-cast, then extract entire qualified expression
|
||||||
if (refInfo.smartCast != null) receiverExpression.getParent() as KtExpression else receiverExpression
|
if (refInfo.smartCast != null) receiverExpression.parent as KtExpression else receiverExpression
|
||||||
}
|
}
|
||||||
receiverToExtract.exists() && refInfo.smartCast == null -> null
|
receiverToExtract.exists() && refInfo.smartCast == null -> null
|
||||||
else -> (originalRef.getParent() as? KtThisExpression) ?: originalRef
|
else -> (originalRef.parent as? KtThisExpression) ?: originalRef
|
||||||
}
|
}
|
||||||
|
|
||||||
val parameterType = suggestParameterType(extractFunctionRef, originalDescriptor, parameterExpression, receiverToExtract, resolvedCall, true)
|
val parameterType = suggestParameterType(extractFunctionRef, originalDescriptor, parameterExpression, receiverToExtract, resolvedCall, true)
|
||||||
@@ -726,7 +723,7 @@ private fun ExtractionData.inferParametersInfo(
|
|||||||
val parameter = extractedDescriptorToParameter.getOrPut(descriptorToExtract) {
|
val parameter = extractedDescriptorToParameter.getOrPut(descriptorToExtract) {
|
||||||
var argumentText =
|
var argumentText =
|
||||||
if (hasThisReceiver && extractThis) {
|
if (hasThisReceiver && extractThis) {
|
||||||
val label = if (descriptorToExtract is ClassDescriptor) "@${descriptorToExtract.getName().asString()}" else ""
|
val label = if (descriptorToExtract is ClassDescriptor) "@${descriptorToExtract.name.asString()}" else ""
|
||||||
"this$label"
|
"this$label"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -736,13 +733,13 @@ private fun ExtractionData.inferParametersInfo(
|
|||||||
val nameElementType = nameElement.node.elementType
|
val nameElementType = nameElement.node.elementType
|
||||||
(nameElementType as? KtToken)?.let {
|
(nameElementType as? KtToken)?.let {
|
||||||
OperatorConventions.getNameForOperationSymbol(it)?.asString()
|
OperatorConventions.getNameForOperationSymbol(it)?.asString()
|
||||||
} ?: nameElement.getText()
|
} ?: nameElement.text
|
||||||
}
|
}
|
||||||
else argumentExpr.getText()
|
else argumentExpr.text
|
||||||
?: throw AssertionError("reference shouldn't be empty: code fragment = $codeFragmentText")
|
?: throw AssertionError("reference shouldn't be empty: code fragment = $codeFragmentText")
|
||||||
}
|
}
|
||||||
if (extractFunctionRef) {
|
if (extractFunctionRef) {
|
||||||
val receiverTypeText = (originalDeclaration as KtCallableDeclaration).getReceiverTypeReference()?.getText() ?: ""
|
val receiverTypeText = (originalDeclaration as KtCallableDeclaration).receiverTypeReference?.text ?: ""
|
||||||
argumentText = "$receiverTypeText::$argumentText"
|
argumentText = "$receiverTypeText::$argumentText"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,7 +749,7 @@ private fun ExtractionData.inferParametersInfo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!extractThis) {
|
if (!extractThis) {
|
||||||
parameter.currentName = originalDeclaration.getNameIdentifier()?.getText()
|
parameter.currentName = originalDeclaration.nameIdentifier?.text
|
||||||
}
|
}
|
||||||
|
|
||||||
parameter.refCount++
|
parameter.refCount++
|
||||||
@@ -761,9 +758,9 @@ private fun ExtractionData.inferParametersInfo(
|
|||||||
parameter.addDefaultType(parameterType)
|
parameter.addDefaultType(parameterType)
|
||||||
|
|
||||||
if (extractThis && thisExpr == null) {
|
if (extractThis && thisExpr == null) {
|
||||||
val callElement = resolvedCall!!.getCall().getCallElement()
|
val callElement = resolvedCall!!.call.callElement
|
||||||
val instruction = pseudocode.getElementValue(callElement)?.createdAt as? InstructionWithReceivers
|
val instruction = pseudocode.getElementValue(callElement)?.createdAt as? InstructionWithReceivers
|
||||||
val receiverValue = instruction?.receiverValues?.entrySet()?.singleOrNull { it.getValue() == receiverToExtract }?.getKey()
|
val receiverValue = instruction?.receiverValues?.entries?.singleOrNull { it.value == receiverToExtract }?.key
|
||||||
if (receiverValue != null) {
|
if (receiverValue != null) {
|
||||||
parameter.addTypePredicate(getExpectedTypePredicate(receiverValue, bindingContext, targetScope.ownerDescriptor.builtIns))
|
parameter.addTypePredicate(getExpectedTypePredicate(receiverValue, bindingContext, targetScope.ownerDescriptor.builtIns))
|
||||||
}
|
}
|
||||||
@@ -798,7 +795,7 @@ private fun ExtractionData.inferParametersInfo(
|
|||||||
if (currentName == null) {
|
if (currentName == null) {
|
||||||
currentName = KotlinNameSuggester.suggestNamesByType(getParameterType(options.allowSpecialClassNames), varNameValidator, "p").first()
|
currentName = KotlinNameSuggester.suggestNamesByType(getParameterType(options.allowSpecialClassNames), varNameValidator, "p").first()
|
||||||
}
|
}
|
||||||
mirrorVarName = if (descriptorToExtract in modifiedVarDescriptors) KotlinNameSuggester.suggestNameByName(name, varNameValidator) else null
|
mirrorVarName = if (modifiedVarDescriptors.containsRaw(descriptorToExtract)) KotlinNameSuggester.suggestNameByName(name, varNameValidator) else null
|
||||||
info.parameters.add(this)
|
info.parameters.add(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -849,7 +846,7 @@ private fun ExtractionData.getLocalInstructions(pseudocode: Pseudocode): List<In
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun ExtractionData.isVisibilityApplicable(): Boolean {
|
fun ExtractionData.isVisibilityApplicable(): Boolean {
|
||||||
return when (targetSibling.getParent()) {
|
return when (targetSibling.parent) {
|
||||||
is KtClassBody, is KtFile -> true
|
is KtClassBody, is KtFile -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
@@ -880,7 +877,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
|||||||
val modifiedVarDescriptorsWithExpressions = localInstructions.getModifiedVarDescriptors(bindingContext)
|
val modifiedVarDescriptorsWithExpressions = localInstructions.getModifiedVarDescriptors(bindingContext)
|
||||||
|
|
||||||
val targetScope = targetSibling.getResolutionScope(bindingContext, commonParent.getResolutionFacade())
|
val targetScope = targetSibling.getResolutionScope(bindingContext, commonParent.getResolutionFacade())
|
||||||
val paramsInfo = inferParametersInfo(commonParent, pseudocode, bindingContext, targetScope, modifiedVarDescriptorsWithExpressions.keySet())
|
val paramsInfo = inferParametersInfo(commonParent, pseudocode, bindingContext, targetScope, modifiedVarDescriptorsWithExpressions.keys)
|
||||||
if (paramsInfo.errorMessage != null) {
|
if (paramsInfo.errorMessage != null) {
|
||||||
return AnalysisResult(null, Status.CRITICAL_ERROR, listOf(paramsInfo.errorMessage!!))
|
return AnalysisResult(null, Status.CRITICAL_ERROR, listOf(paramsInfo.errorMessage!!))
|
||||||
}
|
}
|
||||||
@@ -888,7 +885,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
|||||||
val messages = ArrayList<ErrorMessage>()
|
val messages = ArrayList<ErrorMessage>()
|
||||||
|
|
||||||
val modifiedVarDescriptorsForControlFlow = HashMap(modifiedVarDescriptorsWithExpressions)
|
val modifiedVarDescriptorsForControlFlow = HashMap(modifiedVarDescriptorsWithExpressions)
|
||||||
modifiedVarDescriptorsForControlFlow.keySet().retainAll(localInstructions.getVarDescriptorsAccessedAfterwards(bindingContext))
|
modifiedVarDescriptorsForControlFlow.keys.retainAll(localInstructions.getVarDescriptorsAccessedAfterwards(bindingContext))
|
||||||
val (controlFlow, controlFlowMessage) =
|
val (controlFlow, controlFlowMessage) =
|
||||||
analyzeControlFlow(
|
analyzeControlFlow(
|
||||||
localInstructions,
|
localInstructions,
|
||||||
@@ -927,7 +924,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
|||||||
val adjustedParameters = paramsInfo.parameters.filterTo(HashSet<Parameter>()) { it.refCount > 0 }
|
val adjustedParameters = paramsInfo.parameters.filterTo(HashSet<Parameter>()) { it.refCount > 0 }
|
||||||
|
|
||||||
val receiverCandidates = adjustedParameters.filterTo(HashSet<Parameter>()) { it.receiverCandidate }
|
val receiverCandidates = adjustedParameters.filterTo(HashSet<Parameter>()) { it.receiverCandidate }
|
||||||
val receiverParameter = if (receiverCandidates.size() == 1) receiverCandidates.first() else null
|
val receiverParameter = if (receiverCandidates.size == 1) receiverCandidates.first() else null
|
||||||
receiverParameter?.let { adjustedParameters.remove(it) }
|
receiverParameter?.let { adjustedParameters.remove(it) }
|
||||||
|
|
||||||
return AnalysisResult(
|
return AnalysisResult(
|
||||||
@@ -938,7 +935,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
|||||||
getDefaultVisibility(),
|
getDefaultVisibility(),
|
||||||
adjustedParameters.sortedBy { it.name },
|
adjustedParameters.sortedBy { it.name },
|
||||||
receiverParameter,
|
receiverParameter,
|
||||||
paramsInfo.typeParameters.sortedBy { it.originalDeclaration.getName()!! },
|
paramsInfo.typeParameters.sortedBy { it.originalDeclaration.name!! },
|
||||||
paramsInfo.replacementMap,
|
paramsInfo.replacementMap,
|
||||||
if (messages.isEmpty()) controlFlow else controlFlow.toDefault(),
|
if (messages.isEmpty()) controlFlow else controlFlow.toDefault(),
|
||||||
returnType
|
returnType
|
||||||
@@ -953,8 +950,8 @@ private fun ExtractionData.suggestFunctionNames(returnType: KotlinType): List<St
|
|||||||
|
|
||||||
val validator =
|
val validator =
|
||||||
NewDeclarationNameValidator(
|
NewDeclarationNameValidator(
|
||||||
targetSibling.getParent(),
|
targetSibling.parent,
|
||||||
if (targetSibling is KtClassInitializer) targetSibling.getParent() else targetSibling,
|
if (targetSibling is KtClassInitializer) targetSibling.parent else targetSibling,
|
||||||
if (options.extractAsProperty) NewDeclarationNameValidator.Target.VARIABLES else NewDeclarationNameValidator.Target.FUNCTIONS_AND_CLASSES
|
if (options.extractAsProperty) NewDeclarationNameValidator.Target.VARIABLES else NewDeclarationNameValidator.Target.FUNCTIONS_AND_CLASSES
|
||||||
)
|
)
|
||||||
if (!returnType.isDefault()) {
|
if (!returnType.isDefault()) {
|
||||||
@@ -963,8 +960,8 @@ private fun ExtractionData.suggestFunctionNames(returnType: KotlinType): List<St
|
|||||||
|
|
||||||
getExpressions().singleOrNull()?.let { expr ->
|
getExpressions().singleOrNull()?.let { expr ->
|
||||||
val property = expr.getStrictParentOfType<KtProperty>()
|
val property = expr.getStrictParentOfType<KtProperty>()
|
||||||
if (property?.getInitializer() == expr) {
|
if (property?.initializer == expr) {
|
||||||
property?.getName()?.let { functionNames.add(KotlinNameSuggester.suggestNameByName("get" + it.capitalize(), validator)) }
|
property?.name?.let { functionNames.add(KotlinNameSuggester.suggestNameByName("get" + it.capitalize(), validator)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -973,16 +970,16 @@ private fun ExtractionData.suggestFunctionNames(returnType: KotlinType): List<St
|
|||||||
|
|
||||||
internal fun KtNamedDeclaration.getGeneratedBody() =
|
internal fun KtNamedDeclaration.getGeneratedBody() =
|
||||||
when (this) {
|
when (this) {
|
||||||
is KtNamedFunction -> getBodyExpression()
|
is KtNamedFunction -> bodyExpression
|
||||||
else -> {
|
else -> {
|
||||||
val property = this as KtProperty
|
val property = this as KtProperty
|
||||||
|
|
||||||
property.getGetter()?.getBodyExpression()?.let { return it }
|
property.getter?.bodyExpression?.let { return it }
|
||||||
property.getInitializer()?.let { return it }
|
property.initializer?.let { return it }
|
||||||
// We assume lazy property here with delegate expression 'by Delegates.lazy { body }'
|
// We assume lazy property here with delegate expression 'by Delegates.lazy { body }'
|
||||||
property.getDelegateExpression()?.let {
|
property.delegateExpression?.let {
|
||||||
val call = it.getCalleeExpressionIfAny()?.getParent() as? KtCallExpression
|
val call = it.getCalleeExpressionIfAny()?.parent as? KtCallExpression
|
||||||
call?.getFunctionLiteralArguments()?.singleOrNull()?.getFunctionLiteral()?.getBodyExpression()
|
call?.functionLiteralArguments?.singleOrNull()?.getFunctionLiteral()?.bodyExpression
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} ?: throw AssertionError("Couldn't get block body for this declaration: ${getElementTextWithContext()}")
|
} ?: throw AssertionError("Couldn't get block body for this declaration: ${getElementTextWithContext()}")
|
||||||
@@ -1000,8 +997,8 @@ fun ExtractableCodeDescriptor.validate(): ExtractableCodeDescriptorWithConflicts
|
|||||||
ExtractionGeneratorOptions(inTempFile = true, allowExpressionBody = false)
|
ExtractionGeneratorOptions(inTempFile = true, allowExpressionBody = false)
|
||||||
).generateDeclaration()
|
).generateDeclaration()
|
||||||
|
|
||||||
val valueParameterList = (result.declaration as? KtNamedFunction)?.getValueParameterList()
|
val valueParameterList = (result.declaration as? KtNamedFunction)?.valueParameterList
|
||||||
val typeParameterList = (result.declaration as? KtNamedFunction)?.getTypeParameterList()
|
val typeParameterList = (result.declaration as? KtNamedFunction)?.typeParameterList
|
||||||
val body = result.declaration.getGeneratedBody()
|
val body = result.declaration.getGeneratedBody()
|
||||||
val bindingContext = body.analyzeFully()
|
val bindingContext = body.analyzeFully()
|
||||||
|
|
||||||
@@ -1013,19 +1010,19 @@ fun ExtractableCodeDescriptor.validate(): ExtractableCodeDescriptorWithConflicts
|
|||||||
(it as? KtThisExpression)?.instanceReference ?: it as? KtSimpleNameExpression
|
(it as? KtThisExpression)?.instanceReference ?: it as? KtSimpleNameExpression
|
||||||
} ?: continue
|
} ?: continue
|
||||||
|
|
||||||
if (currentRefExpr.getParent() is KtThisExpression) continue
|
if (currentRefExpr.parent is KtThisExpression) continue
|
||||||
|
|
||||||
val diagnostics = bindingContext.getDiagnostics().forElement(currentRefExpr)
|
val diagnostics = bindingContext.diagnostics.forElement(currentRefExpr)
|
||||||
|
|
||||||
val currentDescriptor = bindingContext[BindingContext.REFERENCE_TARGET, currentRefExpr]
|
val currentDescriptor = bindingContext[BindingContext.REFERENCE_TARGET, currentRefExpr]
|
||||||
val currentTarget =
|
val currentTarget =
|
||||||
currentDescriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(extractionData.project, it) } as? PsiNamedElement
|
currentDescriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(extractionData.project, it) } as? PsiNamedElement
|
||||||
if (currentTarget is KtParameter && currentTarget.getParent() == valueParameterList) continue
|
if (currentTarget is KtParameter && currentTarget.parent == valueParameterList) continue
|
||||||
if (currentTarget is KtTypeParameter && currentTarget.getParent() == typeParameterList) continue
|
if (currentTarget is KtTypeParameter && currentTarget.parent == typeParameterList) continue
|
||||||
if (currentDescriptor is LocalVariableDescriptor
|
if (currentDescriptor is LocalVariableDescriptor
|
||||||
&& parameters.any { it.mirrorVarName == currentDescriptor.getName().asString() }) continue
|
&& parameters.any { it.mirrorVarName == currentDescriptor.name.asString() }) continue
|
||||||
|
|
||||||
if (diagnostics.any { it.getFactory() in Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS }
|
if (diagnostics.any { it.factory in Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS }
|
||||||
|| (currentDescriptor != null
|
|| (currentDescriptor != null
|
||||||
&& !ErrorUtils.isError(currentDescriptor)
|
&& !ErrorUtils.isError(currentDescriptor)
|
||||||
&& !compareDescriptors(extractionData.project, currentDescriptor, resolveResult.descriptor))) {
|
&& !compareDescriptors(extractionData.project, currentDescriptor, resolveResult.descriptor))) {
|
||||||
@@ -1036,8 +1033,8 @@ fun ExtractableCodeDescriptor.validate(): ExtractableCodeDescriptorWithConflicts
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
diagnostics.firstOrNull { it.getFactory() in Errors.INVISIBLE_REFERENCE_DIAGNOSTICS }?.let {
|
diagnostics.firstOrNull { it.factory in Errors.INVISIBLE_REFERENCE_DIAGNOSTICS }?.let {
|
||||||
val message = when (it.getFactory()) {
|
val message = when (it.factory) {
|
||||||
Errors.INVISIBLE_SETTER ->
|
Errors.INVISIBLE_SETTER ->
|
||||||
getDeclarationMessage(resolveResult.declaration, "setter.of.0.will.become.invisible.after.extraction", false)
|
getDeclarationMessage(resolveResult.declaration, "setter.of.0.will.become.invisible.after.extraction", false)
|
||||||
else ->
|
else ->
|
||||||
@@ -1051,10 +1048,10 @@ fun ExtractableCodeDescriptor.validate(): ExtractableCodeDescriptorWithConflicts
|
|||||||
result.declaration.accept(
|
result.declaration.accept(
|
||||||
object : KtTreeVisitorVoid() {
|
object : KtTreeVisitorVoid() {
|
||||||
override fun visitUserType(userType: KtUserType) {
|
override fun visitUserType(userType: KtUserType) {
|
||||||
val refExpr = userType.getReferenceExpression() ?: return
|
val refExpr = userType.referenceExpression ?: return
|
||||||
val declaration = refExpr.mainReference.resolve() as? PsiNamedElement ?: return
|
val declaration = refExpr.mainReference.resolve() as? PsiNamedElement ?: return
|
||||||
val diagnostics = bindingContext.getDiagnostics().forElement(refExpr)
|
val diagnostics = bindingContext.diagnostics.forElement(refExpr)
|
||||||
diagnostics.firstOrNull { it.getFactory() == Errors.INVISIBLE_REFERENCE }?.let {
|
diagnostics.firstOrNull { it.factory == Errors.INVISIBLE_REFERENCE }?.let {
|
||||||
conflicts.putValue(declaration, getDeclarationMessage(declaration, "0.will.become.invisible.after.extraction"))
|
conflicts.putValue(declaration, getDeclarationMessage(declaration, "0.will.become.invisible.after.extraction"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1072,4 +1069,4 @@ fun ExtractableCodeDescriptor.validate(): ExtractableCodeDescriptorWithConflicts
|
|||||||
return ExtractableCodeDescriptorWithConflicts(this, conflicts)
|
return ExtractableCodeDescriptorWithConflicts(this, conflicts)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val LOG = Logger.getInstance(javaClass<ExtractionEngine>())
|
private val LOG = Logger.getInstance(ExtractionEngine::class.java)
|
||||||
|
|||||||
+56
-55
@@ -98,14 +98,14 @@ fun ExtractionGeneratorConfiguration.getDeclarationText(
|
|||||||
}
|
}
|
||||||
|
|
||||||
with(descriptor.returnType) {
|
with(descriptor.returnType) {
|
||||||
if (isDefault() || isError() || extractionTarget == ExtractionTarget.PROPERTY_WITH_INITIALIZER) {
|
if (isDefault() || isError || extractionTarget == ExtractionTarget.PROPERTY_WITH_INITIALIZER) {
|
||||||
builder.noReturnType()
|
builder.noReturnType()
|
||||||
} else {
|
} else {
|
||||||
builder.returnType(typeAsString())
|
builder.returnType(typeAsString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.typeConstraints(descriptor.typeParameters.flatMap { it.originalConstraints }.map { it.getText()!! })
|
builder.typeConstraints(descriptor.typeParameters.flatMap { it.originalConstraints }.map { it.text!! })
|
||||||
|
|
||||||
if (withBody) {
|
if (withBody) {
|
||||||
val bodyText = descriptor.extractionData.codeFragmentText
|
val bodyText = descriptor.extractionData.codeFragmentText
|
||||||
@@ -130,13 +130,13 @@ fun KotlinType.isSpecial(): Boolean {
|
|||||||
fun createNameCounterpartMap(from: KtElement, to: KtElement): Map<KtSimpleNameExpression, KtSimpleNameExpression> {
|
fun createNameCounterpartMap(from: KtElement, to: KtElement): Map<KtSimpleNameExpression, KtSimpleNameExpression> {
|
||||||
val map = HashMap<KtSimpleNameExpression, KtSimpleNameExpression>()
|
val map = HashMap<KtSimpleNameExpression, KtSimpleNameExpression>()
|
||||||
|
|
||||||
val fromOffset = from.getTextRange()!!.getStartOffset()
|
val fromOffset = from.textRange!!.startOffset
|
||||||
from.accept(
|
from.accept(
|
||||||
object : KtTreeVisitorVoid() {
|
object : KtTreeVisitorVoid() {
|
||||||
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
|
||||||
val offset = expression.getTextRange()!!.getStartOffset() - fromOffset
|
val offset = expression.textRange!!.startOffset - fromOffset
|
||||||
val newExpression = to.findElementAt(offset)?.getNonStrictParentOfType<KtSimpleNameExpression>()
|
val newExpression = to.findElementAt(offset)?.getNonStrictParentOfType<KtSimpleNameExpression>()
|
||||||
assert(newExpression != null) { "Couldn't find expression at $offset in '${to.getText()}'" }
|
assert(newExpression != null) { "Couldn't find expression at $offset in '${to.text}'" }
|
||||||
|
|
||||||
map[expression] = newExpression!!
|
map[expression] = newExpression!!
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ class DuplicateInfo(
|
|||||||
|
|
||||||
fun ExtractableCodeDescriptor.findDuplicates(): List<DuplicateInfo> {
|
fun ExtractableCodeDescriptor.findDuplicates(): List<DuplicateInfo> {
|
||||||
fun processWeakMatch(match: Match, newControlFlow: ControlFlow): Boolean {
|
fun processWeakMatch(match: Match, newControlFlow: ControlFlow): Boolean {
|
||||||
val valueCount = controlFlow.outputValues.size()
|
val valueCount = controlFlow.outputValues.size
|
||||||
|
|
||||||
val weakMatches = HashMap((match.result as WeaklyMatched).weakMatches)
|
val weakMatches = HashMap((match.result as WeaklyMatched).weakMatches)
|
||||||
val currentValuesToNew = HashMap<OutputValue, OutputValue>()
|
val currentValuesToNew = HashMap<OutputValue, OutputValue>()
|
||||||
@@ -163,7 +163,7 @@ fun ExtractableCodeDescriptor.findDuplicates(): List<DuplicateInfo> {
|
|||||||
if ((currentValue is Jump) != (newValue is Jump)) return false
|
if ((currentValue is Jump) != (newValue is Jump)) return false
|
||||||
if (currentValue.originalExpressions.zip(newValue.originalExpressions).all { weakMatches[it.first] == it.second }) {
|
if (currentValue.originalExpressions.zip(newValue.originalExpressions).all { weakMatches[it.first] == it.second }) {
|
||||||
currentValuesToNew[currentValue] = newValue
|
currentValuesToNew[currentValue] = newValue
|
||||||
weakMatches.keySet().removeAll(currentValue.originalExpressions)
|
weakMatches.keys.removeAll(currentValue.originalExpressions)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -180,7 +180,7 @@ fun ExtractableCodeDescriptor.findDuplicates(): List<DuplicateInfo> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentValuesToNew.size() == valueCount && weakMatches.isEmpty()
|
return currentValuesToNew.size == valueCount && weakMatches.isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getControlFlowIfMatched(match: Match): ControlFlow? {
|
fun getControlFlowIfMatched(match: Match): ControlFlow? {
|
||||||
@@ -189,7 +189,7 @@ fun ExtractableCodeDescriptor.findDuplicates(): List<DuplicateInfo> {
|
|||||||
|
|
||||||
val newControlFlow = analysisResult.descriptor!!.controlFlow
|
val newControlFlow = analysisResult.descriptor!!.controlFlow
|
||||||
if (newControlFlow.outputValues.isEmpty()) return newControlFlow
|
if (newControlFlow.outputValues.isEmpty()) return newControlFlow
|
||||||
if (controlFlow.outputValues.size() != newControlFlow.outputValues.size()) return null
|
if (controlFlow.outputValues.size != newControlFlow.outputValues.size) return null
|
||||||
|
|
||||||
val matched = when (match.result) {
|
val matched = when (match.result) {
|
||||||
is StronglyMatched -> true
|
is StronglyMatched -> true
|
||||||
@@ -213,14 +213,14 @@ fun ExtractableCodeDescriptor.findDuplicates(): List<DuplicateInfo> {
|
|||||||
.filter { !(it.range.getTextRange() intersects originalTextRange) }
|
.filter { !(it.range.getTextRange() intersects originalTextRange) }
|
||||||
.map { match ->
|
.map { match ->
|
||||||
val controlFlow = getControlFlowIfMatched(match)
|
val controlFlow = getControlFlowIfMatched(match)
|
||||||
controlFlow?.let { DuplicateInfo(match.range, it, unifierParameters.map { match.result.substitution[it]!!.getText()!! }) }
|
controlFlow?.let { DuplicateInfo(match.range, it, unifierParameters.map { match.result.substitution[it]!!.text!! }) }
|
||||||
}
|
}
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.toList()
|
.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ExtractableCodeDescriptor.getOccurrenceContainer(): PsiElement? {
|
private fun ExtractableCodeDescriptor.getOccurrenceContainer(): PsiElement? {
|
||||||
return extractionData.duplicateContainer ?: extractionData.targetSibling.getParent()
|
return extractionData.duplicateContainer ?: extractionData.targetSibling.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeCall(
|
private fun makeCall(
|
||||||
@@ -256,16 +256,16 @@ private fun makeCall(
|
|||||||
if (rangeToReplace !is KotlinPsiRange.ListRange) return
|
if (rangeToReplace !is KotlinPsiRange.ListRange) return
|
||||||
|
|
||||||
val anchor = rangeToReplace.startElement
|
val anchor = rangeToReplace.startElement
|
||||||
val anchorParent = anchor.getParent()!!
|
val anchorParent = anchor.parent!!
|
||||||
|
|
||||||
anchor.getNextSibling()?.let { from ->
|
anchor.nextSibling?.let { from ->
|
||||||
val to = rangeToReplace.endElement
|
val to = rangeToReplace.endElement
|
||||||
if (to != anchor) {
|
if (to != anchor) {
|
||||||
anchorParent.deleteChildRange(from, to);
|
anchorParent.deleteChildRange(from, to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val calleeName = declaration.getName()
|
val calleeName = declaration.name
|
||||||
val callText = when (declaration) {
|
val callText = when (declaration) {
|
||||||
is KtNamedFunction -> {
|
is KtNamedFunction -> {
|
||||||
val argumentsText = arguments.joinToString(separator = ", ", prefix = "(", postfix = ")")
|
val argumentsText = arguments.joinToString(separator = ", ", prefix = "(", postfix = ")")
|
||||||
@@ -278,22 +278,22 @@ private fun makeCall(
|
|||||||
else -> calleeName
|
else -> calleeName
|
||||||
}
|
}
|
||||||
|
|
||||||
val anchorInBlock = sequence(anchor) { it.getParent() }.firstOrNull { it.getParent() is KtBlockExpression }
|
val anchorInBlock = sequence(anchor) { it.parent }.firstOrNull { it.parent is KtBlockExpression }
|
||||||
val block = (anchorInBlock?.getParent() as? KtBlockExpression) ?: anchorParent
|
val block = (anchorInBlock?.parent as? KtBlockExpression) ?: anchorParent
|
||||||
|
|
||||||
val psiFactory = KtPsiFactory(anchor.getProject())
|
val psiFactory = KtPsiFactory(anchor.project)
|
||||||
val newLine = psiFactory.createNewLine()
|
val newLine = psiFactory.createNewLine()
|
||||||
|
|
||||||
if (controlFlow.outputValueBoxer is AsTuple && controlFlow.outputValues.size() > 1 && controlFlow.outputValues.all { it is Initializer }) {
|
if (controlFlow.outputValueBoxer is AsTuple && controlFlow.outputValues.size > 1 && controlFlow.outputValues.all { it is Initializer }) {
|
||||||
val declarationsToMerge = controlFlow.outputValues.map { (it as Initializer).initializedDeclaration }
|
val declarationsToMerge = controlFlow.outputValues.map { (it as Initializer).initializedDeclaration }
|
||||||
val isVar = declarationsToMerge.first().isVar()
|
val isVar = declarationsToMerge.first().isVar
|
||||||
if (declarationsToMerge.all { it.isVar() == isVar }) {
|
if (declarationsToMerge.all { it.isVar == isVar }) {
|
||||||
controlFlow.declarationsToCopy.subtract(declarationsToMerge).forEach {
|
controlFlow.declarationsToCopy.subtract(declarationsToMerge).forEach {
|
||||||
block.addBefore(psiFactory.createDeclaration<KtDeclaration>(it.getText()!!), anchorInBlock) as KtDeclaration
|
block.addBefore(psiFactory.createDeclaration<KtDeclaration>(it.text!!), anchorInBlock) as KtDeclaration
|
||||||
block.addBefore(newLine, anchorInBlock)
|
block.addBefore(newLine, anchorInBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
val entries = declarationsToMerge.map { p -> p.getName() + (p.getTypeReference()?.let { ": ${it.getText()}" } ?: "") }
|
val entries = declarationsToMerge.map { p -> p.name + (p.typeReference?.let { ": ${it.text}" } ?: "") }
|
||||||
anchorInBlock?.replace(
|
anchorInBlock?.replace(
|
||||||
psiFactory.createDeclaration("${if (isVar) "var" else "val"} (${entries.joinToString()}) = $callText")
|
psiFactory.createDeclaration("${if (isVar) "var" else "val"} (${entries.joinToString()}) = $callText")
|
||||||
)
|
)
|
||||||
@@ -302,7 +302,7 @@ private fun makeCall(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val inlinableCall = controlFlow.outputValues.size() <= 1
|
val inlinableCall = controlFlow.outputValues.size <= 1
|
||||||
val unboxingExpressions =
|
val unboxingExpressions =
|
||||||
if (inlinableCall) {
|
if (inlinableCall) {
|
||||||
controlFlow.outputValueBoxer.getUnboxingExpressions(callText!!)
|
controlFlow.outputValueBoxer.getUnboxingExpressions(callText!!)
|
||||||
@@ -317,7 +317,7 @@ private fun makeCall(
|
|||||||
|
|
||||||
val copiedDeclarations = HashMap<KtDeclaration, KtDeclaration>()
|
val copiedDeclarations = HashMap<KtDeclaration, KtDeclaration>()
|
||||||
for (decl in controlFlow.declarationsToCopy) {
|
for (decl in controlFlow.declarationsToCopy) {
|
||||||
val declCopy = psiFactory.createDeclaration<KtDeclaration>(decl.getText()!!)
|
val declCopy = psiFactory.createDeclaration<KtDeclaration>(decl.text!!)
|
||||||
copiedDeclarations[decl] = block.addBefore(declCopy, anchorInBlock) as KtDeclaration
|
copiedDeclarations[decl] = block.addBefore(declCopy, anchorInBlock) as KtDeclaration
|
||||||
block.addBefore(newLine, anchorInBlock)
|
block.addBefore(newLine, anchorInBlock)
|
||||||
}
|
}
|
||||||
@@ -332,7 +332,7 @@ private fun makeCall(
|
|||||||
is OutputValue.ExpressionValue -> {
|
is OutputValue.ExpressionValue -> {
|
||||||
val exprText = if (outputValue.callSiteReturn) {
|
val exprText = if (outputValue.callSiteReturn) {
|
||||||
val firstReturn = outputValue.originalExpressions.filterIsInstance<KtReturnExpression>().firstOrNull()
|
val firstReturn = outputValue.originalExpressions.filterIsInstance<KtReturnExpression>().firstOrNull()
|
||||||
val label = firstReturn?.getTargetLabel()?.getText() ?: ""
|
val label = firstReturn?.getTargetLabel()?.text ?: ""
|
||||||
"return$label $callText"
|
"return$label $callText"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -352,14 +352,14 @@ private fun makeCall(
|
|||||||
}
|
}
|
||||||
else if (outputValue.conditional) {
|
else if (outputValue.conditional) {
|
||||||
Collections.singletonList(
|
Collections.singletonList(
|
||||||
psiFactory.createExpression("if ($callText) ${outputValue.elementToInsertAfterCall.getText()}")
|
psiFactory.createExpression("if ($callText) ${outputValue.elementToInsertAfterCall.text}")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
listOf(
|
listOf(
|
||||||
psiFactory.createExpression(callText),
|
psiFactory.createExpression(callText),
|
||||||
newLine,
|
newLine,
|
||||||
psiFactory.createExpression(outputValue.elementToInsertAfterCall.getText()!!)
|
psiFactory.createExpression(outputValue.elementToInsertAfterCall.text!!)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -396,7 +396,7 @@ private fun makeCall(
|
|||||||
insertCall(anchor, wrapCall(it, unboxingExpressions[it]!!).first() as KtExpression)
|
insertCall(anchor, wrapCall(it, unboxingExpressions[it]!!).first() as KtExpression)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (anchor.isValid()) {
|
if (anchor.isValid) {
|
||||||
anchor.delete()
|
anchor.delete()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -422,10 +422,10 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
return descriptor.controlFlow.outputValues
|
return descriptor.controlFlow.outputValues
|
||||||
.map {
|
.map {
|
||||||
when (it) {
|
when (it) {
|
||||||
is ExpressionValue -> resultExpression?.getText()
|
is ExpressionValue -> resultExpression?.text
|
||||||
is Jump -> if (it.conditional) "false" else null
|
is Jump -> if (it.conditional) "false" else null
|
||||||
is ParameterUpdate -> it.parameter.nameForRef
|
is ParameterUpdate -> it.parameter.nameForRef
|
||||||
is Initializer -> it.initializedDeclaration.getName()
|
is Initializer -> it.initializedDeclaration.name
|
||||||
else -> throw IllegalArgumentException("Unknown output value: $it")
|
else -> throw IllegalArgumentException("Unknown output value: $it")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,19 +438,20 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
expressionToUnifyWith: KtExpression?
|
expressionToUnifyWith: KtExpression?
|
||||||
) {
|
) {
|
||||||
val currentResultExpression =
|
val currentResultExpression =
|
||||||
if (originalExpression is KtReturnExpression) originalExpression.getReturnedExpression() else originalExpression
|
(if (originalExpression is KtReturnExpression) originalExpression.returnedExpression else originalExpression) ?: return
|
||||||
if (currentResultExpression == null) return
|
|
||||||
|
|
||||||
val newResultExpression = descriptor.controlFlow.defaultOutputValue?.let {
|
val newResultExpression = descriptor.controlFlow.defaultOutputValue?.let {
|
||||||
val boxedExpression = originalExpression.replaced(replacingExpression).getReturnedExpression()!!
|
val boxedExpression = originalExpression.replaced(replacingExpression).returnedExpression!!
|
||||||
descriptor.controlFlow.outputValueBoxer.extractExpressionByValue(boxedExpression, it)
|
descriptor.controlFlow.outputValueBoxer.extractExpressionByValue(boxedExpression, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress
|
||||||
if (newResultExpression == null) {
|
if (newResultExpression == null) {
|
||||||
throw AssertionError("Can' replace '${originalExpression.getText()}' with '${replacingExpression.getText()}'")
|
throw AssertionError("Can' replace '${originalExpression.text}' with '${replacingExpression.text}'")
|
||||||
}
|
}
|
||||||
|
|
||||||
val counterpartMap = createNameCounterpartMap(currentResultExpression, expressionToUnifyWith ?: newResultExpression)
|
val counterpartMap = createNameCounterpartMap(currentResultExpression, expressionToUnifyWith ?: newResultExpression)
|
||||||
nameByOffset.entrySet().forEach { e -> counterpartMap[e.getValue()]?.let { e.setValue(it) } }
|
nameByOffset.entries.forEach { e -> counterpartMap.getRaw(e.value)?.let { e.setValue(it) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCounterparts<T : KtExpression>(originalExpressions: Collection<T>,
|
fun getCounterparts<T : KtExpression>(originalExpressions: Collection<T>,
|
||||||
@@ -458,9 +459,9 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
bodyOffset: Int,
|
bodyOffset: Int,
|
||||||
file: PsiFile): List<T> {
|
file: PsiFile): List<T> {
|
||||||
return originalExpressions.map { originalExpression ->
|
return originalExpressions.map { originalExpression ->
|
||||||
val offsetInBody = originalExpression.getTextRange()!!.getStartOffset() - descriptor.extractionData.originalStartOffset!!
|
val offsetInBody = originalExpression.textRange!!.startOffset - descriptor.extractionData.originalStartOffset!!
|
||||||
file.findElementAt(bodyOffset + offsetInBody)?.getNonStrictParentOfType(originalExpression.javaClass)
|
file.findElementAt(bodyOffset + offsetInBody)?.getNonStrictParentOfType(originalExpression.javaClass)
|
||||||
?: throw AssertionError("Couldn't find expression at $offsetInBody in '${body.getText()}'")
|
?: throw AssertionError("Couldn't find expression at $offsetInBody in '${body.text}'")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,15 +472,15 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
val originalOffsetByExpr = LinkedHashMap<KtElement, Int>()
|
val originalOffsetByExpr = LinkedHashMap<KtElement, Int>()
|
||||||
|
|
||||||
val bodyOffset = body.getBlockContentOffset()
|
val bodyOffset = body.getBlockContentOffset()
|
||||||
val file = body.getContainingFile()!!
|
val file = body.containingFile!!
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sort by descending position so that internals of value/type arguments in calls and qualified types are replaced
|
* Sort by descending position so that internals of value/type arguments in calls and qualified types are replaced
|
||||||
* before calls/types themselves
|
* before calls/types themselves
|
||||||
*/
|
*/
|
||||||
for ((offsetInBody, resolveResult) in descriptor.extractionData.refOffsetToDeclaration.entrySet().sortedByDescending { it.key }) {
|
for ((offsetInBody, resolveResult) in descriptor.extractionData.refOffsetToDeclaration.entries.sortedByDescending { it.key }) {
|
||||||
val expr = file.findElementAt(bodyOffset + offsetInBody)?.getNonStrictParentOfType<KtSimpleNameExpression>()
|
val expr = file.findElementAt(bodyOffset + offsetInBody)?.getNonStrictParentOfType<KtSimpleNameExpression>()
|
||||||
assert(expr != null) { "Couldn't find expression at $offsetInBody in '${body.getText()}'" }
|
assert(expr != null) { "Couldn't find expression at $offsetInBody in '${body.text}'" }
|
||||||
|
|
||||||
originalOffsetByExpr[expr!!] = offsetInBody
|
originalOffsetByExpr[expr!!] = offsetInBody
|
||||||
|
|
||||||
@@ -497,7 +498,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
val jumpValue = descriptor.controlFlow.jumpOutputValue
|
val jumpValue = descriptor.controlFlow.jumpOutputValue
|
||||||
if (jumpValue != null) {
|
if (jumpValue != null) {
|
||||||
replacingReturn = psiFactory.createExpression(if (jumpValue.conditional) "return true" else "return")
|
replacingReturn = psiFactory.createExpression(if (jumpValue.conditional) "return true" else "return")
|
||||||
returnsForLabelRemoval.removeAll(jumpValue.elementsToReplace)
|
returnsForLabelRemoval.removeAllRaw(jumpValue.elementsToReplace)
|
||||||
expressionsToReplaceWithReturn = getCounterparts(jumpValue.elementsToReplace, body, bodyOffset, file)
|
expressionsToReplaceWithReturn = getCounterparts(jumpValue.elementsToReplace, body, bodyOffset, file)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -514,7 +515,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
getCounterparts(returnsForLabelRemoval, body, bodyOffset, file).forEach { it.getTargetLabel()?.delete() }
|
getCounterparts(returnsForLabelRemoval, body, bodyOffset, file).forEach { it.getTargetLabel()?.delete() }
|
||||||
|
|
||||||
for ((expr, originalOffset) in originalOffsetByExpr) {
|
for ((expr, originalOffset) in originalOffsetByExpr) {
|
||||||
if (expr.isValid()) {
|
if (expr.isValid) {
|
||||||
nameByOffset.put(originalOffset, exprReplacementMap[expr]?.invoke(descriptor, expr) ?: expr)
|
nameByOffset.put(originalOffset, exprReplacementMap[expr]?.invoke(descriptor, expr) ?: expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -523,7 +524,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
|
|
||||||
if (body !is KtBlockExpression) throw AssertionError("Block body expected: ${descriptor.extractionData.codeFragmentText}")
|
if (body !is KtBlockExpression) throw AssertionError("Block body expected: ${descriptor.extractionData.codeFragmentText}")
|
||||||
|
|
||||||
val firstExpression = body.getStatements().firstOrNull()
|
val firstExpression = body.statements.firstOrNull()
|
||||||
if (firstExpression != null) {
|
if (firstExpression != null) {
|
||||||
for (param in descriptor.parameters) {
|
for (param in descriptor.parameters) {
|
||||||
param.mirrorVarName?.let { varName ->
|
param.mirrorVarName?.let { varName ->
|
||||||
@@ -535,16 +536,16 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
|
|
||||||
val defaultValue = descriptor.controlFlow.defaultOutputValue
|
val defaultValue = descriptor.controlFlow.defaultOutputValue
|
||||||
|
|
||||||
val lastExpression = body.getStatements().lastOrNull()
|
val lastExpression = body.statements.lastOrNull()
|
||||||
if (lastExpression is KtReturnExpression) return
|
if (lastExpression is KtReturnExpression) return
|
||||||
|
|
||||||
val (defaultExpression, expressionToUnifyWith) =
|
val (defaultExpression, expressionToUnifyWith) =
|
||||||
if (!generatorOptions.inTempFile && defaultValue != null && descriptor.controlFlow.outputValueBoxer.boxingRequired && lastExpression!!.isMultiLine()) {
|
if (!generatorOptions.inTempFile && defaultValue != null && descriptor.controlFlow.outputValueBoxer.boxingRequired && lastExpression!!.isMultiLine()) {
|
||||||
val varNameValidator = NewDeclarationNameValidator(body, lastExpression, NewDeclarationNameValidator.Target.VARIABLES)
|
val varNameValidator = NewDeclarationNameValidator(body, lastExpression, NewDeclarationNameValidator.Target.VARIABLES)
|
||||||
val resultVal = KotlinNameSuggester.suggestNamesByType(defaultValue.valueType, varNameValidator, null).first()
|
val resultVal = KotlinNameSuggester.suggestNamesByType(defaultValue.valueType, varNameValidator, null).first()
|
||||||
val newDecl = body.addBefore(psiFactory.createDeclaration("val $resultVal = ${lastExpression!!.getText()}"), lastExpression) as KtProperty
|
val newDecl = body.addBefore(psiFactory.createDeclaration("val $resultVal = ${lastExpression!!.text}"), lastExpression) as KtProperty
|
||||||
body.addBefore(psiFactory.createNewLine(), lastExpression)
|
body.addBefore(psiFactory.createNewLine(), lastExpression)
|
||||||
psiFactory.createExpression(resultVal) to newDecl.getInitializer()!!
|
psiFactory.createExpression(resultVal) to newDecl.initializer!!
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
lastExpression to null
|
lastExpression to null
|
||||||
@@ -557,7 +558,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
// In the case of lazy property absence of default value means that output values are of OutputValue.Initializer type
|
// In the case of lazy property absence of default value means that output values are of OutputValue.Initializer type
|
||||||
// We just add resulting expressions without return, since returns are prohibited in the body of lazy property
|
// We just add resulting expressions without return, since returns are prohibited in the body of lazy property
|
||||||
if (defaultValue == null) {
|
if (defaultValue == null) {
|
||||||
body.appendElement(returnExpression.getReturnedExpression()!!)
|
body.appendElement(returnExpression.returnedExpression!!)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -573,8 +574,8 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
|
|
||||||
if (generatorOptions.allowExpressionBody) {
|
if (generatorOptions.allowExpressionBody) {
|
||||||
val convertToExpressionBody = ConvertToExpressionBodyIntention()
|
val convertToExpressionBody = ConvertToExpressionBodyIntention()
|
||||||
val bodyExpression = body.getStatements().singleOrNull()
|
val bodyExpression = body.statements.singleOrNull()
|
||||||
val bodyOwner = body.getParent() as KtDeclarationWithBody
|
val bodyOwner = body.parent as KtDeclarationWithBody
|
||||||
if (bodyExpression != null && !bodyExpression.isMultiLine() && convertToExpressionBody.isApplicableTo(bodyOwner)) {
|
if (bodyExpression != null && !bodyExpression.isMultiLine() && convertToExpressionBody.isApplicableTo(bodyOwner)) {
|
||||||
convertToExpressionBody.applyTo(bodyOwner, !descriptor.returnType.isFlexible())
|
convertToExpressionBody.applyTo(bodyOwner, !descriptor.returnType.isFlexible())
|
||||||
}
|
}
|
||||||
@@ -585,7 +586,7 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
declarationToReplace?.let { return it.replace(declaration) as KtNamedDeclaration }
|
declarationToReplace?.let { return it.replace(declaration) as KtNamedDeclaration }
|
||||||
|
|
||||||
return with(descriptor.extractionData) {
|
return with(descriptor.extractionData) {
|
||||||
val targetContainer = anchor.getParent()!!
|
val targetContainer = anchor.parent!!
|
||||||
// TODO: Get rid of explicit new-lines in favor of formatter rules
|
// TODO: Get rid of explicit new-lines in favor of formatter rules
|
||||||
val emptyLines = psiFactory.createWhiteSpace("\n\n")
|
val emptyLines = psiFactory.createWhiteSpace("\n\n")
|
||||||
if (insertBefore) {
|
if (insertBefore) {
|
||||||
@@ -630,14 +631,14 @@ fun ExtractionGeneratorConfiguration.generateDeclaration(
|
|||||||
adjustDeclarationBody(declaration)
|
adjustDeclarationBody(declaration)
|
||||||
|
|
||||||
if (declaration is KtNamedFunction && declaration.getContainingJetFile().suppressDiagnosticsInDebugMode) {
|
if (declaration is KtNamedFunction && declaration.getContainingJetFile().suppressDiagnosticsInDebugMode) {
|
||||||
declaration.getReceiverTypeReference()?.debugTypeInfo = descriptor.receiverParameter?.getParameterType(true)
|
declaration.receiverTypeReference?.debugTypeInfo = descriptor.receiverParameter?.getParameterType(true)
|
||||||
|
|
||||||
for ((i, param) in declaration.getValueParameters().withIndex()) {
|
for ((i, param) in declaration.valueParameters.withIndex()) {
|
||||||
param.getTypeReference()?.debugTypeInfo = descriptor.parameters[i].getParameterType(true)
|
param.typeReference?.debugTypeInfo = descriptor.parameters[i].getParameterType(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (declaration.getTypeReference() != null) {
|
if (declaration.typeReference != null) {
|
||||||
declaration.getTypeReference()?.debugTypeInfo = descriptor.returnType.builtIns.anyType
|
declaration.typeReference?.debugTypeInfo = descriptor.returnType.builtIns.anyType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user