Create from usage: Support static Java members
This commit is contained in:
+3
-6
@@ -769,15 +769,10 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
val newJavaMember: PsiMember = when (declaration) {
|
val newJavaMember: PsiMember = when (declaration) {
|
||||||
is JetNamedFunction -> {
|
is JetNamedFunction -> {
|
||||||
val method = createJavaMethod(declaration, targetClass)
|
val method = createJavaMethod(declaration, targetClass)
|
||||||
|
method.getModifierList().setModifierProperty(PsiModifier.FINAL, false)
|
||||||
val modifierList = method.getModifierList()
|
|
||||||
modifierList.setModifierProperty(PsiModifier.STATIC, false)
|
|
||||||
modifierList.setModifierProperty(PsiModifier.FINAL, false)
|
|
||||||
|
|
||||||
method
|
method
|
||||||
}
|
}
|
||||||
is JetProperty -> {
|
is JetProperty -> {
|
||||||
if (targetClass.isInterface()) return false
|
|
||||||
createJavaField(declaration, targetClass)
|
createJavaField(declaration, targetClass)
|
||||||
}
|
}
|
||||||
else -> return false
|
else -> return false
|
||||||
@@ -785,6 +780,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
|
|
||||||
declaration.delete()
|
declaration.delete()
|
||||||
|
|
||||||
|
newJavaMember.getModifierList().setModifierProperty(PsiModifier.STATIC, callableInfo.receiverTypeInfo.classObjectRequired)
|
||||||
|
|
||||||
JavaCodeStyleManager.getInstance(project).shortenClassReferences(newJavaMember);
|
JavaCodeStyleManager.getInstance(project).shortenClassReferences(newJavaMember);
|
||||||
|
|
||||||
val descriptor = OpenFileDescriptor(project, targetClass.getContainingFile().getVirtualFile())
|
val descriptor = OpenFileDescriptor(project, targetClass.getContainingFile().getVirtualFile())
|
||||||
|
|||||||
+5
@@ -75,7 +75,12 @@ abstract class TypeInfo(val variance: Variance) {
|
|||||||
override val substitutionsAllowed: Boolean = false
|
override val substitutionsAllowed: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ClassObjectRequired(delegate: TypeInfo): DelegatingTypeInfo(delegate) {
|
||||||
|
override val classObjectRequired: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
open val substitutionsAllowed: Boolean = true
|
open val substitutionsAllowed: Boolean = true
|
||||||
|
open val classObjectRequired: Boolean = false
|
||||||
open val possibleNamesFromExpression: Array<String> get() = ArrayUtil.EMPTY_STRING_ARRAY
|
open val possibleNamesFromExpression: Array<String> get() = ArrayUtil.EMPTY_STRING_ARRAY
|
||||||
abstract fun getPossibleTypes(builder: CallableBuilder): List<JetType>
|
abstract fun getPossibleTypes(builder: CallableBuilder): List<JetType>
|
||||||
|
|
||||||
|
|||||||
+11
-3
@@ -79,18 +79,26 @@ public class CreateCallableFromUsageFix(
|
|||||||
val receiverInfo = callableInfo.receiverTypeInfo
|
val receiverInfo = callableInfo.receiverTypeInfo
|
||||||
|
|
||||||
if (receiverInfo is TypeInfo.Empty) return !isExtension
|
if (receiverInfo is TypeInfo.Empty) return !isExtension
|
||||||
|
// TODO: Remove after class object extensions are supported
|
||||||
|
if (isExtension && receiverInfo.classObjectRequired) return false
|
||||||
|
|
||||||
val file = element.getContainingFile() as JetFile
|
val file = element.getContainingFile() as JetFile
|
||||||
val project = file.getProject()
|
val project = file.getProject()
|
||||||
val callableBuilder =
|
val callableBuilder =
|
||||||
CallableBuilderConfiguration(callableInfos, element as JetExpression, file, null, isExtension).createBuilder()
|
CallableBuilderConfiguration(callableInfos, element as JetExpression, file, null, isExtension).createBuilder()
|
||||||
val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfos.first().receiverTypeInfo)
|
val receiverTypeCandidates = callableBuilder.computeTypeCandidates(callableInfos.first().receiverTypeInfo)
|
||||||
val isProperty = callableInfos.any { it.kind == CallableKind.PROPERTY }
|
val propertyInfo = callableInfos.firstOrNull { it is PropertyInfo } as PropertyInfo?
|
||||||
|
val isFunction = callableInfos.any { it.kind == CallableKind.FUNCTION }
|
||||||
return receiverTypeCandidates.any {
|
return receiverTypeCandidates.any {
|
||||||
val declaration = getDeclarationIfApplicable(project, it)
|
val declaration = getDeclarationIfApplicable(project, it)
|
||||||
|
val insertToJavaInterface = declaration is PsiClass && declaration.isInterface()
|
||||||
when {
|
when {
|
||||||
isProperty && declaration is PsiClass && declaration.isInterface() -> false
|
propertyInfo != null && insertToJavaInterface && (!receiverInfo.classObjectRequired || propertyInfo.writable) ->
|
||||||
else -> declaration != null
|
false
|
||||||
|
isFunction && insertToJavaInterface && receiverInfo.classObjectRequired ->
|
||||||
|
false
|
||||||
|
else ->
|
||||||
|
declaration != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-11
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.diagnostics.Errors
|
|||||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
import org.jetbrains.kotlin.psi.JetExpression
|
import org.jetbrains.kotlin.psi.JetExpression
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers
|
|
||||||
import org.jetbrains.kotlin.psi.JetClassBody
|
import org.jetbrains.kotlin.psi.JetClassBody
|
||||||
import org.jetbrains.kotlin.psi.JetFile
|
import org.jetbrains.kotlin.psi.JetFile
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS
|
import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS
|
||||||
@@ -42,6 +41,11 @@ import com.intellij.psi.util.PsiTreeUtil
|
|||||||
import org.jetbrains.kotlin.psi.JetAnnotationEntry
|
import org.jetbrains.kotlin.psi.JetAnnotationEntry
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory
|
import org.jetbrains.kotlin.idea.quickfix.JetIntentionActionsFactory
|
||||||
|
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToDeclarationUtil
|
||||||
|
import com.intellij.psi.PsiClass
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.*
|
||||||
|
|
||||||
object CreateFunctionOrPropertyFromCallActionFactory : JetIntentionActionsFactory() {
|
object CreateFunctionOrPropertyFromCallActionFactory : JetIntentionActionsFactory() {
|
||||||
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction>? {
|
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction>? {
|
||||||
@@ -60,6 +64,8 @@ object CreateFunctionOrPropertyFromCallActionFactory : JetIntentionActionsFactor
|
|||||||
else -> throw AssertionError("Unexpected diagnostic: ${diagnostic.getFactory()}")
|
else -> throw AssertionError("Unexpected diagnostic: ${diagnostic.getFactory()}")
|
||||||
} as? JetExpression ?: return null
|
} as? JetExpression ?: return null
|
||||||
|
|
||||||
|
val project = callExpr.getProject()
|
||||||
|
|
||||||
val calleeExpr = when (callExpr) {
|
val calleeExpr = when (callExpr) {
|
||||||
is JetCallExpression -> callExpr.getCalleeExpression()
|
is JetCallExpression -> callExpr.getCalleeExpression()
|
||||||
is JetSimpleNameExpression -> callExpr
|
is JetSimpleNameExpression -> callExpr
|
||||||
@@ -73,16 +79,8 @@ object CreateFunctionOrPropertyFromCallActionFactory : JetIntentionActionsFactor
|
|||||||
if (callParent is JetQualifiedExpression && callParent.getSelectorExpression() == callExpr) callParent else callExpr
|
if (callParent is JetQualifiedExpression && callParent.getSelectorExpression() == callExpr) callParent else callExpr
|
||||||
|
|
||||||
val context = calleeExpr.analyze()
|
val context = calleeExpr.analyze()
|
||||||
val receiver = callExpr.getCall(context)?.getExplicitReceiver()
|
val receiver = callExpr.getCall(context)?.getExplicitReceiver() ?: ReceiverValue.NO_RECEIVER
|
||||||
|
val receiverType = getReceiverTypeInfo(context, project, receiver) ?: return null
|
||||||
val receiverType = when (receiver) {
|
|
||||||
null, ReceiverValue.NO_RECEIVER -> TypeInfo.Empty
|
|
||||||
is Qualifier -> {
|
|
||||||
val qualifierType = context[BindingContext.EXPRESSION_TYPE, receiver.expression] ?: return null
|
|
||||||
TypeInfo(qualifierType, Variance.IN_VARIANCE)
|
|
||||||
}
|
|
||||||
else -> TypeInfo(receiver.getType(), Variance.IN_VARIANCE)
|
|
||||||
}
|
|
||||||
|
|
||||||
val possibleContainers =
|
val possibleContainers =
|
||||||
if (receiverType is TypeInfo.Empty) {
|
if (receiverType is TypeInfo.Empty) {
|
||||||
@@ -121,4 +119,20 @@ object CreateFunctionOrPropertyFromCallActionFactory : JetIntentionActionsFactor
|
|||||||
|
|
||||||
return CreateCallableFromUsageFixes(callExpr, callableInfo)
|
return CreateCallableFromUsageFixes(callExpr, callableInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getReceiverTypeInfo(context: BindingContext, project: Project, receiver: ReceiverValue): TypeInfo? {
|
||||||
|
return when {
|
||||||
|
!receiver.exists() -> TypeInfo.Empty
|
||||||
|
receiver is Qualifier -> {
|
||||||
|
val qualifierType = context[BindingContext.EXPRESSION_TYPE, receiver.expression]
|
||||||
|
if (qualifierType != null) return TypeInfo(qualifierType, Variance.IN_VARIANCE)
|
||||||
|
|
||||||
|
val classifier = receiver.classifier as? JavaClassDescriptor ?: return null
|
||||||
|
val javaClass = DescriptorToDeclarationUtil.getDeclaration(project, classifier) as? PsiClass
|
||||||
|
if (javaClass == null || !javaClass.canRefactor()) return null
|
||||||
|
TypeInfo.ClassObjectRequired(TypeInfo(classifier.getDefaultType(), Variance.IN_VARIANCE))
|
||||||
|
}
|
||||||
|
else -> TypeInfo(receiver.getType(), Variance.IN_VARIANCE)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -501,7 +501,7 @@ fun createJavaField(property: JetProperty, targetClass: PsiClass): PsiField {
|
|||||||
with(field.getModifierList()) {
|
with(field.getModifierList()) {
|
||||||
val templateModifiers = template.getModifierList()
|
val templateModifiers = template.getModifierList()
|
||||||
setModifierProperty(VisibilityUtil.getVisibilityModifier(templateModifiers), true)
|
setModifierProperty(VisibilityUtil.getVisibilityModifier(templateModifiers), true)
|
||||||
if (!property.isVar()) {
|
if (!property.isVar() || targetClass.isInterface()) {
|
||||||
setModifierProperty(PsiModifier.FINAL, true)
|
setModifierProperty(PsiModifier.FINAL, true)
|
||||||
}
|
}
|
||||||
copyModifierListItems(templateModifiers, this, false)
|
copyModifierListItems(templateModifiers, this, false)
|
||||||
|
|||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// "Create extension function 'foo'" "false"
|
||||||
|
// ACTION: Create function 'foo'
|
||||||
|
// ACTION: Split property declaration
|
||||||
|
// ERROR: Unresolved reference: foo
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val a: Int = J.<caret>foo("1", 2)
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
class J {
|
||||||
|
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
class J {
|
||||||
|
|
||||||
|
public static int foo(@NotNull String s, int i) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Create function 'foo'" "true"
|
||||||
|
// ERROR: Unresolved reference: foo
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val a: Int = J.foo("1", 2)
|
||||||
|
}
|
||||||
|
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// "Create function 'foo'" "true"
|
||||||
|
// ERROR: Unresolved reference: foo
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val a: Int = J.<caret>foo("1", 2)
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
class J {
|
||||||
|
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Create function 'foo'" "false"
|
||||||
|
// ACTION: Split property declaration
|
||||||
|
// ERROR: Unresolved reference: foo
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val a: Int = J.<caret>foo("1", 2)
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
interface J {
|
||||||
|
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
// "Create extension property 'foo'" "false"
|
||||||
|
// ACTION: Create property 'foo'
|
||||||
|
// ACTION: Split property declaration
|
||||||
|
// ERROR: Unresolved reference: foo
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val a: Int = J.<caret>foo
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
class J {
|
||||||
|
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
class J {
|
||||||
|
|
||||||
|
public static final int foo;
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Create property 'foo'" "true"
|
||||||
|
// ERROR: Unresolved reference: foo
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val a: Int = J.foo
|
||||||
|
}
|
||||||
|
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// "Create property 'foo'" "true"
|
||||||
|
// ERROR: Unresolved reference: foo
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val a: Int = J.<caret>foo
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
class J {
|
||||||
|
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
interface J {
|
||||||
|
|
||||||
|
int foo;
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Create property 'foo'" "true"
|
||||||
|
// ERROR: Unresolved reference: foo
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val a: Int = J.foo
|
||||||
|
}
|
||||||
|
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// "Create property 'foo'" "true"
|
||||||
|
// ERROR: Unresolved reference: foo
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val a: Int = J.<caret>foo
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
interface J {
|
||||||
|
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// "Create property 'foo'" "false"
|
||||||
|
// ERROR: Unresolved reference: foo
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
J.<caret>foo = 1
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
interface J {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -355,6 +355,24 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
|||||||
doTestWithExtraFile(fileName);
|
doTestWithExtraFile(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("staticExtensionFunOnJavaClass.before.Main.kt")
|
||||||
|
public void testStaticExtensionFunOnJavaClass() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/staticExtensionFunOnJavaClass.before.Main.kt");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("staticFunOnJavaClass.before.Main.kt")
|
||||||
|
public void testStaticFunOnJavaClass() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/staticFunOnJavaClass.before.Main.kt");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("staticFunOnJavaInterface.before.Main.kt")
|
||||||
|
public void testStaticFunOnJavaInterface() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/staticFunOnJavaInterface.before.Main.kt");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -403,6 +421,30 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
|||||||
doTestWithExtraFile(fileName);
|
doTestWithExtraFile(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("staticExtensionValOnJavaType.before.Main.kt")
|
||||||
|
public void testStaticExtensionValOnJavaType() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/staticExtensionValOnJavaType.before.Main.kt");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("staticValOnJavaClass.before.Main.kt")
|
||||||
|
public void testStaticValOnJavaClass() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/staticValOnJavaClass.before.Main.kt");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("staticValOnJavaInterface.before.Main.kt")
|
||||||
|
public void testStaticValOnJavaInterface() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/staticValOnJavaInterface.before.Main.kt");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("staticVarOnJavaInterface.before.Main.kt")
|
||||||
|
public void testStaticVarOnJavaInterface() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/staticVarOnJavaInterface.before.Main.kt");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("valOnGroovyType.before.Main.kt")
|
@TestMetadata("valOnGroovyType.before.Main.kt")
|
||||||
public void testValOnGroovyType() throws Exception {
|
public void testValOnGroovyType() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/valOnGroovyType.before.Main.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/valOnGroovyType.before.Main.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user