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:
@@ -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
|
||||
|
||||
|
||||
+1
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user