Add internal WasExperimental for stdlib

Usages of declarations annotated with WasExperimental are allowed even
if the API version requirement is not satisfied, provided that the
opt-in to all mentioned markers is given. This is needed for smooth
graduation of API in kotlin-stdlib
This commit is contained in:
Alexander Udalov
2018-04-27 16:30:52 +02:00
parent fdc4313860
commit ed6f044fb0
18 changed files with 199 additions and 51 deletions
@@ -20,6 +20,7 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.diagnostics.Errors.API_NOT_AVAILABLE import org.jetbrains.kotlin.diagnostics.Errors.API_NOT_AVAILABLE
import org.jetbrains.kotlin.resolve.SinceKotlinAccessibility
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import org.jetbrains.kotlin.resolve.checkSinceKotlinVersionAccessibility import org.jetbrains.kotlin.resolve.checkSinceKotlinVersionAccessibility
@@ -34,13 +35,15 @@ object ApiVersionCallChecker : CallChecker {
// Objects will be checked by ApiVersionClassifierUsageChecker // Objects will be checked by ApiVersionClassifierUsageChecker
if (targetDescriptor is FakeCallableDescriptorForObject) return if (targetDescriptor is FakeCallableDescriptorForObject) return
val accessible = targetDescriptor.checkSinceKotlinVersionAccessibility(context.languageVersionSettings) { version -> val accessibility = targetDescriptor.checkSinceKotlinVersionAccessibility(context.languageVersionSettings)
if (accessibility is SinceKotlinAccessibility.NotAccessible) {
context.trace.report( context.trace.report(
API_NOT_AVAILABLE.on(element, version.versionString, context.languageVersionSettings.apiVersion.versionString) API_NOT_AVAILABLE.on(element, accessibility.version.versionString, context.languageVersionSettings.apiVersion.versionString)
) )
} }
if (accessible && targetDescriptor is PropertyDescriptor && DeprecatedCallChecker.shouldCheckPropertyGetter(element)) { if (accessibility == SinceKotlinAccessibility.Accessible &&
targetDescriptor is PropertyDescriptor && DeprecatedCallChecker.shouldCheckPropertyGetter(element)) {
targetDescriptor.getter?.let { check(it, context, element) } targetDescriptor.getter?.let { check(it, context, element) }
} }
} }
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isConventionCall import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isConventionCall
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInfixCall import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInfixCall
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isSuperOrDelegatingConstructorCall import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isSuperOrDelegatingConstructorCall
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionCallbacks
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionStatelessCallbacks import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionStatelessCallbacks
import org.jetbrains.kotlin.resolve.calls.inference.isCoroutineCallWithAdditionalInference import org.jetbrains.kotlin.resolve.calls.inference.isCoroutineCallWithAdditionalInference
import org.jetbrains.kotlin.resolve.calls.model.CallableReferenceKotlinCallArgument import org.jetbrains.kotlin.resolve.calls.model.CallableReferenceKotlinCallArgument
@@ -51,8 +52,15 @@ class KotlinResolutionStatelessCallbacksImpl(
override fun isSuperOrDelegatingConstructorCall(kotlinCall: KotlinCall) = override fun isSuperOrDelegatingConstructorCall(kotlinCall: KotlinCall) =
kotlinCall is PSIKotlinCallImpl && isSuperOrDelegatingConstructorCall(kotlinCall.psiCall) kotlinCall is PSIKotlinCallImpl && isSuperOrDelegatingConstructorCall(kotlinCall.psiCall)
override fun isHiddenInResolution(descriptor: DeclarationDescriptor, kotlinCall: KotlinCall) = override fun isHiddenInResolution(
deprecationResolver.isHiddenInResolution(descriptor, isSuperOrDelegatingConstructorCall(kotlinCall)) descriptor: DeclarationDescriptor, kotlinCall: KotlinCall, resolutionCallbacks: KotlinResolutionCallbacks
) =
deprecationResolver.isHiddenInResolution(
descriptor,
(kotlinCall as? PSIKotlinCall)?.psiCall,
(resolutionCallbacks as? KotlinResolutionCallbacksImpl)?.trace?.bindingContext,
isSuperOrDelegatingConstructorCall(kotlinCall)
)
override fun isSuperExpression(receiver: SimpleKotlinCallArgument?): Boolean = override fun isSuperExpression(receiver: SimpleKotlinCallArgument?): Boolean =
receiver?.psiExpression is KtSuperExpression receiver?.psiExpression is KtSuperExpression
@@ -35,7 +35,10 @@ import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isInfixCall
import org.jetbrains.kotlin.resolve.calls.callUtil.createLookupLocation import org.jetbrains.kotlin.resolve.calls.callUtil.createLookupLocation
import org.jetbrains.kotlin.resolve.calls.context.* import org.jetbrains.kotlin.resolve.calls.context.*
import org.jetbrains.kotlin.resolve.calls.inference.CoroutineInferenceSupport import org.jetbrains.kotlin.resolve.calls.inference.CoroutineInferenceSupport
import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
import org.jetbrains.kotlin.resolve.calls.model.MutableResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCallImpl
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCallImpl
import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsImpl import org.jetbrains.kotlin.resolve.calls.results.OverloadResolutionResultsImpl
import org.jetbrains.kotlin.resolve.calls.results.ResolutionResultsHandler import org.jetbrains.kotlin.resolve.calls.results.ResolutionResultsHandler
import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus import org.jetbrains.kotlin.resolve.calls.results.ResolutionStatus
@@ -219,7 +222,10 @@ class NewResolutionOldInference(
val candidateTrace = TemporaryBindingTrace.create(basicCallContext.trace, "Context for resolve candidate") val candidateTrace = TemporaryBindingTrace.create(basicCallContext.trace, "Context for resolve candidate")
val resolvedCall = ResolvedCallImpl.create(candidate, candidateTrace, tracing, basicCallContext.dataFlowInfoForArguments) val resolvedCall = ResolvedCallImpl.create(candidate, candidateTrace, tracing, basicCallContext.dataFlowInfoForArguments)
if (deprecationResolver.isHiddenInResolution(candidate.descriptor, basicCallContext.isSuperCall)) { if (deprecationResolver.isHiddenInResolution(
candidate.descriptor, basicCallContext.call, basicCallContext.trace.bindingContext, basicCallContext.isSuperCall
)
) {
return@map MyCandidate(listOf(HiddenDescriptor), resolvedCall) return@map MyCandidate(listOf(HiddenDescriptor), resolvedCall)
} }
@@ -448,8 +454,10 @@ class NewResolutionOldInference(
} }
} }
if (deprecationResolver.isHiddenInResolution(
if (deprecationResolver.isHiddenInResolution(towerCandidate.descriptor, basicCallContext.isSuperCall)) { towerCandidate.descriptor, basicCallContext.call, basicCallContext.trace.bindingContext, basicCallContext.isSuperCall
)
) {
return MyCandidate(listOf(HiddenDescriptor), candidateCall) return MyCandidate(listOf(HiddenDescriptor), candidateCall)
} }
@@ -385,7 +385,7 @@ class PSICallResolver(
override fun factoryForVariable(stripExplicitReceiver: Boolean): CandidateFactory<KotlinResolutionCandidate> { override fun factoryForVariable(stripExplicitReceiver: Boolean): CandidateFactory<KotlinResolutionCandidate> {
val explicitReceiver = if (stripExplicitReceiver) null else kotlinCall.explicitReceiver val explicitReceiver = if (stripExplicitReceiver) null else kotlinCall.explicitReceiver
val variableCall = PSIKotlinCallForVariable(kotlinCall, explicitReceiver, kotlinCall.name) val variableCall = PSIKotlinCallForVariable(kotlinCall, explicitReceiver, kotlinCall.name)
return SimpleCandidateFactory(callComponents, scopeTower, variableCall, context.inferenceSession) return SimpleCandidateFactory(callComponents, scopeTower, variableCall, createResolutionCallbacks(context))
} }
override fun factoryForInvoke(variable: KotlinResolutionCandidate, useExplicitReceiver: Boolean): override fun factoryForInvoke(variable: KotlinResolutionCandidate, useExplicitReceiver: Boolean):
@@ -406,7 +406,7 @@ class PSICallResolver(
} }
return variableCallArgument.receiver to SimpleCandidateFactory( return variableCallArgument.receiver to SimpleCandidateFactory(
callComponents, scopeTower, callForInvoke, context.inferenceSession callComponents, scopeTower, callForInvoke, createResolutionCallbacks(context)
) )
} }
@@ -19,13 +19,17 @@ package org.jetbrains.kotlin.resolve.checkers
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.resolve.SinceKotlinAccessibility
import org.jetbrains.kotlin.resolve.checkSinceKotlinVersionAccessibility import org.jetbrains.kotlin.resolve.checkSinceKotlinVersionAccessibility
object ApiVersionClassifierUsageChecker : ClassifierUsageChecker { object ApiVersionClassifierUsageChecker : ClassifierUsageChecker {
override fun check(targetDescriptor: ClassifierDescriptor, element: PsiElement, context: ClassifierUsageCheckerContext) { override fun check(targetDescriptor: ClassifierDescriptor, element: PsiElement, context: ClassifierUsageCheckerContext) {
targetDescriptor.checkSinceKotlinVersionAccessibility(context.languageVersionSettings) { version -> val accessibility = targetDescriptor.checkSinceKotlinVersionAccessibility(context.languageVersionSettings)
if (accessibility is SinceKotlinAccessibility.NotAccessible) {
context.trace.report( context.trace.report(
Errors.API_NOT_AVAILABLE.on(element, version.versionString, context.languageVersionSettings.apiVersion.versionString) Errors.API_NOT_AVAILABLE.on(
element, accessibility.version.versionString, context.languageVersionSettings.apiVersion.versionString
)
) )
} }
} }
@@ -21,6 +21,7 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.AnalysisFlag import org.jetbrains.kotlin.config.AnalysisFlag
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -62,7 +63,9 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
companion object { companion object {
val EXPERIMENTAL_FQ_NAME = FqName("kotlin.Experimental") val EXPERIMENTAL_FQ_NAME = FqName("kotlin.Experimental")
internal val USE_EXPERIMENTAL_FQ_NAME = FqName("kotlin.UseExperimental") internal val USE_EXPERIMENTAL_FQ_NAME = FqName("kotlin.UseExperimental")
private val WAS_EXPERIMENTAL_FQ_NAME = FqName("kotlin.WasExperimental")
internal val USE_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass") internal val USE_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass")
private val WAS_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass")
private val LEVEL = Name.identifier("level") private val LEVEL = Name.identifier("level")
private val WARNING_LEVEL = Name.identifier("WARNING") private val WARNING_LEVEL = Name.identifier("WARNING")
@@ -119,21 +122,24 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
return Experimentality(fqNameSafe, severity) return Experimentality(fqNameSafe, severity)
} }
private fun PsiElement.isExperimentalityAccepted(annotationFqName: FqName, context: CheckerContext): Boolean =
isExperimentalityAccepted(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 * 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 * annotated with `@X` where [annotationFqName] is the FQ name of X
*/ */
private fun PsiElement.isExperimentalityAccepted(annotationFqName: FqName, context: CheckerContext): Boolean { fun PsiElement.isExperimentalityAccepted(
val languageVersionSettings = context.languageVersionSettings annotationFqName: FqName,
val bindingContext = context.trace.bindingContext languageVersionSettings: LanguageVersionSettings,
bindingContext: BindingContext
return annotationFqName.asString() in languageVersionSettings.getFlag(AnalysisFlag.experimental) || ): Boolean =
annotationFqName.asString() in languageVersionSettings.getFlag(AnalysisFlag.experimental) ||
annotationFqName.asString() in languageVersionSettings.getFlag(AnalysisFlag.useExperimental) || annotationFqName.asString() in languageVersionSettings.getFlag(AnalysisFlag.useExperimental) ||
anyParentMatches { element -> anyParentMatches { element ->
element.isDeclarationAnnotatedWith(annotationFqName, bindingContext) || element.isDeclarationAnnotatedWith(annotationFqName, bindingContext) ||
element.isElementAnnotatedWithUseExperimentalOf(annotationFqName, bindingContext) element.isElementAnnotatedWithUseExperimentalOf(annotationFqName, bindingContext)
} }
}
private fun PsiElement.isDeclarationAnnotatedWith(annotationFqName: FqName, bindingContext: BindingContext): Boolean { private fun PsiElement.isDeclarationAnnotatedWith(annotationFqName: FqName, bindingContext: BindingContext): Boolean {
if (this !is KtDeclaration) return false if (this !is KtDeclaration) return false
@@ -163,6 +169,20 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
} }
} }
internal fun Annotated.loadWasExperimentalMarkerFqNames(): List<FqName> {
val wasExperimental = annotations.findAnnotation(WAS_EXPERIMENTAL_FQ_NAME)
if (wasExperimental != null) {
val annotationClasses = wasExperimental.allValueArguments[WAS_EXPERIMENTAL_ANNOTATION_CLASS]
if (annotationClasses is ArrayValue) {
return annotationClasses.value.mapNotNull { annotationClass ->
(annotationClass as? KClassValue)?.value?.constructor?.declarationDescriptor?.fqNameSafe
}
}
}
return emptyList()
}
fun checkCompilerArguments( fun checkCompilerArguments(
module: ModuleDescriptor, module: ModuleDescriptor,
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
@@ -32,10 +32,12 @@ import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.VersionRequirement import org.jetbrains.kotlin.metadata.deserialization.VersionRequirement
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.Call
import org.jetbrains.kotlin.resolve.DeprecationLevelValue.* import org.jetbrains.kotlin.resolve.DeprecationLevelValue.*
import org.jetbrains.kotlin.resolve.annotations.argumentValue import org.jetbrains.kotlin.resolve.annotations.argumentValue
import org.jetbrains.kotlin.resolve.calls.checkers.isOperatorMod import org.jetbrains.kotlin.resolve.calls.checkers.isOperatorMod
import org.jetbrains.kotlin.resolve.calls.checkers.shouldWarnAboutDeprecatedModFromBuiltIns import org.jetbrains.kotlin.resolve.calls.checkers.shouldWarnAboutDeprecatedModFromBuiltIns
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
import org.jetbrains.kotlin.resolve.constants.AnnotationValue import org.jetbrains.kotlin.resolve.constants.AnnotationValue
import org.jetbrains.kotlin.resolve.constants.EnumValue import org.jetbrains.kotlin.resolve.constants.EnumValue
import org.jetbrains.kotlin.resolve.constants.StringValue import org.jetbrains.kotlin.resolve.constants.StringValue
@@ -210,6 +212,8 @@ class DeprecationResolver(
@JvmOverloads @JvmOverloads
fun isHiddenInResolution( fun isHiddenInResolution(
descriptor: DeclarationDescriptor, descriptor: DeclarationDescriptor,
call: Call? = null,
bindingContext: BindingContext? = null,
isSuperCall: Boolean = false isSuperCall: Boolean = false
): Boolean { ): Boolean {
if (descriptor is FunctionDescriptor) { if (descriptor is FunctionDescriptor) {
@@ -217,7 +221,19 @@ class DeprecationResolver(
if (descriptor.isHiddenForResolutionEverywhereBesideSupercalls && !isSuperCall) return true if (descriptor.isHiddenForResolutionEverywhereBesideSupercalls && !isSuperCall) return true
} }
if (!isHiddenBecauseOfKotlinVersionAccessibility(descriptor.original)) return true val sinceKotlinAccessibility = isHiddenBecauseOfKotlinVersionAccessibility(descriptor.original)
if (sinceKotlinAccessibility is SinceKotlinAccessibility.NotAccessible) return true
if (sinceKotlinAccessibility is SinceKotlinAccessibility.NotAccessibleButWasExperimental) {
if (call != null && bindingContext != null) {
return with(ExperimentalUsageChecker) {
sinceKotlinAccessibility.annotationFqNames.any { fqName ->
!call.callElement.isExperimentalityAccepted(fqName, languageVersionSettings, bindingContext)
}
}
}
return true
}
return isDeprecatedHidden(descriptor) return isDeprecatedHidden(descriptor)
} }
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.isChildOf import org.jetbrains.kotlin.name.isChildOf
import org.jetbrains.kotlin.name.isSubpackageOf import org.jetbrains.kotlin.name.isSubpackageOf
import org.jetbrains.kotlin.resolve.ImportPath import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.resolve.SinceKotlinAccessibility
import org.jetbrains.kotlin.resolve.TargetPlatform import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.checkSinceKotlinVersionAccessibility import org.jetbrains.kotlin.resolve.checkSinceKotlinVersionAccessibility
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
@@ -69,7 +70,7 @@ class DefaultImportProvider(
val excludedImports: List<FqName> by storageManager.createLazyValue { val excludedImports: List<FqName> by storageManager.createLazyValue {
val builtinTypeAliases = val builtinTypeAliases =
moduleDescriptor.findTypeAliasesInPackages(PACKAGES_WITH_ALIASES) moduleDescriptor.findTypeAliasesInPackages(PACKAGES_WITH_ALIASES)
.filter { it.checkSinceKotlinVersionAccessibility(languageVersionSettings) } .filter { it.checkSinceKotlinVersionAccessibility(languageVersionSettings) == SinceKotlinAccessibility.Accessible }
val nonKotlinDefaultImportedPackages = val nonKotlinDefaultImportedPackages =
defaultImports defaultImports
@@ -1,17 +1,6 @@
/* /*
* Copyright 2010-2017 JetBrains s.r.o. * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* * that can be found in the license/LICENSE.txt file.
* 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 package org.jetbrains.kotlin.resolve
@@ -22,17 +11,22 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor import org.jetbrains.kotlin.descriptors.impl.TypeAliasConstructorDescriptor
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForTypeAliasObject
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
val SINCE_KOTLIN_FQ_NAME = FqName("kotlin.SinceKotlin") sealed class SinceKotlinAccessibility {
object Accessible : SinceKotlinAccessibility()
/** data class NotAccessibleButWasExperimental(
* @return true if the descriptor is accessible according to [languageVersionSettings], or false otherwise. The [actionIfInaccessible] val version: ApiVersion,
* callback is called with the version specified in the [SinceKotlin] annotation if the descriptor is inaccessible. val annotationFqNames: List<FqName>
*/ ) : SinceKotlinAccessibility()
fun DeclarationDescriptor.checkSinceKotlinVersionAccessibility(
languageVersionSettings: LanguageVersionSettings, data class NotAccessible(
actionIfInaccessible: ((ApiVersion) -> Unit)? = null val version: ApiVersion
): Boolean { ) : SinceKotlinAccessibility()
}
fun DeclarationDescriptor.checkSinceKotlinVersionAccessibility(languageVersionSettings: LanguageVersionSettings): SinceKotlinAccessibility {
val version = val version =
if (this is CallableMemberDescriptor && !kind.isReal) getSinceKotlinVersionByOverridden(this) if (this is CallableMemberDescriptor && !kind.isReal) getSinceKotlinVersionByOverridden(this)
else getOwnSinceKotlinVersion() else getOwnSinceKotlinVersion()
@@ -41,11 +35,14 @@ fun DeclarationDescriptor.checkSinceKotlinVersionAccessibility(
// 1) There's no @SinceKotlin annotation for this descriptor // 1) There's no @SinceKotlin annotation for this descriptor
// 2) There's a @SinceKotlin annotation but its value is some unrecognizable nonsense // 2) There's a @SinceKotlin annotation but its value is some unrecognizable nonsense
// 3) The value as a version is not greater than our API version // 3) The value as a version is not greater than our API version
if (version == null || version <= languageVersionSettings.apiVersion) return true if (version == null || version <= languageVersionSettings.apiVersion) return SinceKotlinAccessibility.Accessible
actionIfInaccessible?.invoke(version) val wasExperimentalFqNames = with(ExperimentalUsageChecker) { loadWasExperimentalMarkerFqNames() }
if (wasExperimentalFqNames.isNotEmpty()) {
return SinceKotlinAccessibility.NotAccessibleButWasExperimental(version, wasExperimentalFqNames)
}
return false return SinceKotlinAccessibility.NotAccessible(version)
} }
/** /**
@@ -0,0 +1,10 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.resolve
import org.jetbrains.kotlin.name.FqName
val SINCE_KOTLIN_FQ_NAME = FqName("kotlin.SinceKotlin")
@@ -45,7 +45,7 @@ class KotlinCallResolver(
): CallResolutionResult { ): CallResolutionResult {
kotlinCall.checkCallInvariants() kotlinCall.checkCallInvariants()
val candidateFactory = SimpleCandidateFactory(callComponents, scopeTower, kotlinCall, resolutionCallbacks.inferenceSession) val candidateFactory = SimpleCandidateFactory(callComponents, scopeTower, kotlinCall, resolutionCallbacks)
val processor = when (kotlinCall.callKind) { val processor = when (kotlinCall.callKind) {
KotlinCallKind.VARIABLE -> { KotlinCallKind.VARIABLE -> {
createVariableAndObjectProcessor(scopeTower, kotlinCall.name, candidateFactory, kotlinCall.explicitReceiver?.receiver) createVariableAndObjectProcessor(scopeTower, kotlinCall.name, candidateFactory, kotlinCall.explicitReceiver?.receiver)
@@ -96,7 +96,7 @@ class KotlinCallResolver(
collectAllCandidates: Boolean collectAllCandidates: Boolean
): CallResolutionResult { ): CallResolutionResult {
kotlinCall.checkCallInvariants() kotlinCall.checkCallInvariants()
val candidateFactory = SimpleCandidateFactory(callComponents, scopeTower, kotlinCall, resolutionCallbacks.inferenceSession) val candidateFactory = SimpleCandidateFactory(callComponents, scopeTower, kotlinCall, resolutionCallbacks)
val resolutionCandidates = givenCandidates.map { candidateFactory.createCandidate(it).forceResolution() } val resolutionCandidates = givenCandidates.map { candidateFactory.createCandidate(it).forceResolution() }
@@ -21,7 +21,10 @@ interface KotlinResolutionStatelessCallbacks {
fun isInfixCall(kotlinCall: KotlinCall): Boolean fun isInfixCall(kotlinCall: KotlinCall): Boolean
fun isOperatorCall(kotlinCall: KotlinCall): Boolean fun isOperatorCall(kotlinCall: KotlinCall): Boolean
fun isSuperOrDelegatingConstructorCall(kotlinCall: KotlinCall): Boolean fun isSuperOrDelegatingConstructorCall(kotlinCall: KotlinCall): Boolean
fun isHiddenInResolution(descriptor: DeclarationDescriptor, kotlinCall: KotlinCall): Boolean fun isHiddenInResolution(
descriptor: DeclarationDescriptor, kotlinCall: KotlinCall, resolutionCallbacks: KotlinResolutionCallbacks
): Boolean
fun isSuperExpression(receiver: SimpleKotlinCallArgument?): Boolean fun isSuperExpression(receiver: SimpleKotlinCallArgument?): Boolean
fun getScopeTowerForCallableReferenceArgument(argument: CallableReferenceKotlinCallArgument): ImplicitScopeTower fun getScopeTowerForCallableReferenceArgument(argument: CallableReferenceKotlinCallArgument): ImplicitScopeTower
fun getVariableCandidateIfInvoke(functionCall: KotlinCall): KotlinResolutionCandidate? fun getVariableCandidateIfInvoke(functionCall: KotlinCall): KotlinResolutionCandidate?
@@ -49,8 +49,10 @@ class SimpleCandidateFactory(
val callComponents: KotlinCallComponents, val callComponents: KotlinCallComponents,
val scopeTower: ImplicitScopeTower, val scopeTower: ImplicitScopeTower,
val kotlinCall: KotlinCall, val kotlinCall: KotlinCall,
val inferenceSession: InferenceSession val resolutionCallbacks: KotlinResolutionCallbacks
) : CandidateFactory<KotlinResolutionCandidate> { ) : CandidateFactory<KotlinResolutionCandidate> {
val inferenceSession: InferenceSession = resolutionCallbacks.inferenceSession
val baseSystem: ConstraintStorage val baseSystem: ConstraintStorage
init { init {
@@ -144,7 +146,7 @@ class SimpleCandidateFactory(
initialDiagnostics.forEach(candidate::addDiagnostic) initialDiagnostics.forEach(candidate::addDiagnostic)
if (callComponents.statelessCallbacks.isHiddenInResolution(descriptor, kotlinCall)) { if (callComponents.statelessCallbacks.isHiddenInResolution(descriptor, kotlinCall, resolutionCallbacks)) {
candidate.addDiagnostic(HiddenDescriptor) candidate.addDiagnostic(HiddenDescriptor)
} }
@@ -0,0 +1,45 @@
// !API_VERSION: 1.2
// !USE_EXPERIMENTAL: kotlin.Experimental
// !DIAGNOSTICS: -INVISIBLE_MEMBER -INVISIBLE_REFERENCE -NEWER_VERSION_IN_SINCE_KOTLIN -UNUSED_PARAMETER
@SinceKotlin("1.3")
fun newPublishedFun() {}
@Experimental
annotation class Marker
@SinceKotlin("1.3")
@WasExperimental(Marker::class)
fun newFunExperimentalInThePast() {}
@SinceKotlin("1.3")
@WasExperimental(Marker::class)
val newValExperimentalInThePast = ""
@SinceKotlin("1.3")
@WasExperimental(Marker::class)
class NewClassExperimentalInThePast
fun use1(c: <!UNRESOLVED_REFERENCE!>NewClassExperimentalInThePast<!>) {
<!UNRESOLVED_REFERENCE!>newPublishedFun<!>()
<!UNRESOLVED_REFERENCE!>newFunExperimentalInThePast<!>()
<!UNRESOLVED_REFERENCE!>newValExperimentalInThePast<!>
<!UNRESOLVED_REFERENCE!>NewClassExperimentalInThePast<!>()
}
@UseExperimental(Marker::class)
fun use2(c: NewClassExperimentalInThePast) {
<!UNRESOLVED_REFERENCE!>newPublishedFun<!>()
newFunExperimentalInThePast()
newValExperimentalInThePast
NewClassExperimentalInThePast()
}
@Marker
fun use3(c: NewClassExperimentalInThePast) {
<!UNRESOLVED_REFERENCE!>newPublishedFun<!>()
newFunExperimentalInThePast()
newValExperimentalInThePast
NewClassExperimentalInThePast()
}
@@ -0,0 +1,14 @@
package
@kotlin.SinceKotlin(version = "1.3") @kotlin.WasExperimental(markerClass = {Marker::class}) public fun newFunExperimentalInThePast(): kotlin.Unit
@kotlin.SinceKotlin(version = "1.3") public fun newPublishedFun(): kotlin.Unit
public fun use1(): kotlin.Unit
@kotlin.UseExperimental(markerClass = {Marker::class}) public fun use2(): kotlin.Unit
@Marker public fun use3(): kotlin.Unit
@kotlin.Experimental public final annotation class Marker : kotlin.Annotation {
public constructor Marker()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -2196,6 +2196,11 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
public void testUseExperimentalWithSeveralAnnotations() throws Exception { public void testUseExperimentalWithSeveralAnnotations() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/useExperimentalWithSeveralAnnotations.kt"); runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/useExperimentalWithSeveralAnnotations.kt");
} }
@TestMetadata("wasExperimental.kt")
public void testWasExperimental() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/wasExperimental.kt");
}
} }
@TestMetadata("compiler/testData/diagnostics/testsWithStdLib/forInArrayLoop") @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/forInArrayLoop")
@@ -2196,6 +2196,11 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno
public void testUseExperimentalWithSeveralAnnotations() throws Exception { public void testUseExperimentalWithSeveralAnnotations() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/useExperimentalWithSeveralAnnotations.kt"); runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/useExperimentalWithSeveralAnnotations.kt");
} }
@TestMetadata("wasExperimental.kt")
public void testWasExperimental() throws Exception {
runTest("compiler/testData/diagnostics/testsWithStdLib/experimental/wasExperimental.kt");
}
} }
@TestMetadata("compiler/testData/diagnostics/testsWithStdLib/forInArrayLoop") @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/forInArrayLoop")
@@ -50,3 +50,10 @@ annotation class Experimental(val level: Level = Level.ERROR) {
annotation class UseExperimental( annotation class UseExperimental(
vararg val markerClass: KClass<out Annotation> vararg val markerClass: KClass<out Annotation>
) )
@Target(CLASS, PROPERTY, CONSTRUCTOR, FUNCTION)
@Retention(BINARY)
internal annotation class WasExperimental(
vararg val markerClass: KClass<out Annotation>
)