Add "Make private and overrides member" quick fix for ACCIDENTAL_OVERRIDE
#KT-18536 Fixed
This commit is contained in:
committed by
Dmitry Gridin
parent
30b98aaeb7
commit
d72e55d1b7
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideImplementMembersHandler
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getType
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.typeUtil.isUnit
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
|
||||
class MakePrivateAndOverrideMemberFix(
|
||||
propertyOrParameter: KtDeclaration,
|
||||
private val memberToOverride: OverrideMemberChooserObject
|
||||
) : KotlinQuickFixAction<KtDeclaration>(propertyOrParameter) {
|
||||
private val makePrivate = !propertyOrParameter.hasModifier(KtTokens.PRIVATE_KEYWORD)
|
||||
|
||||
override fun getText(): String {
|
||||
val descriptor = memberToOverride.descriptor
|
||||
val implement = (descriptor.containingDeclaration as? ClassDescriptor)?.kind == ClassKind.INTERFACE
|
||||
val name = descriptor.name.asString()
|
||||
return if (makePrivate) {
|
||||
"Make private and ${if (implement) "implements" else "overrides"} '$name'"
|
||||
} else {
|
||||
"${if (implement) "Implements" else "Overrides"} '$name'"
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFamilyName() = text
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||
if (editor == null) return
|
||||
val element = element ?: return
|
||||
val containingClassOrObject = element.containingClassOrObject ?: return
|
||||
if (makePrivate) {
|
||||
element.addModifier(KtTokens.PRIVATE_KEYWORD)
|
||||
}
|
||||
OverrideImplementMembersHandler.generateMembers(editor, containingClassOrObject, listOf(memberToOverride), false)
|
||||
}
|
||||
|
||||
object AccidentalOverrideFactory : KotlinSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtModifierListOwner>? {
|
||||
val casted = ErrorsJvm.ACCIDENTAL_OVERRIDE.cast(diagnostic)
|
||||
val element = casted.psiElement as? KtDeclaration ?: return null
|
||||
if (element !is KtProperty && element !is KtParameter) return null
|
||||
val containingClassOrObject = element.containingClassOrObject ?: return null
|
||||
val memberNameToOverride = casted.a.signature.name
|
||||
val returnType = (element.descriptor as? CallableDescriptor)?.returnType ?: return null
|
||||
|
||||
val isGetter = JvmAbi.isGetterName(memberNameToOverride)
|
||||
val isSetter = JvmAbi.isSetterName(memberNameToOverride)
|
||||
val memberToOverride = implementMembersHandler.collectMembersToGenerate(containingClassOrObject).firstOrNull {
|
||||
val descriptor = it.descriptor
|
||||
if (descriptor.name.asString() != memberNameToOverride) return@firstOrNull false
|
||||
val accessorReturnType = descriptor.returnType?.makeNotNullable() ?: return@firstOrNull false
|
||||
val params = descriptor.valueParameters
|
||||
(isGetter && params.isEmpty() && accessorReturnType == returnType) ||
|
||||
(isSetter && params.singleOrNull()?.type?.makeNotNullable() == returnType && accessorReturnType.isUnit())
|
||||
} ?: return null
|
||||
|
||||
return MakePrivateAndOverrideMemberFix(element, memberToOverride)
|
||||
}
|
||||
|
||||
private val implementMembersHandler = ImplementMembersHandler()
|
||||
}
|
||||
}
|
||||
@@ -617,5 +617,8 @@ class QuickFixRegistrar : QuickFixContributor {
|
||||
CONSTRUCTOR_IN_OBJECT.registerFactory(ChangeObjectToClassFix)
|
||||
|
||||
REDUNDANT_LABEL_WARNING.registerFactory(RemoveRedundantLabelFix)
|
||||
|
||||
ACCIDENTAL_OVERRIDE.registerFactory(MakePrivateAndOverrideMemberFix.AccidentalOverrideFactory)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
public interface JavaInterface {
|
||||
String getName(String name);
|
||||
String getName();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Make private and implements 'getName'" "true"
|
||||
// DISABLE-ERRORS
|
||||
class A(private val name: String) : JavaInterface {
|
||||
override fun getName(): String {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
public interface JavaInterface {
|
||||
String getName(String name);
|
||||
String getName();
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Make private and implements 'getName'" "true"
|
||||
// DISABLE-ERRORS
|
||||
class A(<caret>val name: String) : JavaInterface {
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
public interface JavaInterface {
|
||||
String getName();
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// "Implements 'getName'" "false"
|
||||
// DISABLE-ERRORS
|
||||
// ACTION: Convert to secondary constructor
|
||||
// ACTION: Create test
|
||||
// ACTION: Make private
|
||||
// ACTION: Make protected
|
||||
// ACTION: Make public
|
||||
// ACTION: Move to class body
|
||||
class A(<caret>internal val name: String) : JavaInterface {
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
public interface JavaInterface {
|
||||
String getName();
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// "Implements 'getName'" "true"
|
||||
// DISABLE-ERRORS
|
||||
class A(private val name: String) : JavaInterface {
|
||||
override fun getName(): String {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
public interface JavaInterface {
|
||||
String getName();
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// "Implements 'getName'" "true"
|
||||
// DISABLE-ERRORS
|
||||
class A(<caret>private val name: String) : JavaInterface {
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
public interface JavaInterface {
|
||||
String getName();
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// "Make private and implements 'getName'" "true"
|
||||
// DISABLE-ERRORS
|
||||
class A(private val name: String) : JavaInterface {
|
||||
override fun getName(): String {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
public interface JavaInterface {
|
||||
String getName();
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// "Make private and implements 'getName'" "true"
|
||||
// DISABLE-ERRORS
|
||||
class A(<caret>protected val name: String) : JavaInterface {
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
public interface JavaInterface {
|
||||
String getName();
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// "Make private and implements 'getName'" "true"
|
||||
// DISABLE-ERRORS
|
||||
class A(private val name: String) : JavaInterface {
|
||||
override fun getName(): String {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
public interface JavaInterface {
|
||||
String getName();
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// "Make private and implements 'getName'" "true"
|
||||
// DISABLE-ERRORS
|
||||
class A(<caret>public val name: String) : JavaInterface {
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
public abstract class JavaClass {
|
||||
abstract String getName();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Make private and overrides 'getName'" "true"
|
||||
// DISABLE-ERRORS
|
||||
class B : JavaClass() {
|
||||
private val name: String = ""
|
||||
override fun getName(): String {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
public abstract class JavaClass {
|
||||
abstract String getName();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// "Make private and overrides 'getName'" "true"
|
||||
// DISABLE-ERRORS
|
||||
class B : JavaClass() {
|
||||
<caret>val name: String = ""
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
public interface JavaInterface {
|
||||
String getName();
|
||||
|
||||
void setName();
|
||||
|
||||
void setName(String name);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// "Make private and implements 'setName'" "true"
|
||||
// DISABLE-ERRORS
|
||||
class A(private var name: String) : JavaInterface {
|
||||
override fun setName(name: String?) {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun getName(): String {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
public interface JavaInterface {
|
||||
String getName();
|
||||
|
||||
void setName();
|
||||
|
||||
void setName(String name);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Make private and implements 'setName'" "true"
|
||||
// DISABLE-ERRORS
|
||||
class A(<caret>var name: String) : JavaInterface {
|
||||
override fun getName(): String {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
+48
@@ -3036,6 +3036,54 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/makePrivateAndOverrideMember")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class MakePrivateAndOverrideMember extends AbstractQuickFixMultiFileTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInMakePrivateAndOverrideMember() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/makePrivateAndOverrideMember"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("parameter.before.Main.kt")
|
||||
public void testParameter() throws Exception {
|
||||
runTest("idea/testData/quickfix/makePrivateAndOverrideMember/parameter.before.Main.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parameterHasInternal.before.Main.kt")
|
||||
public void testParameterHasInternal() throws Exception {
|
||||
runTest("idea/testData/quickfix/makePrivateAndOverrideMember/parameterHasInternal.before.Main.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parameterHasPrivate.before.Main.kt")
|
||||
public void testParameterHasPrivate() throws Exception {
|
||||
runTest("idea/testData/quickfix/makePrivateAndOverrideMember/parameterHasPrivate.before.Main.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parameterHasProtected.before.Main.kt")
|
||||
public void testParameterHasProtected() throws Exception {
|
||||
runTest("idea/testData/quickfix/makePrivateAndOverrideMember/parameterHasProtected.before.Main.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parameterHasPublic.before.Main.kt")
|
||||
public void testParameterHasPublic() throws Exception {
|
||||
runTest("idea/testData/quickfix/makePrivateAndOverrideMember/parameterHasPublic.before.Main.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("property.before.Main.kt")
|
||||
public void testProperty() throws Exception {
|
||||
runTest("idea/testData/quickfix/makePrivateAndOverrideMember/property.before.Main.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("setter.before.Main.kt")
|
||||
public void testSetter() throws Exception {
|
||||
runTest("idea/testData/quickfix/makePrivateAndOverrideMember/setter.before.Main.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/makeTypeParameterReified")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -8231,6 +8231,19 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/makePrivateAndOverrideMember")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class MakePrivateAndOverrideMember extends AbstractQuickFixTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInMakePrivateAndOverrideMember() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/makePrivateAndOverrideMember"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/makeTypeParameterReified")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user