Forbid using experimental markers on override declarations
#KT-45844 Fixed
This commit is contained in:
committed by
teamcityserver
parent
bb9efab3c4
commit
603afe89a2
@@ -287,6 +287,7 @@ public interface Errors {
|
||||
DiagnosticFactory0<PsiElement> EXPERIMENTAL_ANNOTATION_WITH_WRONG_RETENTION = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<KtAnnotationEntry> EXPERIMENTAL_ANNOTATION_ON_GETTER = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtAnnotationEntry> EXPERIMENTAL_ANNOTATION_ON_OVERRIDE = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory1<PsiElement, String> EXPERIMENTAL_UNSIGNED_LITERALS = DiagnosticFactory1.create(WARNING);
|
||||
DiagnosticFactory1<PsiElement, String> EXPERIMENTAL_UNSIGNED_LITERALS_ERROR = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
+1
@@ -171,6 +171,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(EXPERIMENTAL_ANNOTATION_WITH_WRONG_RETENTION, "Opt-in requirement marker annotation cannot be used with SOURCE retention. Please replace retention with BINARY");
|
||||
|
||||
MAP.put(EXPERIMENTAL_ANNOTATION_ON_GETTER, "Opt-in requirement marker annotation cannot be used on getter. Please annotate property instead");
|
||||
MAP.put(EXPERIMENTAL_ANNOTATION_ON_OVERRIDE, "Opt-in requirement marker annotation on override requires the same marker on base declaration");
|
||||
|
||||
MAP.put(EXPERIMENTAL_UNSIGNED_LITERALS, "{0}", STRING);
|
||||
MAP.put(EXPERIMENTAL_UNSIGNED_LITERALS_ERROR, "{0}", STRING);
|
||||
|
||||
+42
-1
@@ -6,13 +6,20 @@
|
||||
package org.jetbrains.kotlin.resolve.checkers
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtAnnotated
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtPropertyAccessor
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.AdditionalAnnotationChecker
|
||||
import org.jetbrains.kotlin.resolve.AnnotationChecker
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
@@ -50,12 +57,27 @@ class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleD
|
||||
isAnnotatedWithExperimental = true
|
||||
}
|
||||
}
|
||||
if (annotation.annotationClass?.annotations?.any { it.fqName in ExperimentalUsageChecker.EXPERIMENTAL_FQ_NAMES } == true) {
|
||||
val annotationClass = annotation.annotationClass ?: continue
|
||||
if (annotationClass.annotations.any { it.fqName in ExperimentalUsageChecker.EXPERIMENTAL_FQ_NAMES }) {
|
||||
if (KotlinTarget.PROPERTY_GETTER in actualTargets ||
|
||||
entry.useSiteTarget?.getAnnotationUseSiteTarget() == AnnotationUseSiteTarget.PROPERTY_GETTER
|
||||
) {
|
||||
trace.report(Errors.EXPERIMENTAL_ANNOTATION_ON_GETTER.on(entry))
|
||||
}
|
||||
val annotated = entry.getStrictParentOfType<KtAnnotated>() ?: continue
|
||||
val useSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget()
|
||||
if (annotated is KtCallableDeclaration &&
|
||||
annotated !is KtPropertyAccessor &&
|
||||
useSiteTarget == null &&
|
||||
annotated.hasModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||
) {
|
||||
val descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, annotated)
|
||||
if (descriptor is CallableMemberDescriptor &&
|
||||
!descriptor.hasExperimentalOverriddenDescriptors(annotationClass.fqNameSafe)
|
||||
) {
|
||||
trace.report(Errors.EXPERIMENTAL_ANNOTATION_ON_OVERRIDE.on(entry))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +86,25 @@ class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleD
|
||||
}
|
||||
}
|
||||
|
||||
private fun CallableMemberDescriptor.hasExperimentalOverriddenDescriptors(
|
||||
experimentalFqName: FqName,
|
||||
visited: MutableSet<CallableMemberDescriptor> = mutableSetOf()
|
||||
): Boolean {
|
||||
if (this in visited) return false
|
||||
visited += this
|
||||
for (overridden in overriddenDescriptors) {
|
||||
if (overridden.annotations.any { it.fqName == experimentalFqName }) {
|
||||
return true
|
||||
}
|
||||
if (overridden.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE &&
|
||||
overridden.hasExperimentalOverriddenDescriptors(experimentalFqName)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun checkUseExperimentalUsage(annotationClasses: List<ConstantValue<*>>, trace: BindingTrace, entry: KtAnnotationEntry) {
|
||||
if (annotationClasses.isEmpty()) {
|
||||
trace.report(Errors.USE_EXPERIMENTAL_WITHOUT_ARGUMENTS.on(entry))
|
||||
|
||||
Reference in New Issue
Block a user