Extraction Engine: Report invisible types in the generated declaration header

#KT-6734 Fixed
This commit is contained in:
Alexey Sedunov
2015-03-05 16:38:11 +03:00
parent 5c871c92af
commit 5517c6c539
4 changed files with 80 additions and 41 deletions
@@ -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<PsiElement, String>()
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<ExtractionEngine>())
@@ -0,0 +1,11 @@
// SIBLING:
class A {
private class Inner {
fun foo() {}
}
fun foo() {
val inner = Inner()
<selection>inner.foo()</selection>
}
}
@@ -0,0 +1 @@
Class A.Inner will become invisible after extraction
@@ -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");