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) {
String descriptor = Type.getType(Target.class).getDescriptor();
if (!annotationDescriptorsAlreadyPresent.add(descriptor)) return;
Set<KotlinTarget> targets = AnnotationChecker.Companion.possibleTargetSet(classDescriptor);
Set<KotlinTarget> targets = AnnotationChecker.Companion.applicableTargetSet(classDescriptor);
Set<ElementType> javaTargets;
if (targets == null) {
javaTargets = getJavaTargetList(classDescriptor);
@@ -51,7 +51,7 @@ public class AnnotationChecker(private val additionalCheckers: Iterable<Addition
}
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) {
for (parameter in expression.valueParameters) {
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()
for (entry in entries) {
checkAnnotationEntry(entry, actualTargets, trace)
@@ -69,26 +69,39 @@ public class AnnotationChecker(private val additionalCheckers: Iterable<Addition
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) {
val possibleTargets = possibleTargetSet(entry, trace)
if (actualTargets.any { it in possibleTargets }) return
trace.report(Errors.WRONG_ANNOTATION_TARGET.on(entry, actualTargets.firstOrNull()?.description ?: "unidentified target"))
private fun checkAnnotationEntry(entry: JetAnnotationEntry, actualTargets: TargetList, trace: BindingTrace) {
val applicableTargets = applicableTargetSet(entry, trace)
val useSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget()
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 {
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
// For descriptor with error type, all targets are considered as possible
if (descriptor.type.isError) return KotlinTarget.ALL_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)
?: return null
val valueArguments = targetEntryDescriptor.allValueArguments
@@ -98,41 +111,71 @@ public class AnnotationChecker(private val additionalCheckers: Iterable<Addition
}.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) {
is JetClassOrObject -> descriptor?.let { KotlinTarget.classActualTargets(it) } ?: listOf(CLASSIFIER)
is JetClassOrObject -> descriptor?.let { TargetList(KotlinTarget.classActualTargets(it)) } ?: targetList(CLASSIFIER)
is JetProperty ->
if (annotated.isLocal) {
listOf(LOCAL_VARIABLE)
extendedTargetList(PROPERTY_USE_SITE_TARGETS, LOCAL_VARIABLE)
}
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 {
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 JetConstructor<*> -> listOf(CONSTRUCTOR)
is JetFunction ->
is JetParameter -> {
if (annotated.hasValOrVar()) {
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) {
listOf(LOCAL_FUNCTION, FUNCTION)
extendedTargetList(extendedTargets, LOCAL_FUNCTION, FUNCTION)
}
else if (annotated.parent is JetClassOrObject || annotated.parent is JetClassBody) {
listOf(MEMBER_FUNCTION, FUNCTION)
extendedTargetList(extendedTargets, MEMBER_FUNCTION, FUNCTION)
}
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 JetTypeReference -> listOf(TYPE)
is JetFile -> listOf(FILE)
is JetTypeParameter -> listOf(TYPE_PARAMETER)
is JetTypeProjection -> if (annotated.projectionKind == JetProjectionKind.STAR) listOf(STAR_PROJECTION) else listOf(TYPE_PROJECTION)
is JetClassInitializer -> listOf(INITIALIZER)
else -> listOf()
}
is JetPropertyAccessor -> if (annotated.isGetter) targetList(PROPERTY_GETTER) else targetList(PROPERTY_SETTER)
is JetPackageDirective -> targetList(PACKAGE)
is JetTypeReference -> targetList(TYPE)
is JetFile -> targetList(FILE)
is JetTypeParameter -> targetList(TYPE_PARAMETER)
is JetTypeProjection -> {
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
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)
}
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)
}
}