Add "Make private and overrides member" quick fix for ACCIDENTAL_OVERRIDE

#KT-18536 Fixed
This commit is contained in:
Toshiaki Kameyama
2019-10-01 18:22:49 +09:00
committed by Dmitry Gridin
parent 30b98aaeb7
commit d72e55d1b7
30 changed files with 282 additions and 0 deletions
@@ -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)
}
}
@@ -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.
}
}
@@ -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 {
}
@@ -0,0 +1,3 @@
public interface JavaInterface {
String getName();
}
@@ -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 {
}
@@ -0,0 +1,3 @@
public interface JavaInterface {
String getName();
}
@@ -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.
}
}
@@ -0,0 +1,3 @@
public interface JavaInterface {
String getName();
}
@@ -0,0 +1,4 @@
// "Implements 'getName'" "true"
// DISABLE-ERRORS
class A(<caret>private val name: String) : JavaInterface {
}
@@ -0,0 +1,3 @@
public interface JavaInterface {
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.
}
}
@@ -0,0 +1,3 @@
public interface JavaInterface {
String getName();
}
@@ -0,0 +1,4 @@
// "Make private and implements 'getName'" "true"
// DISABLE-ERRORS
class A(<caret>protected val name: String) : JavaInterface {
}
@@ -0,0 +1,3 @@
public interface JavaInterface {
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.
}
}
@@ -0,0 +1,3 @@
public interface JavaInterface {
String getName();
}
@@ -0,0 +1,4 @@
// "Make private and implements 'getName'" "true"
// DISABLE-ERRORS
class A(<caret>public val name: String) : JavaInterface {
}
@@ -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.
}
}
@@ -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 = ""
}
@@ -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.
}
}
@@ -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.
}
}
@@ -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)