Do not check backing field presence in AnnotationSplitter

This code can be invoked early, during body resolution and before the
fact that a property has backing field (which is only known for certain
after body resolution, because an implicit 'field' identifier may be
used). Since split annotations are cached until the end of the program,
they may end up on incorrect elements in the bytecode (or disappear
completely) as in KT-29507 or KT-28182.

Because the FIELD target has the lowest priority among implicit
annotation targets (see TARGET_PRIORITIES), it's safe to always assume
that FIELD is a valid target when splitting annotations. This only
changes the way annotations are split in case of incorrect code, as
changes in test data show.

 #KT-28182 Fixed
 #KT-29507 Fixed
This commit is contained in:
Alexander Udalov
2019-02-13 18:57:25 +01:00
parent 5595a2dd1b
commit 4692247897
6 changed files with 34 additions and 50 deletions
@@ -22,7 +22,6 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.resolve.AnnotationChecker
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.LazyEntity
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.storage.getValue
@@ -49,39 +48,26 @@ class AnnotationSplitter(
private val TARGET_PRIORITIES = setOf(CONSTRUCTOR_PARAMETER, PROPERTY, FIELD)
@JvmStatic
fun create(
storageManager: StorageManager,
annotations: Annotations,
targets: Set<AnnotationUseSiteTarget>
): AnnotationSplitter {
return AnnotationSplitter(storageManager, annotations, { targets })
}
fun create(storageManager: StorageManager, annotations: Annotations, targets: Set<AnnotationUseSiteTarget>): AnnotationSplitter =
AnnotationSplitter(storageManager, annotations) { targets }
@JvmStatic
fun getTargetSet(parameter: Boolean, context: BindingContext, wrapper: PropertyWrapper): Set<AnnotationUseSiteTarget> {
val descriptor = wrapper.descriptor
assert(descriptor != null)
val hasBackingField = context[BindingContext.BACKING_FIELD_REQUIRED, descriptor] ?: false
val hasDelegate = wrapper.declaration is KtProperty && wrapper.declaration.hasDelegate()
return getTargetSet(parameter, descriptor!!.isVar, hasBackingField, hasDelegate)
}
@JvmStatic
fun getTargetSet(
parameter: Boolean, isVar: Boolean, hasBackingField: Boolean, hasDelegate: Boolean
): Set<AnnotationUseSiteTarget> = hashSetOf(PROPERTY, PROPERTY_GETTER).apply {
if (parameter) {
add(CONSTRUCTOR_PARAMETER)
add(PROPERTY_GETTER)
add(PROPERTY_SETTER)
fun getTargetSet(parameter: Boolean, wrapper: PropertyWrapper): Set<AnnotationUseSiteTarget> =
hashSetOf(PROPERTY, PROPERTY_GETTER).apply {
if (parameter) {
add(CONSTRUCTOR_PARAMETER)
add(PROPERTY_GETTER)
add(PROPERTY_SETTER)
}
add(FIELD)
if (wrapper.descriptor!!.isVar) {
add(PROPERTY_SETTER)
add(SETTER_PARAMETER)
}
if (wrapper.declaration is KtProperty && wrapper.declaration.hasDelegate()) {
add(PROPERTY_DELEGATE_FIELD)
}
}
if (hasBackingField) add(FIELD)
if (isVar) {
add(PROPERTY_SETTER)
add(SETTER_PARAMETER)
}
if (hasDelegate) add(PROPERTY_DELEGATE_FIELD)
}
}
class PropertyWrapper @JvmOverloads constructor(val declaration: KtDeclaration, var descriptor: PropertyDescriptor? = null)
@@ -872,8 +872,7 @@ public class DescriptorResolver {
Annotations allAnnotations = annotationResolver.resolveAnnotationsWithoutArguments(scopeForDeclarationResolution, modifierList, trace);
AnnotationSplitter annotationSplitter =
new AnnotationSplitter(storageManager, allAnnotations,
() -> AnnotationSplitter.getTargetSet(false, trace.getBindingContext(), wrapper));
new AnnotationSplitter(storageManager, allAnnotations, () -> AnnotationSplitter.getTargetSet(false, wrapper));
Annotations propertyAnnotations = new CompositeAnnotations(CollectionsKt.listOf(
annotationSplitter.getAnnotationsForTarget(PROPERTY),
@@ -1228,8 +1227,7 @@ public class DescriptorResolver {
AnnotationSplitter.PropertyWrapper propertyWrapper = new AnnotationSplitter.PropertyWrapper(parameter);
Annotations allAnnotations = annotationResolver.resolveAnnotationsWithoutArguments(scope, parameter.getModifierList(), trace);
AnnotationSplitter annotationSplitter =
new AnnotationSplitter(storageManager, allAnnotations,
() -> AnnotationSplitter.getTargetSet(true, trace.getBindingContext(), propertyWrapper));
new AnnotationSplitter(storageManager, allAnnotations, () -> AnnotationSplitter.getTargetSet(true, propertyWrapper));
Annotations propertyAnnotations = new CompositeAnnotations(
annotationSplitter.getAnnotationsForTarget(PROPERTY),
@@ -1,7 +1,7 @@
package
@field:Field public val x: kotlin.Int = 42
@Field public val y: kotlin.Int
@field:Field public val y: kotlin.Int
@Field public final annotation class Another : kotlin.Annotation {
public constructor Another()
@@ -18,8 +18,8 @@ package
}
public interface His {
@Field public abstract val x: kotlin.Int
@Field public open val y: kotlin.Int
@field:Field public abstract val x: kotlin.Int
@field:Field public open val y: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
@@ -27,11 +27,11 @@ public interface His {
@Field public abstract class My {
public constructor My(/*0*/ @Field arg: kotlin.Int, /*1*/ w: kotlin.Int)
@Field public final val v: kotlin.Int
@field:Field public final val v: kotlin.Int
@field:Field public final val w: kotlin.Int
@field:Field public final val x: kotlin.Int
@Field public final val y: kotlin.Int
@Field public abstract val z: kotlin.Int
@field:Field public final val y: kotlin.Int
@field:Field public abstract val z: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@Field public final fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
@@ -17,8 +17,8 @@ public final class CustomDelegate {
@Ann public final class SomeClass {
public constructor SomeClass()
protected final val delegatedProperty: kotlin.String
public final val propertyWithCustomGetter: kotlin.Int
@field:Ann protected final val delegatedProperty: kotlin.String
@field:Ann public final val propertyWithCustomGetter: kotlin.Int
@field:Ann protected final val simpleProperty: kotlin.String = "text"
@field:Ann protected final val simplePropertyWithAnnotationList: kotlin.String = "text"
public final fun anotherFun(/*0*/ s: kotlin.String): kotlin.Unit
@@ -17,7 +17,7 @@ abstract class C : I{
<!WRONG_ANNOTATION_TARGET!>@kotlin.jvm.JvmField<!> private fun foo(s: String = "OK") {
}
<!WRONG_ANNOTATION_TARGET!>@JvmField<!> val a: String by lazy { "A" }
<!INAPPLICABLE_JVM_FIELD, WRONG_ANNOTATION_TARGET!>@JvmField<!> val a: String by lazy { "A" }
<!INAPPLICABLE_JVM_FIELD!>@JvmField<!> open val b: Int = 3
@@ -7,18 +7,18 @@ package
@kotlin.jvm.JvmField public abstract class C : I {
@kotlin.jvm.JvmField public constructor C(/*0*/ s: kotlin.String)
@kotlin.jvm.JvmField public final val a: kotlin.String
@field:kotlin.jvm.JvmField public final val a: kotlin.String
@field:kotlin.jvm.JvmField public final override /*1*/ val ai: kotlin.Int = 3
@field:kotlin.jvm.JvmField public open val b: kotlin.Int = 3
@kotlin.jvm.JvmField public open override /*1*/ /*fake_override*/ val bi: kotlin.Int
@kotlin.jvm.JvmField public abstract val c: kotlin.Int
@field:kotlin.jvm.JvmField public open override /*1*/ /*fake_override*/ val bi: kotlin.Int
@field:kotlin.jvm.JvmField public abstract val c: kotlin.Int
@field:kotlin.jvm.JvmField public final val customGetter: kotlin.String = ""
@field:kotlin.jvm.JvmField public final var customSetter: kotlin.String
@field:kotlin.jvm.JvmField @get:DemoAnnotation public final val explicitDefaultAnnotatedGetter: kotlin.String = ""
@field:kotlin.jvm.JvmField @set:DemoAnnotation public final var explicitDefaultAnnotatedSetter: kotlin.String
@field:kotlin.jvm.JvmField public final val explicitDefaultGetter: kotlin.String = ""
@field:kotlin.jvm.JvmField public final var explicitDefaultSetter: kotlin.String
@kotlin.jvm.JvmField public final val noBackingField: kotlin.String
@field:kotlin.jvm.JvmField public final val noBackingField: kotlin.String
@field:kotlin.jvm.JvmField private final val private: kotlin.Int = 3
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
@kotlin.jvm.JvmField private final fun foo(/*0*/ s: kotlin.String = ...): kotlin.Unit
@@ -66,8 +66,8 @@ public final class H {
}
public interface I {
@kotlin.jvm.JvmField public abstract val ai: kotlin.Int
@kotlin.jvm.JvmField public open val bi: kotlin.Int
@field:kotlin.jvm.JvmField public abstract val ai: kotlin.Int
@field:kotlin.jvm.JvmField public open val bi: kotlin.Int
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String