Add 'Incorrect Transient' diagnostic
when users write @Transient, they get @kotlin.jvm.Transient, because it's always in auto-import. This annotation does not work with @Serializable classes; @kotlinx.serialization.Transient should be used instead. Add quickfix for 'incorrect transient' diagnostic: 'Import kotlinx.serialization.Transient'
This commit is contained in:
@@ -5,5 +5,6 @@
|
|||||||
<jsSyntheticTranslateExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEJsExtension"/>
|
<jsSyntheticTranslateExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEJsExtension"/>
|
||||||
<irGenerationExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEIrExtension"/>
|
<irGenerationExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEIrExtension"/>
|
||||||
<storageComponentContainerContributor implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEContainerContributor"/>
|
<storageComponentContainerContributor implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEContainerContributor"/>
|
||||||
|
<quickFixContributor implementation="org.jetbrains.kotlinx.serialization.idea.quickfixes.SerializationQuickFixContributor"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
|
|||||||
+3
-1
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -32,6 +32,8 @@ public interface SerializationErrors {
|
|||||||
|
|
||||||
DiagnosticFactory0<PsiElement> TRANSIENT_IS_REDUNDANT = DiagnosticFactory0.create(WARNING);
|
DiagnosticFactory0<PsiElement> TRANSIENT_IS_REDUNDANT = DiagnosticFactory0.create(WARNING);
|
||||||
|
|
||||||
|
DiagnosticFactory0<PsiElement> INCORRECT_TRANSIENT = DiagnosticFactory0.create(WARNING);
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
@SuppressWarnings("UnusedDeclaration")
|
||||||
Object _initializer = new Object() {
|
Object _initializer = new Object() {
|
||||||
{
|
{
|
||||||
|
|||||||
+22
-1
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -14,10 +14,14 @@ import org.jetbrains.kotlin.psi.*
|
|||||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
|
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
|
||||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
|
||||||
import org.jetbrains.kotlin.resolve.hasBackingField
|
import org.jetbrains.kotlin.resolve.hasBackingField
|
||||||
import org.jetbrains.kotlin.resolve.isInlineClassType
|
import org.jetbrains.kotlin.resolve.isInlineClassType
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.annotations.TRANSIENT_ANNOTATION_FQ_NAME
|
||||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
|
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
|
||||||
|
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||||
import org.jetbrains.kotlin.util.slicedMap.Slices
|
import org.jetbrains.kotlin.util.slicedMap.Slices
|
||||||
@@ -37,10 +41,27 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
|||||||
if (!canBeSerializedInternally(descriptor, declaration, context.trace)) return
|
if (!canBeSerializedInternally(descriptor, declaration, context.trace)) return
|
||||||
if (declaration !is KtPureClassOrObject) return
|
if (declaration !is KtPureClassOrObject) return
|
||||||
val props = buildSerializableProperties(descriptor, context.trace) ?: return
|
val props = buildSerializableProperties(descriptor, context.trace) ?: return
|
||||||
|
checkCorrectTransientAnnotationIsUsed(descriptor, props.serializableProperties, context.trace)
|
||||||
checkTransients(declaration, context.trace)
|
checkTransients(declaration, context.trace)
|
||||||
analyzePropertiesSerializers(context.trace, descriptor, props.serializableProperties)
|
analyzePropertiesSerializers(context.trace, descriptor, props.serializableProperties)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkCorrectTransientAnnotationIsUsed(
|
||||||
|
descriptor: ClassDescriptor,
|
||||||
|
properties: List<SerializableProperty>,
|
||||||
|
trace: BindingTrace
|
||||||
|
) {
|
||||||
|
if (descriptor.getSuperInterfaces().any { it.fqNameSafe.asString() == "java.io.Serializable" }) return // do not check
|
||||||
|
for (prop in properties) {
|
||||||
|
if (prop.transient) continue // correct annotation is used
|
||||||
|
val incorrectTransient = prop.descriptor.backingField?.annotations?.findAnnotation(TRANSIENT_ANNOTATION_FQ_NAME)
|
||||||
|
if (incorrectTransient != null) {
|
||||||
|
val elementToReport = incorrectTransient.source.getPsi() ?: prop.descriptor.findPsi() ?: continue
|
||||||
|
trace.report(SerializationErrors.INCORRECT_TRANSIENT.on(elementToReport))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun canBeSerializedInternally(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace): Boolean {
|
private fun canBeSerializedInternally(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace): Boolean {
|
||||||
if (descriptor.isSerializableEnumWithMissingSerializer()) {
|
if (descriptor.isSerializableEnumWithMissingSerializer()) {
|
||||||
val declarationToReport = declaration.modifierList ?: declaration
|
val declarationToReport = declaration.modifierList ?: declaration
|
||||||
|
|||||||
+4
@@ -65,5 +65,9 @@ object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension {
|
|||||||
SerializationErrors.TRANSIENT_IS_REDUNDANT,
|
SerializationErrors.TRANSIENT_IS_REDUNDANT,
|
||||||
"Property does not have backing field which makes it non-serializable and therefore @Transient is redundant"
|
"Property does not have backing field which makes it non-serializable and therefore @Transient is redundant"
|
||||||
)
|
)
|
||||||
|
MAP.put(
|
||||||
|
SerializationErrors.INCORRECT_TRANSIENT,
|
||||||
|
"@kotlin.jvm.Transient does not affect @Serializable classes. Please use @kotlinx.serialization.Transient instead."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -23,7 +23,7 @@ object SerializationAnnotations {
|
|||||||
val serializerAnnotationFqName = FqName("kotlinx.serialization.Serializer")
|
val serializerAnnotationFqName = FqName("kotlinx.serialization.Serializer")
|
||||||
internal val serialNameAnnotationFqName = FqName("kotlinx.serialization.SerialName")
|
internal val serialNameAnnotationFqName = FqName("kotlinx.serialization.SerialName")
|
||||||
internal val requiredAnnotationFqName = FqName("kotlinx.serialization.Required")
|
internal val requiredAnnotationFqName = FqName("kotlinx.serialization.Required")
|
||||||
internal val serialTransientFqName = FqName("kotlinx.serialization.Transient")
|
val serialTransientFqName = FqName("kotlinx.serialization.Transient")
|
||||||
internal val serialInfoFqName = FqName("kotlinx.serialization.SerialInfo")
|
internal val serialInfoFqName = FqName("kotlinx.serialization.SerialInfo")
|
||||||
|
|
||||||
internal val contextualFqName = FqName("kotlinx.serialization.ContextualSerialization") // this one is deprecated
|
internal val contextualFqName = FqName("kotlinx.serialization.ContextualSerialization") // this one is deprecated
|
||||||
|
|||||||
+13
-1
@@ -25,7 +25,9 @@ public class SerializationPluginDiagnosticTestGenerated extends AbstractSerializ
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testAllFilesPresentInDiagnostics() throws Exception {
|
public void testAllFilesPresentInDiagnostics() throws Exception {
|
||||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File(
|
||||||
|
"plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics"), Pattern.compile("^(.+)\\.kt$"), null,
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("DuplicateSerialName.kt")
|
@TestMetadata("DuplicateSerialName.kt")
|
||||||
@@ -33,6 +35,16 @@ public class SerializationPluginDiagnosticTestGenerated extends AbstractSerializ
|
|||||||
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/DuplicateSerialName.kt");
|
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/DuplicateSerialName.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IncorrectTransient.kt")
|
||||||
|
public void testIncorrectTransient() throws Exception {
|
||||||
|
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/IncorrectTransient.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("IncorrectTransient2.kt")
|
||||||
|
public void testIncorrectTransient2() throws Exception {
|
||||||
|
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/IncorrectTransient2.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("LazyRecursionBug.kt")
|
@TestMetadata("LazyRecursionBug.kt")
|
||||||
public void testLazyRecursionBug() throws Exception {
|
public void testLazyRecursionBug() throws Exception {
|
||||||
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/LazyRecursionBug.kt");
|
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/LazyRecursionBug.kt");
|
||||||
|
|||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// SKIP_TXT
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import java.io.Serializable as JavaSerializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Data(val x: Int, <!INCORRECT_TRANSIENT!>@Transient<!> val y: String)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Data2(val x: Int, @Transient val y: String) : JavaSerializable
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
// !DIAGNOSTICS: -UNUSED_PARAMETER,-UNUSED_VARIABLE
|
||||||
|
// WITH_RUNTIME
|
||||||
|
// SKIP_TXT
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.Transient
|
||||||
|
import java.io.Serializable as JavaSerializable
|
||||||
|
import kotlin.jvm.Transient as JavaTransient
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Data(val x: Int, @Transient val y: String = "a")
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Data2(val x: Int, @Transient val y: String = "a") : JavaSerializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Data3(val x: Int, @Transient @JavaTransient val y: String = "a") : JavaSerializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Data4(val x: Int, <!INCORRECT_TRANSIENT!>@JavaTransient<!> val y: String)
|
||||||
@@ -15,6 +15,9 @@ dependencies {
|
|||||||
compile(project(":idea:idea-maven"))
|
compile(project(":idea:idea-maven"))
|
||||||
compile(project(":plugins:annotation-based-compiler-plugins-ide-support"))
|
compile(project(":plugins:annotation-based-compiler-plugins-ide-support"))
|
||||||
compileOnly(intellijDep())
|
compileOnly(intellijDep())
|
||||||
|
Platform[192].orHigher {
|
||||||
|
compileOnly(intellijPluginDep("java"))
|
||||||
|
}
|
||||||
excludeInAndroidStudio(rootProject) { compileOnly(intellijPluginDep("maven")) }
|
excludeInAndroidStudio(rootProject) { compileOnly(intellijPluginDep("maven")) }
|
||||||
compileOnly(intellijPluginDep("gradle"))
|
compileOnly(intellijPluginDep("gradle"))
|
||||||
|
|
||||||
@@ -30,5 +33,5 @@ sourceSets {
|
|||||||
runtimeJar()
|
runtimeJar()
|
||||||
|
|
||||||
projectTest(parallel = true) {
|
projectTest(parallel = true) {
|
||||||
|
|
||||||
}
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlinx.serialization.idea.quickfixes
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
|
||||||
|
import org.jetbrains.kotlin.idea.util.ImportInsertHelperImpl
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SerializationErrors
|
||||||
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
|
||||||
|
|
||||||
|
internal class AddKotlinxSerializationTransientImportQuickFix(expression: PsiElement) :
|
||||||
|
KotlinQuickFixAction<PsiElement>(expression) {
|
||||||
|
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||||
|
// Do not use Helper itself because it does not insert import on conflict;
|
||||||
|
// so it will always fail because k.jvm.Transient is always in auto-import
|
||||||
|
ImportInsertHelperImpl.addImport(project, file, SerializationAnnotations.serialTransientFqName, allUnder = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFamilyName(): String = text
|
||||||
|
|
||||||
|
override fun getText(): String = "Import ${SerializationAnnotations.serialTransientFqName}"
|
||||||
|
|
||||||
|
object Factory : KotlinSingleIntentionActionFactory() {
|
||||||
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
|
if (diagnostic.factory != SerializationErrors.INCORRECT_TRANSIENT) return null
|
||||||
|
val castedDiagnostic = SerializationErrors.INCORRECT_TRANSIENT.cast(diagnostic)
|
||||||
|
|
||||||
|
val element = castedDiagnostic.psiElement
|
||||||
|
|
||||||
|
return AddKotlinxSerializationTransientImportQuickFix(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlinx.serialization.idea.quickfixes
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.QuickFixContributor
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.QuickFixes
|
||||||
|
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SerializationErrors
|
||||||
|
|
||||||
|
class SerializationQuickFixContributor : QuickFixContributor {
|
||||||
|
override fun registerQuickFixes(quickFixes: QuickFixes) {
|
||||||
|
quickFixes.register(SerializationErrors.INCORRECT_TRANSIENT, AddKotlinxSerializationTransientImportQuickFix.Factory)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user