From 1e9ce5b10f80b77020acffee8ba9e1c7efb28739 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Thu, 20 Oct 2016 17:26:29 +0300 Subject: [PATCH] Moved nested classes out --- ...edExpressionWrapperWithIntroduceFeature.kt | 144 ++++++++++++++++++ .../idea/replacement/ReplacementEngine.kt | 115 -------------- 2 files changed, 144 insertions(+), 115 deletions(-) create mode 100644 idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ConstructedExpressionWrapperWithIntroduceFeature.kt diff --git a/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ConstructedExpressionWrapperWithIntroduceFeature.kt b/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ConstructedExpressionWrapperWithIntroduceFeature.kt new file mode 100644 index 00000000000..98c1fd03481 --- /dev/null +++ b/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ConstructedExpressionWrapperWithIntroduceFeature.kt @@ -0,0 +1,144 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.replacement + +import org.jetbrains.kotlin.idea.analysis.computeTypeInContext +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.idea.core.KotlinNameSuggester +import org.jetbrains.kotlin.idea.core.setType +import org.jetbrains.kotlin.idea.util.getResolutionScope +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType +import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression +import org.jetbrains.kotlin.psi.psiUtil.isAncestor +import org.jetbrains.kotlin.renderer.render +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo +import org.jetbrains.kotlin.resolve.scopes.utils.findLocalVariable +import org.jetbrains.kotlin.types.ErrorUtils +import org.jetbrains.kotlin.types.KotlinType +import java.util.* + +internal open class ConstructedExpressionWrapper( + var expression: KtExpression, + val bindingContext: BindingContext +) { + val psiFactory = KtPsiFactory(expression) + + fun replaceExpression(oldExpression: KtExpression, newExpression: KtExpression): KtExpression { + assert(expression.isAncestor(oldExpression)) + val result = oldExpression.replace(newExpression) as KtExpression + if (oldExpression == expression) { + expression = result + } + return result + } +} + +internal class ConstructedExpressionWrapperWithIntroduceFeature( + expression: KtExpression, + bindingContext: BindingContext, + val expressionToBeReplaced: KtExpression +) : ConstructedExpressionWrapper(expression, bindingContext) { + val addedStatements = ArrayList() + + fun introduceValue( + value: KtExpression, + valueType: KotlinType?, + usages: Collection, + nameSuggestion: String? = null, + safeCall: Boolean = false + ) { + assert(usages.all { expression.isAncestor(it, strict = true) }) + + fun replaceUsages(name: Name) { + val nameInCode = psiFactory.createExpression(name.render()) + for (usage in usages) { + usage.replace(nameInCode) + } + } + + fun suggestName(validator: (String) -> Boolean): Name { + val name = if (nameSuggestion != null) + KotlinNameSuggester.suggestNameByName(nameSuggestion, validator) + else + KotlinNameSuggester.suggestNamesByExpressionOnly(value, bindingContext, validator, "t").first() + return Name.identifier(name) + } + + // checks that name is used (without receiver) inside expression being constructed but not inside usages that will be replaced + fun isNameUsed(name: String) = collectNameUsages(expression, name).any { nameUsage -> usages.none { it.isAncestor(nameUsage) } } + + if (!safeCall) { + val block = expressionToBeReplaced.parent as? KtBlockExpression + if (block != null) { + val resolutionScope = expressionToBeReplaced.getResolutionScope(bindingContext, expressionToBeReplaced.getResolutionFacade()) + + if (usages.isNotEmpty()) { + var explicitType: KotlinType? = null + if (valueType != null && !ErrorUtils.containsErrorType(valueType)) { + val valueTypeWithoutExpectedType = value.computeTypeInContext( + resolutionScope, + expressionToBeReplaced, + dataFlowInfo = bindingContext.getDataFlowInfo(expressionToBeReplaced) + ) + if (valueTypeWithoutExpectedType == null || ErrorUtils.containsErrorType(valueTypeWithoutExpectedType)) { + explicitType = valueType + } + } + + val name = suggestName { name -> + resolutionScope.findLocalVariable(Name.identifier(name)) == null && !isNameUsed(name) + } + + var declaration = psiFactory.createDeclarationByPattern("val $0 = $1", name, value) + declaration = block.addBefore(declaration, expressionToBeReplaced) as KtVariableDeclaration + block.addBefore(psiFactory.createNewLine(), expressionToBeReplaced) + + if (explicitType != null) { + declaration.setType(explicitType) + } + + replaceUsages(name) + + addedStatements.add(declaration) + } + else { + addedStatements.add(block.addBefore(value, expressionToBeReplaced) as KtExpression) + block.addBefore(psiFactory.createNewLine(), expressionToBeReplaced) + } + return + } + } + + val dot = if (safeCall) "?." else "." + + expression = if (!isNameUsed("it")) { + replaceUsages(Name.identifier("it")) + psiFactory.createExpressionByPattern("$0${dot}let { $1 }", value, expression) + } + else { + val name = suggestName { !isNameUsed(it) } + replaceUsages(name) + psiFactory.createExpressionByPattern("$0${dot}let { $1 -> $2 }", value, name, expression) + } + } + + private fun collectNameUsages(scope: KtExpression, name: String) + = scope.collectDescendantsOfType { it.getReceiverExpression() == null && it.getReferencedName() == name } +} \ No newline at end of file diff --git a/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ReplacementEngine.kt b/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ReplacementEngine.kt index 033854ae92b..aa390e1a33e 100644 --- a/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ReplacementEngine.kt +++ b/idea/idea-replacement-engine/src/org/jetbrains/kotlin/idea/replacement/ReplacementEngine.kt @@ -22,7 +22,6 @@ import com.intellij.psi.PsiElement import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor -import org.jetbrains.kotlin.idea.analysis.computeTypeInContext import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.resolveImportReference @@ -33,21 +32,16 @@ import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.ImportInsertHelper import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.* -import org.jetbrains.kotlin.renderer.render import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils -import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver -import org.jetbrains.kotlin.resolve.scopes.utils.findLocalVariable -import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.utils.addIfNotNull import java.util.* @@ -572,113 +566,4 @@ object ReplacementEngine { // these keys are used on KtValueArgument private val MAKE_ARGUMENT_NAMED_KEY = Key("MAKE_ARGUMENT_NAMED") private val DEFAULT_PARAMETER_VALUE_KEY = Key("DEFAULT_PARAMETER_VALUE") - - private open class ConstructedExpressionWrapper( - var expression: KtExpression, - val bindingContext: BindingContext - ) { - val psiFactory = KtPsiFactory(expression) - - fun replaceExpression(oldExpression: KtExpression, newExpression: KtExpression): KtExpression { - assert(expression.isAncestor(oldExpression)) - val result = oldExpression.replace(newExpression) as KtExpression - if (oldExpression == expression) { - expression = result - } - return result - } - } - - private class ConstructedExpressionWrapperWithIntroduceFeature( - expression: KtExpression, - bindingContext: BindingContext, - val expressionToBeReplaced: KtExpression - ) : ConstructedExpressionWrapper(expression, bindingContext) { - val addedStatements = ArrayList() - - fun introduceValue( - value: KtExpression, - valueType: KotlinType?, - usages: Collection, - nameSuggestion: String? = null, - safeCall: Boolean = false - ) { - assert(usages.all { expression.isAncestor(it, strict = true) }) - - fun replaceUsages(name: Name) { - val nameInCode = psiFactory.createExpression(name.render()) - for (usage in usages) { - usage.replace(nameInCode) - } - } - - fun suggestName(validator: (String) -> Boolean): Name { - val name = if (nameSuggestion != null) - KotlinNameSuggester.suggestNameByName(nameSuggestion, validator) - else - KotlinNameSuggester.suggestNamesByExpressionOnly(value, bindingContext, validator, "t").first() - return Name.identifier(name) - } - - // checks that name is used (without receiver) inside expression being constructed but not inside usages that will be replaced - fun isNameUsed(name: String) = collectNameUsages(expression, name).any { nameUsage -> usages.none { it.isAncestor(nameUsage) } } - - if (!safeCall) { - val block = expressionToBeReplaced.parent as? KtBlockExpression - if (block != null) { - val resolutionScope = expressionToBeReplaced.getResolutionScope(bindingContext, expressionToBeReplaced.getResolutionFacade()) - - if (usages.isNotEmpty()) { - var explicitType: KotlinType? = null - if (valueType != null && !ErrorUtils.containsErrorType(valueType)) { - val valueTypeWithoutExpectedType = value.computeTypeInContext( - resolutionScope, - expressionToBeReplaced, - dataFlowInfo = bindingContext.getDataFlowInfo(expressionToBeReplaced) - ) - if (valueTypeWithoutExpectedType == null || ErrorUtils.containsErrorType(valueTypeWithoutExpectedType)) { - explicitType = valueType - } - } - - val name = suggestName { name -> - resolutionScope.findLocalVariable(Name.identifier(name)) == null && !isNameUsed(name) - } - - var declaration = psiFactory.createDeclarationByPattern("val $0 = $1", name, value) - declaration = block.addBefore(declaration, expressionToBeReplaced) as KtVariableDeclaration - block.addBefore(psiFactory.createNewLine(), expressionToBeReplaced) - - if (explicitType != null) { - declaration.setType(explicitType) - } - - replaceUsages(name) - - addedStatements.add(declaration) - } - else { - addedStatements.add(block.addBefore(value, expressionToBeReplaced) as KtExpression) - block.addBefore(psiFactory.createNewLine(), expressionToBeReplaced) - } - return - } - } - - val dot = if (safeCall) "?." else "." - - expression = if (!isNameUsed("it")) { - replaceUsages(Name.identifier("it")) - psiFactory.createExpressionByPattern("$0${dot}let { $1 }", value, expression) - } - else { - val name = suggestName { !isNameUsed(it) } - replaceUsages(name) - psiFactory.createExpressionByPattern("$0${dot}let { $1 -> $2 }", value, name, expression) - } - } - - private fun collectNameUsages(scope: KtExpression, name: String) - = scope.collectDescendantsOfType { it.getReceiverExpression() == null && it.getReferencedName() == name } - } } \ No newline at end of file