Annotation on a function literal cannot be written to binary if it has FUNCTION target and the literal is inlined

This commit is contained in:
Mikhail Glukhikh
2015-09-23 15:46:34 +03:00
parent 6b9e2b0bbb
commit bd7ccc0138
6 changed files with 64 additions and 4 deletions
@@ -120,6 +120,7 @@ public interface Errors {
DiagnosticFactory1<JetAnnotationEntry, String> WRONG_ANNOTATION_TARGET = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<JetAnnotationEntry, String, String> WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET = DiagnosticFactory2.create(ERROR);
DiagnosticFactory0<JetAnnotationEntry> REPEATED_ANNOTATION = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<JetAnnotationEntry> NON_SOURCE_ANNOTATION_ON_INLINED_FUNCTION_LITERAL = DiagnosticFactory0.create(ERROR);
// Annotations
@@ -127,6 +127,7 @@ public class DefaultErrorMessages {
MAP.put(WRONG_ANNOTATION_TARGET, "This annotation is not applicable to target ''{0}''", TO_STRING);
MAP.put(WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET, "This annotation is not applicable to target ''{0}'' and use site target ''@{1}''", TO_STRING, TO_STRING);
MAP.put(REPEATED_ANNOTATION, "This annotation is not repeatable");
MAP.put(NON_SOURCE_ANNOTATION_ON_INLINED_FUNCTION_LITERAL, "Function literal here is an inlined argument so this annotation cannot be stored anywhere");
MAP.put(INAPPLICABLE_TARGET_ON_PROPERTY, "''@{0}'' annotations could be applied only to property declarations", TO_STRING);
MAP.put(INAPPLICABLE_FIELD_TARGET_NO_BACKING_FIELD, "Property has neither a backing field nor a delegate");
@@ -874,7 +874,7 @@ public class JetPsiUtil {
else if (parent instanceof JetValueArgument || parent instanceof JetValueArgumentList) {
parent = parent.getParent();
}
else if (parent instanceof JetFunctionLiteralExpression) {
else if (parent instanceof JetFunctionLiteralExpression || parent instanceof JetAnnotatedExpression) {
parent = parent.getParent();
}
else {
@@ -28,7 +28,10 @@ import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.isRepeatableAnnotation
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.getAnnotationRetention
import org.jetbrains.kotlin.resolve.inline.InlineUtil
public class AnnotationChecker(private val additionalCheckers: Iterable<AdditionalAnnotationChecker>) {
@@ -92,11 +95,28 @@ public class AnnotationChecker(private val additionalCheckers: Iterable<Addition
it in applicableTargets && (useSiteTarget == null || KotlinTarget.USE_SITE_MAPPING[useSiteTarget] == it)
}
if (check(actualTargets.defaultTargets) || check(actualTargets.canBeSubstituted)) return
fun checkUselessFunctionLiteralAnnotation() {
// TODO: tests on different JetAnnotatedExpression (?!)
if (KotlinTarget.FUNCTION !in applicableTargets) return
val annotatedExpression = entry.parent as? JetAnnotatedExpression ?: return
val descriptor = trace.get(BindingContext.ANNOTATION, entry) ?: return
val retention = descriptor.type.constructor.declarationDescriptor?.getAnnotationRetention()
if (retention == KotlinRetention.SOURCE) return
if (useSiteTarget != null && actualTargets.onlyWithUseSiteTarget.any {
val functionLiteralExpression = annotatedExpression.baseExpression as? JetFunctionLiteralExpression ?: return
if (InlineUtil.isInlinedArgument(functionLiteralExpression.functionLiteral, trace.bindingContext, false)) {
trace.report(Errors.NON_SOURCE_ANNOTATION_ON_INLINED_FUNCTION_LITERAL.on(entry))
}
}
fun applicableWithUseSiteTarget() = useSiteTarget != null && actualTargets.onlyWithUseSiteTarget.any {
it in applicableTargets && KotlinTarget.USE_SITE_MAPPING[useSiteTarget] == it
}) return
}
if (check(actualTargets.defaultTargets) || check(actualTargets.canBeSubstituted) || applicableWithUseSiteTarget()) {
checkUselessFunctionLiteralAnnotation()
return
}
if (useSiteTarget != null) {
trace.report(Errors.WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET.on(
@@ -1,16 +1,38 @@
@Target(AnnotationTarget.FUNCTION)
annotation class FunAnn
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.SOURCE)
annotation class SourceAnn
@Target(AnnotationTarget.EXPRESSION)
annotation class ExprAnn
fun bar(arg: () -> Int) = arg()
inline fun fast(arg: () -> Int) = arg()
inline fun fast2(x: Int, arg: () -> Int) = x + arg()
@FunAnn fun gav() = 13
fun foo(arg: Int) {
// Literal is annotatable
bar @FunAnn { arg }
// Annotatable in principle but useless, fast is inline
fast <!NON_SOURCE_ANNOTATION_ON_INLINED_FUNCTION_LITERAL!>@FunAnn<!> { arg }
fast2(1, <!NON_SOURCE_ANNOTATION_ON_INLINED_FUNCTION_LITERAL!>@FunAnn<!> { arg })
// Source annotation, ok
fast @SourceAnn { arg }
fast2(1, @SourceAnn { arg })
// Expression annotation, ok
fast @ExprAnn { arg }
fast2(1, @ExprAnn { arg })
// Function expression too
val f = @FunAnn fun(): Int { return 42 }
// But here, f and gav should be annotated instead
bar(<!WRONG_ANNOTATION_TARGET!>@FunAnn<!> f)
bar(<!WRONG_ANNOTATION_TARGET!>@FunAnn<!> ::gav)
// Function expression, ok
fast(f)
}
@@ -1,12 +1,28 @@
package
public fun bar(/*0*/ arg: () -> kotlin.Int): kotlin.Int
@kotlin.inline() public fun fast(/*0*/ arg: () -> kotlin.Int): kotlin.Int
@kotlin.inline() public fun fast2(/*0*/ x: kotlin.Int, /*1*/ arg: () -> kotlin.Int): kotlin.Int
public fun foo(/*0*/ arg: kotlin.Int): kotlin.Unit
@FunAnn() public fun gav(): kotlin.Int
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.EXPRESSION}) @kotlin.annotation.annotation() public final class ExprAnn : kotlin.Annotation {
public constructor ExprAnn()
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
}
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.FUNCTION}) @kotlin.annotation.annotation() public final class FunAnn : kotlin.Annotation {
public constructor FunAnn()
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
}
@kotlin.annotation.Target(allowedTargets = {AnnotationTarget.FUNCTION}) @kotlin.annotation.Retention(value = AnnotationRetention.SOURCE) @kotlin.annotation.annotation() public final class SourceAnn : kotlin.Annotation {
public constructor SourceAnn()
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
}