Report EXPERIMENTAL_API diagnostics on various implicit usages

#KT-32443 Fixed
#KT-22852 Fixed
This commit is contained in:
Mikhail Glukhikh
2021-05-20 12:20:42 +03:00
committed by teamcityserver
parent d8d38862d9
commit 0a670bf055
17 changed files with 472 additions and 17 deletions
@@ -46,6 +46,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isError
import org.jetbrains.kotlin.utils.SmartSet
import org.jetbrains.kotlin.utils.addIfNotNull
@@ -79,11 +81,36 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
)
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val experimentalities =
resolvedCall.resultingDescriptor.loadExperimentalities(moduleAnnotationsResolver, context.languageVersionSettings)
val resultingDescriptor = resolvedCall.resultingDescriptor
val experimentalities = resultingDescriptor.loadExperimentalities(moduleAnnotationsResolver, context.languageVersionSettings)
if (resultingDescriptor is FunctionDescriptor &&
resultingDescriptor.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
) {
val propertyDescriptor = resultingDescriptor.findRelevantDataClassPropertyIfAny(context)
if (propertyDescriptor != null) {
reportNotAcceptedExperimentalities(
experimentalities + propertyDescriptor.loadExperimentalities(
moduleAnnotationsResolver, context.languageVersionSettings
), reportOn, context
)
return
}
}
reportNotAcceptedExperimentalities(experimentalities, reportOn, context)
}
private fun FunctionDescriptor.findRelevantDataClassPropertyIfAny(context: CallCheckerContext): PropertyDescriptor? {
val index = name.asString().removePrefix("component").toIntOrNull()
val container = containingDeclaration
if (container is ClassDescriptor && container.isData && index != null) {
val dataClassParameterDescriptor = container.unsubstitutedPrimaryConstructor?.valueParameters?.getOrNull(index - 1)
if (dataClassParameterDescriptor != null) {
return context.trace.bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, dataClassParameterDescriptor]
}
}
return null
}
companion object {
val OLD_EXPERIMENTAL_FQ_NAME = FqName("kotlin.Experimental")
val OLD_USE_EXPERIMENTAL_FQ_NAME = FqName("kotlin.UseExperimental")
@@ -145,14 +172,47 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
fun DeclarationDescriptor.loadExperimentalities(
moduleAnnotationsResolver: ModuleAnnotationsResolver,
languageVersionSettings: LanguageVersionSettings
languageVersionSettings: LanguageVersionSettings,
visited: MutableSet<DeclarationDescriptor> = mutableSetOf()
): Set<Experimentality> {
if (this in visited) return emptySet()
visited += this
val result = SmartSet.create<Experimentality>()
if (this is CallableMemberDescriptor && kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
for (overridden in overriddenDescriptors) {
result.addAll(overridden.loadExperimentalities(moduleAnnotationsResolver, languageVersionSettings, visited))
}
return result
}
for (annotation in annotations) {
result.addIfNotNull(annotation.annotationClass?.loadExperimentalityForMarkerAnnotation())
}
if (this is CallableDescriptor && this !is ClassConstructorDescriptor) {
result.addAll(
returnType.loadExperimentalities(moduleAnnotationsResolver, languageVersionSettings, visited)
)
result.addAll(
extensionReceiverParameter?.type.loadExperimentalities(
moduleAnnotationsResolver, languageVersionSettings, visited
)
)
if (this is FunctionDescriptor) {
valueParameters.forEach {
result.addAll(
it.type.loadExperimentalities(
moduleAnnotationsResolver, languageVersionSettings, visited
)
)
}
}
}
if (this is TypeAliasDescriptor) {
result.addAll(expandedType.loadExperimentalities(moduleAnnotationsResolver, languageVersionSettings, visited))
}
if (annotations.any { it.fqName == WAS_EXPERIMENTAL_FQ_NAME }) {
val accessibility = checkSinceKotlinVersionAccessibility(languageVersionSettings)
if (accessibility is SinceKotlinAccessibility.NotAccessibleButWasExperimental) {
@@ -162,7 +222,7 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
val container = containingDeclaration
if (container is ClassDescriptor && this !is ConstructorDescriptor) {
result.addAll(container.loadExperimentalities(moduleAnnotationsResolver, languageVersionSettings))
result.addAll(container.loadExperimentalities(moduleAnnotationsResolver, languageVersionSettings, visited))
}
for (moduleAnnotationClassId in moduleAnnotationsResolver.getAnnotationsOnContainingModule(this)) {
@@ -173,6 +233,19 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
return result
}
private fun KotlinType?.loadExperimentalities(
moduleAnnotationsResolver: ModuleAnnotationsResolver,
languageVersionSettings: LanguageVersionSettings,
visitedClassifiers: MutableSet<DeclarationDescriptor>
): Set<Experimentality> =
if (this?.isError != false) emptySet()
else constructor.declarationDescriptor?.loadExperimentalities(
moduleAnnotationsResolver, languageVersionSettings, visitedClassifiers
).orEmpty() + arguments.flatMap {
if (it.isStarProjection) emptySet()
else it.type.loadExperimentalities(moduleAnnotationsResolver, languageVersionSettings, visitedClassifiers)
}
internal fun ClassDescriptor.loadExperimentalityForMarkerAnnotation(): Experimentality? {
val experimental =
annotations.findAnnotation(REQUIRES_OPT_IN_FQ_NAME)
@@ -316,7 +389,15 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
}
if (element.getParentOfType<KtImportDirective>(false) == null) {
val experimentalities = targetDescriptor.loadExperimentalities(moduleAnnotationsResolver, context.languageVersionSettings)
val experimentalities = mutableSetOf<Experimentality>()
experimentalities += targetDescriptor.loadExperimentalities(moduleAnnotationsResolver, context.languageVersionSettings)
if (targetDescriptor is TypeAliasDescriptor) {
experimentalities.addAll(
targetDescriptor.expandedType.loadExperimentalities(
moduleAnnotationsResolver, context.languageVersionSettings, mutableSetOf(targetDescriptor)
)
)
}
reportNotAcceptedExperimentalities(experimentalities, element, context)
}
}