Report EXPERIMENTAL_API diagnostics on various implicit usages
#KT-32443 Fixed #KT-22852 Fixed
This commit is contained in:
committed by
teamcityserver
parent
d8d38862d9
commit
0a670bf055
+86
-5
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user