Skip to content

Commit

Permalink
Support @field: annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
yanex committed Aug 31, 2015
1 parent 0499335 commit 2bacbc9
Show file tree
Hide file tree
Showing 34 changed files with 431 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,17 +81,30 @@ private AnnotationCodegen(JetTypeMapper mapper) {
* @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<String> annotationDescriptorsAlreadyPresent = new HashSet<String>();

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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
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;
Expand Down Expand Up @@ -293,7 +294,7 @@ else if (kind != OwnerKind.PACKAGE || isDelegate) {

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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ public interface Errors {
DiagnosticFactory0<JetExpression> ANNOTATION_PARAMETER_MUST_BE_ENUM_CONST = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<JetExpression> ANNOTATION_PARAMETER_DEFAULT_VALUE_MUST_BE_CONSTANT = DiagnosticFactory0.create(ERROR);

DiagnosticFactory0<PsiElement> INAPPLICABLE_FIELD_TARGET = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> INAPPLICABLE_FIELD_TARGET_NO_BACKING_FIELD = DiagnosticFactory0.create(ERROR);

// Classes and traits

DiagnosticFactory0<JetTypeProjection> PROJECTION_IN_IMMEDIATE_ARGUMENT_TO_SUPERTYPE =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ public Unit invoke(DescriptorRendererOptions options) {
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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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
);

/*
Expand Down
25 changes: 18 additions & 7 deletions compiler/frontend/src/org/jetbrains/kotlin/parsing/JetParsing.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -544,7 +545,7 @@ boolean parseAnnotations(AnnotationParsingMode mode) {
* ;
*
* annotationPrefix:
* : ("@" (":" "file")?)
* : ("@" (":" ("file" | "field"))?)
* ;
*/
private boolean parseAnnotationOrList(AnnotationParsingMode mode) {
Expand All @@ -556,7 +557,7 @@ else if (at(AT)) {
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;
}
Expand All @@ -578,7 +579,7 @@ else if (tokenToMatch == LBRACKET) {
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 {
Expand Down Expand Up @@ -634,7 +635,7 @@ private boolean parseAnnotationList(AnnotationParsingMode mode) {

// 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"
Expand Down Expand Up @@ -666,19 +667,29 @@ else if (targetKeyword != null) {

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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

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;

Expand All @@ -41,4 +42,9 @@ public <R, D> R accept(@NotNull JetVisitor<R, D> visitor, D data) {
public List<JetAnnotationEntry> getEntries() {
return getStubOrPsiChildrenAsList(JetStubElementTypes.ANNOTATION_ENTRY);
}

@Nullable
public JetAnnotationUseSiteTarget getUseSiteTarget() {
return getStubOrPsiChild(JetStubElementTypes.ANNOTATION_TARGET);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,18 @@ public JetTypeArgumentList getTypeArgumentList() {
public PsiElement getAtSymbol() {
return findChildByType(JetTokens.AT);
}

@Nullable
public JetAnnotationUseSiteTarget getUseSiteTarget() {
JetAnnotationUseSiteTarget target = getStubOrPsiChild(JetStubElementTypes.ANNOTATION_TARGET);

if (target == null) {
PsiElement parent = getStubOrPsiParent();
if (parent instanceof JetAnnotation) {
return ((JetAnnotation) parent).getUseSiteTarget();
}
}

return target;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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.psi

import com.intellij.lang.ASTNode
import com.intellij.psi.impl.source.tree.TreeElement
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.lexer.JetKeywordToken
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.stubs.KotlinAnnotationEntryStub
import org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub
import org.jetbrains.kotlin.psi.stubs.elements.JetStubElementTypes

public class JetAnnotationUseSiteTarget : JetElementImplStub<KotlinPlaceHolderStub<JetAnnotationUseSiteTarget>> {

constructor(node: ASTNode) : super(node)

constructor(stub: KotlinPlaceHolderStub<JetAnnotationUseSiteTarget>) : super(stub, JetStubElementTypes.ANNOTATION_TARGET)

override fun <R, D> accept(visitor: JetVisitor<R, D>, 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())
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ public R visitAnnotationEntry(@NotNull JetAnnotationEntry annotationEntry, D dat
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public interface JetStubElementTypes {
JetPlaceHolderStubElementType<JetAnnotation> ANNOTATION =
new JetPlaceHolderStubElementType<JetAnnotation>("ANNOTATION", JetAnnotation.class);

JetPlaceHolderStubElementType<JetAnnotationUseSiteTarget> ANNOTATION_TARGET =
new JetPlaceHolderStubElementType<JetAnnotationUseSiteTarget>("ANNOTATION_TARGET", JetAnnotationUseSiteTarget.class);

JetPlaceHolderStubElementType<JetClassBody> CLASS_BODY =
new JetPlaceHolderStubElementType<JetClassBody>("CLASS_BODY", JetClassBody.class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@
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;
Expand All @@ -45,6 +43,8 @@
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;
Expand Down Expand Up @@ -130,7 +130,8 @@ private Annotations resolveAnnotationEntries(
boolean shouldResolveArguments
) {
if (annotationEntryElements.isEmpty()) return Annotations.EMPTY;
List<AnnotationDescriptor> result = Lists.newArrayList();
List<AnnotationWithTarget> result = new ArrayList<AnnotationWithTarget>(0);

for (JetAnnotationEntry entryElement : annotationEntryElements) {
AnnotationDescriptor descriptor = trace.get(BindingContext.ANNOTATION, entryElement);
if (descriptor == null) {
Expand All @@ -140,9 +141,15 @@ private Annotations resolveAnnotationEntries(
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
Expand Down
Loading

0 comments on commit 2bacbc9

Please sign in to comment.