Annotation target checking in front-end, a set of tests for different annotation targets, existing test fixes

No checks for erroneous annotations. Additional checks for identifiers.
This commit is contained in:
Mikhail Glukhikh
2015-07-06 20:00:06 +03:00
parent 609d696202
commit 0d2a81f098
82 changed files with 1445 additions and 46 deletions
@@ -114,6 +114,7 @@ public interface Errors {
DiagnosticFactory2<PsiElement, JetModifierKeywordToken, JetModifierKeywordToken> REDUNDANT_MODIFIER = DiagnosticFactory2.create(WARNING);
DiagnosticFactory0<PsiElement> INAPPLICABLE_ANNOTATION = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<PsiElement> INAPPLICABLE_PLATFORM_NAME = DiagnosticFactory0.create(ERROR);
DiagnosticFactory1<PsiElement, String> WRONG_ANNOTATION_TARGET = DiagnosticFactory1.create(ERROR);
// Annotations
@@ -137,6 +137,7 @@ public class DefaultErrorMessages {
MAP.put(REPEATED_MODIFIER, "Repeated ''{0}''", TO_STRING);
MAP.put(INAPPLICABLE_ANNOTATION, "This annotation is only applicable to top level functions");
MAP.put(INAPPLICABLE_PLATFORM_NAME, "platformName annotation is not applicable to this declaration");
MAP.put(WRONG_ANNOTATION_TARGET, "This annotation is not applicable to target ''{0}''", TO_STRING);
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");
@@ -0,0 +1,135 @@
/*
* 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.resolve
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getAnnotationEntries
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.EnumValue
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import java.util.*
public object AnnotationTargetChecker {
// NOTE: this enum must have the same entries with kotlin.annotation.AnnotationTarget
public enum class Target(val description: String, val isDefault: Boolean = true) {
PACKAGE("package"),
CLASSIFIER("classifier"),
ANNOTATION_CLASS("annotation class"),
TYPE_PARAMETER("type parameter", false),
PROPERTY("property"),
FIELD("field"),
LOCAL_VARIABLE("local variable"),
VALUE_PARAMETER("value parameter"),
CONSTRUCTOR("constructor"),
FUNCTION("function"),
PROPERTY_GETTER("getter"),
PROPERTY_SETTER("setter"),
TYPE("type usage", false),
EXPRESSION("expression", false),
FILE("file", false)
}
private val DEFAULT_TARGET_LIST = Target.values().filter { it.isDefault }.map { it.name() }
private val ALL_TARGET_LIST = Target.values().map { it.name() }
public fun check(annotated: JetAnnotated, trace: BindingTrace) {
if (annotated is JetTypeParameter) return // TODO: support type parameter annotations
val actualTargets = getActualTargetList(annotated)
for (entry in annotated.getAnnotationEntries()) {
checkAnnotationEntry(entry, actualTargets, trace)
}
if (annotated is JetCallableDeclaration) {
annotated.getTypeReference()?.let { check(it, trace) }
}
if (annotated is JetFunction) {
for (parameter in annotated.getValueParameters()) {
if (!parameter.hasValOrVar()) {
check(parameter, trace)
if (annotated is JetFunctionLiteral) {
parameter.getTypeReference()?.let { check(it, trace) }
}
}
}
}
if (annotated is JetClassOrObject) {
for (initializer in annotated.getAnonymousInitializers()) {
check(initializer, trace)
}
}
}
public fun checkExpression(expression: JetExpression, trace: BindingTrace) {
for (entry in expression.getAnnotationEntries()) {
checkAnnotationEntry(entry, listOf(Target.EXPRESSION), trace)
}
if (expression is JetFunctionLiteralExpression) {
for (parameter in expression.getValueParameters()) {
parameter.getTypeReference()?.let { check(it, trace) }
}
}
}
private fun possibleTargetList(entry: JetAnnotationEntry, trace: BindingTrace): List<String> {
val descriptor = trace.get(BindingContext.ANNOTATION, entry) ?: return DEFAULT_TARGET_LIST
// For descriptor with error type, all targets are considered as possible
if (descriptor.getType().isError()) return ALL_TARGET_LIST
val classDescriptor = TypeUtils.getClassDescriptor(descriptor.getType()) ?: return DEFAULT_TARGET_LIST
val targetEntryDescriptor = classDescriptor.getAnnotations().findAnnotation(KotlinBuiltIns.FQ_NAMES.target)
?: return DEFAULT_TARGET_LIST
val valueArguments = targetEntryDescriptor.getAllValueArguments()
val valueArgument = valueArguments.entrySet().firstOrNull()?.getValue() as? ArrayValue ?: return DEFAULT_TARGET_LIST
return valueArgument.getValue().filterIsInstance<EnumValue>().map { it.getValue().getName().asString() }
}
private fun checkAnnotationEntry(entry: JetAnnotationEntry, actualTargets: List<Target>, trace: BindingTrace) {
val possibleTargets = possibleTargetList(entry, trace)
for (actualTarget in actualTargets) {
if (actualTarget.name() in possibleTargets) return
}
trace.report(Errors.WRONG_ANNOTATION_TARGET.on(entry, actualTargets.firstOrNull()?.description ?: "unidentified target"))
}
private fun getActualTargetList(annotated: JetAnnotated): List<Target> {
if (annotated is JetClassOrObject) {
if (annotated is JetEnumEntry) return listOf(Target.PROPERTY, Target.FIELD)
return if (annotated.isAnnotation()) listOf(Target.ANNOTATION_CLASS, Target.CLASSIFIER) else listOf(Target.CLASSIFIER)
}
if (annotated is JetProperty) {
return if (annotated.isLocal()) listOf(Target.LOCAL_VARIABLE) else listOf(Target.PROPERTY, Target.FIELD)
}
if (annotated is JetParameter) {
return if (annotated.hasValOrVar()) listOf(Target.PROPERTY, Target.FIELD) else listOf(Target.VALUE_PARAMETER)
}
if (annotated is JetConstructor<*>) return listOf(Target.CONSTRUCTOR)
if (annotated is JetFunction) return listOf(Target.FUNCTION)
if (annotated is JetPropertyAccessor) {
return if (annotated.isGetter()) listOf(Target.PROPERTY_GETTER) else listOf(Target.PROPERTY_SETTER)
}
if (annotated is JetPackageDirective) return listOf(Target.PACKAGE)
if (annotated is JetTypeReference) return listOf(Target.TYPE)
if (annotated is JetFile) return listOf(Target.FILE)
if (annotated is JetTypeParameter) return listOf(Target.TYPE_PARAMETER)
return listOf()
}
}
@@ -67,6 +67,7 @@ public class DeclarationsChecker {
public void process(@NotNull BodiesResolveContext bodiesResolveContext) {
for (JetFile file : bodiesResolveContext.getFiles()) {
checkModifiersAndAnnotationsInPackageDirective(file);
AnnotationTargetChecker.INSTANCE$.check(file, trace);
}
Map<JetClassOrObject, ClassDescriptorWithResolutionScopes> classes = bodiesResolveContext.getDeclaredClasses();
@@ -146,6 +147,7 @@ public class DeclarationsChecker {
}
}
}
AnnotationTargetChecker.INSTANCE$.check(packageDirective, trace);
ModifiersChecker.reportIllegalModifiers(modifierList, Arrays.asList(JetTokens.MODIFIER_KEYWORDS_ARRAY), trace);
}
@@ -302,6 +304,7 @@ public class DeclarationsChecker {
if (typeParameter != null) {
DescriptorResolver.checkConflictingUpperBounds(trace, typeParameter, jetTypeParameter);
}
AnnotationTargetChecker.INSTANCE$.check(jetTypeParameter, trace);
}
}
@@ -150,6 +150,7 @@ public class ModifiersChecker {
}
checkPlatformNameApplicability(descriptor);
runDeclarationCheckers(modifierListOwner, descriptor);
AnnotationTargetChecker.INSTANCE$.check(modifierListOwner, trace);
}
private void checkVarargsModifiers(@NotNull JetDeclaration owner, @NotNull MemberDescriptor descriptor) {
@@ -163,6 +164,7 @@ public class ModifiersChecker {
reportIllegalVisibilityModifiers(modifierListOwner);
checkPlatformNameApplicability(descriptor);
runDeclarationCheckers(modifierListOwner, descriptor);
AnnotationTargetChecker.INSTANCE$.check(modifierListOwner, trace);
}
public void reportIllegalModalityModifiers(@NotNull JetModifierListOwner modifierListOwner) {
@@ -24,6 +24,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.diagnostics.DiagnosticUtils;
import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.AnnotationTargetChecker;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.BindingContextUtils;
import org.jetbrains.kotlin.resolve.scopes.WritableScope;
@@ -143,7 +144,9 @@ public class ExpressionTypingVisitorDispatcher extends JetVisitor<JetTypeInfo, E
@Override
@NotNull
public final JetTypeInfo getTypeInfo(@NotNull JetExpression expression, ExpressionTypingContext context) {
return getTypeInfo(expression, context, this);
JetTypeInfo result = getTypeInfo(expression, context, this);
AnnotationTargetChecker.INSTANCE$.checkExpression(expression, context.trace);
return result;
}
@Override
@@ -29,10 +29,7 @@ import org.jetbrains.kotlin.diagnostics.Errors;
import org.jetbrains.kotlin.lexer.JetTokens;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.resolve.AnnotationResolver;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.ModifiersChecker;
import org.jetbrains.kotlin.resolve.TemporaryBindingTrace;
import org.jetbrains.kotlin.resolve.*;
import org.jetbrains.kotlin.resolve.calls.context.TemporaryTraceAndCache;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResults;