Correct handling of erroneous code during code extraction
This commit is contained in:
@@ -27,7 +27,6 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ThrowExceptionInst
|
|||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||||
@@ -287,20 +286,21 @@ fun Pseudocode.getElementValuesRecursively(element: KtElement): List<PseudoValue
|
|||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtElement.getContainingPseudocode(context: BindingContext): Pseudocode? {
|
val KtElement.containingDeclarationForPseudocode: KtDeclaration?
|
||||||
val pseudocodeDeclaration =
|
get() = PsiTreeUtil.getParentOfType(this, KtDeclarationWithBody::class.java, KtClassOrObject::class.java, KtScript::class.java)
|
||||||
PsiTreeUtil.getParentOfType(this, KtDeclarationWithBody::class.java, KtClassOrObject::class.java, KtScript::class.java)
|
?: getNonStrictParentOfType<KtProperty>()
|
||||||
?: getNonStrictParentOfType<KtProperty>()
|
|
||||||
?: return null
|
|
||||||
|
|
||||||
val enclosingPseudocodeDeclaration = (pseudocodeDeclaration as? KtFunctionLiteral)?.let {
|
fun KtDeclaration.getContainingPseudocode(context: BindingContext): Pseudocode? {
|
||||||
|
val enclosingPseudocodeDeclaration = (this as? KtFunctionLiteral)?.let {
|
||||||
it.parents.firstOrNull { it is KtDeclaration && it !is KtFunctionLiteral } as? KtDeclaration
|
it.parents.firstOrNull { it is KtDeclaration && it !is KtFunctionLiteral } as? KtDeclaration
|
||||||
} ?: pseudocodeDeclaration
|
} ?: this
|
||||||
|
|
||||||
val enclosingPseudocode = PseudocodeUtil.generatePseudocode(enclosingPseudocodeDeclaration, context)
|
val enclosingPseudocode = PseudocodeUtil.generatePseudocode(enclosingPseudocodeDeclaration, context)
|
||||||
return enclosingPseudocode.getPseudocodeByElement(pseudocodeDeclaration)
|
return enclosingPseudocode.getPseudocodeByElement(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun KtElement.getContainingPseudocode(context: BindingContext) = containingDeclarationForPseudocode?.getContainingPseudocode(context)
|
||||||
|
|
||||||
fun Pseudocode.getPseudocodeByElement(element: KtElement): Pseudocode? {
|
fun Pseudocode.getPseudocodeByElement(element: KtElement): Pseudocode? {
|
||||||
if (correspondingElement == element) return this
|
if (correspondingElement == element) return this
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ fun getFunctionForExtractedFragment(
|
|||||||
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.name}:$breakpointLine"
|
ErrorMessage.NO_CONTAINER -> "Cannot perform an action at this breakpoint ${breakpointFile.name}:$breakpointLine"
|
||||||
|
ErrorMessage.SYNTAX_ERRORS -> "Cannot perform an action due to erroneous code"
|
||||||
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"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ introduce.property=Introduce Property
|
|||||||
introduce.parameter=Introduce Parameter
|
introduce.parameter=Introduce Parameter
|
||||||
cannot.refactor.no.container=Cannot refactor in this place
|
cannot.refactor.no.container=Cannot refactor in this place
|
||||||
cannot.refactor.no.expression=Cannot perform refactoring without an expression
|
cannot.refactor.no.expression=Cannot perform refactoring without an expression
|
||||||
|
cannot.refactor.syntax.errors=Cannot refactor due to erroneous code
|
||||||
cannot.refactor.expression.has.unit.type=Cannot introduce expression of unit type
|
cannot.refactor.expression.has.unit.type=Cannot introduce expression of unit type
|
||||||
cannot.refactor.package.expression=Cannot introduce package reference
|
cannot.refactor.package.expression=Cannot introduce package reference
|
||||||
extract.function=Extract Function
|
extract.function=Extract Function
|
||||||
|
|||||||
+2
@@ -505,6 +505,7 @@ class AnalysisResult (
|
|||||||
enum class ErrorMessage {
|
enum class ErrorMessage {
|
||||||
NO_EXPRESSION,
|
NO_EXPRESSION,
|
||||||
NO_CONTAINER,
|
NO_CONTAINER,
|
||||||
|
SYNTAX_ERRORS,
|
||||||
SUPER_CALL,
|
SUPER_CALL,
|
||||||
DENOTABLE_TYPES,
|
DENOTABLE_TYPES,
|
||||||
ERROR_TYPES,
|
ERROR_TYPES,
|
||||||
@@ -526,6 +527,7 @@ class AnalysisResult (
|
|||||||
when (this) {
|
when (this) {
|
||||||
NO_EXPRESSION -> "cannot.refactor.no.expression"
|
NO_EXPRESSION -> "cannot.refactor.no.expression"
|
||||||
NO_CONTAINER -> "cannot.refactor.no.container"
|
NO_CONTAINER -> "cannot.refactor.no.container"
|
||||||
|
SYNTAX_ERRORS -> "cannot.refactor.syntax.errors"
|
||||||
SUPER_CALL -> "cannot.extract.super.call"
|
SUPER_CALL -> "cannot.extract.super.call"
|
||||||
DENOTABLE_TYPES -> "parameter.types.are.not.denotable"
|
DENOTABLE_TYPES -> "parameter.types.are.not.denotable"
|
||||||
ERROR_TYPES -> "error.types.in.generated.function"
|
ERROR_TYPES -> "error.types.in.generated.function"
|
||||||
|
|||||||
+3
-1
@@ -624,7 +624,9 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
|
|||||||
|
|
||||||
val bindingContext = bindingContext ?: return noContainerError
|
val bindingContext = bindingContext ?: return noContainerError
|
||||||
|
|
||||||
val pseudocode = commonParent.getContainingPseudocode(bindingContext) ?: return noContainerError
|
val declaration = commonParent.containingDeclarationForPseudocode ?: return noContainerError
|
||||||
|
val pseudocode = declaration.getContainingPseudocode(bindingContext)
|
||||||
|
?: return AnalysisResult(null, Status.CRITICAL_ERROR, listOf(ErrorMessage.SYNTAX_ERRORS))
|
||||||
val localInstructions = getLocalInstructions(pseudocode)
|
val localInstructions = getLocalInstructions(pseudocode)
|
||||||
|
|
||||||
val modifiedVarDescriptorsWithExpressions = localInstructions.getModifiedVarDescriptors(bindingContext)
|
val modifiedVarDescriptorsWithExpressions = localInstructions.getModifiedVarDescriptors(bindingContext)
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// EXTRACTION_TARGET: property with getter
|
||||||
|
|
||||||
|
fun doSomethingStrangeWithCollection(collection: Collection<String>): Collection<String>? {
|
||||||
|
val groupsByLength = collection.groupBy { s -> { s.length } }
|
||||||
|
|
||||||
|
val maximumSizeOfGroup = groupsByLength.values.maxBy { it.size }.
|
||||||
|
return groupsByLength.values.firstOrNull { group -> {<selection>group.size == maximumSizeOfGroup</selection>} }
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
Cannot refactor due to erroneous code
|
||||||
+6
@@ -3124,6 +3124,12 @@ public class ExtractionTestGenerated extends AbstractExtractionTest {
|
|||||||
doIntroducePropertyTest(fileName);
|
doIntroducePropertyTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("syntaxErrors.kt")
|
||||||
|
public void testSyntaxErrors() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/syntaxErrors.kt");
|
||||||
|
doIntroducePropertyTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("typeParameterNotResolvableInTargetScope.kt")
|
@TestMetadata("typeParameterNotResolvableInTargetScope.kt")
|
||||||
public void testTypeParameterNotResolvableInTargetScope() throws Exception {
|
public void testTypeParameterNotResolvableInTargetScope() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/typeParameterNotResolvableInTargetScope.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/introduceProperty/typeParameterNotResolvableInTargetScope.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user