From 5517c6c539569af42242bfa2b175070e95094693 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Thu, 5 Mar 2015 16:38:11 +0300 Subject: [PATCH] Extraction Engine: Report invisible types in the generated declaration header #KT-6734 Fixed --- .../extractableAnalysisUtil.kt | 103 +++++++++++------- .../extractFunction/basic/invisibleType.kt | 11 ++ .../basic/invisibleType.kt.conflicts | 1 + .../introduce/JetExtractionTestGenerated.java | 6 + 4 files changed, 80 insertions(+), 41 deletions(-) create mode 100644 idea/testData/refactoring/extractFunction/basic/invisibleType.kt create mode 100644 idea/testData/refactoring/extractFunction/basic/invisibleType.kt.conflicts diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt index e5272f9c1db..d33358bc7bc 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt @@ -41,7 +41,6 @@ import org.jetbrains.kotlin.utils.DFS import org.jetbrains.kotlin.utils.DFS.* import com.intellij.refactoring.util.RefactoringUIUtil import com.intellij.util.containers.MultiMap -import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.AnalysisResult.Status import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.AnalysisResult.ErrorMessage import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.* @@ -70,6 +69,9 @@ import org.jetbrains.kotlin.idea.refactoring.comparePossiblyOverridingDescriptor import org.jetbrains.kotlin.idea.util.makeNullable import org.jetbrains.kotlin.resolve.calls.CallTransformer import org.jetbrains.kotlin.resolve.calls.callUtil.* +import org.jetbrains.kotlin.diagnostics.* +import java.util.logging.* +import com.intellij.openapi.diagnostic.Logger private val DEFAULT_FUNCTION_NAME = "myFun" private val DEFAULT_RETURN_TYPE = KotlinBuiltIns.getInstance().getUnitType() @@ -862,63 +864,82 @@ private fun JetNamedDeclaration.getGeneratedBody() = } ?: throw AssertionError("Couldn't get block body for this declaration: ${JetPsiUtil.getElementTextWithContext(this)}") fun ExtractableCodeDescriptor.validate(): ExtractableCodeDescriptorWithConflicts { + fun getDeclarationMessage(declaration: PsiNamedElement, messageKey: String, capitalize: Boolean = true): String { + val message = JetRefactoringBundle.message(messageKey, RefactoringUIUtil.getDescription(declaration, true)) + return if (capitalize) message.capitalize() else message + } + val conflicts = MultiMap() val result = ExtractionGeneratorConfiguration(this, ExtractionGeneratorOptions(inTempFile = true)).generateDeclaration() val valueParameterList = (result.declaration as? JetNamedFunction)?.getValueParameterList() - val bindingContext = result.declaration.getGeneratedBody().analyze() + val body = result.declaration.getGeneratedBody() + val bindingContext = body.analyze() - for ((originalOffset, resolveResult) in extractionData.refOffsetToDeclaration) { - if (resolveResult.declaration.isInsideOf(extractionData.originalElements)) continue + fun validateBody() { + for ((originalOffset, resolveResult) in extractionData.refOffsetToDeclaration) { + if (resolveResult.declaration.isInsideOf(extractionData.originalElements)) continue - val currentRefExpr = result.nameByOffset[originalOffset] as JetSimpleNameExpression? - if (currentRefExpr == null) continue + val currentRefExpr = result.nameByOffset[originalOffset] as JetSimpleNameExpression? + if (currentRefExpr == null) continue - if (currentRefExpr.getParent() is JetThisExpression) continue + if (currentRefExpr.getParent() is JetThisExpression) continue - val diagnostics = bindingContext.getDiagnostics().forElement(currentRefExpr) + val diagnostics = bindingContext.getDiagnostics().forElement(currentRefExpr) - val currentDescriptor = bindingContext[BindingContext.REFERENCE_TARGET, currentRefExpr] - val currentTarget = - currentDescriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(extractionData.project, it) } as? PsiNamedElement - if (currentTarget is JetParameter && currentTarget.getParent() == valueParameterList) continue - if (currentDescriptor is LocalVariableDescriptor - && parameters.any { it.mirrorVarName == currentDescriptor.getName().asString() }) continue + val currentDescriptor = bindingContext[BindingContext.REFERENCE_TARGET, currentRefExpr] + val currentTarget = + currentDescriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(extractionData.project, it) } as? PsiNamedElement + if (currentTarget is JetParameter && currentTarget.getParent() == valueParameterList) continue + if (currentDescriptor is LocalVariableDescriptor + && parameters.any { it.mirrorVarName == currentDescriptor.getName().asString() }) continue - if (diagnostics.any { it.getFactory() == Errors.UNRESOLVED_REFERENCE } + if (diagnostics.any { it.getFactory() == Errors.UNRESOLVED_REFERENCE } || (currentDescriptor != null - && !ErrorUtils.isError(currentDescriptor) - && !comparePossiblyOverridingDescriptors(currentDescriptor, resolveResult.descriptor))) { - conflicts.putValue( - resolveResult.originalRefExpr, - JetRefactoringBundle.message( - "0.will.no.longer.be.accessible.after.extraction", - RefactoringUIUtil.getDescription(resolveResult.declaration, true) - ).capitalize() - ) - continue - } - - diagnostics.firstOrNull { it.getFactory() in Errors.INVISIBLE_REFERENCE_DIAGNOSTICS }?.let { - val message = when (it.getFactory()) { - Errors.INVISIBLE_SETTER -> - JetRefactoringBundle.message( - "setter.of.0.will.become.invisible.after.extraction", - RefactoringUIUtil.getDescription(resolveResult.declaration, true) - ) - - else -> - JetRefactoringBundle.message( - "0.will.become.invisible.after.extraction", - RefactoringUIUtil.getDescription(resolveResult.declaration, true).capitalize() - ) + && !ErrorUtils.isError(currentDescriptor) + && !comparePossiblyOverridingDescriptors(currentDescriptor, resolveResult.descriptor))) { + conflicts.putValue( + resolveResult.originalRefExpr, + getDeclarationMessage(resolveResult.declaration, "0.will.no.longer.be.accessible.after.extraction") + ) + continue } - conflicts.putValue(resolveResult.originalRefExpr, message) + diagnostics.firstOrNull { it.getFactory() in Errors.INVISIBLE_REFERENCE_DIAGNOSTICS }?.let { + val message = when (it.getFactory()) { + Errors.INVISIBLE_SETTER -> + getDeclarationMessage(resolveResult.declaration, "setter.of.0.will.become.invisible.after.extraction", false) + else -> + getDeclarationMessage(resolveResult.declaration, "0.will.become.invisible.after.extraction") + } + conflicts.putValue(resolveResult.originalRefExpr, message) + } } } + result.declaration.accept( + object : JetTreeVisitorVoid() { + override fun visitUserType(userType: JetUserType) { + val refExpr = userType.getReferenceExpression() ?: return + val declaration = refExpr.getReference()?.resolve() as? PsiNamedElement ?: return + val diagnostics = bindingContext.getDiagnostics().forElement(refExpr) + diagnostics.firstOrNull { it.getFactory() == Errors.INVISIBLE_REFERENCE }?.let { + conflicts.putValue(declaration, getDeclarationMessage(declaration, "0.will.become.invisible.after.extraction")) + } + } + + override fun visitJetElement(element: JetElement) { + if (element == body) { + validateBody() + return + } + super.visitJetElement(element) + } + } + ) + return ExtractableCodeDescriptorWithConflicts(this, conflicts) } +private val LOG = Logger.getInstance(javaClass()) \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/basic/invisibleType.kt b/idea/testData/refactoring/extractFunction/basic/invisibleType.kt new file mode 100644 index 00000000000..d6cbec2947d --- /dev/null +++ b/idea/testData/refactoring/extractFunction/basic/invisibleType.kt @@ -0,0 +1,11 @@ +// SIBLING: +class A { + private class Inner { + fun foo() {} + } + + fun foo() { + val inner = Inner() + inner.foo() + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/extractFunction/basic/invisibleType.kt.conflicts b/idea/testData/refactoring/extractFunction/basic/invisibleType.kt.conflicts new file mode 100644 index 00000000000..8a7524e6db2 --- /dev/null +++ b/idea/testData/refactoring/extractFunction/basic/invisibleType.kt.conflicts @@ -0,0 +1 @@ +Class A.Inner will become invisible after extraction \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java index 4bbcdeec8f8..253998bae6c 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/introduce/JetExtractionTestGenerated.java @@ -370,6 +370,12 @@ public class JetExtractionTestGenerated extends AbstractJetExtractionTest { doExtractFunctionTest(fileName); } + @TestMetadata("invisibleType.kt") + public void testInvisibleType() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/basic/invisibleType.kt"); + doExtractFunctionTest(fileName); + } + @TestMetadata("localClassExtraction.kt") public void testLocalClassExtraction() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/extractFunction/basic/localClassExtraction.kt");