From e6171dc4c5e01c3fc3161a6e2de1be44dfcb89ce Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Thu, 7 Sep 2017 23:52:39 +0300 Subject: [PATCH] Parcelable: Add quick fixes --- .../kotlin/diagnostics/diagnosticUtils.kt | 7 + .../kotlin/resolve/AnnotationUtil.kt | 2 + .../kotlin/generators/tests/GenerateTests.kt | 4 + .../idea/test/DirectiveBasedActionUtils.kt | 4 + idea/src/META-INF/android.xml | 1 + .../quickfixes/AbstractParcelableQuickFix.kt | 53 ++++ .../ParcelMigrateToParcelizeQuickFix.kt | 287 ++++++++++++++++++ .../ParcelRemoveCustomCreatorProperty.kt | 34 +++ .../ParcelRemoveCustomWriteToParcel.kt | 29 ++ ...ParcelableAddPrimaryConstructorQuickfix.kt | 38 +++ .../ParcelableAddSupertypeQuickfix.kt | 29 ++ ...arcelableAddTransientAnnotationQuickfix.kt | 29 ++ .../ParcelableQuickFixContributor.kt | 40 +++ .../android/parcel/checker/constructors.kt | 4 +- .../parcel/checker/customWriteToParcel.kt | 6 +- .../android/parcel/checker/delegate.kt | 2 +- .../parcel/checker/emptyPrimaryConstructor.kt | 2 +- .../android/parcel/checker/kt20062.kt | 2 +- .../android/parcel/checker/modality.kt | 10 +- .../android/parcel/checker/properties.kt | 6 +- .../android/parcel/checker/unsupportedType.kt | 6 +- .../checker/withoutParcelableSupertype.kt | 2 +- .../parcel/checker/wrongAnnotationTarget.kt | 10 +- .../constructorWithDelegate.after.kt | 14 + .../constructorWithDelegate.before.Main.kt | 14 + .../noQuickFix.before.Main.kt | 11 + .../addPrimaryConstructor/simple.after.kt | 13 + .../simple.before.Main.kt | 13 + .../quickfix/cantBeInnerClass/simple.after.kt | 13 + .../cantBeInnerClass/simple.before.Main.kt | 13 + .../deleteIncompatible/creatorField.after.kt | 24 ++ .../creatorField.before.Main.kt | 37 +++ .../deleteIncompatible/writeToParcel.after.kt | 30 ++ .../writeToParcel.before.Main.kt | 34 +++ .../parcel/quickfix/migrations/basic.after.kt | 30 ++ .../quickfix/migrations/basic.before.Main.kt | 36 +++ .../quickfix/migrations/complexCase1.after.kt | 34 +++ .../migrations/complexCase1.before.Main.kt | 34 +++ .../customDescribeContents.after.kt | 34 +++ .../customDescribeContents.before.Main.kt | 36 +++ .../migrations/fromCreatorObject.after.kt | 30 ++ .../fromCreatorObject.before.Main.kt | 32 ++ .../migrations/innerClassFactory.after.kt | 36 +++ .../innerClassFactory.before.Main.kt | 34 +++ .../quickfix/migrations/jvmField.after.kt | 26 ++ .../migrations/jvmField.before.Main.kt | 32 ++ .../migrations/noWriteToParcel.after.kt | 25 ++ .../migrations/noWriteToParcel.before.Main.kt | 30 ++ .../withoutDescribeContents.after.kt | 30 ++ .../withoutDescribeContents.before.Main.kt | 32 ++ .../alreadyHasSupertype.before.Main.kt | 13 + .../noParcelableSupertype/simple.after.kt | 11 + .../simple.before.Main.kt | 11 + .../propertyWontBeSerialized/simple.after.kt | 13 + .../simple.before.Main.kt | 12 + .../android/AbstractParcelQuickFixTest.kt | 39 +++ .../android/ParcelQuickFixTestGenerated.java | 194 ++++++++++++ .../inspections/klint/ParcelableQuickFix.kt | 7 +- 58 files changed, 1607 insertions(+), 27 deletions(-) create mode 100644 plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/AbstractParcelableQuickFix.kt create mode 100644 plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelMigrateToParcelizeQuickFix.kt create mode 100644 plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelRemoveCustomCreatorProperty.kt create mode 100644 plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelRemoveCustomWriteToParcel.kt create mode 100644 plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableAddPrimaryConstructorQuickfix.kt create mode 100644 plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableAddSupertypeQuickfix.kt create mode 100644 plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableAddTransientAnnotationQuickfix.kt create mode 100644 plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableQuickFixContributor.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/constructorWithDelegate.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/constructorWithDelegate.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/noQuickFix.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/simple.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/simple.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/cantBeInnerClass/simple.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/cantBeInnerClass/simple.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/creatorField.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/creatorField.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/writeToParcel.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/writeToParcel.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/basic.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/basic.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/complexCase1.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/complexCase1.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/customDescribeContents.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/customDescribeContents.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/fromCreatorObject.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/fromCreatorObject.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/innerClassFactory.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/innerClassFactory.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/jvmField.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/jvmField.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/noWriteToParcel.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/noWriteToParcel.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/withoutDescribeContents.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/withoutDescribeContents.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype/alreadyHasSupertype.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype/simple.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype/simple.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/propertyWontBeSerialized/simple.after.kt create mode 100644 plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/propertyWontBeSerialized/simple.before.Main.kt create mode 100644 plugins/android-extensions/android-extensions-idea/tests/org/jetbrains/kotlin/android/AbstractParcelQuickFixTest.kt create mode 100644 plugins/android-extensions/android-extensions-idea/tests/org/jetbrains/kotlin/android/ParcelQuickFixTestGenerated.java diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/diagnosticUtils.kt b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/diagnosticUtils.kt index 11105950639..e2dc55a4b0b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/diagnosticUtils.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/diagnosticUtils.kt @@ -16,6 +16,9 @@ package org.jetbrains.kotlin.diagnostics +import com.intellij.mock.MockApplication +import com.intellij.openapi.application.Application +import com.intellij.openapi.application.ApplicationManager import com.intellij.psi.PsiElement import org.jetbrains.kotlin.builtins.isFunctionType import org.jetbrains.kotlin.descriptors.CallableDescriptor @@ -164,6 +167,10 @@ inline fun reportOnDeclarationAs(trace: BindingTrace } fun DiagnosticSink.reportFromPlugin(diagnostic: D, ext: DefaultErrorMessages.Extension) { + if (ApplicationManager.getApplication() !is MockApplication) { + return this.report(diagnostic) + } + @Suppress("UNCHECKED_CAST") val renderer = ext.map[diagnostic.factory] as? DiagnosticRenderer ?: error("Renderer not found for diagnostic ${diagnostic.factory.name}") diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUtil.kt index d94017e8205..13fe1844a4d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUtil.kt @@ -25,6 +25,8 @@ import org.jetbrains.kotlin.resolve.constants.ErrorValue private val JVM_STATIC_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmStatic") +val JVM_FIELD_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmField") + fun DeclarationDescriptor.hasJvmStaticAnnotation(): Boolean { return annotations.findAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME) != null } diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index d9c4e2898d5..1790e86d898 100755 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -1406,6 +1406,10 @@ fun main(args: Array) { testClass { model("android/parcel/checker", excludeParentDirs = true) } + + testClass { + model("android/parcel/quickfix", pattern = """^(\w+)\.((before\.Main\.\w+)|(test))$""", testMethod = "doTestWithExtraFile") + } } testGroup("idea/idea-android/tests", "idea/testData") { diff --git a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/DirectiveBasedActionUtils.kt b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/DirectiveBasedActionUtils.kt index 12f50447807..dca2e857150 100644 --- a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/DirectiveBasedActionUtils.kt +++ b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/DirectiveBasedActionUtils.kt @@ -49,6 +49,10 @@ object DirectiveBasedActionUtils { UsefulTestCase.assertEmpty("Irrelevant actions should not be specified in ACTION directive for they are not checked anyway", expectedActions.filter { isIrrelevantAction(it) }) + if (InTextDirectivesUtils.findLinesWithPrefixesRemoved(file.text, "// IGNORE_IRRELEVANT_ACTIONS").isNotEmpty()) { + return + } + val actualActions = availableActions.map { it.text }.sorted() UsefulTestCase.assertOrderedEquals("Some unexpected actions available at current position. Use // ACTION: directive", diff --git a/idea/src/META-INF/android.xml b/idea/src/META-INF/android.xml index b6a5e9931ee..b12f7681fc7 100644 --- a/idea/src/META-INF/android.xml +++ b/idea/src/META-INF/android.xml @@ -96,6 +96,7 @@ + diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/AbstractParcelableQuickFix.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/AbstractParcelableQuickFix.kt new file mode 100644 index 00000000000..70a84de0891 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/AbstractParcelableQuickFix.kt @@ -0,0 +1,53 @@ +/* + * 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.android.parcel.quickfixes + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.core.ShortenReferences +import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction +import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType + +abstract class AbstractParcelableQuickFix(element: T) : KotlinQuickFixAction(element) { + protected companion object { + fun T.shortenReferences() = ShortenReferences.DEFAULT.process(this) + } + + override fun getFamilyName() = text + + abstract fun invoke(ktPsiFactory: KtPsiFactory, element: T) + + final override fun invoke(project: Project, editor: Editor?, file: KtFile) { + val clazz = element ?: return + val ktPsiFactory = KtPsiFactory(project, markGenerated = true) + invoke(ktPsiFactory, clazz) + } + + abstract class AbstractFactory(private val f: Diagnostic.() -> IntentionAction?) : KotlinSingleIntentionActionFactory() { + companion object { + inline fun Diagnostic.findElement() = psiElement.getNonStrictParentOfType() + } + + override fun createAction(diagnostic: Diagnostic) = f(diagnostic) + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelMigrateToParcelizeQuickFix.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelMigrateToParcelizeQuickFix.kt new file mode 100644 index 00000000000..c62a3dc45be --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelMigrateToParcelizeQuickFix.kt @@ -0,0 +1,287 @@ +/* + * 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.android.parcel.quickfixes + +import com.intellij.openapi.diagnostic.Logger +import kotlinx.android.parcel.Parceler +import org.jetbrains.kotlin.android.parcel.ANDROID_PARCELABLE_CREATOR_CLASS_FQNAME +import org.jetbrains.kotlin.android.parcel.ANDROID_PARCEL_CLASS_FQNAME +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ConstructorDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.core.getOrCreateCompanionObject +import org.jetbrains.kotlin.idea.intentions.branchedTransformations.unwrapBlockOrParenthesis +import org.jetbrains.kotlin.idea.util.findAnnotation +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.addRemoveModifier.setModifierList +import org.jetbrains.kotlin.psi.psiUtil.containingClass +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.annotations.JVM_FIELD_ANNOTATION_FQ_NAME +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.source.KotlinSourceElement +import org.jetbrains.kotlin.types.TypeUtils + +class ParcelMigrateToParcelizeQuickFix(function: KtClass) : AbstractParcelableQuickFix(function) { + companion object { + private val PARCELER_FQNAME = FqName(Parceler::class.java.name) + private val PARCELER_WRITE_FUNCTION_NAME = Name.identifier("write") + private val PARCELER_CREATE_FUNCTION_NAME = Name.identifier("create") + private val LOG = Logger.getInstance(ParcelMigrateToParcelizeQuickFix::class.java) + + private fun KtClass.findParcelerCompanionObject(): Pair? { + for (obj in companionObjects) { + val bindingContext = obj.analyze(BodyResolveMode.PARTIAL) + val objDescriptor = bindingContext[BindingContext.CLASS, obj] ?: continue + for (superClassifier in objDescriptor.getAllSuperClassifiers()) { + val superClass = superClassifier as? ClassDescriptor ?: continue + if (superClass.fqNameSafe == PARCELER_FQNAME) return Pair(obj, objDescriptor) + } + } + + return null + } + + private fun KtNamedFunction.doesLookLikeWriteToParcelOverride(): Boolean { + return name == "writeToParcel" + && hasModifier(KtTokens.OVERRIDE_KEYWORD) + && receiverTypeReference == null + && valueParameters.size == 2 + && typeParameters.size == 0 + && valueParameters[0].typeReference?.getFqName() == ANDROID_PARCEL_CLASS_FQNAME.asString() + && valueParameters[1].typeReference?.getFqName() == KotlinBuiltIns.FQ_NAMES._int.asString() + } + + private fun KtNamedFunction.doesLookLikeNewArrayOverride(): Boolean { + return name == "newArray" + && hasModifier(KtTokens.OVERRIDE_KEYWORD) + && receiverTypeReference == null + && valueParameters.size == 1 + && typeParameters.size == 0 + && valueParameters[0].typeReference?.getFqName() == KotlinBuiltIns.FQ_NAMES._int.asString() + } + + private fun KtNamedFunction.doesLookLikeDescribeContentsOverride(): Boolean { + return name == "describeContents" + && hasModifier(KtTokens.OVERRIDE_KEYWORD) + && receiverTypeReference == null + && valueParameters.size == 0 + && typeParameters.size == 0 + && typeReference?.getFqName() == KotlinBuiltIns.FQ_NAMES._int.asString() + } + + private fun KtClass.findWriteToParcelOverride() = findFunction { doesLookLikeWriteToParcelOverride() } + private fun KtClass.findDescribeContentsOverride() = findFunction { doesLookLikeDescribeContentsOverride() } + private fun KtObjectDeclaration.findNewArrayOverride() = findFunction { doesLookLikeNewArrayOverride() } + + private fun KtClass.findCreatorClass(): KtClassOrObject? { + for (companion in companionObjects) { + if (companion.name == "CREATOR") { + return companion + } + + val creatorProperty = companion.declarations.asSequence() + .filterIsInstance() + .firstOrNull { it.name == "CREATOR" } + ?: continue + + creatorProperty.findAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) ?: continue + + val initializer = creatorProperty.initializer ?: continue + when (initializer) { + is KtObjectLiteralExpression -> return initializer.objectDeclaration + is KtCallExpression -> { + val constructedClass = (initializer.getResolvedCall(initializer.analyze(BodyResolveMode.PARTIAL)) + ?.resultingDescriptor as? ConstructorDescriptor)?.constructedClass + if (constructedClass != null) { + val sourceElement = constructedClass.source as? KotlinSourceElement + (sourceElement?.psi as? KtClassOrObject)?.let { return it } + } + } + } + } + + return null + } + + private fun KtNamedFunction.doesLookLikeCreateFromParcelOverride(): Boolean { + return name == "createFromParcel" + && hasModifier(KtTokens.OVERRIDE_KEYWORD) + && receiverTypeReference == null + && valueParameters.size == 1 + && typeParameters.size == 0 + && valueParameters[0].typeReference?.getFqName() == ANDROID_PARCEL_CLASS_FQNAME.asString() + } + + private fun findCreateFromParcel(creator: KtClassOrObject) = creator.findFunction { doesLookLikeCreateFromParcelOverride() } + + private fun KtNamedFunction.doesLookLikeWriteImplementation(): Boolean { + val containingParcelableClassFqName = containingClassOrObject?.containingClass()?.fqName?.asString() + + return name == PARCELER_WRITE_FUNCTION_NAME.asString() + && hasModifier(KtTokens.OVERRIDE_KEYWORD) + && receiverTypeReference?.getFqName() == containingParcelableClassFqName + && valueParameters.size == 2 + && typeParameters.size == 0 + && valueParameters[0].typeReference?.getFqName() == ANDROID_PARCEL_CLASS_FQNAME.asString() + && valueParameters[1].typeReference?.getFqName() == KotlinBuiltIns.FQ_NAMES._int.asString() + } + + private fun KtNamedFunction.doesLookLikeCreateImplementation(): Boolean { + return name == PARCELER_CREATE_FUNCTION_NAME.asString() + && hasModifier(KtTokens.OVERRIDE_KEYWORD) + && receiverTypeReference == null + && valueParameters.size == 1 + && typeParameters.size == 0 + && valueParameters[0].typeReference?.getFqName() == ANDROID_PARCEL_CLASS_FQNAME.asString() + } + + private fun KtObjectDeclaration.findCreateImplementation() = findFunction { doesLookLikeCreateImplementation() } + private fun KtObjectDeclaration.findWriteImplementation() = findFunction { doesLookLikeWriteImplementation() } + + private fun KtClassOrObject.findFunction(f: KtNamedFunction.() -> Boolean) + = declarations.asSequence().filterIsInstance().firstOrNull(f) + + private fun KtTypeReference.getFqName(): String? = analyze(BodyResolveMode.PARTIAL)[BindingContext.TYPE, this] + ?.constructor?.declarationDescriptor?.fqNameSafe?.asString() + } + + object FactoryForWrite : AbstractFactory({ findElement()?.let { ParcelMigrateToParcelizeQuickFix(it) } }) + + object FactoryForCREATOR : AbstractFactory({ + findElement()?.getStrictParentOfType()?.let { ParcelMigrateToParcelizeQuickFix(it) } + }) + + override fun getText() = "Migrate to ''Parceler'' companion object" + + @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") + override fun invoke(ktPsiFactory: KtPsiFactory, parcelableClass: KtClass) { + val parcelerObject = parcelableClass.findParcelerCompanionObject()?.first ?: parcelableClass.getOrCreateCompanionObject() + val bindingContext = parcelerObject.analyze(BodyResolveMode.PARTIAL) + + val parcelerTypeArg = parcelableClass.name ?: run { + LOG.error("Parceler class should not be an anonymous class") + return + } + + val parcelerObjectDescriptor = bindingContext[BindingContext.CLASS, parcelerObject] ?: run { + LOG.error("Unable to resolve parceler object for ${parcelableClass.name ?: ""}") + return + } + + if (!parcelerObjectDescriptor.getAllSuperClassifiers().any { it.fqNameSafe == PARCELER_FQNAME }) { + val entryText = PARCELER_FQNAME.asString() + "<" + parcelerTypeArg + ">" + parcelerObject.addSuperTypeListEntry(ktPsiFactory.createSuperTypeEntry(entryText)).shortenReferences() + } + + val oldWriteToParcelFunction = parcelableClass.findWriteToParcelOverride() + val oldCreateFromParcelFunction = parcelableClass.findCreatorClass()?.let { findCreateFromParcel(it) } + + for (superTypeEntry in parcelerObject.superTypeListEntries) { + val superClass = bindingContext[BindingContext.TYPE, superTypeEntry.typeReference]?.constructor?.declarationDescriptor ?: continue + if (superClass.getAllSuperClassifiers().any { it.fqNameSafe == ANDROID_PARCELABLE_CREATOR_CLASS_FQNAME }) { + parcelerObject.removeSuperTypeListEntry(superTypeEntry) + } + } + + if (parcelerObject.name == "CREATOR") { + parcelerObject.nameIdentifier?.delete() + } + + if (oldWriteToParcelFunction != null) { + parcelerObject.findWriteImplementation()?.delete() // Remove old implementation + + val newFunction = oldWriteToParcelFunction.copy() as KtFunction + oldWriteToParcelFunction.delete() + + newFunction.setName(PARCELER_WRITE_FUNCTION_NAME.asString()) + newFunction.setModifierList(ktPsiFactory.createModifierList(KtTokens.OVERRIDE_KEYWORD)) + newFunction.setReceiverTypeReference(ktPsiFactory.createType(parcelerTypeArg)) + newFunction.valueParameterList?.apply { + assert(parameters.size == 2) + val parcelParameterName = parameters[0].name ?: "parcel" + val flagsParameterName = parameters[1].name ?: "flags" + + repeat(parameters.size) { removeParameter(0) } + addParameter(ktPsiFactory.createParameter("$parcelParameterName : ${ANDROID_PARCEL_CLASS_FQNAME.asString()}")) + addParameter(ktPsiFactory.createParameter("$flagsParameterName : Int")) + } + + parcelerObject.addDeclaration(newFunction).valueParameterList?.shortenReferences() + } else if (parcelerObject.findWriteImplementation() == null) { + val writeFunction = "fun $parcelerTypeArg.write(parcel: ${ANDROID_PARCEL_CLASS_FQNAME.asString()}, flags: Int) = TODO()" + parcelerObject.addDeclaration(ktPsiFactory.createFunction(writeFunction)).valueParameterList?.shortenReferences() + } + + if (oldCreateFromParcelFunction != null) { + parcelerObject.findCreateImplementation()?.delete() // Remove old implementation + + val newFunction = oldCreateFromParcelFunction.copy() as KtFunction + if (oldCreateFromParcelFunction.containingClassOrObject == parcelerObject) { + oldCreateFromParcelFunction.delete() + } + + newFunction.setName(PARCELER_CREATE_FUNCTION_NAME.asString()) + newFunction.setModifierList(ktPsiFactory.createModifierList(KtTokens.OVERRIDE_KEYWORD)) + newFunction.setReceiverTypeReference(null) + newFunction.valueParameterList?.apply { + assert(parameters.size == 1) + val parcelParameterName = parameters[0].name ?: "parcel" + + removeParameter(0) + addParameter(ktPsiFactory.createParameter("$parcelParameterName : ${ANDROID_PARCEL_CLASS_FQNAME.asString()}")) + } + + parcelerObject.addDeclaration(newFunction).valueParameterList?.shortenReferences() + } else if (parcelerObject.findCreateImplementation() == null) { + val createFunction = "override fun create(parcel: ${ANDROID_PARCEL_CLASS_FQNAME.asString()}): $parcelerTypeArg = TODO()" + parcelerObject.addDeclaration(ktPsiFactory.createFunction(createFunction)).valueParameterList?.shortenReferences() + } + + // Always use the default newArray() implementation + parcelerObject.findNewArrayOverride()?.delete() + + parcelableClass.findDescribeContentsOverride()?.let { describeContentsFunction -> + val returnExpr = describeContentsFunction.bodyExpression?.unwrapBlockOrParenthesis() + if (returnExpr is KtReturnExpression && returnExpr.getTargetLabel() == null) { + val returnValue = returnExpr.analyze()[BindingContext.COMPILE_TIME_VALUE, returnExpr.returnedExpression] + ?.getValue(TypeUtils.NO_EXPECTED_TYPE) + if (returnValue == 0) { + // There are no additional overrides in the hierarchy + if (bindingContext[BindingContext.FUNCTION, describeContentsFunction]?.overriddenDescriptors?.size == 1) { + describeContentsFunction.delete() + } + } + } + } + + for (property in parcelerObject.declarations.asSequence().filterIsInstance().filter { it.name == "CREATOR" }) { + if (property.findAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) != null) { + property.delete() + } + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelRemoveCustomCreatorProperty.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelRemoveCustomCreatorProperty.kt new file mode 100644 index 00000000000..6336691256c --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelRemoveCustomCreatorProperty.kt @@ -0,0 +1,34 @@ +/* + * 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.android.parcel.quickfixes + +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtPsiFactory + +class ParcelRemoveCustomCreatorProperty(property: KtProperty) : AbstractParcelableQuickFix(property) { + object Factory : AbstractFactory(f@ { + // KtProperty or its name identifier + psiElement as? KtProperty ?: (psiElement.parent as? KtProperty) ?: return@f null + findElement()?.let(::ParcelRemoveCustomCreatorProperty) + }) + + override fun getText() = "Remove custom ''CREATOR'' property" + + override fun invoke(ktPsiFactory: KtPsiFactory, element: KtProperty) { + element.delete() + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelRemoveCustomWriteToParcel.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelRemoveCustomWriteToParcel.kt new file mode 100644 index 00000000000..8ebab691eea --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelRemoveCustomWriteToParcel.kt @@ -0,0 +1,29 @@ +/* + * 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.android.parcel.quickfixes + +import org.jetbrains.kotlin.psi.KtFunction +import org.jetbrains.kotlin.psi.KtPsiFactory + +class ParcelRemoveCustomWriteToParcel(function: KtFunction) : AbstractParcelableQuickFix(function) { + object Factory : AbstractFactory({ findElement()?.let(::ParcelRemoveCustomWriteToParcel) }) + override fun getText() = "Remove custom ''writeToParcel()'' function" + + override fun invoke(ktPsiFactory: KtPsiFactory, element: KtFunction) { + element.delete() + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableAddPrimaryConstructorQuickfix.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableAddPrimaryConstructorQuickfix.kt new file mode 100644 index 00000000000..dd8d38d1827 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableAddPrimaryConstructorQuickfix.kt @@ -0,0 +1,38 @@ +/* + * 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.android.parcel.quickfixes + +import org.jetbrains.kotlin.psi.KtClass +import org.jetbrains.kotlin.psi.KtPsiFactory +import org.jetbrains.kotlin.psi.createPrimaryConstructorIfAbsent + +class ParcelableAddPrimaryConstructorQuickfix(clazz: KtClass) : AbstractParcelableQuickFix(clazz) { + object Factory : AbstractFactory({ findElement()?.let(::ParcelableAddPrimaryConstructorQuickfix) }) + override fun getText() = "Add empty primary constructor" + + override fun invoke(ktPsiFactory: KtPsiFactory, element: KtClass) { + element.createPrimaryConstructorIfAbsent() + + for (secondaryConstructor in element.secondaryConstructors) { + if (secondaryConstructor.getDelegationCall().isImplicit) { + val parameterList = secondaryConstructor.valueParameterList ?: return + val colon = secondaryConstructor.addAfter(ktPsiFactory.createColon(), parameterList) + secondaryConstructor.addAfter(ktPsiFactory.createExpression("this()"), colon) + } + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableAddSupertypeQuickfix.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableAddSupertypeQuickfix.kt new file mode 100644 index 00000000000..d75b6745b29 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableAddSupertypeQuickfix.kt @@ -0,0 +1,29 @@ +/* + * 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.android.parcel.quickfixes + +import com.android.SdkConstants.CLASS_PARCELABLE +import org.jetbrains.kotlin.psi.* + +class ParcelableAddSupertypeQuickfix(clazz: KtClassOrObject) : AbstractParcelableQuickFix(clazz) { + object Factory : AbstractFactory({ findElement()?.let(::ParcelableAddSupertypeQuickfix) }) + override fun getText() = "Add ''Parcelable'' supertype" + + override fun invoke(ktPsiFactory: KtPsiFactory, element: KtClassOrObject) { + element.addSuperTypeListEntry(ktPsiFactory.createSuperTypeEntry(CLASS_PARCELABLE)).shortenReferences() + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableAddTransientAnnotationQuickfix.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableAddTransientAnnotationQuickfix.kt new file mode 100644 index 00000000000..6acb8982919 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableAddTransientAnnotationQuickfix.kt @@ -0,0 +1,29 @@ +/* + * 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.android.parcel.quickfixes + +import org.jetbrains.kotlin.psi.KtProperty +import org.jetbrains.kotlin.psi.KtPsiFactory + +class ParcelableAddTransientAnnotationQuickfix(property: KtProperty) : AbstractParcelableQuickFix(property) { + object Factory : AbstractFactory({ findElement()?.let(::ParcelableAddTransientAnnotationQuickfix) }) + override fun getText() = "Add ''@Transient'' annotation" + + override fun invoke(ktPsiFactory: KtPsiFactory, element: KtProperty) { + element.addAnnotationEntry(ktPsiFactory.createAnnotationEntry("@" + Transient::class.java.name)).shortenReferences() + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableQuickFixContributor.kt b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableQuickFixContributor.kt new file mode 100644 index 00000000000..d50ef697a26 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/src/org/jetbrains/kotlin/android/parcel/quickfixes/ParcelableQuickFixContributor.kt @@ -0,0 +1,40 @@ +/* + * 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.android.parcel.quickfixes + +import org.jetbrains.kotlin.android.synthetic.diagnostic.ErrorsAndroid +import org.jetbrains.kotlin.idea.quickfix.QuickFixContributor +import org.jetbrains.kotlin.idea.quickfix.QuickFixes +import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix +import org.jetbrains.kotlin.lexer.KtTokens + +class ParcelableQuickFixContributor : QuickFixContributor { + override fun registerQuickFixes(quickFixes: QuickFixes) { + quickFixes.register(ErrorsAndroid.PARCELABLE_CANT_BE_INNER_CLASS, + RemoveModifierFix.createRemoveModifierFromListOwnerFactory(KtTokens.INNER_KEYWORD, false)) + + quickFixes.register(ErrorsAndroid.NO_PARCELABLE_SUPERTYPE, ParcelableAddSupertypeQuickfix.Factory) + quickFixes.register(ErrorsAndroid.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR, ParcelableAddPrimaryConstructorQuickfix.Factory) + quickFixes.register(ErrorsAndroid.PROPERTY_WONT_BE_SERIALIZED, ParcelableAddTransientAnnotationQuickfix.Factory) + + quickFixes.register(ErrorsAndroid.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED, ParcelMigrateToParcelizeQuickFix.FactoryForWrite) + quickFixes.register(ErrorsAndroid.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED, ParcelRemoveCustomWriteToParcel.Factory) + + quickFixes.register(ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED, ParcelMigrateToParcelizeQuickFix.FactoryForCREATOR) + quickFixes.register(ErrorsAndroid.CREATOR_DEFINITION_IS_NOT_ALLOWED, ParcelRemoveCustomCreatorProperty.Factory) + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/constructors.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/constructors.kt index 757c667725d..a900a978953 100644 --- a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/constructors.kt +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/constructors.kt @@ -10,7 +10,7 @@ class A : Parcelable class B(val firstName: String, val secondName: String) : Parcelable @Parcelize -class C(val firstName: String, secondName: String) : Parcelable +class C(val firstName: String, secondName: String) : Parcelable @Parcelize class D(val firstName: String, vararg val secondName: String) : Parcelable @@ -21,7 +21,7 @@ class E(val firstName: String, val secondName: String) : Parcelable { } @Parcelize -class F : Parcelable { +class F : Parcelable { constructor(a: String) { println(a) } diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/customWriteToParcel.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/customWriteToParcel.kt index fcc34791881..73eaeacdf03 100644 --- a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/customWriteToParcel.kt +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/customWriteToParcel.kt @@ -6,16 +6,16 @@ import android.os.Parcel @Parcelize class A(val a: String) : Parcelable { - override fun writeToParcel(p: Parcel?, flags: Int) {} + override fun writeToParcel(p: Parcel?, flags: Int) {} override fun describeContents() = 0 } @Parcelize class B(val a: String) : Parcelable { - override fun writeToParcel(p: Parcel?, flags: Int) {} + override fun writeToParcel(p: Parcel?, flags: Int) {} } @Parcelize class C(val a: String) : Parcelable { - override fun writeToParcel(p: Parcel, flags: Int) {} + override fun writeToParcel(p: Parcel, flags: Int) {} } \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/delegate.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/delegate.kt index 2d3cb008e27..6dbb00a48ee 100644 --- a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/delegate.kt +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/delegate.kt @@ -10,4 +10,4 @@ open class Delegate : Parcelable { } @Parcelize -class Test : Parcelable by Delegate() \ No newline at end of file +class Test : Parcelable by Delegate() \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/emptyPrimaryConstructor.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/emptyPrimaryConstructor.kt index 7c605d3acd0..15049851d56 100644 --- a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/emptyPrimaryConstructor.kt +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/emptyPrimaryConstructor.kt @@ -7,4 +7,4 @@ import android.os.Parcelable class User : Parcelable @Parcelize -class User2() : Parcelable \ No newline at end of file +class User2() : Parcelable \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/kt20062.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/kt20062.kt index c1d1db3ee97..a5faa917c2a 100644 --- a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/kt20062.kt +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/kt20062.kt @@ -17,4 +17,4 @@ class Foo(val box: Box): Parcelable { } @Parcelize -class Foo2(val box: Box): Parcelable \ No newline at end of file +class Foo2(val box: Box): Parcelable \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/modality.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/modality.kt index d7797a59a04..43c46039215 100644 --- a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/modality.kt +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/modality.kt @@ -10,22 +10,22 @@ open class Open(val foo: String) : Parcelable class Final(val foo: String) : Parcelable @Parcelize -abstract class Abstract(val foo: String) : Parcelable +abstract class Abstract(val foo: String) : Parcelable @Parcelize -sealed class Sealed(val foo: String) : Parcelable { +sealed class Sealed(val foo: String) : Parcelable { class X : Sealed("") } class Outer { @Parcelize - inner class Inner(val foo: String) : Parcelable + inner class Inner(val foo: String) : Parcelable } fun foo() { @Parcelize - object : Parcelable {} + object : Parcelable {} @Parcelize - class Local {} + class Local {} } \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/properties.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/properties.kt index 09d70383c8a..9595db73195 100644 --- a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/properties.kt +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/properties.kt @@ -5,11 +5,11 @@ import android.os.Parcelable @Parcelize class A(val firstName: String) : Parcelable { - val secondName: String = "" + val secondName: String = "" - val delegated by lazy { "" } + val delegated by lazy { "" } - lateinit var lateinit: String + lateinit var lateinit: String val customGetter: String get() = "" diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/unsupportedType.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/unsupportedType.kt index 1f14479bf00..485c1ca2c9a 100644 --- a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/unsupportedType.kt +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/unsupportedType.kt @@ -7,9 +7,9 @@ import android.os.Parcelable @Parcelize class User( val a: String, - val b: Any, - val c: Any?, - val d: Map, + val b: Any, + val c: Any?, + val d: Map, val e: @RawValue Any?, val f: @RawValue Map, val g: Map, diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/withoutParcelableSupertype.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/withoutParcelableSupertype.kt index 9eed0adeb3a..98e3cdb416d 100644 --- a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/withoutParcelableSupertype.kt +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/withoutParcelableSupertype.kt @@ -4,7 +4,7 @@ import kotlinx.android.parcel.Parcelize import android.os.Parcelable @Parcelize -class Without(val firstName: String, val secondName: String, val age: Int) +class Without(val firstName: String, val secondName: String, val age: Int) @Parcelize class With(val firstName: String, val secondName: String, val age: Int) : Parcelable diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/wrongAnnotationTarget.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/wrongAnnotationTarget.kt index fadea294e3c..a37b8a62e15 100644 --- a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/wrongAnnotationTarget.kt +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/checker/wrongAnnotationTarget.kt @@ -4,22 +4,22 @@ import kotlinx.android.parcel.Parcelize import android.os.Parcelable @Parcelize -interface Intf : Parcelable +interface Intf : Parcelable @Parcelize -object Obj +object Obj class A { @Parcelize - companion object { + companion object { fun foo() {} } } @Parcelize -enum class Enum { +enum class Enum { WHITE, BLACK } @Parcelize -annotation class Anno \ No newline at end of file +annotation class Anno \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/constructorWithDelegate.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/constructorWithDelegate.after.kt new file mode 100644 index 00000000000..d7de8c333c2 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/constructorWithDelegate.after.kt @@ -0,0 +1,14 @@ +// "Add empty primary constructor" "true" +// ERROR: 'Parcelable' should have a primary constructor +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Test() : Parcelable { + constructor(s: String) : this() + constructor(s: String, i: Int) : this(s) +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/constructorWithDelegate.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/constructorWithDelegate.before.Main.kt new file mode 100644 index 00000000000..30ae2177b74 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/constructorWithDelegate.before.Main.kt @@ -0,0 +1,14 @@ +// "Add empty primary constructor" "true" +// ERROR: 'Parcelable' should have a primary constructor +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Test : Parcelable { + constructor(s: String) + constructor(s: String, i: Int) : this(s) +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/noQuickFix.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/noQuickFix.before.Main.kt new file mode 100644 index 00000000000..e3d1e479810 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/noQuickFix.before.Main.kt @@ -0,0 +1,11 @@ +// "Add empty primary constructor" "false" +// IGNORE_IRRELEVANT_ACTIONS +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Test() : Parcelable \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/simple.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/simple.after.kt new file mode 100644 index 00000000000..d936300b70b --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/simple.after.kt @@ -0,0 +1,13 @@ +// "Add empty primary constructor" "true" +// ERROR: 'Parcelable' should have a primary constructor +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Test() : Parcelable { + constructor(a: Int) : this() +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/simple.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/simple.before.Main.kt new file mode 100644 index 00000000000..c60277759dd --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/simple.before.Main.kt @@ -0,0 +1,13 @@ +// "Add empty primary constructor" "true" +// ERROR: 'Parcelable' should have a primary constructor +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Test : Parcelable { + constructor(a: Int) +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/cantBeInnerClass/simple.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/cantBeInnerClass/simple.after.kt new file mode 100644 index 00000000000..31e7ba028df --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/cantBeInnerClass/simple.after.kt @@ -0,0 +1,13 @@ +// "Remove 'inner' modifier" "true" +// ERROR: 'Parcelable' can't be an inner class +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +class Foo { + @Parcelize + class Bar : Parcelable +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/cantBeInnerClass/simple.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/cantBeInnerClass/simple.before.Main.kt new file mode 100644 index 00000000000..953d03bb3c2 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/cantBeInnerClass/simple.before.Main.kt @@ -0,0 +1,13 @@ +// "Remove 'inner' modifier" "true" +// ERROR: 'Parcelable' can't be an inner class +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +class Foo { + @Parcelize + inner class Bar : Parcelable +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/creatorField.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/creatorField.after.kt new file mode 100644 index 00000000000..5f6b62b449b --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/creatorField.after.kt @@ -0,0 +1,24 @@ +// "Remove custom ''CREATOR'' property" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val a: String) : Parcelable { + constructor(parcel: Parcel) : this(parcel.readString()) { + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(a) + } + + override fun describeContents(): Int { + return 0 + } + +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/creatorField.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/creatorField.before.Main.kt new file mode 100644 index 00000000000..4372fd86f18 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/creatorField.before.Main.kt @@ -0,0 +1,37 @@ +// "Remove custom ''CREATOR'' property" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val a: String) : Parcelable { + constructor(parcel: Parcel) : this(parcel.readString()) { + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(a) + } + + override fun describeContents(): Int { + return 0 + } + + companion object { + @JvmField + val CREATOR = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Foo { + return Foo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } + +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/writeToParcel.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/writeToParcel.after.kt new file mode 100644 index 00000000000..84731737ae3 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/writeToParcel.after.kt @@ -0,0 +1,30 @@ +// "Remove custom ''writeToParcel()'' function" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val a: String) : Parcelable { + constructor(parcel: Parcel) : this(parcel.readString()) { + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Foo { + return Foo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/writeToParcel.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/writeToParcel.before.Main.kt new file mode 100644 index 00000000000..6b901fb325f --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/writeToParcel.before.Main.kt @@ -0,0 +1,34 @@ +// "Remove custom ''writeToParcel()'' function" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val a: String) : Parcelable { + constructor(parcel: Parcel) : this(parcel.readString()) { + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(a) + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Foo { + return Foo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/basic.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/basic.after.kt new file mode 100644 index 00000000000..952a8335a07 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/basic.after.kt @@ -0,0 +1,30 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parceler +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this( + parcel.readString(), + parcel.readInt()) { + } + + companion object : Parceler { + + override fun Foo.write(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeInt(age) + } + + override fun create(parcel: Parcel): Foo { + return Foo(parcel) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/basic.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/basic.before.Main.kt new file mode 100644 index 00000000000..6012da570fc --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/basic.before.Main.kt @@ -0,0 +1,36 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this( + parcel.readString(), + parcel.readInt()) { + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeInt(age) + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Foo { + return Foo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/complexCase1.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/complexCase1.after.kt new file mode 100644 index 00000000000..ae15c55547b --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/complexCase1.after.kt @@ -0,0 +1,34 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parceler +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this(parcel.readString(), parcel.readInt()) + + companion object : Parceler { + override fun Foo.write(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeInt(age) + } + + override fun create(parcel: Parcel): Foo = TODO() + } + + private abstract class Creator : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Foo { + return Foo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/complexCase1.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/complexCase1.before.Main.kt new file mode 100644 index 00000000000..714aad86b57 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/complexCase1.before.Main.kt @@ -0,0 +1,34 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this(parcel.readString(), parcel.readInt()) + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeInt(age) + } + + companion object { + @JvmField + val CREATOR: Parcelable.Creator = object : Creator() {} + } + + private abstract class Creator : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Foo { + return Foo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/customDescribeContents.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/customDescribeContents.after.kt new file mode 100644 index 00000000000..e7dbbe42aa7 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/customDescribeContents.after.kt @@ -0,0 +1,34 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parceler +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this( + parcel.readString(), + parcel.readInt()) { + } + + override fun describeContents(): Int { + return 50 + } + + companion object : Parceler { + + override fun Foo.write(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeInt(age) + } + + override fun create(parcel: Parcel): Foo { + return Foo(parcel) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/customDescribeContents.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/customDescribeContents.before.Main.kt new file mode 100644 index 00000000000..2590a16e6f2 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/customDescribeContents.before.Main.kt @@ -0,0 +1,36 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this( + parcel.readString(), + parcel.readInt()) { + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeInt(age) + } + + override fun describeContents(): Int { + return 50 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Foo { + return Foo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/fromCreatorObject.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/fromCreatorObject.after.kt new file mode 100644 index 00000000000..c1ad1e0ff31 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/fromCreatorObject.after.kt @@ -0,0 +1,30 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parceler +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this( + parcel.readString(), + parcel.readInt()) { + } + + companion object : Parceler { + + override fun Foo.write(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeInt(age) + } + + override fun create(parcel: Parcel): Foo { + return Foo(parcel) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/fromCreatorObject.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/fromCreatorObject.before.Main.kt new file mode 100644 index 00000000000..59b99480ed3 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/fromCreatorObject.before.Main.kt @@ -0,0 +1,32 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this( + parcel.readString(), + parcel.readInt()) { + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeInt(age) + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Foo { + return Foo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/innerClassFactory.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/innerClassFactory.after.kt new file mode 100644 index 00000000000..bdeb250f35f --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/innerClassFactory.after.kt @@ -0,0 +1,36 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parceler +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this(parcel.readString(), parcel.readInt()) + + companion object : Parceler { + override fun Foo.write(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeInt(age) + } + + override fun create(parcel: Parcel): Foo { + return Foo(parcel) + } + } + + private class Creator : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Foo { + return Foo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/innerClassFactory.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/innerClassFactory.before.Main.kt new file mode 100644 index 00000000000..802642504a2 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/innerClassFactory.before.Main.kt @@ -0,0 +1,34 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this(parcel.readString(), parcel.readInt()) + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeInt(age) + } + + companion object { + @JvmField + val CREATOR: Parcelable.Creator = Creator() + } + + private class Creator : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Foo { + return Foo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/jvmField.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/jvmField.after.kt new file mode 100644 index 00000000000..97e28d2bcf0 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/jvmField.after.kt @@ -0,0 +1,26 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parceler +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this(parcel.readString(), parcel.readInt()) + + companion object : Parceler { + override fun Foo.write(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeInt(age) + } + + override fun create(parcel: Parcel): Foo { + return Foo(parcel) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/jvmField.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/jvmField.before.Main.kt new file mode 100644 index 00000000000..270e48066fd --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/jvmField.before.Main.kt @@ -0,0 +1,32 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this(parcel.readString(), parcel.readInt()) + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeInt(age) + } + + companion object { + @JvmField + val CREATOR = object : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Foo { + return Foo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/noWriteToParcel.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/noWriteToParcel.after.kt new file mode 100644 index 00000000000..d2fa90fe7a0 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/noWriteToParcel.after.kt @@ -0,0 +1,25 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parceler +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this( + parcel.readString(), + parcel.readInt()) { + } + + companion object : Parceler { + + fun Foo.write(parcel: Parcel, flags: Int) = TODO() + override fun create(parcel: Parcel): Foo { + return Foo(parcel) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/noWriteToParcel.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/noWriteToParcel.before.Main.kt new file mode 100644 index 00000000000..83579295566 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/noWriteToParcel.before.Main.kt @@ -0,0 +1,30 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this( + parcel.readString(), + parcel.readInt()) { + } + + override fun describeContents(): Int { + return 0 + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Foo { + return Foo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/withoutDescribeContents.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/withoutDescribeContents.after.kt new file mode 100644 index 00000000000..952a8335a07 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/withoutDescribeContents.after.kt @@ -0,0 +1,30 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parceler +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this( + parcel.readString(), + parcel.readInt()) { + } + + companion object : Parceler { + + override fun Foo.write(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeInt(age) + } + + override fun create(parcel: Parcel): Foo { + return Foo(parcel) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/withoutDescribeContents.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/withoutDescribeContents.before.Main.kt new file mode 100644 index 00000000000..0e972b46d5e --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/withoutDescribeContents.before.Main.kt @@ -0,0 +1,32 @@ +// "Migrate to ''Parceler'' companion object" "true" +// ERROR: 'CREATOR' definition is not allowed. Use 'Parceler' companion object instead. +// ERROR: Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead. +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.* +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Foo(val firstName: String, val age: Int) : Parcelable { + constructor(parcel: Parcel) : this( + parcel.readString(), + parcel.readInt()) { + } + + override fun writeToParcel(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeInt(age) + } + + companion object CREATOR : Parcelable.Creator { + override fun createFromParcel(parcel: Parcel): Foo { + return Foo(parcel) + } + + override fun newArray(size: Int): Array { + return arrayOfNulls(size) + } + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype/alreadyHasSupertype.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype/alreadyHasSupertype.before.Main.kt new file mode 100644 index 00000000000..08d9e9c1076 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype/alreadyHasSupertype.before.Main.kt @@ -0,0 +1,13 @@ +// "Add ''Parcelable'' supertype" "false" +// IGNORE_IRRELEVANT_ACTIONS +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +abstract class AbstractParcelable : Parcelable + +@Parcelize +class Test(val s: String) : AbstractParcelable() \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype/simple.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype/simple.after.kt new file mode 100644 index 00000000000..54b32f5f394 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype/simple.after.kt @@ -0,0 +1,11 @@ +// "Add ''Parcelable'' supertype" "true" +// ERROR: No 'Parcelable' supertype +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Test(val s: String) : Parcelable \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype/simple.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype/simple.before.Main.kt new file mode 100644 index 00000000000..e3fb683c0a9 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype/simple.before.Main.kt @@ -0,0 +1,11 @@ +// "Add ''Parcelable'' supertype" "true" +// ERROR: No 'Parcelable' supertype +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Test(val s: String) \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/propertyWontBeSerialized/simple.after.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/propertyWontBeSerialized/simple.after.kt new file mode 100644 index 00000000000..dbb8ad5360f --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/propertyWontBeSerialized/simple.after.kt @@ -0,0 +1,13 @@ +// "Add ''@Transient'' annotation" "true" +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Test : Parcelable { + @Transient + val a = 5 +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/propertyWontBeSerialized/simple.before.Main.kt b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/propertyWontBeSerialized/simple.before.Main.kt new file mode 100644 index 00000000000..c13552aba64 --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/propertyWontBeSerialized/simple.before.Main.kt @@ -0,0 +1,12 @@ +// "Add ''@Transient'' annotation" "true" +// WITH_RUNTIME + +package com.myapp.activity + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +@Parcelize +class Test : Parcelable { + val a = 5 +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/tests/org/jetbrains/kotlin/android/AbstractParcelQuickFixTest.kt b/plugins/android-extensions/android-extensions-idea/tests/org/jetbrains/kotlin/android/AbstractParcelQuickFixTest.kt new file mode 100644 index 00000000000..9fcfab8a47b --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/tests/org/jetbrains/kotlin/android/AbstractParcelQuickFixTest.kt @@ -0,0 +1,39 @@ +/* + * 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.android + +import org.jetbrains.kotlin.android.quickfix.AbstractAndroidQuickFixMultiFileTest +import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil +import java.io.File + +open class AbstractParcelQuickFixTest : AbstractAndroidQuickFixMultiFileTest() { + override fun setUp() { + super.setUp() + + val androidJarDir = File("dependencies/androidSDK/platforms").listFiles().first { it.name.startsWith("android-") } + ConfigLibraryUtil.addLibrary(myModule, "androidJar", androidJarDir.absolutePath, arrayOf("android.jar")) + + ConfigLibraryUtil.addLibrary(myModule, "androidExtensionsRuntime", "dist/kotlinc/lib", arrayOf("android-extensions-runtime.jar")) + } + + override fun tearDown() { + ConfigLibraryUtil.removeLibrary(myModule, "androidJar") + ConfigLibraryUtil.removeLibrary(myModule, "androidExtensionsRuntime") + + super.tearDown() + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-idea/tests/org/jetbrains/kotlin/android/ParcelQuickFixTestGenerated.java b/plugins/android-extensions/android-extensions-idea/tests/org/jetbrains/kotlin/android/ParcelQuickFixTestGenerated.java new file mode 100644 index 00000000000..6488a15ccac --- /dev/null +++ b/plugins/android-extensions/android-extensions-idea/tests/org/jetbrains/kotlin/android/ParcelQuickFixTestGenerated.java @@ -0,0 +1,194 @@ +/* + * 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.android; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class ParcelQuickFixTestGenerated extends AbstractParcelQuickFixTest { + public void testAllFilesPresentInQuickfix() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + + @TestMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class AddPrimaryConstructor extends AbstractParcelQuickFixTest { + public void testAllFilesPresentInAddPrimaryConstructor() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + + @TestMetadata("constructorWithDelegate.before.Main.kt") + public void testConstructorWithDelegate() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/constructorWithDelegate.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("noQuickFix.before.Main.kt") + public void testNoQuickFix() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/noQuickFix.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("simple.before.Main.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/addPrimaryConstructor/simple.before.Main.kt"); + doTestWithExtraFile(fileName); + } + } + + @TestMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/cantBeInnerClass") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class CantBeInnerClass extends AbstractParcelQuickFixTest { + public void testAllFilesPresentInCantBeInnerClass() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/cantBeInnerClass"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + + @TestMetadata("simple.before.Main.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/cantBeInnerClass/simple.before.Main.kt"); + doTestWithExtraFile(fileName); + } + } + + @TestMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class DeleteIncompatible extends AbstractParcelQuickFixTest { + public void testAllFilesPresentInDeleteIncompatible() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + + @TestMetadata("creatorField.before.Main.kt") + public void testCreatorField() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/creatorField.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("writeToParcel.before.Main.kt") + public void testWriteToParcel() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/deleteIncompatible/writeToParcel.before.Main.kt"); + doTestWithExtraFile(fileName); + } + } + + @TestMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Migrations extends AbstractParcelQuickFixTest { + public void testAllFilesPresentInMigrations() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + + @TestMetadata("basic.before.Main.kt") + public void testBasic() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/basic.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("complexCase1.before.Main.kt") + public void testComplexCase1() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/complexCase1.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("customDescribeContents.before.Main.kt") + public void testCustomDescribeContents() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/customDescribeContents.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("fromCreatorObject.before.Main.kt") + public void testFromCreatorObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/fromCreatorObject.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("innerClassFactory.before.Main.kt") + public void testInnerClassFactory() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/innerClassFactory.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("jvmField.before.Main.kt") + public void testJvmField() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/jvmField.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("noWriteToParcel.before.Main.kt") + public void testNoWriteToParcel() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/noWriteToParcel.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("withoutDescribeContents.before.Main.kt") + public void testWithoutDescribeContents() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/migrations/withoutDescribeContents.before.Main.kt"); + doTestWithExtraFile(fileName); + } + } + + @TestMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class NoParcelableSupertype extends AbstractParcelQuickFixTest { + public void testAllFilesPresentInNoParcelableSupertype() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + + @TestMetadata("alreadyHasSupertype.before.Main.kt") + public void testAlreadyHasSupertype() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype/alreadyHasSupertype.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("simple.before.Main.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/noParcelableSupertype/simple.before.Main.kt"); + doTestWithExtraFile(fileName); + } + } + + @TestMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/propertyWontBeSerialized") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class PropertyWontBeSerialized extends AbstractParcelQuickFixTest { + public void testAllFilesPresentInPropertyWontBeSerialized() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/propertyWontBeSerialized"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + + @TestMetadata("simple.before.Main.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-idea/testData/android/parcel/quickfix/propertyWontBeSerialized/simple.before.Main.kt"); + doTestWithExtraFile(fileName); + } + } +} diff --git a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ParcelableQuickFix.kt b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ParcelableQuickFix.kt index 527bf641dae..e938134332f 100644 --- a/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ParcelableQuickFix.kt +++ b/plugins/lint/lint-idea/src/org/jetbrains/android/inspections/klint/ParcelableQuickFix.kt @@ -21,6 +21,7 @@ import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.android.util.AndroidBundle import org.jetbrains.kotlin.android.canAddParcelable import org.jetbrains.kotlin.android.implementParcelable +import org.jetbrains.kotlin.android.isParcelize import org.jetbrains.kotlin.psi.KtClass @@ -29,8 +30,10 @@ class ParcelableQuickFix : AndroidLintQuickFix { startElement.getTargetClass()?.implementParcelable() } - override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean = - startElement.getTargetClass()?.canAddParcelable() ?: false + override fun isApplicable(startElement: PsiElement, endElement: PsiElement, contextType: AndroidQuickfixContexts.ContextType): Boolean { + val targetClass = startElement.getTargetClass() ?: return false + return targetClass.canAddParcelable() && !targetClass.isParcelize() + } override fun getName(): String = AndroidBundle.message("implement.parcelable.intention.text")