Compare commits

...

3 Commits

Author SHA1 Message Date
Leonid Startsev
dfbcde5b93 Add quickfix for 'incorrect transient' diagnostic:
Import kotlinx.serialization.Transient
2020-07-07 01:16:58 +03:00
Leonid Startsev
016c45dcdf Add 'Incorrect Transient' diagnostic 2020-07-07 01:16:58 +03:00
Leonid Startsev
7433638d33 Split ContextualSerialization annotation in two 2020-07-07 01:16:57 +03:00
13 changed files with 153 additions and 23 deletions

View File

@@ -5,5 +5,6 @@
<jsSyntheticTranslateExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEJsExtension"/>
<irGenerationExtension implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEIrExtension"/>
<storageComponentContainerContributor implementation="org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationIDEContainerContributor"/>
<quickFixContributor implementation="org.jetbrains.kotlinx.serialization.idea.quickfixes.SerializationQuickFixContributor"/>
</extensions>
</idea-plugin>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 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.
*/
@@ -38,6 +38,11 @@ abstract class AbstractSerialGenerator(val bindingContext: BindingContext, val c
getKClassListFromFileAnnotation(
SerializationAnnotations.contextualFqName,
currentDeclaration
).plus(
getKClassListFromFileAnnotation(
SerializationAnnotations.contextualOnFileFqName,
currentDeclaration
)
).toSet()
}

View File

@@ -1,17 +1,6 @@
/*
* 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.
* 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.compiler.backend.common
@@ -118,7 +107,7 @@ fun analyzeSpecialSerializers(
moduleDescriptor: ModuleDescriptor,
annotations: Annotations
): ClassDescriptor? = when {
annotations.hasAnnotation(SerializationAnnotations.contextualFqName) ->
annotations.hasAnnotation(SerializationAnnotations.contextualFqName) || annotations.hasAnnotation(SerializationAnnotations.contextualOnPropertyFqName) ->
moduleDescriptor.getClassFromSerializationPackage(SpecialBuiltins.contextSerializer)
// can be annotation on type usage, e.g. List<@Polymorphic Any>
annotations.hasAnnotation(SerializationAnnotations.polymorphicFqName) ->
@@ -147,7 +136,7 @@ fun AbstractSerialGenerator.findTypeSerializerOrContext(
if (kType.isTypeParameter()) return null
return findTypeSerializerOrContextUnchecked(module, kType) ?: throw CompilationException(
"Serializer for element of type $kType has not been found.\n" +
"To use context serializer as fallback, explicitly annotate element with @ContextualSerialization",
"To use context serializer as fallback, explicitly annotate element with @Contextual",
null,
sourceElement
)

View File

@@ -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.
*/
@@ -32,6 +32,8 @@ public interface SerializationErrors {
DiagnosticFactory0<PsiElement> TRANSIENT_IS_REDUNDANT = DiagnosticFactory0.create(WARNING);
DiagnosticFactory0<PsiElement> INCORRECT_TRANSIENT = DiagnosticFactory0.create(WARNING);
@SuppressWarnings("UnusedDeclaration")
Object _initializer = new Object() {
{

View File

@@ -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.
*/
@@ -10,13 +10,17 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
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.getSuperInterfaces
import org.jetbrains.kotlin.resolve.hasBackingField
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.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.supertypes
@@ -37,10 +41,28 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
if (!canBeSerializedInternally(descriptor, declaration, context.trace)) return
if (declaration !is KtPureClassOrObject) return
val props = buildSerializableProperties(descriptor, context.trace) ?: return
checkCorrectTransientAnnotationIsUsed(descriptor, props.serializableProperties, context.trace)
checkTransients(declaration, context.trace)
analyzePropertiesSerializers(context.trace, descriptor, props.serializableProperties)
}
private fun checkCorrectTransientAnnotationIsUsed(
descriptor: ClassDescriptor,
properties: List<SerializableProperty>,
trace: BindingTrace
) {
if (descriptor.getSuperInterfaces().any { it.fqNameSafe == FqName("java.io.Serializable") }) return // do not check
for (prop in properties) {
if (prop.transient) continue // correct annotation is used
if (prop.descriptor.backingField?.annotations?.hasAnnotation(TRANSIENT_ANNOTATION_FQ_NAME) == true) { // incorrect transient
val annotationEntry =
(prop.descriptor.backingField?.annotations?.findAnnotation(TRANSIENT_ANNOTATION_FQ_NAME) as? LazyAnnotationDescriptor)?.annotationEntry
val elementToReport = annotationEntry ?: prop.descriptor.findPsi() ?: continue
trace.report(SerializationErrors.INCORRECT_TRANSIENT.on(elementToReport))
}
}
}
private fun canBeSerializedInternally(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace): Boolean {
if (descriptor.isSerializableEnumWithMissingSerializer()) {
val declarationToReport = declaration.modifierList ?: declaration

View File

@@ -48,7 +48,7 @@ object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension {
MAP.put(
SerializationErrors.SERIALIZER_NOT_FOUND,
"Serializer has not been found for type ''{0}''. " +
"To use context serializer as fallback, explicitly annotate type or property with @ContextualSerialization",
"To use context serializer as fallback, explicitly annotate type or property with @Contextual",
Renderers.RENDER_TYPE_WITH_ANNOTATIONS
)
MAP.put(
@@ -65,5 +65,9 @@ object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension {
SerializationErrors.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."
)
}
}

View File

@@ -23,10 +23,12 @@ object SerializationAnnotations {
val serializerAnnotationFqName = FqName("kotlinx.serialization.Serializer")
internal val serialNameAnnotationFqName = FqName("kotlinx.serialization.SerialName")
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 contextualFqName = FqName("kotlinx.serialization.ContextualSerialization")
internal val contextualFqName = FqName("kotlinx.serialization.ContextualSerialization") // this one is deprecated
internal val contextualOnFileFqName = FqName("kotlinx.serialization.UseContextualSerialization")
internal val contextualOnPropertyFqName = FqName("kotlinx.serialization.Contextual")
internal val polymorphicFqName = FqName("kotlinx.serialization.Polymorphic")
internal val additionalSerializersFqName = FqName("kotlinx.serialization.UseSerializers")
}

View File

@@ -25,7 +25,9 @@ public class SerializationPluginDiagnosticTestGenerated extends AbstractSerializ
}
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")
@@ -33,6 +35,16 @@ public class SerializationPluginDiagnosticTestGenerated extends AbstractSerializ
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")
public void testLazyRecursionBug() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/diagnostics/LazyRecursionBug.kt");

View File

@@ -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

View File

@@ -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)

View File

@@ -15,6 +15,9 @@ dependencies {
compile(project(":idea:idea-maven"))
compile(project(":plugins:annotation-based-compiler-plugins-ide-support"))
compileOnly(intellijDep())
Platform[192].orHigher {
compileOnly(intellijPluginDep("java"))
}
excludeInAndroidStudio(rootProject) { compileOnly(intellijPluginDep("maven")) }
compileOnly(intellijPluginDep("gradle"))
@@ -30,5 +33,5 @@ sourceSets {
runtimeJar()
projectTest(parallel = true) {
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}