Change signature fix: correct parameter add / remove from enum constructor calls, relevant test fixes

This commit is contained in:
Mikhail Glukhikh
2015-08-06 15:46:37 +03:00
parent 8405a9a021
commit fe716d7e56
11 changed files with 57 additions and 45 deletions
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.psi package org.jetbrains.kotlin.psi
import com.intellij.lang.ASTNode import com.intellij.lang.ASTNode
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.psi.PsiComment import com.intellij.psi.PsiComment
@@ -27,14 +26,11 @@ import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.LocalTimeCounter import com.intellij.util.LocalTimeCounter
import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.analyzer.ModuleInfo
import org.jetbrains.kotlin.idea.JetFileType import org.jetbrains.kotlin.idea.JetFileType
import org.jetbrains.kotlin.lexer.JetKeywordToken
import org.jetbrains.kotlin.lexer.JetModifierKeywordToken import org.jetbrains.kotlin.lexer.JetModifierKeywordToken
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.JetPsiFactory.CallableBuilder.Target import org.jetbrains.kotlin.psi.JetPsiFactory.CallableBuilder.Target
import org.jetbrains.kotlin.resolve.ImportPath import org.jetbrains.kotlin.resolve.ImportPath
import java.io.PrintWriter
import java.io.StringWriter
public fun JetPsiFactory(project: Project?): JetPsiFactory = JetPsiFactory(project!!) public fun JetPsiFactory(project: Project?): JetPsiFactory = JetPsiFactory(project!!)
public fun JetPsiFactory(elementForProject: PsiElement): JetPsiFactory = JetPsiFactory(elementForProject.getProject()) public fun JetPsiFactory(elementForProject: PsiElement): JetPsiFactory = JetPsiFactory(elementForProject.getProject())
@@ -262,9 +258,13 @@ public class JetPsiFactory(private val project: Project) {
return createDeclaration<JetClass>("enum class E {$text}").getDeclarations()[0] as JetEnumEntry return createDeclaration<JetClass>("enum class E {$text}").getDeclarations()[0] as JetEnumEntry
} }
public fun createEnumEntryInitializerList(): JetInitializerList {
return createEnumEntry("Entry()").initializerList!!
}
public fun createEnumEntrySuperclassReferenceExpression(): JetEnumEntrySuperclassReferenceExpression { public fun createEnumEntrySuperclassReferenceExpression(): JetEnumEntrySuperclassReferenceExpression {
val userType = createEnumEntry("Entry()").getInitializerList()!!.getInitializers()[0].getTypeReference()!!.getTypeElement() as JetUserType val userType = createEnumEntryInitializerList().initializers[0].typeReference!!.typeElement as JetUserType
return userType.getReferenceExpression() as JetEnumEntrySuperclassReferenceExpression return userType.referenceExpression as JetEnumEntrySuperclassReferenceExpression
} }
public fun createWhenEntry(entryText: String): JetWhenEntry { public fun createWhenEntry(entryText: String): JetWhenEntry {
@@ -110,12 +110,12 @@ private fun JetElement.getConstructorCallDescriptor(): DeclarationDescriptor? {
return null return null
} }
public fun PsiElement.processDelegationCallConstructorUsages(scope: SearchScope, process: (JetConstructorDelegationCall) -> Unit) { public fun PsiElement.processDelegationCallConstructorUsages(scope: SearchScope, process: (JetCallElement) -> Unit) {
processDelegationCallKotlinConstructorUsages(scope, process) processDelegationCallKotlinConstructorUsages(scope, process)
processDelegationCallJavaConstructorUsages(scope, process) processDelegationCallJavaConstructorUsages(scope, process)
} }
private fun PsiElement.processDelegationCallKotlinConstructorUsages(scope: SearchScope, process: (JetConstructorDelegationCall) -> Unit) { private fun PsiElement.processDelegationCallKotlinConstructorUsages(scope: SearchScope, process: (JetCallElement) -> Unit) {
val element = unwrapped val element = unwrapped
val klass = when (element) { val klass = when (element) {
is JetConstructor<*> -> element.getContainingClassOrObject() is JetConstructor<*> -> element.getContainingClassOrObject()
@@ -130,7 +130,7 @@ private fun PsiElement.processDelegationCallKotlinConstructorUsages(scope: Searc
processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, descriptor, process) processInheritorsDelegatingCallToSpecifiedConstructor(klass, scope, descriptor, process)
} }
private fun PsiElement.processDelegationCallJavaConstructorUsages(scope: SearchScope, process: (JetConstructorDelegationCall) -> Unit) { private fun PsiElement.processDelegationCallJavaConstructorUsages(scope: SearchScope, process: (JetCallElement) -> Unit) {
if (this is KotlinLightElement<*, *>) return if (this is KotlinLightElement<*, *>) return
// TODO: Temporary hack to avoid NPE while KotlinNoOriginLightMethod is around // TODO: Temporary hack to avoid NPE while KotlinNoOriginLightMethod is around
if (this is KotlinNoOriginLightMethod) return if (this is KotlinNoOriginLightMethod) return
@@ -145,7 +145,7 @@ private fun processInheritorsDelegatingCallToSpecifiedConstructor(
klass: PsiElement, klass: PsiElement,
scope: SearchScope, scope: SearchScope,
descriptor: ConstructorDescriptor, descriptor: ConstructorDescriptor,
process: (JetConstructorDelegationCall) -> Unit process: (JetCallElement) -> Unit
) { ) {
HierarchySearchRequest(klass, scope, false).searchInheritors().forEach() { HierarchySearchRequest(klass, scope, false).searchInheritors().forEach() {
val unwrapped = it.unwrapped val unwrapped = it.unwrapped
@@ -156,7 +156,7 @@ private fun processInheritorsDelegatingCallToSpecifiedConstructor(
} }
private fun processClassDelegationCallsToSpecifiedConstructor( private fun processClassDelegationCallsToSpecifiedConstructor(
klass: JetClass, constructor: DeclarationDescriptor, process: (JetConstructorDelegationCall) -> Unit klass: JetClass, constructor: DeclarationDescriptor, process: (JetCallElement) -> Unit
) { ) {
for (secondaryConstructor in klass.getSecondaryConstructors()) { for (secondaryConstructor in klass.getSecondaryConstructors()) {
val delegationCallDescriptor = secondaryConstructor.getDelegationCall().getConstructorCallDescriptor() val delegationCallDescriptor = secondaryConstructor.getDelegationCall().getConstructorCallDescriptor()
@@ -164,6 +164,15 @@ private fun processClassDelegationCallsToSpecifiedConstructor(
process(secondaryConstructor.getDelegationCall()) process(secondaryConstructor.getDelegationCall())
} }
} }
if (!klass.isEnum()) return
for (declaration in klass.declarations) {
if (declaration is JetEnumEntry) {
val delegationCall = declaration.getDelegationSpecifiers().firstOrNull()
if (delegationCall is JetDelegatorToSuperCall && constructor == delegationCall.calleeExpression.getConstructorCallDescriptor()) {
process(delegationCall)
}
}
}
} }
// Check if reference resolves to extension function whose receiver is the same as declaration's parent (or its superclass) // Check if reference resolves to extension function whose receiver is the same as declaration's parent (or its superclass)
@@ -252,7 +252,7 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
} }
private static void findOneMethodUsages( private static void findOneMethodUsages(
@NotNull JetCallableDefinitionUsage<?> functionUsageInfo, @NotNull final JetCallableDefinitionUsage<?> functionUsageInfo,
final JetChangeInfo changeInfo, final JetChangeInfo changeInfo,
final Set<UsageInfo> result final Set<UsageInfo> result
) { ) {
@@ -333,10 +333,16 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
UsagesSearchPackage.processDelegationCallConstructorUsages( UsagesSearchPackage.processDelegationCallConstructorUsages(
functionPsi, functionPsi,
functionPsi.getUseScope(), functionPsi.getUseScope(),
new Function1<JetConstructorDelegationCall, Unit>() { new Function1<JetCallElement, Unit>() {
@Override @Override
public Unit invoke(JetConstructorDelegationCall element) { public Unit invoke(JetCallElement element) {
result.add(new JetConstructorDelegationCallUsage(element, changeInfo)); if (element instanceof JetConstructorDelegationCall) {
result.add(new JetConstructorDelegationCallUsage((JetConstructorDelegationCall) element, changeInfo));
}
else if (element instanceof JetDelegatorToSuperCall) {
result.add(new JetFunctionCallUsage(element, functionUsageInfo));
}
return null; return null;
} }
} }
@@ -478,10 +484,12 @@ public class JetChangeSignatureUsageProcessor implements ChangeSignatureUsagePro
UsagesSearchPackage.processDelegationCallConstructorUsages( UsagesSearchPackage.processDelegationCallConstructorUsages(
psiMethod, psiMethod,
psiMethod.getUseScope(), psiMethod.getUseScope(),
new Function1<JetConstructorDelegationCall, Unit>() { new Function1<JetCallElement, Unit>() {
@Override @Override
public Unit invoke(JetConstructorDelegationCall element) { public Unit invoke(JetCallElement element) {
result.add(new JavaConstructorDeferredUsageInDelegationCall(element)); if (element instanceof JetConstructorDelegationCall) {
result.add(new JavaConstructorDeferredUsageInDelegationCall((JetConstructorDelegationCall) element));
}
return null; return null;
} }
} }
@@ -17,24 +17,18 @@
package org.jetbrains.kotlin.idea.refactoring.changeSignature.usages package org.jetbrains.kotlin.idea.refactoring.changeSignature.usages
import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.psi.JetEnumEntry
import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo import org.jetbrains.kotlin.idea.refactoring.changeSignature.JetChangeInfo
import org.jetbrains.kotlin.psi.JetPsiFactory import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.JetClass
import org.jetbrains.kotlin.psi.JetDelegatorToSuperCall
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
public class JetEnumEntryWithoutSuperCallUsage(enumEntry: JetEnumEntry) : JetUsageInfo<JetEnumEntry>(enumEntry) { public class JetEnumEntryWithoutSuperCallUsage(enumEntry: JetEnumEntry) : JetUsageInfo<JetEnumEntry>(enumEntry) {
override fun processUsage(changeInfo: JetChangeInfo, element: JetEnumEntry, allUsages: Array<out UsageInfo>): Boolean { override fun processUsage(changeInfo: JetChangeInfo, element: JetEnumEntry, allUsages: Array<out UsageInfo>): Boolean {
if (changeInfo.getNewParameters().size() > 0) { if (changeInfo.newParameters.size() > 0) {
val psiFactory = JetPsiFactory(element) val psiFactory = JetPsiFactory(element)
val enumClass = element.getStrictParentOfType<JetClass>()!! val delegatorToSuperCall = (element.addAfter(
val delegatorToSuperCall = element.addAfter( psiFactory.createEnumEntryInitializerList(),
psiFactory.createDelegatorToSuperCall("${enumClass.getName()}()"),
element.getNameAsDeclaration() element.getNameAsDeclaration()
) as JetDelegatorToSuperCall ) as JetInitializerList).initializers[0] as JetDelegatorToSuperCall
element.addBefore(psiFactory.createColon(), delegatorToSuperCall)
return JetFunctionCallUsage(delegatorToSuperCall, changeInfo.methodDescriptor.originalPrimaryCallable) return JetFunctionCallUsage(delegatorToSuperCall, changeInfo.methodDescriptor.originalPrimaryCallable)
.processUsage(changeInfo, delegatorToSuperCall, allUsages) .processUsage(changeInfo, delegatorToSuperCall, allUsages)
@@ -120,9 +120,10 @@ public class JetFunctionCallUsage extends JetUsageInfo<JetCallElement> {
private boolean shouldSkipUsage(JetCallElement element) { private boolean shouldSkipUsage(JetCallElement element) {
// TODO: We probable need more clever processing of invalid calls, but for now default to Java-like behaviour // TODO: We probable need more clever processing of invalid calls, but for now default to Java-like behaviour
// TODO: Investigate why resolved call is not recorded for enum constructor call
if (resolvedCall == null && !(element instanceof JetDelegatorToSuperCall)) return true; if (resolvedCall == null && !(element instanceof JetDelegatorToSuperCall)) return true;
if (resolvedCall != null && !resolvedCall.getStatus().isSuccess()) { if (resolvedCall != null && !resolvedCall.getStatus().isSuccess()) {
// TODO: investigate why arguments are not recorded for enum constructor call
if (element instanceof JetDelegatorToSuperCall && element.getParent().getParent() instanceof JetEnumEntry) return false;
for (ValueArgument valueArgument : resolvedCall.getCall().getValueArguments()) { for (ValueArgument valueArgument : resolvedCall.getCall().getValueArguments()) {
if (!(resolvedCall.getArgumentMapping(valueArgument) instanceof ArgumentMatch)) return true; if (!(resolvedCall.getArgumentMapping(valueArgument) instanceof ArgumentMatch)) return true;
} }
@@ -4,9 +4,9 @@
// ERROR: No value passed for parameter foo // ERROR: No value passed for parameter foo
enum class E { enum class E {
A A,
B B,
C C;
val t: Int = <caret>foo val t: Int = <caret>foo
} }
@@ -4,9 +4,9 @@
// ERROR: No value passed for parameter foo // ERROR: No value passed for parameter foo
enum class E(foo: Int) { enum class E(foo: Int) {
A : E() A(),
B : E() B(),
C : E() C();
val t: Int = foo val t: Int = foo
} }
@@ -4,9 +4,9 @@
// ERROR: No value passed for parameter foo // ERROR: No value passed for parameter foo
enum class E { enum class E {
A A,
B { B {
val t: Int = <caret>foo val t: Int = <caret>foo
} },
C C
} }
@@ -4,9 +4,9 @@
// ERROR: No value passed for parameter foo // ERROR: No value passed for parameter foo
enum class E(val foo: Int) { enum class E(val foo: Int) {
A : E() A(),
B : E() { B() {
val t: Int = foo val t: Int = foo
} },
C : E() C()
} }
@@ -1,4 +1,4 @@
enum class E(n: Int) { enum class E(n: Int) {
A : E(1) A(1),
B : E(1) B(1)
} }
@@ -1,4 +1,4 @@
enum class <caret>E { enum class <caret>E {
A A,
B B
} }