Check annotation site targets using psi elements

This commit is contained in:
Yan Zhulanow
2015-08-13 03:55:10 +03:00
parent 499d5e1103
commit c488ce4cb3
@@ -16,13 +16,8 @@
package org.jetbrains.kotlin.resolve package org.jetbrains.kotlin.resolve
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1
import org.jetbrains.kotlin.diagnostics.Errors.* import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -44,8 +39,8 @@ public object AnnotationUseSiteTargetChecker {
private fun BindingTrace.checkReceiverAnnotations(descriptor: CallableDescriptor) { private fun BindingTrace.checkReceiverAnnotations(descriptor: CallableDescriptor) {
val extensionReceiver = descriptor.extensionReceiverParameter ?: return val extensionReceiver = descriptor.extensionReceiverParameter ?: return
for (annotationWithTarget in extensionReceiver.type.annotations.getUseSiteTargetedAnnotations()) { for (annotationWithTarget in extensionReceiver.type.annotations.getUseSiteTargetedAnnotations()) {
val annotation = annotationWithTarget.annotation
val target = annotationWithTarget.target ?: continue val target = annotationWithTarget.target ?: continue
fun annotationEntry() = DescriptorToSourceUtils.getSourceFromAnnotation(annotationWithTarget.annotation)
when (target) { when (target) {
AnnotationUseSiteTarget.RECEIVER -> {} AnnotationUseSiteTarget.RECEIVER -> {}
@@ -53,92 +48,80 @@ public object AnnotationUseSiteTargetChecker {
AnnotationUseSiteTarget.PROPERTY, AnnotationUseSiteTarget.PROPERTY,
AnnotationUseSiteTarget.PROPERTY_GETTER, AnnotationUseSiteTarget.PROPERTY_GETTER,
AnnotationUseSiteTarget.PROPERTY_SETTER, AnnotationUseSiteTarget.PROPERTY_SETTER,
AnnotationUseSiteTarget.SETTER_PARAMETER -> report(annotationWithTarget, INAPPLICABLE_TARGET_ON_PROPERTY) AnnotationUseSiteTarget.SETTER_PARAMETER -> {
AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER -> report(annotation, INAPPLICABLE_PARAM_TARGET) annotationEntry()?.let { report(INAPPLICABLE_TARGET_ON_PROPERTY.on(it, target.renderName)) }
}
AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER -> annotationEntry()?.let { report(INAPPLICABLE_PARAM_TARGET.on(it)) }
AnnotationUseSiteTarget.FILE -> throw IllegalArgumentException("@file annotations are not allowed here") AnnotationUseSiteTarget.FILE -> throw IllegalArgumentException("@file annotations are not allowed here")
} }
} }
} }
private fun BindingTrace.checkDeclaration(annotated: JetAnnotated, descriptor: DeclarationDescriptor) { private fun BindingTrace.checkDeclaration(annotated: JetAnnotated, descriptor: DeclarationDescriptor) {
for (annotationWithTarget in descriptor.annotations.getUseSiteTargetedAnnotations()) { for (annotation in annotated.annotationEntries) {
val annotation = annotationWithTarget.annotation val target = annotation.useSiteTarget?.getAnnotationUseSiteTarget() ?: continue
val target = annotationWithTarget.target ?: continue
when (target) { when (target) {
AnnotationUseSiteTarget.FIELD -> checkFieldTargetApplicability(annotated, descriptor, annotationWithTarget) AnnotationUseSiteTarget.FIELD -> checkFieldTargetApplicability(annotated, annotation, descriptor)
AnnotationUseSiteTarget.PROPERTY -> checkIfPropertyDescriptor(descriptor, annotationWithTarget) AnnotationUseSiteTarget.PROPERTY -> checkIfProperty(annotated, annotation)
AnnotationUseSiteTarget.PROPERTY_GETTER -> checkIfPropertyDescriptor(descriptor, annotationWithTarget) AnnotationUseSiteTarget.PROPERTY_GETTER -> checkIfProperty(annotated, annotation)
AnnotationUseSiteTarget.PROPERTY_SETTER -> checkMutableProperty(descriptor, annotationWithTarget) AnnotationUseSiteTarget.PROPERTY_SETTER -> checkIfMutableProperty(annotated, annotation)
AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER -> { AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER -> {
if (annotated !is JetParameter) { if (annotated !is JetParameter) {
report(annotation, INAPPLICABLE_PARAM_TARGET) report(INAPPLICABLE_PARAM_TARGET.on(annotation))
} }
else { else {
val containingDeclaration = bindingContext[BindingContext.VALUE_PARAMETER, annotated]?.containingDeclaration val containingDeclaration = bindingContext[BindingContext.VALUE_PARAMETER, annotated]?.containingDeclaration
if (containingDeclaration !is ConstructorDescriptor || !containingDeclaration.isPrimary) { if (containingDeclaration !is ConstructorDescriptor || !containingDeclaration.isPrimary) {
report(annotation, INAPPLICABLE_PARAM_TARGET) report(INAPPLICABLE_PARAM_TARGET.on(annotation))
} }
else if (!annotated.hasValOrVar()) { else if (!annotated.hasValOrVar()) {
report(annotationWithTarget, REDUNDANT_ANNOTATION_TARGET) report(REDUNDANT_ANNOTATION_TARGET.on(annotation, target.renderName))
} }
} }
} }
AnnotationUseSiteTarget.SETTER_PARAMETER -> checkMutableProperty(descriptor, annotationWithTarget) AnnotationUseSiteTarget.SETTER_PARAMETER -> checkIfMutableProperty(annotated, annotation)
AnnotationUseSiteTarget.FILE -> throw IllegalArgumentException("@file annotations are not allowed here") AnnotationUseSiteTarget.FILE -> throw IllegalArgumentException("@file annotations are not allowed here")
AnnotationUseSiteTarget.RECEIVER -> report(annotation, INAPPLICABLE_RECEIVER_TARGET) AnnotationUseSiteTarget.RECEIVER -> report(INAPPLICABLE_RECEIVER_TARGET.on(annotation))
} }
} }
} }
private fun BindingTrace.checkFieldTargetApplicability( private fun BindingTrace.checkFieldTargetApplicability(
modifierListOwner: JetAnnotated, annotated: JetAnnotated,
descriptor: DeclarationDescriptor, annotation: JetAnnotationEntry,
annotationWithTarget: AnnotationWithTarget) { descriptor: DeclarationDescriptor
val propertyDescriptor = checkIfPropertyDescriptor(descriptor, annotationWithTarget) ?: return ) {
if (!checkIfProperty(annotated, annotation)) return
val hasDelegate = modifierListOwner is JetProperty && modifierListOwner.hasDelegate() if (annotated is JetProperty && descriptor is PropertyDescriptor) {
if (!annotated.hasDelegate() && !(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, descriptor) ?: false)) {
if (!hasDelegate && !(bindingContext.get(BindingContext.BACKING_FIELD_REQUIRED, propertyDescriptor) ?: false)) { report(INAPPLICABLE_FIELD_TARGET_NO_BACKING_FIELD.on(annotation))
report(annotationWithTarget.annotation, INAPPLICABLE_FIELD_TARGET_NO_BACKING_FIELD) }
} }
} }
private fun BindingTrace.checkMutableProperty(descriptor: DeclarationDescriptor, annotationWIthTarget: AnnotationWithTarget) { private fun BindingTrace.checkIfMutableProperty(annotated: JetAnnotated, annotation: JetAnnotationEntry) {
val propertyDescriptor = checkIfPropertyDescriptor(descriptor, annotationWIthTarget) ?: return if (!checkIfProperty(annotated, annotation)) return
if (!propertyDescriptor.isVar) { val isMutable = if (annotated is JetProperty)
report(annotationWIthTarget.annotation, INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE) annotated.isVar
} else if (annotated is JetParameter)
annotated.isMutable
else false
if (!isMutable) report(INAPPLICABLE_TARGET_PROPERTY_IMMUTABLE.on(annotation))
} }
private fun BindingTrace.checkIfPropertyDescriptor( private fun BindingTrace.checkIfProperty(annotated: JetAnnotated, annotation: JetAnnotationEntry): Boolean {
descriptor: DeclarationDescriptor, val isProperty = if (annotated is JetProperty)
annotationWithTarget: AnnotationWithTarget): PropertyDescriptor? { !annotated.isLocal
if (descriptor is PropertyDescriptor) { else if (annotated is JetParameter)
return descriptor annotated.hasValOrVar()
} else false
else if (descriptor is ValueParameterDescriptor) {
val jetParameter = DescriptorToSourceUtils.descriptorToDeclaration(descriptor) as? JetParameter
if (jetParameter != null && jetParameter.hasValOrVar()) {
val propertyDescriptor = bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, descriptor]
if (propertyDescriptor != null) return propertyDescriptor
}
}
report(annotationWithTarget, INAPPLICABLE_TARGET_ON_PROPERTY) val target = annotation.useSiteTarget?.getAnnotationUseSiteTarget()?.renderName ?: "unknown target" // should not happen
return null if (!isProperty) report(INAPPLICABLE_TARGET_ON_PROPERTY.on(annotation, target))
return isProperty
} }
private fun BindingTrace.report(annotation: AnnotationDescriptor, diagnosticFactory: DiagnosticFactory0<PsiElement>) {
val annotationEntry = DescriptorToSourceUtils.getSourceFromAnnotation(annotation) ?: return
report(diagnosticFactory.on(annotationEntry))
}
private fun BindingTrace.report(annotationWithTarget: AnnotationWithTarget, diagnosticFactory: DiagnosticFactory1<PsiElement, String>) {
val annotationEntry = DescriptorToSourceUtils.getSourceFromAnnotation(annotationWithTarget.annotation) ?: return
report(diagnosticFactory.on(annotationEntry, annotationWithTarget.target?.renderName ?: "invalid target"))
}
} }