From 2bacbc9046635802913cb6627d505bef34050c4c Mon Sep 17 00:00:00 2001 From: Yan Zhulanow Date: Thu, 16 Jul 2015 16:38:08 +0300 Subject: [PATCH] Support @field: annotations --- .../kotlin/codegen/AnnotationCodegen.java | 23 +++++-- .../kotlin/codegen/PropertyCodegen.java | 3 +- .../org/jetbrains/kotlin/JetNodeTypes.java | 1 + .../jetbrains/kotlin/diagnostics/Errors.java | 3 + .../rendering/DefaultErrorMessages.java | 3 + .../org/jetbrains/kotlin/lexer/JetTokens.java | 4 +- .../jetbrains/kotlin/parsing/JetParsing.java | 25 +++++--- .../jetbrains/kotlin/psi/JetAnnotation.java | 6 ++ .../kotlin/psi/JetAnnotationEntry.java | 14 +++++ .../kotlin/psi/JetAnnotationUseSiteTarget.kt | 45 ++++++++++++++ .../org/jetbrains/kotlin/psi/JetVisitor.java | 4 ++ .../stubs/elements/JetStubElementTypes.java | 3 + .../kotlin/resolve/AnnotationResolver.java | 21 ++++--- .../kotlin/resolve/ModifiersChecker.java | 62 +++++++++++++++++++ .../kotlin/resolve/lazy/ForceResolveUtil.java | 5 +- .../lazy/descriptors/LazyAnnotations.kt | 40 +++++++++--- .../psi/DocCommentAfterFileAnnotations.txt | 3 +- .../psi/LineCommentAfterFileAnnotations.txt | 5 +- .../onFile/fileAnnotationInWrongPlace.txt | 12 ++-- .../onFile/manyAnnotationBlocks.txt | 6 +- .../onFile/manyInOneAnnotationBlock.txt | 3 +- .../onFile/nonFIleAnnotationBeforePackage.txt | 9 +-- .../testData/psi/annotation/onFile/single.txt | 3 +- .../psi/annotation/onFile/withoutPackage.txt | 7 ++- .../withoutPackageWithSimpleAnnotation.txt | 7 ++- .../psi/script/manyAnnotationsOnFile.txt | 8 ++- .../load/java/lazy/LazyJavaAnnotations.kt | 5 ++ .../java/typeEnhacement/typeEnhancement.kt | 5 ++ .../annotations/AnnotationUseSiteTarget.kt | 22 +++++++ .../annotations/AnnotationWithTarget.kt | 53 ++++++++++++++++ .../descriptors/annotations/Annotations.kt | 46 +++++++++++--- .../annotations/AnnotationsImpl.kt | 35 ++++++++++- .../descriptors/DeserializedAnnotations.kt | 5 ++ grammar/src/attributes.grm | 2 +- 34 files changed, 431 insertions(+), 67 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/psi/JetAnnotationUseSiteTarget.kt create mode 100644 core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationUseSiteTarget.kt create mode 100644 core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationWithTarget.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java index d2b99680e51..4e7294e2c8f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java @@ -81,17 +81,30 @@ public abstract class AnnotationCodegen { * @param returnType can be null if not applicable (e.g. {@code annotated} is a class) */ public void genAnnotations(@Nullable Annotated annotated, @Nullable Type returnType) { - if (annotated == null) { - return; - } + genAnnotations(annotated, returnType, null); + } - if (!(annotated instanceof DeclarationDescriptor)) { + public void genAnnotations(@Nullable Annotated annotated, @Nullable Type returnType, @Nullable AnnotationUseSiteTarget target) { + if (annotated == null) { return; } Set annotationDescriptorsAlreadyPresent = new HashSet(); - for (AnnotationDescriptor annotation : annotated.getAnnotations()) { + Annotations annotations = annotated.getAnnotations(); + + if (target != null) { + for (AnnotationWithTarget annotationWithTarget : annotations.getUseSiteTargetedAnnotations()) { + if (target != annotationWithTarget.getTarget()) continue; + + String descriptor = genAnnotation(annotationWithTarget.getAnnotation()); + if (descriptor != null) { + annotationDescriptorsAlreadyPresent.add(descriptor); + } + } + } + + for (AnnotationDescriptor annotation : annotations) { String descriptor = genAnnotation(annotation); if (descriptor != null) { annotationDescriptorsAlreadyPresent.add(descriptor); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java index 828ce88f00b..fce234055e0 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.codegen.context.*; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.JetTypeMapper; import org.jetbrains.kotlin.descriptors.*; +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget; import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.psiUtil.PsiUtilPackage; @@ -293,7 +294,7 @@ public class PropertyCodegen { FieldVisitor fv = builder.newField(OtherOrigin(element, propertyDescriptor), modifiers, name, type.getDescriptor(), typeMapper.mapFieldSignature(jetType), defaultValue); - AnnotationCodegen.forField(fv, typeMapper).genAnnotations(propertyDescriptor, type); + AnnotationCodegen.forField(fv, typeMapper).genAnnotations(propertyDescriptor, type, AnnotationUseSiteTarget.FIELD); } private void generatePropertyDelegateAccess(JetProperty p, PropertyDescriptor propertyDescriptor) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/JetNodeTypes.java b/compiler/frontend/src/org/jetbrains/kotlin/JetNodeTypes.java index 25cc719d19b..5c545e55c85 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/JetNodeTypes.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/JetNodeTypes.java @@ -58,6 +58,7 @@ public interface JetNodeTypes { IElementType MODIFIER_LIST = JetStubElementTypes.MODIFIER_LIST; IElementType ANNOTATION = JetStubElementTypes.ANNOTATION; IElementType ANNOTATION_ENTRY = JetStubElementTypes.ANNOTATION_ENTRY; + IElementType ANNOTATION_TARGET = JetStubElementTypes.ANNOTATION_TARGET; IElementType TYPE_ARGUMENT_LIST = JetStubElementTypes.TYPE_ARGUMENT_LIST; JetNodeType VALUE_ARGUMENT_LIST = new JetNodeType("VALUE_ARGUMENT_LIST", JetValueArgumentList.class); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 881bc6e4236..6d9dc527708 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -131,6 +131,9 @@ public interface Errors { DiagnosticFactory0 ANNOTATION_PARAMETER_MUST_BE_ENUM_CONST = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 INAPPLICABLE_FIELD_TARGET = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 INAPPLICABLE_FIELD_TARGET_NO_BACKING_FIELD = DiagnosticFactory0.create(ERROR); + // Classes and traits DiagnosticFactory0 PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE = diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 859cf1c11fc..657f957e039 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -124,6 +124,9 @@ public class DefaultErrorMessages { MAP.put(WRONG_ANNOTATION_TARGET, "This annotation is not applicable to target ''{0}''", TO_STRING); MAP.put(REPEATED_ANNOTATION, "This annotation is not repeatable"); + MAP.put(INAPPLICABLE_FIELD_TARGET, "''@field:'' annotations could be applied only to property declarations"); + MAP.put(INAPPLICABLE_FIELD_TARGET_NO_BACKING_FIELD, "Property has neither a backing field nor a delegate"); + MAP.put(REDUNDANT_MODIFIER, "Modifier ''{0}'' is redundant because ''{1}'' is present", TO_STRING, TO_STRING); MAP.put(ABSTRACT_MODIFIER_IN_TRAIT, "Modifier ''abstract'' is redundant in interface"); MAP.put(REDUNDANT_MODIFIER_IN_GETTER, "Visibility modifiers are redundant in getter"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/lexer/JetTokens.java b/compiler/frontend/src/org/jetbrains/kotlin/lexer/JetTokens.java index 9d4fe520bc6..6e5006a80b6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/lexer/JetTokens.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/lexer/JetTokens.java @@ -133,6 +133,7 @@ public interface JetTokens { JetToken EOL_OR_SEMICOLON = new JetToken("EOL_OR_SEMICOLON"); JetKeywordToken FILE_KEYWORD = JetKeywordToken.softKeyword("file"); + JetKeywordToken FIELD_KEYWORD = JetKeywordToken.softKeyword("field"); JetKeywordToken IMPORT_KEYWORD = JetKeywordToken.softKeyword("import"); JetKeywordToken WHERE_KEYWORD = JetKeywordToken.softKeyword("where"); JetKeywordToken BY_KEYWORD = JetKeywordToken.softKeyword("by"); @@ -174,7 +175,8 @@ public interface JetTokens { SET_KEYWORD, ABSTRACT_KEYWORD, ENUM_KEYWORD, OPEN_KEYWORD, INNER_KEYWORD, OVERRIDE_KEYWORD, PRIVATE_KEYWORD, PUBLIC_KEYWORD, INTERNAL_KEYWORD, PROTECTED_KEYWORD, CATCH_KEYWORD, FINALLY_KEYWORD, OUT_KEYWORD, FINAL_KEYWORD, VARARG_KEYWORD, REIFIED_KEYWORD, - DYNAMIC_KEYWORD, COMPANION_KEYWORD, CONSTRUCTOR_KEYWORD, INIT_KEYWORD, SEALED_KEYWORD + DYNAMIC_KEYWORD, COMPANION_KEYWORD, CONSTRUCTOR_KEYWORD, INIT_KEYWORD, SEALED_KEYWORD, + FIELD_KEYWORD ); /* diff --git a/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java b/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java index e313cbec9d1..823a9ee4b2d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java @@ -64,6 +64,7 @@ public class JetParsing extends AbstractJetParsing { private static final TokenSet LAMBDA_VALUE_PARAMETER_FIRST = TokenSet.orSet(TokenSet.create(IDENTIFIER, LBRACKET), MODIFIER_KEYWORDS); private static final TokenSet SOFT_KEYWORDS_AT_MEMBER_START = TokenSet.create(CONSTRUCTOR_KEYWORD, INIT_KEYWORD); + private static final TokenSet ANNOTATION_TARGETS = TokenSet.create(FILE_KEYWORD, FIELD_KEYWORD); static JetParsing createForTopLevel(SemanticWhitespaceAwarePsiBuilder builder) { JetParsing jetParsing = new JetParsing(builder); @@ -544,7 +545,7 @@ public class JetParsing extends AbstractJetParsing { * ; * * annotationPrefix: - * : ("@" (":" "file")?) + * : ("@" (":" ("file" | "field"))?) * ; */ private boolean parseAnnotationOrList(AnnotationParsingMode mode) { @@ -556,7 +557,7 @@ public class JetParsing extends AbstractJetParsing { IElementType tokenToMatch = nextRawToken; boolean isTargetedAnnotation = false; - if ((nextRawToken == IDENTIFIER || nextRawToken == FILE_KEYWORD) && lookahead(2) == COLON) { + if ((nextRawToken == IDENTIFIER || ANNOTATION_TARGETS.contains(nextRawToken)) && lookahead(2) == COLON) { tokenToMatch = lookahead(3); isTargetedAnnotation = true; } @@ -578,7 +579,7 @@ public class JetParsing extends AbstractJetParsing { errorAndAdvance("Expected annotation identifier after ':'", 2); // AT, COLON } else { - errorAndAdvance("Expected annotation identifier after '@file:'", 3); // AT, FILE_KEYWORD, COLON + errorAndAdvance("Expected annotation identifier after ':'", 3); // AT, (ANNOTATION TARGET KEYWORD), COLON } } else { @@ -634,7 +635,7 @@ public class JetParsing extends AbstractJetParsing { // Returns true if we should continue parse annotation private boolean parseAnnotationTargetIfNeeded(AnnotationParsingMode mode) { - String expectedAnnotationTargetBeforeColon = "Expected 'file' keyword before ':'"; + String expectedAnnotationTargetBeforeColon = "Expected annotation target before ':'"; if (at(COLON)) { // recovery for "@:ann" @@ -666,19 +667,29 @@ public class JetParsing extends AbstractJetParsing { private void parseAnnotationTarget(AnnotationParsingMode mode, JetKeywordToken keyword) { if (keyword == FILE_KEYWORD && !mode.isFileAnnotationParsingMode && at(keyword) && lookahead(1) == COLON) { - errorAndAdvance("File annotations are only allowed before package declaration", 2); + errorAndAdvance(AT.getValue() + keyword.getValue() + " annotations are only allowed before package declaration", 2); return; } String message = "Expecting \"" + keyword.getValue() + COLON.getValue() + "\" prefix for " + keyword.getValue() + " annotations"; - expect(keyword, message); + PsiBuilder.Marker marker = mark(); + + if (!expect(keyword, message)) { + marker.drop(); + } + else { + marker.done(ANNOTATION_TARGET); + } + expect(COLON, message, TokenSet.create(IDENTIFIER, RBRACKET, LBRACKET)); } @Nullable private JetKeywordToken atTargetKeyword() { - if (at(FILE_KEYWORD)) return (JetKeywordToken) FILE_KEYWORD; + for (IElementType target : ANNOTATION_TARGETS.getTypes()) { + if (at(target)) return (JetKeywordToken) target; + } return null; } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetAnnotation.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetAnnotation.java index 676abdbebad..15f5186a4c0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetAnnotation.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetAnnotation.java @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.psi; import com.intellij.lang.ASTNode; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub; import org.jetbrains.kotlin.psi.stubs.elements.JetStubElementTypes; @@ -41,4 +42,9 @@ public class JetAnnotation extends JetElementImplStub getEntries() { return getStubOrPsiChildrenAsList(JetStubElementTypes.ANNOTATION_ENTRY); } + + @Nullable + public JetAnnotationUseSiteTarget getUseSiteTarget() { + return getStubOrPsiChild(JetStubElementTypes.ANNOTATION_TARGET); + } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetAnnotationEntry.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetAnnotationEntry.java index 5b7a1d031b0..ebba0e692dd 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetAnnotationEntry.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetAnnotationEntry.java @@ -107,4 +107,18 @@ public class JetAnnotationEntry extends JetElementImplStub> { + + constructor(node: ASTNode) : super(node) + + constructor(stub: KotlinPlaceHolderStub) : super(stub, JetStubElementTypes.ANNOTATION_TARGET) + + override fun accept(visitor: JetVisitor, data: D) = visitor.visitAnnotationUseSiteTarget(this, data) + + public fun getAnnotationUseSiteTarget(): AnnotationUseSiteTarget { + val node = getFirstChild().getNode() + return when (node.getElementType()) { + JetTokens.FIELD_KEYWORD -> AnnotationUseSiteTarget.FIELD + JetTokens.FILE_KEYWORD -> AnnotationUseSiteTarget.FILE + else -> throw IllegalStateException("Unknown annotation target " + node.getText()) + } + } + +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetVisitor.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetVisitor.java index ff7e98bc42d..fd9012abb70 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/JetVisitor.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/JetVisitor.java @@ -106,6 +106,10 @@ public class JetVisitor extends PsiElementVisitor { return visitJetElement(annotationEntry, data); } + public R visitAnnotationUseSiteTarget(@NotNull JetAnnotationUseSiteTarget annotationTarget, D data) { + return visitJetElement(annotationTarget, data); + } + public R visitConstructorCalleeExpression(@NotNull JetConstructorCalleeExpression constructorCalleeExpression, D data) { return visitJetElement(constructorCalleeExpression, data); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/stubs/elements/JetStubElementTypes.java b/compiler/frontend/src/org/jetbrains/kotlin/psi/stubs/elements/JetStubElementTypes.java index fa100b48255..aa2bdf5575f 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/stubs/elements/JetStubElementTypes.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/stubs/elements/JetStubElementTypes.java @@ -50,6 +50,9 @@ public interface JetStubElementTypes { JetPlaceHolderStubElementType ANNOTATION = new JetPlaceHolderStubElementType("ANNOTATION", JetAnnotation.class); + JetPlaceHolderStubElementType ANNOTATION_TARGET = + new JetPlaceHolderStubElementType("ANNOTATION_TARGET", JetAnnotationUseSiteTarget.class); + JetPlaceHolderStubElementType CLASS_BODY = new JetPlaceHolderStubElementType("CLASS_BODY", JetClassBody.class); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationResolver.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationResolver.java index f986ff5cb05..bdaf2f2142e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationResolver.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationResolver.java @@ -21,13 +21,11 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor; +import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget; import org.jetbrains.kotlin.descriptors.annotations.Annotations; import org.jetbrains.kotlin.descriptors.annotations.AnnotationsImpl; import org.jetbrains.kotlin.diagnostics.Errors; -import org.jetbrains.kotlin.psi.JetAnnotationEntry; -import org.jetbrains.kotlin.psi.JetModifierList; -import org.jetbrains.kotlin.psi.JetTypeParameter; -import org.jetbrains.kotlin.psi.JetTypeReference; +import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.calls.CallResolver; import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument; import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults; @@ -45,6 +43,8 @@ import org.jetbrains.kotlin.types.ErrorUtils; import org.jetbrains.kotlin.types.JetType; import javax.inject.Inject; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import static org.jetbrains.kotlin.diagnostics.Errors.NOT_AN_ANNOTATION_CLASS; @@ -130,7 +130,8 @@ public class AnnotationResolver { boolean shouldResolveArguments ) { if (annotationEntryElements.isEmpty()) return Annotations.EMPTY; - List result = Lists.newArrayList(); + List result = new ArrayList(0); + for (JetAnnotationEntry entryElement : annotationEntryElements) { AnnotationDescriptor descriptor = trace.get(BindingContext.ANNOTATION, entryElement); if (descriptor == null) { @@ -140,9 +141,15 @@ public class AnnotationResolver { ForceResolveUtil.forceResolveAllContents(descriptor); } - result.add(descriptor); + JetAnnotationUseSiteTarget target = entryElement.getUseSiteTarget(); + if (target != null) { + result.add(new AnnotationWithTarget(descriptor, target.getAnnotationUseSiteTarget())); + } + else { + result.add(new AnnotationWithTarget(descriptor, null)); + } } - return new AnnotationsImpl(result); + return AnnotationsImpl.create(result); } @NotNull diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java index d095b54e868..035e661d17c 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java @@ -23,6 +23,10 @@ import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; 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.AnnotationWithTarget; +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0; import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1; import org.jetbrains.kotlin.lexer.JetKeywordToken; import org.jetbrains.kotlin.lexer.JetModifierKeywordToken; @@ -34,6 +38,9 @@ import java.util.*; import static org.jetbrains.kotlin.diagnostics.Errors.NESTED_CLASS_NOT_ALLOWED; import static org.jetbrains.kotlin.lexer.JetTokens.*; import static org.jetbrains.kotlin.psi.JetStubbedPsiUtil.getContainingDeclaration; +import static org.jetbrains.kotlin.resolve.BindingContext.BACKING_FIELD_REQUIRED; +import static org.jetbrains.kotlin.diagnostics.Errors.INAPPLICABLE_FIELD_TARGET_NO_BACKING_FIELD; +import static org.jetbrains.kotlin.diagnostics.Errors.INAPPLICABLE_FIELD_TARGET; public class ModifiersChecker { private static final Collection MODALITY_MODIFIERS = @@ -183,6 +190,7 @@ public class ModifiersChecker { checkNestedClassAllowed(modifierListOwner, descriptor); ModifierCheckerCore.INSTANCE$.check(modifierListOwner, trace, descriptor); checkTypeParametersModifiers(modifierListOwner); + checkAnnotationUseSiteTargetApplicability(modifierListOwner, descriptor); runDeclarationCheckers(modifierListOwner, descriptor); ClassDescriptor classDescriptor = descriptor instanceof ClassDescriptor ? (ClassDescriptor) descriptor : null; annotationChecker.check(modifierListOwner, trace, classDescriptor); @@ -192,6 +200,7 @@ public class ModifiersChecker { @NotNull JetDeclaration modifierListOwner, @NotNull DeclarationDescriptor descriptor ) { + checkAnnotationUseSiteTargetApplicability(modifierListOwner, descriptor); runDeclarationCheckers(modifierListOwner, descriptor); annotationChecker.check(modifierListOwner, trace, descriptor instanceof ClassDescriptor ? (ClassDescriptor) descriptor : null); @@ -209,6 +218,59 @@ public class ModifiersChecker { } } + + private void checkAnnotationUseSiteTargetApplicability( + @NotNull JetDeclaration modifierListOwner, + @NotNull DeclarationDescriptor descriptor + ) { + for (AnnotationWithTarget annotationWithTarget : descriptor.getAnnotations().getUseSiteTargetedAnnotations()) { + AnnotationDescriptor annotation = annotationWithTarget.getAnnotation(); + AnnotationUseSiteTarget target = annotationWithTarget.getTarget(); + if (target == null) return; + + switch (target) { + case FIELD: + checkFieldTargetApplicability(modifierListOwner, descriptor, annotation); + break; + case FILE: + throw new IllegalArgumentException("@file annotations are not allowed here"); + } + } + } + + private void checkFieldTargetApplicability( + JetDeclaration modifierListOwner, + DeclarationDescriptor descriptor, + AnnotationDescriptor annotation + ) { + if (reportIfNotPropertyDescriptor(descriptor, annotation, INAPPLICABLE_FIELD_TARGET)) return; + + PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor; + boolean hasDelegate = modifierListOwner instanceof JetProperty && ((JetProperty) modifierListOwner).hasDelegate(); + + if (!hasDelegate && Boolean.FALSE.equals(trace.getBindingContext().get(BACKING_FIELD_REQUIRED, propertyDescriptor))) { + reportAnnotationTargetNotApplicable(annotation, INAPPLICABLE_FIELD_TARGET_NO_BACKING_FIELD); + } + } + + private boolean reportIfNotPropertyDescriptor( + DeclarationDescriptor descriptor, + AnnotationDescriptor annotation, + DiagnosticFactory0 diagnosticFactory + ) { + if (!(descriptor instanceof PropertyDescriptor)) { + reportAnnotationTargetNotApplicable(annotation, diagnosticFactory); + return true; + } + return false; + } + + private void reportAnnotationTargetNotApplicable(AnnotationDescriptor annotation, DiagnosticFactory0 diagnosticFactory) { + JetAnnotationEntry annotationEntry = DescriptorToSourceUtils.getSourceFromAnnotation(annotation); + if (annotationEntry == null) return; + trace.report(diagnosticFactory.on(annotationEntry)); + } + @NotNull public Map getTokensCorrespondingToModifiers( @NotNull JetModifierList modifierList, diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/ForceResolveUtil.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/ForceResolveUtil.java index f23adca5641..cf1d9908528 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/ForceResolveUtil.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/ForceResolveUtil.java @@ -21,6 +21,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor; +import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget; import org.jetbrains.kotlin.descriptors.annotations.Annotations; import org.jetbrains.kotlin.resolve.scopes.JetScope; import org.jetbrains.kotlin.types.JetType; @@ -62,8 +63,8 @@ public class ForceResolveUtil { public static void forceResolveAllContents(@NotNull Annotations annotations) { doForceResolveAllContents(annotations); - for (AnnotationDescriptor annotation : annotations) { - doForceResolveAllContents(annotation); + for (AnnotationWithTarget annotationWithTarget : annotations.getAllAnnotations()) { + doForceResolveAllContents(annotationWithTarget.getAnnotation()); } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyAnnotations.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyAnnotations.kt index 536851e6366..be29aeac7be 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyAnnotations.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyAnnotations.kt @@ -18,9 +18,13 @@ package org.jetbrains.kotlin.resolve.lazy.descriptors import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget +import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.lexer.JetTokens import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.JetAnnotationEntry +import org.jetbrains.kotlin.psi.JetAnnotationUseSiteTarget import org.jetbrains.kotlin.resolve.AnnotationResolver import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTrace @@ -33,9 +37,9 @@ import org.jetbrains.kotlin.resolve.source.toSourceElement import org.jetbrains.kotlin.storage.StorageManager abstract class LazyAnnotationsContext( - val annotationResolver: AnnotationResolver, - val storageManager: StorageManager, - val trace: BindingTrace + val annotationResolver: AnnotationResolver, + val storageManager: StorageManager, + val trace: BindingTrace ) { abstract val scope: LexicalScope } @@ -55,7 +59,10 @@ public class LazyAnnotations( private val annotation = c.storageManager.createMemoizedFunction { entry: JetAnnotationEntry -> - LazyAnnotationDescriptor(c, entry) + + val descriptor = LazyAnnotationDescriptor(c, entry) + val target = entry.getUseSiteTarget()?.getAnnotationUseSiteTarget() + AnnotationWithTarget(descriptor, target) } override fun findAnnotation(fqName: FqName): AnnotationDescriptor? { @@ -65,8 +72,7 @@ public class LazyAnnotations( val annotationType = annotationDescriptor.getType() if (annotationType.isError()) continue - val descriptor = annotationType.getConstructor().getDeclarationDescriptor() - if (descriptor == null) continue + val descriptor = annotationType.getConstructor().getDeclarationDescriptor() ?: continue if (DescriptorUtils.getFqNameSafe(descriptor) == fqName) { return annotationDescriptor @@ -78,11 +84,29 @@ public class LazyAnnotations( override fun findExternalAnnotation(fqName: FqName) = null - override fun iterator(): Iterator = annotationEntries.asSequence().map(annotation).iterator() + override fun getUseSiteTargetedAnnotations(): List { + return annotationEntries + .asSequence() + .map { + val (descriptor, target) = annotation(it) + if (target == null) null else AnnotationWithTarget(descriptor, target) + }.filterNotNull().toList() + } + + override fun getAllAnnotations() = annotationEntries.map(annotation) + + override fun iterator(): Iterator { + return annotationEntries + .asSequence() + .map { + val (descriptor, target) = annotation(it) + if (target == null) descriptor else null // Filter out annotations with target + }.filterNotNull().iterator() + } override fun forceResolveAllContents() { // To resolve all entries - this.toList() + getAllAnnotations() } } diff --git a/compiler/testData/psi/DocCommentAfterFileAnnotations.txt b/compiler/testData/psi/DocCommentAfterFileAnnotations.txt index 27adf18aaa2..938ea56dfc0 100644 --- a/compiler/testData/psi/DocCommentAfterFileAnnotations.txt +++ b/compiler/testData/psi/DocCommentAfterFileAnnotations.txt @@ -2,7 +2,8 @@ JetFile: DocCommentAfterFileAnnotations.kt FILE_ANNOTATION_LIST ANNOTATION PsiElement(AT)('@') - PsiElement(file)('file') + ANNOTATION_TARGET + PsiElement(file)('file') PsiElement(COLON)(':') PsiElement(LBRACKET)('[') ANNOTATION_ENTRY diff --git a/compiler/testData/psi/LineCommentAfterFileAnnotations.txt b/compiler/testData/psi/LineCommentAfterFileAnnotations.txt index 2ea22297fa9..c1885589420 100644 --- a/compiler/testData/psi/LineCommentAfterFileAnnotations.txt +++ b/compiler/testData/psi/LineCommentAfterFileAnnotations.txt @@ -2,7 +2,8 @@ JetFile: LineCommentAfterFileAnnotations.kt FILE_ANNOTATION_LIST ANNOTATION PsiElement(AT)('@') - PsiElement(file)('file') + ANNOTATION_TARGET + PsiElement(file)('file') PsiElement(COLON)(':') PsiElement(LBRACKET)('[') ANNOTATION_ENTRY @@ -25,4 +26,4 @@ JetFile: LineCommentAfterFileAnnotations.kt PsiElement(IDENTIFIER)('C') CLASS_BODY PsiElement(LBRACE)('{') - PsiElement(RBRACE)('}') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/onFile/fileAnnotationInWrongPlace.txt b/compiler/testData/psi/annotation/onFile/fileAnnotationInWrongPlace.txt index 607686a4567..5f8a4cbb3d8 100644 --- a/compiler/testData/psi/annotation/onFile/fileAnnotationInWrongPlace.txt +++ b/compiler/testData/psi/annotation/onFile/fileAnnotationInWrongPlace.txt @@ -11,7 +11,7 @@ JetFile: fileAnnotationInWrongPlace.kt MODIFIER_LIST ANNOTATION_ENTRY PsiElement(AT)('@') - PsiErrorElement:File annotations are only allowed before package declaration + PsiErrorElement:@file annotations are only allowed before package declaration PsiElement(file)('file') PsiElement(COLON)(':') CONSTRUCTOR_CALLEE @@ -28,7 +28,7 @@ JetFile: fileAnnotationInWrongPlace.kt MODIFIER_LIST ANNOTATION PsiElement(AT)('@') - PsiErrorElement:File annotations are only allowed before package declaration + PsiErrorElement:@file annotations are only allowed before package declaration PsiElement(file)('file') PsiElement(COLON)(':') PsiElement(LBRACKET)('[') @@ -62,7 +62,7 @@ JetFile: fileAnnotationInWrongPlace.kt MODIFIER_LIST ANNOTATION PsiElement(AT)('@') - PsiErrorElement:File annotations are only allowed before package declaration + PsiErrorElement:@file annotations are only allowed before package declaration PsiElement(file)('file') PsiElement(COLON)(':') PsiElement(LBRACKET)('[') @@ -80,7 +80,7 @@ JetFile: fileAnnotationInWrongPlace.kt PsiWhiteSpace('\n\n') CLASS MODIFIER_LIST - PsiErrorElement:Expected annotation identifier after '@file:' + PsiErrorElement:Expected annotation identifier after ':' PsiElement(AT)('@') PsiElement(IDENTIFIER)('file') PsiElement(COLON)(':') @@ -93,7 +93,7 @@ JetFile: fileAnnotationInWrongPlace.kt MODIFIER_LIST ANNOTATION PsiElement(AT)('@') - PsiErrorElement:File annotations are only allowed before package declaration + PsiErrorElement:@file annotations are only allowed before package declaration PsiElement(file)('file') PsiElement(COLON)(':') PsiElement(LBRACKET)('[') @@ -103,4 +103,4 @@ JetFile: fileAnnotationInWrongPlace.kt PsiWhiteSpace('\n') PsiElement(trait)('trait') PsiWhiteSpace(' ') - PsiElement(IDENTIFIER)('T') + PsiElement(IDENTIFIER)('T') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/onFile/manyAnnotationBlocks.txt b/compiler/testData/psi/annotation/onFile/manyAnnotationBlocks.txt index ccf3eb36b58..033e89e1524 100644 --- a/compiler/testData/psi/annotation/onFile/manyAnnotationBlocks.txt +++ b/compiler/testData/psi/annotation/onFile/manyAnnotationBlocks.txt @@ -2,7 +2,8 @@ JetFile: manyAnnotationBlocks.kt FILE_ANNOTATION_LIST ANNOTATION_ENTRY PsiElement(AT)('@') - PsiElement(file)('file') + ANNOTATION_TARGET + PsiElement(file)('file') PsiElement(COLON)(':') CONSTRUCTOR_CALLEE TYPE_REFERENCE @@ -12,7 +13,8 @@ JetFile: manyAnnotationBlocks.kt PsiWhiteSpace('\n') ANNOTATION PsiElement(AT)('@') - PsiElement(file)('file') + ANNOTATION_TARGET + PsiElement(file)('file') PsiElement(COLON)(':') PsiElement(LBRACKET)('[') ANNOTATION_ENTRY diff --git a/compiler/testData/psi/annotation/onFile/manyInOneAnnotationBlock.txt b/compiler/testData/psi/annotation/onFile/manyInOneAnnotationBlock.txt index 91dd8b88fb1..7889e910cce 100644 --- a/compiler/testData/psi/annotation/onFile/manyInOneAnnotationBlock.txt +++ b/compiler/testData/psi/annotation/onFile/manyInOneAnnotationBlock.txt @@ -2,7 +2,8 @@ JetFile: manyInOneAnnotationBlock.kt FILE_ANNOTATION_LIST ANNOTATION PsiElement(AT)('@') - PsiElement(file)('file') + ANNOTATION_TARGET + PsiElement(file)('file') PsiElement(COLON)(':') PsiWhiteSpace(' ') PsiElement(LBRACKET)('[') diff --git a/compiler/testData/psi/annotation/onFile/nonFIleAnnotationBeforePackage.txt b/compiler/testData/psi/annotation/onFile/nonFIleAnnotationBeforePackage.txt index 389c4c5cd58..5728eb2cb58 100644 --- a/compiler/testData/psi/annotation/onFile/nonFIleAnnotationBeforePackage.txt +++ b/compiler/testData/psi/annotation/onFile/nonFIleAnnotationBeforePackage.txt @@ -12,7 +12,8 @@ JetFile: nonFIleAnnotationBeforePackage.kt PsiWhiteSpace('\n') ANNOTATION_ENTRY PsiElement(AT)('@') - PsiElement(file)('file') + ANNOTATION_TARGET + PsiElement(file)('file') PsiWhiteSpace(' ') PsiErrorElement:Expecting "file:" prefix for file annotations PsiElement(AT)('@') @@ -22,14 +23,14 @@ JetFile: nonFIleAnnotationBeforePackage.kt REFERENCE_EXPRESSION PsiElement(IDENTIFIER)('bar') PsiWhiteSpace('\n') - PsiErrorElement:Expected annotation identifier after '@file:' + PsiErrorElement:Expected annotation identifier after ':' PsiElement(AT)('@') PsiElement(IDENTIFIER)('file') PsiElement(COLON)(':') PsiWhiteSpace('\n') ANNOTATION_ENTRY PsiElement(AT)('@') - PsiErrorElement:Expected 'file' keyword before ':' + PsiErrorElement:Expected annotation target before ':' PsiElement(COLON)(':') CONSTRUCTOR_CALLEE TYPE_REFERENCE @@ -39,7 +40,7 @@ JetFile: nonFIleAnnotationBeforePackage.kt PsiWhiteSpace('\n') ANNOTATION_ENTRY PsiElement(AT)('@') - PsiErrorElement:Expected 'file' keyword before ':' + PsiErrorElement:Expected annotation target before ':' PsiElement(IDENTIFIER)('fil') PsiElement(COLON)(':') CONSTRUCTOR_CALLEE diff --git a/compiler/testData/psi/annotation/onFile/single.txt b/compiler/testData/psi/annotation/onFile/single.txt index 55e9a68f034..b63873e0aaf 100644 --- a/compiler/testData/psi/annotation/onFile/single.txt +++ b/compiler/testData/psi/annotation/onFile/single.txt @@ -2,7 +2,8 @@ JetFile: single.kt FILE_ANNOTATION_LIST ANNOTATION_ENTRY PsiElement(AT)('@') - PsiElement(file)('file') + ANNOTATION_TARGET + PsiElement(file)('file') PsiElement(COLON)(':') CONSTRUCTOR_CALLEE TYPE_REFERENCE diff --git a/compiler/testData/psi/annotation/onFile/withoutPackage.txt b/compiler/testData/psi/annotation/onFile/withoutPackage.txt index 56221af03c9..0a598349cfe 100644 --- a/compiler/testData/psi/annotation/onFile/withoutPackage.txt +++ b/compiler/testData/psi/annotation/onFile/withoutPackage.txt @@ -2,7 +2,8 @@ JetFile: withoutPackage.kt FILE_ANNOTATION_LIST ANNOTATION_ENTRY PsiElement(AT)('@') - PsiElement(file)('file') + ANNOTATION_TARGET + PsiElement(file)('file') PsiElement(COLON)(':') CONSTRUCTOR_CALLEE TYPE_REFERENCE @@ -34,7 +35,7 @@ JetFile: withoutPackage.kt PsiWhiteSpace('\n') ANNOTATION PsiElement(AT)('@') - PsiErrorElement:File annotations are only allowed before package declaration + PsiErrorElement:@file annotations are only allowed before package declaration PsiElement(file)('file') PsiElement(COLON)(':') PsiElement(LBRACKET)('[') @@ -55,4 +56,4 @@ JetFile: withoutPackage.kt PsiWhiteSpace(' ') BLOCK PsiElement(LBRACE)('{') - PsiElement(RBRACE)('}') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/annotation/onFile/withoutPackageWithSimpleAnnotation.txt b/compiler/testData/psi/annotation/onFile/withoutPackageWithSimpleAnnotation.txt index e3b6ab4809d..3b58e690a47 100644 --- a/compiler/testData/psi/annotation/onFile/withoutPackageWithSimpleAnnotation.txt +++ b/compiler/testData/psi/annotation/onFile/withoutPackageWithSimpleAnnotation.txt @@ -2,7 +2,8 @@ JetFile: withoutPackageWithSimpleAnnotation.kt FILE_ANNOTATION_LIST ANNOTATION_ENTRY PsiElement(AT)('@') - PsiElement(file)('file') + ANNOTATION_TARGET + PsiElement(file)('file') PsiElement(COLON)(':') CONSTRUCTOR_CALLEE TYPE_REFERENCE @@ -32,7 +33,7 @@ JetFile: withoutPackageWithSimpleAnnotation.kt PsiWhiteSpace('\n') ANNOTATION_ENTRY PsiElement(AT)('@') - PsiErrorElement:File annotations are only allowed before package declaration + PsiErrorElement:@file annotations are only allowed before package declaration PsiElement(file)('file') PsiElement(COLON)(':') PsiWhiteSpace(' ') @@ -51,4 +52,4 @@ JetFile: withoutPackageWithSimpleAnnotation.kt PsiWhiteSpace(' ') BLOCK PsiElement(LBRACE)('{') - PsiElement(RBRACE)('}') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/testData/psi/script/manyAnnotationsOnFile.txt b/compiler/testData/psi/script/manyAnnotationsOnFile.txt index 9ce81939a0f..0ef543e85ce 100644 --- a/compiler/testData/psi/script/manyAnnotationsOnFile.txt +++ b/compiler/testData/psi/script/manyAnnotationsOnFile.txt @@ -2,7 +2,8 @@ JetFile: manyAnnotationsOnFile.kts FILE_ANNOTATION_LIST ANNOTATION PsiElement(AT)('@') - PsiElement(file)('file') + ANNOTATION_TARGET + PsiElement(file)('file') PsiElement(COLON)(':') PsiElement(LBRACKET)('[') PsiWhiteSpace(' ') @@ -16,7 +17,8 @@ JetFile: manyAnnotationsOnFile.kts PsiWhiteSpace('\n') ANNOTATION PsiElement(AT)('@') - PsiElement(file)('file') + ANNOTATION_TARGET + PsiElement(file)('file') PsiElement(COLON)(':') PsiElement(LBRACKET)('[') ANNOTATION_ENTRY @@ -43,4 +45,4 @@ JetFile: manyAnnotationsOnFile.kts SCRIPT BLOCK - + \ No newline at end of file diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaAnnotations.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaAnnotations.kt index 5bb1d0f58e0..c4bfa94eb1f 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaAnnotations.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/lazy/LazyJavaAnnotations.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.load.java.lazy import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.load.java.components.JavaAnnotationMapper import org.jetbrains.kotlin.load.java.structure.JavaAnnotation @@ -38,6 +39,10 @@ class LazyJavaAnnotations( override fun findExternalAnnotation(fqName: FqName) = c.components.externalAnnotationResolver.findExternalAnnotation(annotationOwner, fqName)?.let(annotationDescriptors) + override fun getUseSiteTargetedAnnotations() = emptyList() + + override fun getAllAnnotations() = this.map { AnnotationWithTarget(it, null) } + override fun iterator() = (annotationOwner.annotations.asSequence().map(annotationDescriptors) + JavaAnnotationMapper.findMappedJavaAnnotation(KotlinBuiltIns.FQ_NAMES.annotation, annotationOwner, c) diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhacement/typeEnhancement.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhacement/typeEnhancement.kt index 1d82fd5d204..f4a2f3ae679 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhacement/typeEnhancement.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/java/typeEnhacement/typeEnhancement.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.load.java.typeEnhacement import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.annotations.CompositeAnnotations import org.jetbrains.kotlin.load.java.JvmAnnotationNames @@ -175,6 +176,10 @@ private class EnhancedTypeAnnotations(private val fqNameToMatch: FqName) : Annot override fun findExternalAnnotation(fqName: FqName) = null + override fun getAllAnnotations() = this.map { AnnotationWithTarget(it, null) } + + override fun getUseSiteTargetedAnnotations() = emptyList() + // Note, that this class may break Annotations contract (!isEmpty && iterator.isEmpty()) // It's a hack that we need unless we have stable "user data" in JetType override fun iterator(): Iterator = emptyList().iterator() diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationUseSiteTarget.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationUseSiteTarget.kt new file mode 100644 index 00000000000..951c5863f12 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationUseSiteTarget.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.descriptors.annotations + +public enum class AnnotationUseSiteTarget() { + FIELD, + FILE +} \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationWithTarget.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationWithTarget.kt new file mode 100644 index 00000000000..112573957b5 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationWithTarget.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.descriptors.annotations + +import org.jetbrains.kotlin.name.FqName + +public data class AnnotationWithTarget(val annotation: AnnotationDescriptor, val target: AnnotationUseSiteTarget?) + +private class UseSiteTargetedAnnotations( + private val original: Annotations, + private val additionalAnnotations: Annotations +) : Annotations { + override fun isEmpty() = original.isEmpty() + + override fun findAnnotation(fqName: FqName) = original.findAnnotation(fqName) + + override fun findExternalAnnotation(fqName: FqName) = original.findExternalAnnotation(fqName) + + private fun getAdditionalTargetedAnnotations() = additionalAnnotations.getUseSiteTargetedAnnotations() + + override fun getUseSiteTargetedAnnotations(): List { + return original.getUseSiteTargetedAnnotations() + getAdditionalTargetedAnnotations() + } + + override fun getAllAnnotations(): List { + return original.getAllAnnotations() + getAdditionalTargetedAnnotations() + } + + override fun iterator() = original.iterator() +} + +public class AnnotatedWithAdditionalAnnotations( + delegate: Annotated?, + additional: Annotated +) : Annotated { + private val annotations: Annotations = UseSiteTargetedAnnotations(delegate?.annotations ?: Annotations.EMPTY, additional.annotations) + + override fun getAnnotations() = annotations +} \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt index 21b50911beb..ad0528af08e 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/Annotations.kt @@ -27,6 +27,20 @@ public interface Annotations : Iterable { public fun findExternalAnnotation(fqName: FqName): AnnotationDescriptor? + public fun getUseSiteTargetedAnnotations(): List + + public fun getUseSiteTargetedAnnotations(target: AnnotationUseSiteTarget): List { + return getUseSiteTargetedAnnotations().fold(arrayListOf()) { list, targeted -> + if (target == targeted.target) { + list.add(targeted.annotation) + } + list + } + } + + // Returns both targeted and annotations without target. Annotation order is preserved. + public fun getAllAnnotations(): List + companion object { public val EMPTY: Annotations = object : Annotations { override fun isEmpty() = true @@ -35,6 +49,10 @@ public interface Annotations : Iterable { override fun findExternalAnnotation(fqName: FqName) = null + override fun getUseSiteTargetedAnnotations() = emptyList() + + override fun getAllAnnotations() = emptyList() + override fun iterator() = emptyList().iterator() override fun toString() = "EMPTY" @@ -54,14 +72,22 @@ class FilteredAnnotations( if (fqNameFilter(fqName)) delegate.findExternalAnnotation(fqName) else null - override fun iterator() = delegate.asSequence() - .filter { annotation -> - val descriptor = annotation.getType().getConstructor().getDeclarationDescriptor() - descriptor != null && DescriptorUtils.getFqName(descriptor).let { fqName -> - fqName.isSafe() && fqNameFilter(fqName.toSafe()) - } - } - .iterator() + override fun getUseSiteTargetedAnnotations(): List { + return delegate.getUseSiteTargetedAnnotations().filter { shouldBeReturned(it.annotation) } + } + + override fun getAllAnnotations(): List { + return delegate.getAllAnnotations().filter { shouldBeReturned(it.annotation) } + } + + override fun iterator() = delegate.filter { shouldBeReturned(it) }.iterator() + + private fun shouldBeReturned(annotation: AnnotationDescriptor): Boolean { + val descriptor = annotation.getType().getConstructor().getDeclarationDescriptor() + return descriptor != null && DescriptorUtils.getFqName(descriptor).let { fqName -> + fqName.isSafe() && fqNameFilter(fqName.toSafe()) + } + } override fun isEmpty() = !iterator().hasNext() } @@ -77,5 +103,9 @@ class CompositeAnnotations( override fun findExternalAnnotation(fqName: FqName) = delegates.asSequence().map { it.findExternalAnnotation(fqName) }.filterNotNull().firstOrNull() + override fun getUseSiteTargetedAnnotations() = delegates.flatMap { it.getUseSiteTargetedAnnotations() } + + override fun getAllAnnotations() = delegates.flatMap { it.getAllAnnotations() } + override fun iterator() = delegates.asSequence().flatMap { it.asSequence() }.iterator() } \ No newline at end of file diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationsImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationsImpl.kt index f033e132ce4..d7be00cb266 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationsImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/annotations/AnnotationsImpl.kt @@ -19,8 +19,26 @@ package org.jetbrains.kotlin.descriptors.annotations import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.DescriptorUtils +import kotlin.platform.platformStatic + +public class AnnotationsImpl : Annotations { + private val annotations: List + private val targetedAnnotations: List + + constructor(annotations: List) { + this.annotations = annotations + this.targetedAnnotations = annotations.map { AnnotationWithTarget(it, null) } + } + + // List and List have the same signature + private constructor( + targetedAnnotations: List, + @suppress("UNUSED_PARAMETER") i: Int + ) { + this.targetedAnnotations = targetedAnnotations + this.annotations = targetedAnnotations.filter { it.target == null }.map { it.annotation } + } -public class AnnotationsImpl(private val annotations: List) : Annotations { override fun isEmpty() = annotations.isEmpty() override fun findAnnotation(fqName: FqName) = annotations.firstOrNull { @@ -28,9 +46,24 @@ public class AnnotationsImpl(private val annotations: List descriptor is ClassDescriptor && fqName.toUnsafe() == DescriptorUtils.getFqName(descriptor) } + override fun getUseSiteTargetedAnnotations(): List { + return targetedAnnotations + .filter { it.target != null } + .map { AnnotationWithTarget(it.annotation, it.target!!) } + } + + override fun getAllAnnotations() = targetedAnnotations + override fun findExternalAnnotation(fqName: FqName) = null override fun iterator() = annotations.iterator() override fun toString() = annotations.toString() + + companion object { + platformStatic + public fun create(annotationsWithTargets: List): AnnotationsImpl { + return AnnotationsImpl(annotationsWithTargets, 0) + } + } } diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedAnnotations.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedAnnotations.kt index 0889e0b8b4f..e9dadc99b8a 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedAnnotations.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedAnnotations.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.serialization.deserialization.descriptors import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -40,5 +41,9 @@ class DeserializedAnnotations( override fun findExternalAnnotation(fqName: FqName) = null + override fun getUseSiteTargetedAnnotations() = emptyList() + + override fun getAllAnnotations() = this.map { AnnotationWithTarget(it, null) } + override fun iterator(): Iterator = annotations().iterator() } diff --git a/grammar/src/attributes.grm b/grammar/src/attributes.grm index 412c3debfaf..bdbc0205be0 100644 --- a/grammar/src/attributes.grm +++ b/grammar/src/attributes.grm @@ -15,7 +15,7 @@ annotationList ; annotationPrefix: - : ("@" (":" "file")?) + : ("@" (":" ("file" | "field"))?) ; unescapedAnnotation