Introduce "Assign to property" quick-fix #KT-17204 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
67a50e92d0
commit
e92af08c98
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||||
|
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.containingClass
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
|
class AssignToPropertyFix(element: KtNameReferenceExpression) : KotlinQuickFixAction<KtNameReferenceExpression>(element) {
|
||||||
|
|
||||||
|
override fun getText() = "Assign to property"
|
||||||
|
|
||||||
|
override fun getFamilyName() = text
|
||||||
|
|
||||||
|
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||||
|
val element = element ?: return
|
||||||
|
val psiFactory = KtPsiFactory(element)
|
||||||
|
if (element.getResolutionScope().getImplicitReceiversHierarchy().size == 1) {
|
||||||
|
element.replace(psiFactory.createExpressionByPattern("this.$0", element))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
element.containingClass()?.name?.let {
|
||||||
|
element.replace(psiFactory.createExpressionByPattern("this@$0.$1", it, element))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : KotlinSingleIntentionActionFactory() {
|
||||||
|
private fun KtCallableDeclaration.hasNameAndTypeOf(name: Name, type: KotlinType) =
|
||||||
|
nameAsName == name && (resolveToDescriptorIfAny() as? CallableDescriptor)?.returnType == type
|
||||||
|
|
||||||
|
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtNameReferenceExpression>? {
|
||||||
|
val expression = diagnostic.psiElement as? KtNameReferenceExpression ?: return null
|
||||||
|
|
||||||
|
val containingClass = expression.containingClass() ?: return null
|
||||||
|
val right = (expression.parent as? KtBinaryExpression)?.right ?: return null
|
||||||
|
val type = expression.analyze().getType(right) ?: return null
|
||||||
|
val name = expression.getReferencedNameAsName()
|
||||||
|
|
||||||
|
val inSecondaryConstructor = expression.getStrictParentOfType<KtSecondaryConstructor>() != null
|
||||||
|
val hasAssignableProperty = containingClass.getProperties().any {
|
||||||
|
(inSecondaryConstructor || it.isVar) &&
|
||||||
|
it.hasNameAndTypeOf(name, type)
|
||||||
|
}
|
||||||
|
val hasAssignablePropertyInPrimaryConstructor = containingClass.primaryConstructor?.valueParameters?.any {
|
||||||
|
it.valOrVarKeyword?.node?.elementType == KtTokens.VAR_KEYWORD &&
|
||||||
|
it.hasNameAndTypeOf(name, type)
|
||||||
|
} ?: false
|
||||||
|
|
||||||
|
if (!hasAssignableProperty && !hasAssignablePropertyInPrimaryConstructor) return null
|
||||||
|
|
||||||
|
return AssignToPropertyFix(expression)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -193,8 +193,8 @@ class QuickFixRegistrar : QuickFixContributor {
|
|||||||
MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED.registerActions(implementMembersHandler, implementMembersAsParametersHandler)
|
MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED.registerActions(implementMembersHandler, implementMembersAsParametersHandler)
|
||||||
|
|
||||||
VAL_WITH_SETTER.registerFactory(ChangeVariableMutabilityFix.VAL_WITH_SETTER_FACTORY)
|
VAL_WITH_SETTER.registerFactory(ChangeVariableMutabilityFix.VAL_WITH_SETTER_FACTORY)
|
||||||
VAL_REASSIGNMENT.registerFactory(ChangeVariableMutabilityFix.VAL_REASSIGNMENT_FACTORY)
|
VAL_REASSIGNMENT.registerFactory(
|
||||||
VAL_REASSIGNMENT.registerFactory(LiftAssignmentOutOfTryFix)
|
ChangeVariableMutabilityFix.VAL_REASSIGNMENT_FACTORY, LiftAssignmentOutOfTryFix, AssignToPropertyFix)
|
||||||
CAPTURED_VAL_INITIALIZATION.registerFactory(ChangeVariableMutabilityFix.CAPTURED_VAL_INITIALIZATION_FACTORY)
|
CAPTURED_VAL_INITIALIZATION.registerFactory(ChangeVariableMutabilityFix.CAPTURED_VAL_INITIALIZATION_FACTORY)
|
||||||
CAPTURED_MEMBER_VAL_INITIALIZATION.registerFactory(ChangeVariableMutabilityFix.CAPTURED_MEMBER_VAL_INITIALIZATION_FACTORY)
|
CAPTURED_MEMBER_VAL_INITIALIZATION.registerFactory(ChangeVariableMutabilityFix.CAPTURED_MEMBER_VAL_INITIALIZATION_FACTORY)
|
||||||
VAR_OVERRIDDEN_BY_VAL.registerFactory(ChangeVariableMutabilityFix.VAR_OVERRIDDEN_BY_VAL_FACTORY)
|
VAR_OVERRIDDEN_BY_VAL.registerFactory(ChangeVariableMutabilityFix.VAR_OVERRIDDEN_BY_VAL_FACTORY)
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// "Assign to property" "false"
|
||||||
|
// ERROR: Val cannot be reassigned
|
||||||
|
class Test {
|
||||||
|
var bar = 1
|
||||||
|
|
||||||
|
fun test(foo: Int) {
|
||||||
|
<caret>foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Assign to property" "false"
|
||||||
|
// ERROR: Val cannot be reassigned
|
||||||
|
class Test(var bar: Int) {
|
||||||
|
fun test(foo: Int) {
|
||||||
|
<caret>foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// "Assign to property" "false"
|
||||||
|
// ERROR: Val cannot be reassigned
|
||||||
|
class Test {
|
||||||
|
var foo = "1"
|
||||||
|
|
||||||
|
fun test(foo: Int) {
|
||||||
|
<caret>foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Assign to property" "false"
|
||||||
|
// ERROR: Val cannot be reassigned
|
||||||
|
class Test(var foo: String) {
|
||||||
|
fun test(foo: Int) {
|
||||||
|
<caret>foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// "Assign to property" "true"
|
||||||
|
class Test {
|
||||||
|
val foo: Int
|
||||||
|
|
||||||
|
constructor(foo: Int) {
|
||||||
|
<caret>foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// "Assign to property" "true"
|
||||||
|
class Test {
|
||||||
|
val foo: Int
|
||||||
|
|
||||||
|
constructor(foo: Int) {
|
||||||
|
this.foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Assign to property" "false"
|
||||||
|
// ERROR: Val cannot be reassigned
|
||||||
|
class Test(foo: Int) {
|
||||||
|
fun test(foo: Int) {
|
||||||
|
<caret>foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// "Assign to property" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
class Test {
|
||||||
|
var foo = 1
|
||||||
|
|
||||||
|
fun test(foo: Int) {
|
||||||
|
"".run {
|
||||||
|
<caret>foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// "Assign to property" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
class Test {
|
||||||
|
var foo = 1
|
||||||
|
|
||||||
|
fun test(foo: Int) {
|
||||||
|
"".run {
|
||||||
|
this@Test.foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// "Assign to property" "true"
|
||||||
|
class Test {
|
||||||
|
var foo = 1
|
||||||
|
|
||||||
|
fun test(foo: Int) {
|
||||||
|
<caret>foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// "Assign to property" "true"
|
||||||
|
class Test {
|
||||||
|
var foo = 1
|
||||||
|
|
||||||
|
fun test(foo: Int) {
|
||||||
|
this.foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// "Assign to property" "true"
|
||||||
|
class Test {
|
||||||
|
var foo = 1
|
||||||
|
|
||||||
|
fun test(foo: String) {
|
||||||
|
<caret>foo = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// "Assign to property" "true"
|
||||||
|
class Test {
|
||||||
|
var foo = 1
|
||||||
|
|
||||||
|
fun test(foo: String) {
|
||||||
|
this.foo = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Assign to property" "true"
|
||||||
|
class Test(var foo: Int) {
|
||||||
|
fun test(foo: Int) {
|
||||||
|
<caret>foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Assign to property" "true"
|
||||||
|
class Test(var foo: Int) {
|
||||||
|
fun test(foo: Int) {
|
||||||
|
this.foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// "Assign to property" "false"
|
||||||
|
// ERROR: Val cannot be reassigned
|
||||||
|
class Test {
|
||||||
|
val foo = 1
|
||||||
|
|
||||||
|
fun test(foo: Int) {
|
||||||
|
<caret>foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Assign to property" "false"
|
||||||
|
// ERROR: Val cannot be reassigned
|
||||||
|
class Test(val foo: Int) {
|
||||||
|
fun test(foo: Int) {
|
||||||
|
<caret>foo = foo
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -954,6 +954,87 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/quickfix/assignToProperty")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class AssignToProperty extends AbstractQuickFixTest {
|
||||||
|
public void testAllFilesPresentInAssignToProperty() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/assignToProperty"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("differentNameProperty.kt")
|
||||||
|
public void testDifferentNameProperty() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/assignToProperty/differentNameProperty.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("differentNameProperty2.kt")
|
||||||
|
public void testDifferentNameProperty2() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/assignToProperty/differentNameProperty2.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("differentTypeProperty.kt")
|
||||||
|
public void testDifferentTypeProperty() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/assignToProperty/differentTypeProperty.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("differentTypeProperty2.kt")
|
||||||
|
public void testDifferentTypeProperty2() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/assignToProperty/differentTypeProperty2.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inSecondaryConstructor.kt")
|
||||||
|
public void testInSecondaryConstructor() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/assignToProperty/inSecondaryConstructor.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("noProperty.kt")
|
||||||
|
public void testNoProperty() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/assignToProperty/noProperty.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("qualifiedThis.kt")
|
||||||
|
public void testQualifiedThis() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/assignToProperty/qualifiedThis.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("sameProperty.kt")
|
||||||
|
public void testSameProperty() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/assignToProperty/sameProperty.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("sameProperty2.kt")
|
||||||
|
public void testSameProperty2() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/assignToProperty/sameProperty2.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("sameProperty3.kt")
|
||||||
|
public void testSameProperty3() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/assignToProperty/sameProperty3.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("valProperty.kt")
|
||||||
|
public void testValProperty() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/assignToProperty/valProperty.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("valProperty2.kt")
|
||||||
|
public void testValProperty2() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/assignToProperty/valProperty2.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/quickfix/autoImports")
|
@TestMetadata("idea/testData/quickfix/autoImports")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user