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) {
|
||||
is JetNamedFunction -> {
|
||||
val method = createJavaMethod(declaration, targetClass)
|
||||
|
||||
val modifierList = method.getModifierList()
|
||||
modifierList.setModifierProperty(PsiModifier.STATIC, false)
|
||||
modifierList.setModifierProperty(PsiModifier.FINAL, false)
|
||||
|
||||
method.getModifierList().setModifierProperty(PsiModifier.FINAL, false)
|
||||
method
|
||||
}
|
||||
is JetProperty -> {
|
||||
if (targetClass.isInterface()) return false
|
||||
createJavaField(declaration, targetClass)
|
||||
}
|
||||
else -> return false
|
||||
@@ -785,6 +780,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
||||
|
||||
declaration.delete()
|
||||
|
||||
newJavaMember.getModifierList().setModifierProperty(PsiModifier.STATIC, callableInfo.receiverTypeInfo.classObjectRequired)
|
||||
|
||||
JavaCodeStyleManager.getInstance(project).shortenClassReferences(newJavaMember);
|
||||
|
||||
val descriptor = OpenFileDescriptor(project, targetClass.getContainingFile().getVirtualFile())
|
||||
|
||||
+5
@@ -75,7 +75,12 @@ abstract class TypeInfo(val variance: Variance) {
|
||||
override val substitutionsAllowed: Boolean = false
|
||||
}
|
||||
|
||||
class ClassObjectRequired(delegate: TypeInfo): DelegatingTypeInfo(delegate) {
|
||||
override val classObjectRequired: Boolean = true
|
||||
}
|
||||
|
||||
open val substitutionsAllowed: Boolean = true
|
||||
open val classObjectRequired: Boolean = false
|
||||
open val possibleNamesFromExpression: Array<String> get() = ArrayUtil.EMPTY_STRING_ARRAY
|
||||
abstract fun getPossibleTypes(builder: CallableBuilder): List<JetType>
|
||||
|
||||
|
||||
+11
-3
@@ -79,18 +79,26 @@ public class CreateCallableFromUsageFix(
|
||||
val receiverInfo = callableInfo.receiverTypeInfo
|
||||
|
||||
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 project = file.getProject()
|
||||
val callableBuilder =
|
||||
CallableBuilderConfiguration(callableInfos, element as JetExpression, file, null, isExtension).createBuilder()
|
||||
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 {
|
||||
val declaration = getDeclarationIfApplicable(project, it)
|
||||
val insertToJavaInterface = declaration is PsiClass && declaration.isInterface()
|
||||
when {
|
||||
isProperty && declaration is PsiClass && declaration.isInterface() -> false
|
||||
else -> declaration != null
|
||||
propertyInfo != null && insertToJavaInterface && (!receiverInfo.classObjectRequired || propertyInfo.writable) ->
|
||||
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.JetExpression
|
||||
import java.util.Collections
|
||||
import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers
|
||||
import org.jetbrains.kotlin.psi.JetClassBody
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
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.idea.caches.resolve.analyze
|
||||
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() {
|
||||
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction>? {
|
||||
@@ -60,6 +64,8 @@ object CreateFunctionOrPropertyFromCallActionFactory : JetIntentionActionsFactor
|
||||
else -> throw AssertionError("Unexpected diagnostic: ${diagnostic.getFactory()}")
|
||||
} as? JetExpression ?: return null
|
||||
|
||||
val project = callExpr.getProject()
|
||||
|
||||
val calleeExpr = when (callExpr) {
|
||||
is JetCallExpression -> callExpr.getCalleeExpression()
|
||||
is JetSimpleNameExpression -> callExpr
|
||||
@@ -73,16 +79,8 @@ object CreateFunctionOrPropertyFromCallActionFactory : JetIntentionActionsFactor
|
||||
if (callParent is JetQualifiedExpression && callParent.getSelectorExpression() == callExpr) callParent else callExpr
|
||||
|
||||
val context = calleeExpr.analyze()
|
||||
val receiver = callExpr.getCall(context)?.getExplicitReceiver()
|
||||
|
||||
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 receiver = callExpr.getCall(context)?.getExplicitReceiver() ?: ReceiverValue.NO_RECEIVER
|
||||
val receiverType = getReceiverTypeInfo(context, project, receiver) ?: return null
|
||||
|
||||
val possibleContainers =
|
||||
if (receiverType is TypeInfo.Empty) {
|
||||
@@ -121,4 +119,20 @@ object CreateFunctionOrPropertyFromCallActionFactory : JetIntentionActionsFactor
|
||||
|
||||
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()) {
|
||||
val templateModifiers = template.getModifierList()
|
||||
setModifierProperty(VisibilityUtil.getVisibilityModifier(templateModifiers), true)
|
||||
if (!property.isVar()) {
|
||||
if (!property.isVar() || targetClass.isInterface()) {
|
||||
setModifierProperty(PsiModifier.FINAL, true)
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@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")
|
||||
public void testValOnGroovyType() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/valOnGroovyType.before.Main.kt");
|
||||
|
||||
Reference in New Issue
Block a user