Introduce "-api-version" CLI option
The `@SinceKotlin("X.Y.Z")` annotation now hides a particular declaration from
resolution when the API version specified by the `-api-version` option is
_less_ than X.Y.Z. The comparison is performed as for versions in Maven:
MavenComparableVersion is in fact a copy of
org.apache.maven.artifact.versioning.ComparableVersion.
Also support "!API_VERSION" directive in diagnostic tests
#KT-14298 Fixed
This commit is contained in:
@@ -73,6 +73,11 @@ public interface Errors {
|
||||
DiagnosticFactory1<KtReferenceExpression, KtReferenceExpression> UNRESOLVED_REFERENCE =
|
||||
DiagnosticFactory1.create(ERROR, FOR_UNRESOLVED_REFERENCE);
|
||||
|
||||
DiagnosticFactory2<PsiElement, DeclarationDescriptor, String> DEPRECATION = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory2<PsiElement, DeclarationDescriptor, String> DEPRECATION_ERROR = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
DiagnosticFactory2<PsiElement, String, String> API_NOT_AVAILABLE = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
//Elements with "INVISIBLE_REFERENCE" error are marked as unresolved, unlike elements with "INVISIBLE_MEMBER" error
|
||||
//"INVISIBLE_REFERENCE" is used for invisible classes references and references in import
|
||||
DiagnosticFactory3<KtSimpleNameExpression, DeclarationDescriptor, Visibility, DeclarationDescriptor> INVISIBLE_REFERENCE =
|
||||
@@ -278,9 +283,6 @@ public interface Errors {
|
||||
|
||||
DiagnosticFactory0<KtObjectDeclaration> MANY_COMPANION_OBJECTS = DiagnosticFactory0.create(ERROR, COMPANION_OBJECT);
|
||||
|
||||
DiagnosticFactory2<PsiElement, DeclarationDescriptor, String> DEPRECATION = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory2<PsiElement, DeclarationDescriptor, String> DEPRECATION_ERROR = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
// Objects
|
||||
|
||||
DiagnosticFactory1<KtObjectDeclaration, ClassDescriptor> LOCAL_OBJECT_NOT_ALLOWED = DiagnosticFactory1.create(ERROR, DECLARATION_NAME);
|
||||
|
||||
+2
@@ -300,6 +300,8 @@ public class DefaultErrorMessages {
|
||||
MAP.put(DEPRECATION, "''{0}'' is deprecated. {1}", DEPRECATION_RENDERER, STRING);
|
||||
MAP.put(DEPRECATION_ERROR, "Using ''{0}'' is an error. {1}", DEPRECATION_RENDERER, STRING);
|
||||
|
||||
MAP.put(API_NOT_AVAILABLE, "This declaration is only available since Kotlin {0} and cannot be used with the specified API version {1}", STRING, STRING);
|
||||
|
||||
MAP.put(LOCAL_OBJECT_NOT_ALLOWED, "Named object ''{0}'' is a singleton and cannot be local. Try to use anonymous object instead", NAME);
|
||||
MAP.put(LOCAL_INTERFACE_NOT_ALLOWED, "''{0}'' is an interface so it cannot be local. Try to use anonymous object or abstract class instead", NAME);
|
||||
MAP.put(ENUM_CLASS_CONSTRUCTOR_CALL, "Enum types cannot be instantiated");
|
||||
|
||||
@@ -71,11 +71,11 @@ private val DEFAULT_DECLARATION_CHECKERS = listOf(
|
||||
private val DEFAULT_CALL_CHECKERS = listOf(
|
||||
CapturingInClosureChecker(), InlineCheckerWrapper(), ReifiedTypeParameterSubstitutionChecker(), SafeCallChecker(),
|
||||
DeprecatedCallChecker, CallReturnsArrayOfNothingChecker(), InfixCallChecker(), OperatorCallChecker(),
|
||||
ConstructorHeaderCallChecker, ProtectedConstructorCallChecker,
|
||||
ConstructorHeaderCallChecker, ProtectedConstructorCallChecker, ApiVersionCallChecker,
|
||||
CoroutineSuspendCallChecker, BuilderFunctionsCallChecker
|
||||
)
|
||||
private val DEFAULT_TYPE_CHECKERS = emptyList<AdditionalTypeChecker>()
|
||||
private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf(DeprecatedClassifierUsageChecker())
|
||||
private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf(DeprecatedClassifierUsageChecker(), ApiVersionClassifierUsageChecker)
|
||||
|
||||
|
||||
abstract class PlatformConfigurator(
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* 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.calls.checkers
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.API_NOT_AVAILABLE
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
|
||||
import org.jetbrains.kotlin.resolve.checkSinceKotlinVersionAccessibility
|
||||
|
||||
// TODO: consider combining with DeprecatedCallChecker somehow
|
||||
object ApiVersionCallChecker : CallChecker {
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
check(resolvedCall.resultingDescriptor, context, reportOn)
|
||||
}
|
||||
|
||||
private fun check(targetDescriptor: CallableDescriptor, context: CallCheckerContext, element: PsiElement) {
|
||||
// Objects will be checked by ApiVersionClassifierUsageChecker
|
||||
if (targetDescriptor is FakeCallableDescriptorForObject) return
|
||||
|
||||
val accessible = targetDescriptor.checkSinceKotlinVersionAccessibility(context.languageVersionSettings) { version ->
|
||||
context.trace.report(
|
||||
API_NOT_AVAILABLE.on(element, version.versionString, context.languageVersionSettings.apiVersion.versionString)
|
||||
)
|
||||
}
|
||||
|
||||
if (accessible && targetDescriptor is PropertyDescriptor && DeprecatedCallChecker.shouldCheckPropertyGetter(element)) {
|
||||
targetDescriptor.getter?.let { check(it, context, element) }
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -57,7 +57,7 @@ object DeprecatedCallChecker : CallChecker {
|
||||
|
||||
private val PROPERTY_SET_OPERATIONS = TokenSet.create(*KtTokens.ALL_ASSIGNMENTS.types, KtTokens.PLUSPLUS, KtTokens.MINUSMINUS)
|
||||
|
||||
private fun shouldCheckPropertyGetter(expression: PsiElement): Boolean {
|
||||
internal fun shouldCheckPropertyGetter(expression: PsiElement): Boolean {
|
||||
// property getters do not come as callable yet, so we analyse surroundings to check for deprecation annotation on getter
|
||||
val binaryExpression = PsiTreeUtil.getParentOfType<KtBinaryExpression>(expression, KtBinaryExpression::class.java)
|
||||
if (binaryExpression != null) {
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* 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.checkers
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.checkSinceKotlinVersionAccessibility
|
||||
|
||||
object ApiVersionClassifierUsageChecker : ClassifierUsageChecker {
|
||||
override fun check(
|
||||
targetDescriptor: ClassifierDescriptor,
|
||||
trace: BindingTrace,
|
||||
element: PsiElement,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
) {
|
||||
targetDescriptor.checkSinceKotlinVersionAccessibility(languageVersionSettings) { version ->
|
||||
trace.report(Errors.API_NOT_AVAILABLE.on(element, version.versionString, languageVersionSettings.apiVersion.versionString))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,5 +196,8 @@ fun DeclarationDescriptor.isHiddenInResolution(languageVersionSettings: Language
|
||||
if (isHiddenToOvercomeSignatureClash) return true
|
||||
if (isHiddenForResolutionEverywhereBesideSupercalls && !isSuperCall) return true
|
||||
}
|
||||
|
||||
if (!checkSinceKotlinVersionAccessibility(languageVersionSettings)) return true
|
||||
|
||||
return isDeprecatedHidden()
|
||||
}
|
||||
|
||||
@@ -16,11 +16,59 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve
|
||||
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyAccessorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
private val SINCE_KOTLIN_FQ_NAME = FqName("kotlin.SinceKotlin")
|
||||
|
||||
// TODO: use-site targeted annotations
|
||||
internal fun DeclarationDescriptor.getSinceKotlinAnnotation(): AnnotationDescriptor? =
|
||||
annotations.findAnnotation(SINCE_KOTLIN_FQ_NAME)
|
||||
|
||||
/**
|
||||
* @return true if the descriptor is accessible according to [languageVersionSettings], or false otherwise. The [actionIfInaccessible]
|
||||
* callback is called with the version specified in the [SinceKotlin] annotation if the descriptor is inaccessible.
|
||||
*/
|
||||
internal fun DeclarationDescriptor.checkSinceKotlinVersionAccessibility(
|
||||
languageVersionSettings: LanguageVersionSettings,
|
||||
actionIfInaccessible: ((ApiVersion) -> Unit)? = null
|
||||
): Boolean {
|
||||
val version =
|
||||
if (this is CallableMemberDescriptor && !kind.isReal) getSinceKotlinVersionByOverridden(this)
|
||||
else getOwnSinceKotlinVersion()
|
||||
|
||||
// Allow access in the following cases:
|
||||
// 1) There's no @SinceKotlin annotation for this descriptor
|
||||
// 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
|
||||
if (version == null || version <= languageVersionSettings.apiVersion) return true
|
||||
|
||||
actionIfInaccessible?.invoke(version)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null if there are no overridden members or if there's at least one declaration in the hierarchy not annotated with [SinceKotlin],
|
||||
* or the minimal value of the version from all declarations annotated with [SinceKotlin] otherwise.
|
||||
*/
|
||||
private fun getSinceKotlinVersionByOverridden(descriptor: CallableMemberDescriptor): ApiVersion? {
|
||||
return DescriptorUtils.getAllOverriddenDeclarations(descriptor).map { it.getOwnSinceKotlinVersion() ?: return null }.min()
|
||||
}
|
||||
|
||||
private fun DeclarationDescriptor.getOwnSinceKotlinVersion(): ApiVersion? {
|
||||
fun DeclarationDescriptor.loadAnnotationValue(): ApiVersion? =
|
||||
(getSinceKotlinAnnotation()?.allValueArguments?.values?.singleOrNull()?.value as? String)?.let(ApiVersion.Companion::parse)
|
||||
|
||||
val ownVersion = loadAnnotationValue()
|
||||
val ctorClass = (this as? ConstructorDescriptor)?.containingDeclaration?.loadAnnotationValue()
|
||||
val property = (this as? PropertyAccessorDescriptor)?.correspondingProperty?.loadAnnotationValue()
|
||||
|
||||
return listOfNotNull(ownVersion, ctorClass, property).max()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user