Check use-site targeted annotations with AnnotationTarget in declaration-site

This commit is contained in:
Yan Zhulanow
2015-07-17 20:08:20 +03:00
parent a1eb544807
commit 8e65ceefd0
4 changed files with 85 additions and 31 deletions
@@ -198,7 +198,7 @@ public abstract class AnnotationCodegen {
private void generateTargetAnnotation(@NotNull ClassDescriptor classDescriptor, @NotNull Set<String> annotationDescriptorsAlreadyPresent) { private void generateTargetAnnotation(@NotNull ClassDescriptor classDescriptor, @NotNull Set<String> annotationDescriptorsAlreadyPresent) {
String descriptor = Type.getType(Target.class).getDescriptor(); String descriptor = Type.getType(Target.class).getDescriptor();
if (!annotationDescriptorsAlreadyPresent.add(descriptor)) return; if (!annotationDescriptorsAlreadyPresent.add(descriptor)) return;
Set<KotlinTarget> targets = AnnotationChecker.Companion.possibleTargetSet(classDescriptor); Set<KotlinTarget> targets = AnnotationChecker.Companion.applicableTargetSet(classDescriptor);
Set<ElementType> javaTargets; Set<ElementType> javaTargets;
if (targets == null) { if (targets == null) {
javaTargets = getJavaTargetList(classDescriptor); javaTargets = getJavaTargetList(classDescriptor);
@@ -51,7 +51,7 @@ public class AnnotationChecker(private val additionalCheckers: Iterable<Addition
} }
public fun checkExpression(expression: JetExpression, trace: BindingTrace) { public fun checkExpression(expression: JetExpression, trace: BindingTrace) {
checkEntries(expression.getAnnotationEntries(), listOf(KotlinTarget.EXPRESSION), trace) checkEntries(expression.getAnnotationEntries(), targetList(KotlinTarget.EXPRESSION), trace)
if (expression is JetFunctionLiteralExpression) { if (expression is JetFunctionLiteralExpression) {
for (parameter in expression.valueParameters) { for (parameter in expression.valueParameters) {
parameter.typeReference?.let { check(it, trace) } parameter.typeReference?.let { check(it, trace) }
@@ -59,7 +59,7 @@ public class AnnotationChecker(private val additionalCheckers: Iterable<Addition
} }
} }
private fun checkEntries(entries: List<JetAnnotationEntry>, actualTargets: List<KotlinTarget>, trace: BindingTrace) { private fun checkEntries(entries: List<JetAnnotationEntry>, actualTargets: TargetList, trace: BindingTrace) {
val entryTypes: MutableSet<JetType> = hashSetOf() val entryTypes: MutableSet<JetType> = hashSetOf()
for (entry in entries) { for (entry in entries) {
checkAnnotationEntry(entry, actualTargets, trace) checkAnnotationEntry(entry, actualTargets, trace)
@@ -69,26 +69,39 @@ public class AnnotationChecker(private val additionalCheckers: Iterable<Addition
trace.report(Errors.REPEATED_ANNOTATION.on(entry)); trace.report(Errors.REPEATED_ANNOTATION.on(entry));
} }
} }
additionalCheckers.forEach { it.checkEntries(entries, actualTargets, trace) } additionalCheckers.forEach { it.checkEntries(entries, actualTargets.declarationSite, trace) }
} }
private fun checkAnnotationEntry(entry: JetAnnotationEntry, actualTargets: List<KotlinTarget>, trace: BindingTrace) { private fun checkAnnotationEntry(entry: JetAnnotationEntry, actualTargets: TargetList, trace: BindingTrace) {
val possibleTargets = possibleTargetSet(entry, trace) val applicableTargets = applicableTargetSet(entry, trace)
if (actualTargets.any { it in possibleTargets }) return val useSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget()
trace.report(Errors.WRONG_ANNOTATION_TARGET.on(entry, actualTargets.firstOrNull()?.description ?: "unidentified target"))
if (actualTargets.declarationSite.any {
it in applicableTargets && (useSiteTarget == null || KotlinTarget.USE_SITE_MAPPING[useSiteTarget] == it)
}) return
if (useSiteTarget != null && actualTargets.useSite.any {
it in applicableTargets && KotlinTarget.USE_SITE_MAPPING[useSiteTarget] == it
}) return
trace.report(Errors.WRONG_ANNOTATION_TARGET.on(entry, actualTargets.declarationSite.firstOrNull()?.description ?: "unidentified target"))
} }
companion object { companion object {
private fun possibleTargetSet(entry: JetAnnotationEntry, trace: BindingTrace): Set<KotlinTarget> { private val PROPERTY_USE_SITE_TARGETS = listOf(
KotlinTarget.FIELD, KotlinTarget.PROPERTY_GETTER, KotlinTarget.PROPERTY_SETTER, KotlinTarget.VALUE_PARAMETER)
private val VALUE_PARAMETER_USE_SITE_TARGETS = PROPERTY_USE_SITE_TARGETS + KotlinTarget.PROPERTY
private fun applicableTargetSet(entry: JetAnnotationEntry, trace: BindingTrace): Set<KotlinTarget> {
val descriptor = trace.get(BindingContext.ANNOTATION, entry) ?: return KotlinTarget.DEFAULT_TARGET_SET val descriptor = trace.get(BindingContext.ANNOTATION, entry) ?: return KotlinTarget.DEFAULT_TARGET_SET
// For descriptor with error type, all targets are considered as possible // For descriptor with error type, all targets are considered as possible
if (descriptor.type.isError) return KotlinTarget.ALL_TARGET_SET if (descriptor.type.isError) return KotlinTarget.ALL_TARGET_SET
val classDescriptor = TypeUtils.getClassDescriptor(descriptor.type) ?: return KotlinTarget.DEFAULT_TARGET_SET val classDescriptor = TypeUtils.getClassDescriptor(descriptor.type) ?: return KotlinTarget.DEFAULT_TARGET_SET
return possibleTargetSet(classDescriptor) ?: KotlinTarget.DEFAULT_TARGET_SET return applicableTargetSet(classDescriptor) ?: KotlinTarget.DEFAULT_TARGET_SET
} }
public fun possibleTargetSet(classDescriptor: ClassDescriptor): Set<KotlinTarget>? { public fun applicableTargetSet(classDescriptor: ClassDescriptor): Set<KotlinTarget>? {
val targetEntryDescriptor = classDescriptor.annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.target) val targetEntryDescriptor = classDescriptor.annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.target)
?: return null ?: return null
val valueArguments = targetEntryDescriptor.allValueArguments val valueArguments = targetEntryDescriptor.allValueArguments
@@ -98,41 +111,71 @@ public class AnnotationChecker(private val additionalCheckers: Iterable<Addition
}.filterNotNull().toSet() }.filterNotNull().toSet()
} }
public fun getActualTargetList(annotated: JetElement, descriptor: ClassDescriptor?): List<KotlinTarget> { public fun getDeclarationSiteActualTargetList(annotated: JetElement, descriptor: ClassDescriptor?): List<KotlinTarget> {
return getActualTargetList(annotated, descriptor).declarationSite
}
private fun getActualTargetList(annotated: JetElement, descriptor: ClassDescriptor?): TargetList {
return when (annotated) { return when (annotated) {
is JetClassOrObject -> descriptor?.let { KotlinTarget.classActualTargets(it) } ?: listOf(CLASSIFIER) is JetClassOrObject -> descriptor?.let { TargetList(KotlinTarget.classActualTargets(it)) } ?: targetList(CLASSIFIER)
is JetProperty -> is JetProperty ->
if (annotated.isLocal) { if (annotated.isLocal) {
listOf(LOCAL_VARIABLE) extendedTargetList(PROPERTY_USE_SITE_TARGETS, LOCAL_VARIABLE)
} }
else if (annotated.parent is JetClassOrObject || annotated.parent is JetClassBody) { else if (annotated.parent is JetClassOrObject || annotated.parent is JetClassBody) {
listOf(MEMBER_PROPERTY, PROPERTY, FIELD) extendedTargetList(PROPERTY_USE_SITE_TARGETS, MEMBER_PROPERTY, PROPERTY, FIELD)
} }
else { else {
listOf(TOP_LEVEL_PROPERTY, PROPERTY, FIELD) extendedTargetList(PROPERTY_USE_SITE_TARGETS, TOP_LEVEL_PROPERTY, PROPERTY, FIELD)
} }
is JetParameter -> if (annotated.hasValOrVar()) listOf(PROPERTY_PARAMETER, MEMBER_PROPERTY, PROPERTY, FIELD) else listOf(VALUE_PARAMETER) is JetParameter -> {
is JetConstructor<*> -> listOf(CONSTRUCTOR) if (annotated.hasValOrVar()) {
is JetFunction -> extendedTargetList(VALUE_PARAMETER_USE_SITE_TARGETS, PROPERTY_PARAMETER, MEMBER_PROPERTY, PROPERTY, FIELD)
}
else {
extendedTargetList(VALUE_PARAMETER_USE_SITE_TARGETS, VALUE_PARAMETER)
}
}
is JetConstructor<*> -> targetList(CONSTRUCTOR)
is JetFunction -> {
val extendedTargets = listOf(KotlinTarget.VALUE_PARAMETER)
if (annotated.isLocal) { if (annotated.isLocal) {
listOf(LOCAL_FUNCTION, FUNCTION) extendedTargetList(extendedTargets, LOCAL_FUNCTION, FUNCTION)
} }
else if (annotated.parent is JetClassOrObject || annotated.parent is JetClassBody) { else if (annotated.parent is JetClassOrObject || annotated.parent is JetClassBody) {
listOf(MEMBER_FUNCTION, FUNCTION) extendedTargetList(extendedTargets, MEMBER_FUNCTION, FUNCTION)
} }
else { else {
listOf(TOP_LEVEL_FUNCTION, FUNCTION) extendedTargetList(extendedTargets, TOP_LEVEL_FUNCTION, FUNCTION)
} }
is JetPropertyAccessor -> if (annotated.isGetter) listOf(PROPERTY_GETTER) else listOf(PROPERTY_SETTER) }
is JetPackageDirective -> listOf(PACKAGE) is JetPropertyAccessor -> if (annotated.isGetter) targetList(PROPERTY_GETTER) else targetList(PROPERTY_SETTER)
is JetTypeReference -> listOf(TYPE) is JetPackageDirective -> targetList(PACKAGE)
is JetFile -> listOf(FILE) is JetTypeReference -> targetList(TYPE)
is JetTypeParameter -> listOf(TYPE_PARAMETER) is JetFile -> targetList(FILE)
is JetTypeProjection -> if (annotated.projectionKind == JetProjectionKind.STAR) listOf(STAR_PROJECTION) else listOf(TYPE_PROJECTION) is JetTypeParameter -> targetList(TYPE_PARAMETER)
is JetClassInitializer -> listOf(INITIALIZER) is JetTypeProjection -> {
else -> listOf() if (annotated.projectionKind == JetProjectionKind.STAR) {
targetList(STAR_PROJECTION)
}
else {
targetList(TYPE_PROJECTION)
}
}
is JetClassInitializer -> targetList(INITIALIZER)
else -> targetList()
} }
} }
private class TargetList(val declarationSite: List<KotlinTarget>, val useSite: List<KotlinTarget> = emptyList())
private fun targetList(vararg target: KotlinTarget): TargetList {
return TargetList(listOf(*target), emptyList())
}
private fun extendedTargetList(extended: List<KotlinTarget>, vararg target: KotlinTarget): TargetList {
return TargetList(listOf(*target), extended)
}
} }
} }
@@ -208,7 +208,7 @@ public object ModifierCheckerCore {
} }
} }
} }
val actualTargets = AnnotationChecker.getActualTargetList(listOwner, descriptor as? ClassDescriptor) val actualTargets = AnnotationChecker.getDeclarationSiteActualTargetList(listOwner, descriptor as? ClassDescriptor)
val list = listOwner.modifierList ?: return val list = listOwner.modifierList ?: return
checkModifierList(list, trace, descriptor?.containingDeclaration, actualTargets) checkModifierList(list, trace, descriptor?.containingDeclaration, actualTargets)
} }
@@ -102,5 +102,16 @@ public enum class KotlinTarget(val description: String, val isDefault: Boolean =
} }
ClassKind.ENUM_ENTRY -> listOf(ENUM_ENTRY, PROPERTY, FIELD) ClassKind.ENUM_ENTRY -> listOf(ENUM_ENTRY, PROPERTY, FIELD)
} }
public val USE_SITE_MAPPING: Map<AnnotationUseSiteTarget, KotlinTarget> = mapOf(
AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER to VALUE_PARAMETER,
AnnotationUseSiteTarget.FIELD to FIELD,
AnnotationUseSiteTarget.PROPERTY to PROPERTY,
AnnotationUseSiteTarget.FILE to FILE,
AnnotationUseSiteTarget.PROPERTY_GETTER to PROPERTY_GETTER,
AnnotationUseSiteTarget.PROPERTY_SETTER to PROPERTY_SETTER,
AnnotationUseSiteTarget.RECEIVER to VALUE_PARAMETER,
AnnotationUseSiteTarget.SETTER_PARAMETER to VALUE_PARAMETER)
} }
} }