Partial rename of 'Experimentality' around K1 checkers
This commit is contained in:
+2
-2
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
import org.jetbrains.kotlin.load.java.components.TraceBasedErrorReporter
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInUsageChecker
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmBindingContextSlices
|
||||
|
||||
class AnalyzerWithCompilerReport(
|
||||
@@ -114,7 +114,7 @@ class AnalyzerWithCompilerReport(
|
||||
override fun analyzeAndReport(files: Collection<KtFile>, analyze: () -> AnalysisResult) {
|
||||
analysisResult = analyze()
|
||||
if (!analysisResult.isError()) {
|
||||
ExperimentalUsageChecker.checkCompilerArguments(
|
||||
OptInUsageChecker.checkCompilerArguments(
|
||||
analysisResult.moduleDescriptor, languageVersionSettings,
|
||||
reportError = { message -> messageCollector.report(ERROR, message) },
|
||||
reportWarning = { message -> messageCollector.report(WARNING, message) }
|
||||
|
||||
+2
-2
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
||||
import org.jetbrains.kotlin.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
|
||||
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
|
||||
import org.jetbrains.kotlin.resolve.checkers.Experimentality
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInDescription
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInNames
|
||||
|
||||
object FirOptInAnnotationClassChecker : FirRegularClassChecker() {
|
||||
@@ -26,7 +26,7 @@ object FirOptInAnnotationClassChecker : FirRegularClassChecker() {
|
||||
reporter.reportOn(target?.source, FirErrors.OPT_IN_MARKER_WITH_WRONG_RETENTION, context)
|
||||
|
||||
}
|
||||
val wrongTargets = declaration.getAllowedAnnotationTargets().intersect(Experimentality.WRONG_TARGETS_FOR_MARKER)
|
||||
val wrongTargets = declaration.getAllowedAnnotationTargets().intersect(OptInDescription.WRONG_TARGETS_FOR_MARKER)
|
||||
if (wrongTargets.isNotEmpty()) {
|
||||
val target = declaration.getTargetAnnotation()
|
||||
reporter.reportOn(
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.components.ClassicConstraint
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.KotlinResolutionStatelessCallbacksImpl
|
||||
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInUsageChecker
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isTypeRefinementEnabled
|
||||
import org.jetbrains.kotlin.resolve.lazy.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactory
|
||||
@@ -105,9 +105,9 @@ private fun StorageComponentContainer.configurePlatformIndependentComponents() {
|
||||
useImpl<KotlinResolutionStatelessCallbacksImpl>()
|
||||
useImpl<DataFlowValueFactoryImpl>()
|
||||
|
||||
useImpl<ExperimentalUsageChecker>()
|
||||
useImpl<ExperimentalUsageChecker.Overrides>()
|
||||
useImpl<ExperimentalUsageChecker.ClassifierUsage>()
|
||||
useImpl<OptInUsageChecker>()
|
||||
useImpl<OptInUsageChecker.Overrides>()
|
||||
useImpl<OptInUsageChecker.ClassifierUsage>()
|
||||
|
||||
useImpl<ContractDeserializerImpl>()
|
||||
useImpl<CompilerDeserializationConfiguration>()
|
||||
|
||||
@@ -123,7 +123,7 @@ abstract class PlatformConfiguratorBase(
|
||||
}
|
||||
|
||||
override fun configureModuleDependentCheckers(container: StorageComponentContainer) {
|
||||
container.useImpl<ExperimentalMarkerDeclarationAnnotationChecker>()
|
||||
container.useImpl<OptInMarkerDeclarationAnnotationChecker>()
|
||||
}
|
||||
|
||||
fun configureExtensionsAndCheckers(container: StorageComponentContainer) {
|
||||
|
||||
+8
-39
@@ -6,9 +6,7 @@
|
||||
package org.jetbrains.kotlin.resolve.checkers
|
||||
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
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
|
||||
@@ -16,12 +14,8 @@ import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
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.resolve.AdditionalAnnotationChecker
|
||||
import org.jetbrains.kotlin.resolve.AnnotationChecker
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
@@ -34,7 +28,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getAnnotationRetention
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleDescriptor) : AdditionalAnnotationChecker {
|
||||
class OptInMarkerDeclarationAnnotationChecker(private val module: ModuleDescriptor) : AdditionalAnnotationChecker {
|
||||
override fun checkEntries(
|
||||
entries: List<KtAnnotationEntry>,
|
||||
actualTargets: List<KotlinTarget>,
|
||||
@@ -42,7 +36,7 @@ class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleD
|
||||
annotated: KtAnnotated?,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
) {
|
||||
var isAnnotatedWithExperimental = false
|
||||
var hasOptIn = false
|
||||
|
||||
for (entry in entries) {
|
||||
val annotation = trace.bindingContext.get(BindingContext.ANNOTATION, entry) ?: continue
|
||||
@@ -54,7 +48,7 @@ class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleD
|
||||
checkOptInUsage(annotationClasses, trace, entry)
|
||||
}
|
||||
in OptInNames.REQUIRES_OPT_IN_FQ_NAMES -> {
|
||||
isAnnotatedWithExperimental = true
|
||||
hasOptIn = true
|
||||
}
|
||||
}
|
||||
val annotationClass = annotation.annotationClass ?: continue
|
||||
@@ -85,36 +79,11 @@ class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleD
|
||||
}
|
||||
}
|
||||
|
||||
if (isAnnotatedWithExperimental) {
|
||||
if (hasOptIn) {
|
||||
checkMarkerTargetsAndRetention(entries, trace)
|
||||
}
|
||||
}
|
||||
|
||||
private fun CallableMemberDescriptor.hasExperimentalOverriddenDescriptors(
|
||||
experimentalFqName: FqName,
|
||||
visited: MutableSet<CallableMemberDescriptor> = mutableSetOf()
|
||||
): Boolean {
|
||||
if (!visited.add(this)) return false
|
||||
for (overridden in overriddenDescriptors) {
|
||||
if (overridden.annotations.any { it.fqName == experimentalFqName }) {
|
||||
return true
|
||||
}
|
||||
var containingDeclaration = overridden.containingDeclaration
|
||||
while (containingDeclaration is ClassDescriptor) {
|
||||
if (containingDeclaration.annotations.any { it.fqName == experimentalFqName }) {
|
||||
return true
|
||||
}
|
||||
containingDeclaration = containingDeclaration.containingDeclaration
|
||||
}
|
||||
if (overridden.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE &&
|
||||
overridden.hasExperimentalOverriddenDescriptors(experimentalFqName)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun checkOptInUsage(annotationClasses: List<ConstantValue<*>>, trace: BindingTrace, entry: KtAnnotationEntry) {
|
||||
if (annotationClasses.isEmpty()) {
|
||||
trace.report(Errors.OPT_IN_WITHOUT_ARGUMENTS.on(entry))
|
||||
@@ -125,10 +94,10 @@ class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleD
|
||||
val classDescriptor =
|
||||
(annotationClass as? KClassValue)?.getArgumentType(module)?.constructor?.declarationDescriptor as? ClassDescriptor
|
||||
?: continue
|
||||
val experimentality = with(ExperimentalUsageChecker) {
|
||||
classDescriptor.loadExperimentalityForMarkerAnnotation()
|
||||
val optInDescription = with(OptInUsageChecker) {
|
||||
classDescriptor.loadOptInForMarkerAnnotation()
|
||||
}
|
||||
if (experimentality == null) {
|
||||
if (optInDescription == null) {
|
||||
trace.report(Errors.OPT_IN_ARGUMENT_IS_NOT_MARKER.on(entry, classDescriptor.fqNameSafe))
|
||||
}
|
||||
}
|
||||
@@ -145,7 +114,7 @@ class ExperimentalMarkerDeclarationAnnotationChecker(private val module: ModuleD
|
||||
if (targetEntry != null) {
|
||||
val (entry, descriptor) = targetEntry
|
||||
val allowedTargets = AnnotationChecker.loadAnnotationTargets(descriptor!!) ?: return
|
||||
val wrongTargets = allowedTargets.intersect(Experimentality.WRONG_TARGETS_FOR_MARKER)
|
||||
val wrongTargets = allowedTargets.intersect(OptInDescription.WRONG_TARGETS_FOR_MARKER)
|
||||
if (wrongTargets.isNotEmpty()) {
|
||||
trace.report(
|
||||
Errors.OPT_IN_MARKER_WITH_WRONG_TARGET.on(
|
||||
+79
-79
@@ -59,45 +59,45 @@ import org.jetbrains.kotlin.types.isError
|
||||
import org.jetbrains.kotlin.utils.SmartSet
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
class OptInUsageChecker(project: Project) : CallChecker {
|
||||
private val moduleAnnotationsResolver = ModuleAnnotationsResolver.getInstance(project)
|
||||
|
||||
interface ExperimentalityDiagnostic {
|
||||
interface OptInDiagnosticReporter {
|
||||
fun report(trace: BindingTrace, element: PsiElement, fqName: FqName, message: String?)
|
||||
}
|
||||
|
||||
class ExperimentalityDiagnostic2(
|
||||
class OptInFactoryBasedReporter(
|
||||
val factory: DiagnosticFactory2<PsiElement, FqName, String>,
|
||||
val defaultMessage: (FqName) -> String
|
||||
) : ExperimentalityDiagnostic {
|
||||
) : OptInDiagnosticReporter {
|
||||
override fun report(trace: BindingTrace, element: PsiElement, fqName: FqName, message: String?) {
|
||||
trace.reportDiagnosticOnce(factory.on(element, fqName, message?.takeIf { it.isNotBlank() } ?: defaultMessage(fqName)))
|
||||
}
|
||||
}
|
||||
|
||||
data class ExperimentalityDiagnostics(
|
||||
val warning: ExperimentalityDiagnostic,
|
||||
val error: ExperimentalityDiagnostic,
|
||||
val futureError: ExperimentalityDiagnostic
|
||||
data class OptInReporterMultiplexer(
|
||||
val warning: OptInDiagnosticReporter,
|
||||
val error: OptInDiagnosticReporter,
|
||||
val futureError: OptInDiagnosticReporter
|
||||
)
|
||||
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
val resultingDescriptor = resolvedCall.resultingDescriptor
|
||||
val experimentalities = resultingDescriptor.loadExperimentalities(moduleAnnotationsResolver, context.languageVersionSettings)
|
||||
val optIns = resultingDescriptor.loadOptIns(moduleAnnotationsResolver, context.languageVersionSettings)
|
||||
if (resultingDescriptor is FunctionDescriptor &&
|
||||
resultingDescriptor.kind == CallableMemberDescriptor.Kind.SYNTHESIZED
|
||||
) {
|
||||
val propertyDescriptor = resultingDescriptor.findRelevantDataClassPropertyIfAny(context)
|
||||
if (propertyDescriptor != null) {
|
||||
reportNotAcceptedExperimentalities(
|
||||
experimentalities + propertyDescriptor.loadExperimentalities(
|
||||
reportNotAllowedOptIns(
|
||||
optIns + propertyDescriptor.loadOptIns(
|
||||
moduleAnnotationsResolver, context.languageVersionSettings
|
||||
), reportOn, context
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
reportNotAcceptedExperimentalities(experimentalities, reportOn, context)
|
||||
reportNotAllowedOptIns(optIns, reportOn, context)
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.findRelevantDataClassPropertyIfAny(context: CallCheckerContext): PropertyDescriptor? {
|
||||
@@ -122,61 +122,61 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
OptInNames.buildDefaultDiagnosticMessage(prefix, fqName.asString())
|
||||
}
|
||||
|
||||
private val USAGE_DIAGNOSTICS = ExperimentalityDiagnostics(
|
||||
warning = ExperimentalityDiagnostic2(
|
||||
private val USAGE_DIAGNOSTICS = OptInReporterMultiplexer(
|
||||
warning = OptInFactoryBasedReporter(
|
||||
Errors.OPT_IN_USAGE,
|
||||
getDefaultDiagnosticMessage(OptInNames.buildMessagePrefix("should"))
|
||||
),
|
||||
error = ExperimentalityDiagnostic2(
|
||||
error = OptInFactoryBasedReporter(
|
||||
Errors.OPT_IN_USAGE_ERROR,
|
||||
getDefaultDiagnosticMessage(OptInNames.buildMessagePrefix("must"))
|
||||
),
|
||||
futureError = ExperimentalityDiagnostic2(
|
||||
futureError = OptInFactoryBasedReporter(
|
||||
Errors.OPT_IN_USAGE_FUTURE_ERROR,
|
||||
getDefaultDiagnosticMessage("This declaration is experimental due to signature types and its usage must be marked (will become an error in 1.6)")
|
||||
),
|
||||
)
|
||||
|
||||
fun reportNotAcceptedExperimentalities(
|
||||
experimentalities: Collection<Experimentality>, element: PsiElement, context: CheckerContext
|
||||
fun reportNotAllowedOptIns(
|
||||
descriptions: Collection<OptInDescription>, element: PsiElement, context: CheckerContext
|
||||
) {
|
||||
reportNotAcceptedExperimentalities(
|
||||
experimentalities, element, context.languageVersionSettings, context.trace, USAGE_DIAGNOSTICS
|
||||
reportNotAllowedOptIns(
|
||||
descriptions, element, context.languageVersionSettings, context.trace, USAGE_DIAGNOSTICS
|
||||
)
|
||||
}
|
||||
|
||||
fun reportNotAcceptedExperimentalities(
|
||||
experimentalities: Collection<Experimentality>,
|
||||
fun reportNotAllowedOptIns(
|
||||
descriptions: Collection<OptInDescription>,
|
||||
element: PsiElement,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
trace: BindingTrace,
|
||||
diagnostics: ExperimentalityDiagnostics
|
||||
diagnostics: OptInReporterMultiplexer
|
||||
) {
|
||||
for ((annotationFqName, severity, message) in experimentalities) {
|
||||
if (!element.isExperimentalityAccepted(annotationFqName, languageVersionSettings, trace.bindingContext)) {
|
||||
for ((annotationFqName, severity, message) in descriptions) {
|
||||
if (!element.isOptInAllowed(annotationFqName, languageVersionSettings, trace.bindingContext)) {
|
||||
val diagnostic = when (severity) {
|
||||
Experimentality.Severity.WARNING -> diagnostics.warning
|
||||
Experimentality.Severity.ERROR -> diagnostics.error
|
||||
Experimentality.Severity.FUTURE_ERROR -> diagnostics.futureError
|
||||
OptInDescription.Severity.WARNING -> diagnostics.warning
|
||||
OptInDescription.Severity.ERROR -> diagnostics.error
|
||||
OptInDescription.Severity.FUTURE_ERROR -> diagnostics.futureError
|
||||
}
|
||||
diagnostic.report(trace, element, annotationFqName, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun DeclarationDescriptor.loadExperimentalities(
|
||||
fun DeclarationDescriptor.loadOptIns(
|
||||
moduleAnnotationsResolver: ModuleAnnotationsResolver,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
visited: MutableSet<DeclarationDescriptor> = mutableSetOf(),
|
||||
useFutureError: Boolean = false,
|
||||
useMarkersFromContainer: Boolean = true,
|
||||
): Set<Experimentality> {
|
||||
): Set<OptInDescription> {
|
||||
if (!visited.add(this)) return emptySet()
|
||||
val result = SmartSet.create<Experimentality>()
|
||||
val result = SmartSet.create<OptInDescription>()
|
||||
if (this is CallableMemberDescriptor && kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) {
|
||||
for (overridden in overriddenDescriptors) {
|
||||
result.addAll(
|
||||
overridden.loadExperimentalities(
|
||||
overridden.loadOptIns(
|
||||
moduleAnnotationsResolver,
|
||||
languageVersionSettings,
|
||||
visited,
|
||||
@@ -187,7 +187,7 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
if (useMarkersFromContainer) {
|
||||
(containingDeclaration as? ClassDescriptor)?.let {
|
||||
result.addAll(
|
||||
it.loadExperimentalities(moduleAnnotationsResolver, languageVersionSettings, visited, useFutureError)
|
||||
it.loadOptIns(moduleAnnotationsResolver, languageVersionSettings, visited, useFutureError)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -196,22 +196,22 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
}
|
||||
|
||||
for (annotation in annotations) {
|
||||
result.addIfNotNull(annotation.annotationClass?.loadExperimentalityForMarkerAnnotation(useFutureError))
|
||||
result.addIfNotNull(annotation.annotationClass?.loadOptInForMarkerAnnotation(useFutureError))
|
||||
}
|
||||
|
||||
if (this is CallableDescriptor && this !is ClassConstructorDescriptor) {
|
||||
result.addAll(
|
||||
returnType.loadExperimentalities(moduleAnnotationsResolver, languageVersionSettings, visited)
|
||||
returnType.loadOptIns(moduleAnnotationsResolver, languageVersionSettings, visited)
|
||||
)
|
||||
result.addAll(
|
||||
extensionReceiverParameter?.type.loadExperimentalities(
|
||||
extensionReceiverParameter?.type.loadOptIns(
|
||||
moduleAnnotationsResolver, languageVersionSettings, visited
|
||||
)
|
||||
)
|
||||
if (this is FunctionDescriptor) {
|
||||
valueParameters.forEach {
|
||||
result.addAll(
|
||||
it.type.loadExperimentalities(
|
||||
it.type.loadOptIns(
|
||||
moduleAnnotationsResolver, languageVersionSettings, visited
|
||||
)
|
||||
)
|
||||
@@ -220,76 +220,76 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
}
|
||||
|
||||
if (this is TypeAliasDescriptor) {
|
||||
result.addAll(expandedType.loadExperimentalities(moduleAnnotationsResolver, languageVersionSettings, visited))
|
||||
result.addAll(expandedType.loadOptIns(moduleAnnotationsResolver, languageVersionSettings, visited))
|
||||
}
|
||||
|
||||
if (annotations.any { it.fqName == WAS_EXPERIMENTAL_FQ_NAME }) {
|
||||
val accessibility = checkSinceKotlinVersionAccessibility(languageVersionSettings)
|
||||
if (accessibility is SinceKotlinAccessibility.NotAccessibleButWasExperimental) {
|
||||
result.addAll(accessibility.markerClasses.mapNotNull { it.loadExperimentalityForMarkerAnnotation() })
|
||||
result.addAll(accessibility.markerClasses.mapNotNull { it.loadOptInForMarkerAnnotation() })
|
||||
}
|
||||
}
|
||||
|
||||
val container = containingDeclaration
|
||||
if (useMarkersFromContainer && container is ClassDescriptor && this !is ConstructorDescriptor) {
|
||||
result.addAll(container.loadExperimentalities(moduleAnnotationsResolver, languageVersionSettings, visited, useFutureError))
|
||||
result.addAll(container.loadOptIns(moduleAnnotationsResolver, languageVersionSettings, visited, useFutureError))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun KotlinType?.loadExperimentalities(
|
||||
private fun KotlinType?.loadOptIns(
|
||||
moduleAnnotationsResolver: ModuleAnnotationsResolver,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
visitedClassifiers: MutableSet<DeclarationDescriptor>
|
||||
): Set<Experimentality> =
|
||||
): Set<OptInDescription> =
|
||||
when {
|
||||
this?.isError != false -> emptySet()
|
||||
this is AbbreviatedType -> abbreviation.constructor.declarationDescriptor?.loadExperimentalities(
|
||||
this is AbbreviatedType -> abbreviation.constructor.declarationDescriptor?.loadOptIns(
|
||||
moduleAnnotationsResolver, languageVersionSettings, visitedClassifiers,
|
||||
useFutureError = !languageVersionSettings.supportsFeature(LanguageFeature.OptInContagiousSignatures)
|
||||
).orEmpty() + expandedType.loadExperimentalities(
|
||||
).orEmpty() + expandedType.loadOptIns(
|
||||
moduleAnnotationsResolver, languageVersionSettings, visitedClassifiers
|
||||
)
|
||||
else -> constructor.declarationDescriptor?.loadExperimentalities(
|
||||
else -> constructor.declarationDescriptor?.loadOptIns(
|
||||
moduleAnnotationsResolver, languageVersionSettings, visitedClassifiers,
|
||||
useFutureError = !languageVersionSettings.supportsFeature(LanguageFeature.OptInContagiousSignatures)
|
||||
).orEmpty() + arguments.flatMap {
|
||||
if (it.isStarProjection) emptySet()
|
||||
else it.type.loadExperimentalities(moduleAnnotationsResolver, languageVersionSettings, visitedClassifiers)
|
||||
else it.type.loadOptIns(moduleAnnotationsResolver, languageVersionSettings, visitedClassifiers)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ClassDescriptor.loadExperimentalityForMarkerAnnotation(useFutureError: Boolean = false): Experimentality? {
|
||||
val experimental =
|
||||
internal fun ClassDescriptor.loadOptInForMarkerAnnotation(useFutureError: Boolean = false): OptInDescription? {
|
||||
val optInAnnotationDescriptor =
|
||||
annotations.findAnnotation(REQUIRES_OPT_IN_FQ_NAME)
|
||||
?: annotations.findAnnotation(OLD_EXPERIMENTAL_FQ_NAME)
|
||||
?: return null
|
||||
|
||||
val arguments = experimental.allValueArguments
|
||||
val arguments = optInAnnotationDescriptor.allValueArguments
|
||||
val severity = when ((arguments[LEVEL] as? EnumValue)?.enumEntryName) {
|
||||
WARNING_LEVEL -> Experimentality.Severity.WARNING
|
||||
ERROR_LEVEL -> if (useFutureError) Experimentality.Severity.FUTURE_ERROR else Experimentality.Severity.ERROR
|
||||
else -> if (Experimentality.DEFAULT_SEVERITY == Experimentality.Severity.ERROR && useFutureError) {
|
||||
Experimentality.Severity.FUTURE_ERROR
|
||||
WARNING_LEVEL -> OptInDescription.Severity.WARNING
|
||||
ERROR_LEVEL -> if (useFutureError) OptInDescription.Severity.FUTURE_ERROR else OptInDescription.Severity.ERROR
|
||||
else -> if (OptInDescription.DEFAULT_SEVERITY == OptInDescription.Severity.ERROR && useFutureError) {
|
||||
OptInDescription.Severity.FUTURE_ERROR
|
||||
} else {
|
||||
Experimentality.DEFAULT_SEVERITY
|
||||
OptInDescription.DEFAULT_SEVERITY
|
||||
}
|
||||
}
|
||||
|
||||
val message = (arguments[MESSAGE] as? StringValue)?.value
|
||||
|
||||
return Experimentality(fqNameSafe, severity, message)
|
||||
return OptInDescription(fqNameSafe, severity, message)
|
||||
}
|
||||
|
||||
private fun PsiElement.isExperimentalityAccepted(annotationFqName: FqName, context: CheckerContext): Boolean =
|
||||
isExperimentalityAccepted(annotationFqName, context.languageVersionSettings, context.trace.bindingContext)
|
||||
private fun PsiElement.isOptInAllowed(annotationFqName: FqName, context: CheckerContext): Boolean =
|
||||
isOptInAllowed(annotationFqName, context.languageVersionSettings, context.trace.bindingContext)
|
||||
|
||||
/**
|
||||
* Checks whether there's an element lexically above in the tree, annotated with `@UseExperimental(X::class)`, or a declaration
|
||||
* annotated with `@X` where [annotationFqName] is the FQ name of X
|
||||
*/
|
||||
fun PsiElement.isExperimentalityAccepted(
|
||||
fun PsiElement.isOptInAllowed(
|
||||
annotationFqName: FqName,
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
bindingContext: BindingContext
|
||||
@@ -340,7 +340,7 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
// "-opt-in" arguments. However, it's not easy to do this. This should be solved in the future with the support of
|
||||
// module annotations. For now, we only check deprecations because this is needed to correctly retire unneeded compiler arguments.
|
||||
val deprecationResolver = DeprecationResolver(
|
||||
LockBasedStorageManager("ExperimentalUsageChecker"),
|
||||
LockBasedStorageManager("OptInUsageChecker"),
|
||||
languageVersionSettings,
|
||||
DeprecationSettings.Default
|
||||
)
|
||||
@@ -353,7 +353,7 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
return false
|
||||
}
|
||||
|
||||
if (descriptor.loadExperimentalityForMarkerAnnotation() == null) {
|
||||
if (descriptor.loadOptInForMarkerAnnotation() == null) {
|
||||
reportWarning("Class $fqName is not an opt-in requirement marker")
|
||||
return false
|
||||
}
|
||||
@@ -396,7 +396,7 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
is TypeAliasDescriptor -> targetDescriptor.classDescriptor
|
||||
else -> null
|
||||
}
|
||||
if (targetClass != null && targetClass.loadExperimentalityForMarkerAnnotation() != null) {
|
||||
if (targetClass != null && targetClass.loadOptInForMarkerAnnotation() != null) {
|
||||
if (!element.isUsageAsAnnotationOrImport() &&
|
||||
!element.isUsageAsOptInArgument(context.trace.bindingContext)
|
||||
) {
|
||||
@@ -407,16 +407,16 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
}
|
||||
|
||||
if (element.getParentOfType<KtImportDirective>(false) == null) {
|
||||
val experimentalities = mutableSetOf<Experimentality>()
|
||||
experimentalities += targetDescriptor.loadExperimentalities(moduleAnnotationsResolver, context.languageVersionSettings)
|
||||
val descriptions = mutableSetOf<OptInDescription>()
|
||||
descriptions += targetDescriptor.loadOptIns(moduleAnnotationsResolver, context.languageVersionSettings)
|
||||
if (targetDescriptor is TypeAliasDescriptor) {
|
||||
experimentalities.addAll(
|
||||
targetDescriptor.expandedType.loadExperimentalities(
|
||||
descriptions.addAll(
|
||||
targetDescriptor.expandedType.loadOptIns(
|
||||
moduleAnnotationsResolver, context.languageVersionSettings, mutableSetOf(targetDescriptor)
|
||||
)
|
||||
)
|
||||
}
|
||||
reportNotAcceptedExperimentalities(experimentalities, element, context)
|
||||
reportNotAllowedOptIns(descriptions, element, context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,27 +466,27 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
if (descriptor !is CallableMemberDescriptor) return
|
||||
|
||||
val experimentalOverridden = descriptor.overriddenDescriptors.flatMap { member ->
|
||||
member.loadExperimentalities(moduleAnnotationsResolver, context.languageVersionSettings)
|
||||
.map { experimentality -> experimentality to member }
|
||||
val optInOverriddenDescriptorMap = descriptor.overriddenDescriptors.flatMap { overriddenMember ->
|
||||
overriddenMember.loadOptIns(moduleAnnotationsResolver, context.languageVersionSettings)
|
||||
.map { description -> description to overriddenMember }
|
||||
}.toMap()
|
||||
|
||||
for ((experimentality, member) in experimentalOverridden) {
|
||||
if (!declaration.isExperimentalityAccepted(experimentality.annotationFqName, context)) {
|
||||
for ((description, overriddenMember) in optInOverriddenDescriptorMap) {
|
||||
if (!declaration.isOptInAllowed(description.annotationFqName, context)) {
|
||||
val reportOn = (declaration as? KtNamedDeclaration)?.nameIdentifier ?: declaration
|
||||
|
||||
val (diagnostic, defaultMessageVerb) = when (experimentality.severity) {
|
||||
Experimentality.Severity.WARNING -> Errors.OPT_IN_OVERRIDE to "should"
|
||||
Experimentality.Severity.ERROR -> Errors.OPT_IN_OVERRIDE_ERROR to "must"
|
||||
Experimentality.Severity.FUTURE_ERROR -> Errors.OPT_IN_OVERRIDE_ERROR to "must"
|
||||
val (diagnostic, defaultMessageVerb) = when (description.severity) {
|
||||
OptInDescription.Severity.WARNING -> Errors.OPT_IN_OVERRIDE to "should"
|
||||
OptInDescription.Severity.ERROR -> Errors.OPT_IN_OVERRIDE_ERROR to "must"
|
||||
OptInDescription.Severity.FUTURE_ERROR -> Errors.OPT_IN_OVERRIDE_ERROR to "must"
|
||||
}
|
||||
val message = OptInNames.buildOverrideMessage(
|
||||
supertypeName = member.containingDeclaration.name.asString(),
|
||||
markerMessage = experimentality.message,
|
||||
supertypeName = overriddenMember.containingDeclaration.name.asString(),
|
||||
markerMessage = description.message,
|
||||
verb = defaultMessageVerb,
|
||||
markerName = experimentality.annotationFqName.asString()
|
||||
markerName = description.annotationFqName.asString()
|
||||
)
|
||||
context.trace.report(diagnostic.on(reportOn, experimentality.annotationFqName, message))
|
||||
context.trace.report(diagnostic.on(reportOn, description.annotationFqName, message))
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -35,7 +35,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.resolve.calls.util.getEffectiveExpectedType
|
||||
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInUsageChecker
|
||||
import org.jetbrains.kotlin.resolve.constants.*
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.CompileTimeType.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
@@ -322,12 +322,12 @@ class ConstantExpressionEvaluator(
|
||||
|
||||
if (!UnsignedTypes.isUnsignedType(constantType)) return
|
||||
|
||||
with(ExperimentalUsageChecker) {
|
||||
with(OptInUsageChecker) {
|
||||
val descriptor = constantType.constructor.declarationDescriptor ?: return
|
||||
val experimentalities = descriptor.loadExperimentalities(moduleAnnotationsResolver, languageVersionSettings)
|
||||
val optInDescriptions = descriptor.loadOptIns(moduleAnnotationsResolver, languageVersionSettings)
|
||||
|
||||
reportNotAcceptedExperimentalities(
|
||||
experimentalities, expression, languageVersionSettings, trace, EXPERIMENTAL_UNSIGNED_LITERALS_DIAGNOSTICS
|
||||
reportNotAllowedOptIns(
|
||||
optInDescriptions, expression, languageVersionSettings, trace, EXPERIMENTAL_UNSIGNED_LITERALS_DIAGNOSTICS
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -336,16 +336,16 @@ class ConstantExpressionEvaluator(
|
||||
private class ExperimentalityDiagnostic1(
|
||||
val factory: DiagnosticFactory1<PsiElement, String>,
|
||||
val verb: String
|
||||
) : ExperimentalUsageChecker.ExperimentalityDiagnostic {
|
||||
) : OptInUsageChecker.OptInDiagnosticReporter {
|
||||
override fun report(trace: BindingTrace, element: PsiElement, fqName: FqName, message: String?) {
|
||||
val defaultMessage = ExperimentalUsageChecker.getDefaultDiagnosticMessage(
|
||||
val defaultMessage = OptInUsageChecker.getDefaultDiagnosticMessage(
|
||||
"Unsigned literals are experimental and their usages $verb be marked"
|
||||
)
|
||||
trace.reportDiagnosticOnce(factory.on(element, defaultMessage(fqName)))
|
||||
}
|
||||
}
|
||||
|
||||
private val EXPERIMENTAL_UNSIGNED_LITERALS_DIAGNOSTICS = ExperimentalUsageChecker.ExperimentalityDiagnostics(
|
||||
private val EXPERIMENTAL_UNSIGNED_LITERALS_DIAGNOSTICS = OptInUsageChecker.OptInReporterMultiplexer(
|
||||
warning = ExperimentalityDiagnostic1(Errors.EXPERIMENTAL_UNSIGNED_LITERALS, "should"),
|
||||
error = ExperimentalityDiagnostic1(Errors.EXPERIMENTAL_UNSIGNED_LITERALS_ERROR, "must"),
|
||||
futureError = ExperimentalityDiagnostic1(Errors.EXPERIMENTAL_UNSIGNED_LITERALS_ERROR, "must"),
|
||||
|
||||
+3
-3
@@ -20,7 +20,7 @@ import org.jetbrains.kotlin.resolve.SinceKotlinAccessibility
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.isOperatorMod
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.shouldWarnAboutDeprecatedModFromBuiltIns
|
||||
import org.jetbrains.kotlin.resolve.checkSinceKotlinVersionAccessibility
|
||||
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.OptInUsageChecker
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
|
||||
@@ -115,9 +115,9 @@ class DeprecationResolver(
|
||||
|
||||
if (sinceKotlinAccessibility is SinceKotlinAccessibility.NotAccessibleButWasExperimental) {
|
||||
return if (callElement != null && bindingContext != null) {
|
||||
with(ExperimentalUsageChecker) {
|
||||
with(OptInUsageChecker) {
|
||||
sinceKotlinAccessibility.markerClasses.any { classDescriptor ->
|
||||
!callElement.isExperimentalityAccepted(classDescriptor.fqNameSafe, languageVersionSettings, bindingContext)
|
||||
!callElement.isOptInAllowed(classDescriptor.fqNameSafe, languageVersionSettings, bindingContext)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ package org.jetbrains.kotlin.resolve.checkers
|
||||
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
data class Experimentality(val annotationFqName: FqName, val severity: Severity, val message: String?) {
|
||||
data class OptInDescription(val annotationFqName: FqName, val severity: Severity, val message: String?) {
|
||||
enum class Severity { WARNING, ERROR, FUTURE_ERROR }
|
||||
|
||||
companion object {
|
||||
Reference in New Issue
Block a user