Add data modifier to a class quickfix #KT-18220 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
f08e9832a6
commit
1043284afe
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.core.isVisible
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
|
||||
class AddDataModifierFix(element: KtClass, private val fqName: String) : AddModifierFix(element, KtTokens.DATA_KEYWORD) {
|
||||
|
||||
override fun getText() = "Make '$fqName' data class"
|
||||
|
||||
override fun getFamilyName() = text
|
||||
|
||||
companion object : KotlinSingleIntentionActionFactory() {
|
||||
|
||||
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtModifierListOwner>? {
|
||||
val element = diagnostic.psiElement as? KtExpression ?: return null
|
||||
val context = element.analyze()
|
||||
|
||||
val callableDescriptor = if (element is KtDestructuringDeclarationEntry) {
|
||||
context[BindingContext.DECLARATION_TO_DESCRIPTOR, element.parent.parent] as? CallableDescriptor
|
||||
}
|
||||
else {
|
||||
element.getResolvedCall(context)?.resultingDescriptor
|
||||
}
|
||||
|
||||
val constructor = callableDescriptor?.returnType?.arguments?.firstOrNull()?.type?.constructor
|
||||
?: callableDescriptor?.returnType?.constructor
|
||||
|
||||
val classDescriptor = constructor?.declarationDescriptor as? ClassDescriptor ?: return null
|
||||
|
||||
val modality = classDescriptor.modality
|
||||
if (modality != Modality.FINAL || classDescriptor.isInner) return null
|
||||
val ctorParams = classDescriptor.constructors.firstOrNull { it.isPrimary }?.valueParameters ?: return null
|
||||
if (ctorParams.isEmpty()) return null
|
||||
|
||||
if (!ctorParams.all {
|
||||
if (it.varargElementType != null) return@all false
|
||||
val property = context[BindingContext.VALUE_PARAMETER_AS_PROPERTY, it] ?: return@all false
|
||||
// NB: we use element as receiver because element is a constructor call
|
||||
// which is effectively used as receiver by destructuring declaration
|
||||
property.isVisible(element, element, context, element.getResolutionFacade())
|
||||
}) return null
|
||||
|
||||
val klass = DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor) as? KtClass ?: return null
|
||||
val fqName = DescriptorUtils.getFqName(classDescriptor).asString()
|
||||
return AddDataModifierFix(klass, fqName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -158,7 +158,7 @@ class QuickFixRegistrar : QuickFixContributor {
|
||||
FUNCTION_EXPECTED.registerFactory(InvokeImportFix)
|
||||
|
||||
DELEGATE_SPECIAL_FUNCTION_MISSING.registerFactory(DelegateAccessorsImportFix)
|
||||
COMPONENT_FUNCTION_MISSING.registerFactory(ComponentsImportFix)
|
||||
COMPONENT_FUNCTION_MISSING.registerFactory(ComponentsImportFix, AddDataModifierFix)
|
||||
|
||||
NO_GET_METHOD.registerFactory(ArrayAccessorImportFix)
|
||||
NO_SET_METHOD.registerFactory(ArrayAccessorImportFix)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// "Make 'Foo' data class" "false"
|
||||
// ACTION: Create extension function 'Foo.component1'
|
||||
// ACTION: Create extension function 'Foo.component2'
|
||||
// ACTION: Create member function 'Foo.component1'
|
||||
// ACTION: Create member function 'Foo.component2'
|
||||
// ACTION: Put arguments on separate lines
|
||||
// ERROR: Cannot create an instance of an abstract class
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component1()' function
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component2()' function
|
||||
abstract class Foo(val bar: String, val baz: Int)
|
||||
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)<caret>
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// "Make 'Foo' data class" "false"
|
||||
// ACTION: Create extension function 'Test.Foo.component1'
|
||||
// ACTION: Create extension function 'Test.Foo.component2'
|
||||
// ACTION: Create member function 'Test.Foo.component1'
|
||||
// ACTION: Create member function 'Test.Foo.component2'
|
||||
// ACTION: Put arguments on separate lines
|
||||
// ERROR: Destructuring declaration initializer of type Test.Foo must have a 'component1()' function
|
||||
// ERROR: Destructuring declaration initializer of type Test.Foo must have a 'component2()' function
|
||||
class Test {
|
||||
inner class Foo(val bar: String, val baz: Int)
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)<caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Make 'Foo' data class" "false"
|
||||
// ACTION: Create extension function 'Foo.component1'
|
||||
// ACTION: Create extension function 'Foo.component2'
|
||||
// ACTION: Create member function 'Foo.component1'
|
||||
// ACTION: Create member function 'Foo.component2'
|
||||
// ACTION: Put arguments on separate lines
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component1()' function
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component2()' function
|
||||
class Foo(private val bar: String, var baz: Int)
|
||||
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)<caret>
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Make 'Foo' data class" "false"
|
||||
// ACTION: Create extension function 'Foo.component1'
|
||||
// ACTION: Create extension function 'Foo.component2'
|
||||
// ACTION: Create member function 'Foo.component1'
|
||||
// ACTION: Create member function 'Foo.component2'
|
||||
// ACTION: Put arguments on separate lines
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component1()' function
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component2()' function
|
||||
class Foo(protected val bar: String, var baz: Int)
|
||||
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)<caret>
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// "Make 'Foo' data class" "false"
|
||||
// ACTION: Create extension function 'Foo.component1'
|
||||
// ACTION: Create extension function 'Foo.component2'
|
||||
// ACTION: Create member function 'Foo.component1'
|
||||
// ACTION: Create member function 'Foo.component2'
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component1()' function
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component2()' function
|
||||
class Foo()
|
||||
|
||||
fun test() {
|
||||
var (bar, baz) = Foo()<caret>
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Make 'Foo' data class" "false"
|
||||
// ACTION: Create extension function 'Foo.component1'
|
||||
// ACTION: Create extension function 'Foo.component2'
|
||||
// ACTION: Create member function 'Foo.component1'
|
||||
// ACTION: Create member function 'Foo.component2'
|
||||
// ACTION: Put arguments on separate lines
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component1()' function
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component2()' function
|
||||
class Foo(val bar: String, baz: Int)
|
||||
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)<caret>
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// "Make 'Foo' data class" "false"
|
||||
// ACTION: Create extension function 'Foo.component1'
|
||||
// ACTION: Create extension function 'Foo.component2'
|
||||
// ACTION: Create member function 'Foo.component1'
|
||||
// ACTION: Create member function 'Foo.component2'
|
||||
// ACTION: Put arguments on separate lines
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component1()' function
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component2()' function
|
||||
open class Foo(val bar: String, val baz: Int)
|
||||
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)<caret>
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// "Make 'Foo' data class" "false"
|
||||
// ACTION: Create extension function 'Foo.component1'
|
||||
// ACTION: Create extension function 'Foo.component2'
|
||||
// ACTION: Create member function 'Foo.component1'
|
||||
// ACTION: Create member function 'Foo.component2'
|
||||
// ACTION: Put arguments on separate lines
|
||||
// ERROR: Cannot access '<init>': it is private in 'Foo'
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component1()' function
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component2()' function
|
||||
// ERROR: Sealed types cannot be instantiated
|
||||
sealed class Foo(val bar: String, val baz: Int)
|
||||
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)<caret>
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Make 'Foo' data class" "true"
|
||||
class Foo(val bar: String, var baz: Int)
|
||||
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)<caret>
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Make 'Foo' data class" "true"
|
||||
data class Foo(val bar: String, var baz: Int)
|
||||
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)<caret>
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Make 'Foo' data class" "true"
|
||||
class Foo(val bar: String, var baz: Int)
|
||||
|
||||
fun test() {
|
||||
val foo = Foo("A", 1)
|
||||
var (bar, baz) = foo<caret>
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// "Make 'Foo' data class" "true"
|
||||
data class Foo(val bar: String, var baz: Int)
|
||||
|
||||
fun test() {
|
||||
val foo = Foo("A", 1)
|
||||
var (bar, baz) = foo<caret>
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Make 'Foo' data class" "true"
|
||||
// WITH_RUNTIME
|
||||
class Foo(val bar: String, var baz: Int)
|
||||
|
||||
fun test() {
|
||||
val list = listOf(Foo("A", 1))
|
||||
list.forEach { (foo<caret>, bar) ->
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Make 'Foo' data class" "true"
|
||||
// WITH_RUNTIME
|
||||
data class Foo(val bar: String, var baz: Int)
|
||||
|
||||
fun test() {
|
||||
val list = listOf(Foo("A", 1))
|
||||
list.forEach { (foo<caret>, bar) ->
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Make 'Foo' data class" "true"
|
||||
// WITH_RUNTIME
|
||||
class Foo(val bar: String, var baz: Int)
|
||||
|
||||
fun test() {
|
||||
val list = listOf(Foo("A", 1))
|
||||
for ((foo, bar) in list<caret>) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// "Make 'Foo' data class" "true"
|
||||
// WITH_RUNTIME
|
||||
data class Foo(val bar: String, var baz: Int)
|
||||
|
||||
fun test() {
|
||||
val list = listOf(Foo("A", 1))
|
||||
for ((foo, bar) in list<caret>) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Make 'Foo' data class" "true"
|
||||
class Foo(private val bar: String, protected var baz: Int) {
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)<caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Make 'Foo' data class" "true"
|
||||
data class Foo(private val bar: String, protected var baz: Int) {
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Make 'Foo' data class" "true"
|
||||
class Foo(private val bar: String, protected var baz: Int) {
|
||||
class A {
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)<caret>
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// "Make 'Foo' data class" "true"
|
||||
data class Foo(private val bar: String, protected var baz: Int) {
|
||||
class A {
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Make 'Foo' data class" "true"
|
||||
class Foo(internal val bar: String, var baz: Int)
|
||||
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)<caret>
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// "Make 'Foo' data class" "true"
|
||||
data class Foo(internal val bar: String, var baz: Int)
|
||||
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)<caret>
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// "Make 'Foo' data class" "false"
|
||||
// ACTION: Create extension function 'Foo.component1'
|
||||
// ACTION: Create extension function 'Foo.component2'
|
||||
// ACTION: Create member function 'Foo.component1'
|
||||
// ACTION: Create member function 'Foo.component2'
|
||||
// ACTION: Put arguments on separate lines
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component1()' function
|
||||
// ERROR: Destructuring declaration initializer of type Foo must have a 'component2()' function
|
||||
class Foo(val bar: String, vararg var baz: Int)
|
||||
|
||||
fun test() {
|
||||
var (bar, baz) = Foo("A", 1)<caret>
|
||||
}
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
// "Create property 'address2' as constructor parameter" "false"
|
||||
// ACTION: Create property 'address' as constructor parameter
|
||||
// ACTION: Make 'Person' data class
|
||||
// ERROR: Destructuring declaration initializer of type Person must have a 'component3()' function
|
||||
// ERROR: Destructuring declaration initializer of type Person must have a 'component4()' function
|
||||
data class Person(val name: String, val age: Int)
|
||||
|
||||
@@ -252,6 +252,111 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/addDataModifier")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class AddDataModifier extends AbstractQuickFixTest {
|
||||
@TestMetadata("abstract.kt")
|
||||
public void testAbstract() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/abstract.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInAddDataModifier() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/addDataModifier"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("inner.kt")
|
||||
public void testInner() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/inner.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("invisibleParameter.kt")
|
||||
public void testInvisibleParameter() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/invisibleParameter.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("invisibleParameter2.kt")
|
||||
public void testInvisibleParameter2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/invisibleParameter2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noParameter.kt")
|
||||
public void testNoParameter() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/noParameter.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notVarVal.kt")
|
||||
public void testNotVarVal() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/notVarVal.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("open.kt")
|
||||
public void testOpen() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/open.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("sealed.kt")
|
||||
public void testSealed() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/sealed.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("test1.kt")
|
||||
public void testTest1() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/test1.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("test2.kt")
|
||||
public void testTest2() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/test2.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("test3.kt")
|
||||
public void testTest3() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/test3.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("test4.kt")
|
||||
public void testTest4() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/test4.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("test5.kt")
|
||||
public void testTest5() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/test5.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("test6.kt")
|
||||
public void testTest6() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/test6.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("test7.kt")
|
||||
public void testTest7() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/test7.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("vararg.kt")
|
||||
public void testVararg() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/addDataModifier/vararg.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/addExclExclCall")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user