Introduce SinceKotlinInfo, load from serialized metadata
This is a way for future compilers to cause previous compilers to report diagnostics on usages of some declarations. Diagnostic can have a message (and/or error code), level (error, warning, or completely hide the declaration from the resolution), and Kotlin version, since which the diagnostic should no longer be reported
This commit is contained in:
@@ -20,9 +20,11 @@ import com.google.common.collect.ImmutableSet;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement;
|
||||
import kotlin.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.cfg.WhenMissingCase;
|
||||
import org.jetbrains.kotlin.config.LanguageFeature;
|
||||
import org.jetbrains.kotlin.config.LanguageVersion;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.lexer.KtKeywordToken;
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken;
|
||||
@@ -79,6 +81,8 @@ public interface Errors {
|
||||
|
||||
DiagnosticFactory2<PsiElement, DeclarationDescriptor, String> DEPRECATION = DiagnosticFactory2.create(WARNING);
|
||||
DiagnosticFactory2<PsiElement, DeclarationDescriptor, String> DEPRECATION_ERROR = DiagnosticFactory2.create(ERROR);
|
||||
DiagnosticFactory3<PsiElement, DeclarationDescriptor, String, Pair<LanguageVersion, String>> SINCE_KOTLIN_INFO_DEPRECATION = DiagnosticFactory3.create(WARNING);
|
||||
DiagnosticFactory3<PsiElement, DeclarationDescriptor, String, Pair<LanguageVersion, String>> SINCE_KOTLIN_INFO_DEPRECATION_ERROR = DiagnosticFactory3.create(ERROR);
|
||||
|
||||
DiagnosticFactory2<PsiElement, String, String> API_NOT_AVAILABLE = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
|
||||
+12
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.diagnostics.rendering;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import kotlin.Pair;
|
||||
import kotlin.jvm.functions.Function1;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -320,6 +321,17 @@ 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);
|
||||
|
||||
DiagnosticParameterRenderer<Pair<LanguageVersion, String>> sinceKotlinInfoRenderer = new DiagnosticParameterRenderer<Pair<LanguageVersion, String>>() {
|
||||
@NotNull
|
||||
@Override
|
||||
public String render(@NotNull Pair<LanguageVersion, String> pair, @NotNull RenderingContext renderingContext) {
|
||||
String message = pair.getSecond();
|
||||
return pair.getFirst().getVersionString() + (message != null ? ". " + message : "");
|
||||
}
|
||||
};
|
||||
MAP.put(SINCE_KOTLIN_INFO_DEPRECATION, "''{0}'' is only supported since Kotlin {1} and should not be used in Kotlin {2}", DEPRECATION_RENDERER, STRING, sinceKotlinInfoRenderer);
|
||||
MAP.put(SINCE_KOTLIN_INFO_DEPRECATION_ERROR, "''{0}'' is only available since Kotlin {1} and cannot be used in Kotlin {2}", DEPRECATION_RENDERER, STRING, sinceKotlinInfoRenderer);
|
||||
|
||||
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(MISSING_DEPENDENCY_CLASS, "Cannot access class ''{0}''. Check your module classpath for missing or conflicting dependencies", TO_STRING);
|
||||
|
||||
+7
-4
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.resolve.calls.checkers
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor
|
||||
@@ -35,10 +36,12 @@ import org.jetbrains.kotlin.resolve.getDeprecations
|
||||
|
||||
object DeprecatedCallChecker : CallChecker {
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
check(resolvedCall.resultingDescriptor, context.trace, reportOn)
|
||||
check(resolvedCall.resultingDescriptor, context.trace, reportOn, context.languageVersionSettings)
|
||||
}
|
||||
|
||||
private fun check(targetDescriptor: CallableDescriptor, trace: BindingTrace, element: PsiElement) {
|
||||
private fun check(
|
||||
targetDescriptor: CallableDescriptor, trace: BindingTrace, element: PsiElement, languageVersionSettings: LanguageVersionSettings
|
||||
) {
|
||||
// Objects will be checked by DeprecatedClassifierUsageChecker
|
||||
if (targetDescriptor is FakeCallableDescriptorForObject) return
|
||||
|
||||
@@ -51,11 +54,11 @@ object DeprecatedCallChecker : CallChecker {
|
||||
|
||||
if (deprecations.isNotEmpty()) {
|
||||
for (deprecation in deprecations) {
|
||||
trace.report(createDeprecationDiagnostic(element, deprecation))
|
||||
trace.report(createDeprecationDiagnostic(element, deprecation, languageVersionSettings))
|
||||
}
|
||||
}
|
||||
else if (targetDescriptor is PropertyDescriptor && shouldCheckPropertyGetter(element)) {
|
||||
targetDescriptor.getter?.let { check(it, trace, element) }
|
||||
targetDescriptor.getter?.let { check(it, trace, element, languageVersionSettings) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ class DeprecatedClassifierUsageChecker : ClassifierUsageChecker {
|
||||
) {
|
||||
val deprecations = targetDescriptor.getDeprecations()
|
||||
for (deprecation in deprecations) {
|
||||
trace.report(createDeprecationDiagnostic(element, deprecation))
|
||||
trace.report(createDeprecationDiagnostic(element, deprecation, languageVersionSettings))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.DeprecationLevelValue.*
|
||||
import org.jetbrains.kotlin.resolve.annotations.argumentValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedMemberDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.SinceKotlinInfo
|
||||
import org.jetbrains.kotlin.utils.SmartList
|
||||
|
||||
private val JAVA_DEPRECATED = FqName("java.lang.Deprecated")
|
||||
@@ -89,8 +91,42 @@ private data class DeprecatedByOverridden(private val deprecations: Collection<D
|
||||
internal fun additionalMessage() = "Overrides deprecated member in '${DescriptorUtils.getContainingClass(target)!!.fqNameSafe.asString()}'"
|
||||
}
|
||||
|
||||
private data class DeprecatedBySinceKotlinInfo(
|
||||
private val sinceKotlinInfo: SinceKotlinInfo,
|
||||
override val target: DeclarationDescriptor
|
||||
) : Deprecation {
|
||||
override val deprecationLevel: DeprecationLevelValue
|
||||
get() = when (sinceKotlinInfo.level) {
|
||||
DeprecationLevel.WARNING -> WARNING
|
||||
DeprecationLevel.ERROR -> ERROR
|
||||
DeprecationLevel.HIDDEN -> HIDDEN
|
||||
}
|
||||
|
||||
override val message: String?
|
||||
get() {
|
||||
val message = sinceKotlinInfo.message
|
||||
val errorCode = sinceKotlinInfo.errorCode
|
||||
if (message == null && errorCode == null) return null
|
||||
|
||||
return buildString {
|
||||
if (message != null) {
|
||||
append(message)
|
||||
if (errorCode != null) {
|
||||
append(" (error code $errorCode)")
|
||||
}
|
||||
}
|
||||
else {
|
||||
append("Error code $errorCode")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val sinceKotlinVersion: String
|
||||
get() = sinceKotlinInfo.version.asString()
|
||||
}
|
||||
|
||||
fun DeclarationDescriptor.getDeprecations(): List<Deprecation> {
|
||||
val deprecations = this.getDeprecationsByAnnotation()
|
||||
val deprecations = this.getOwnDeprecations()
|
||||
if (deprecations.isNotEmpty()) {
|
||||
return deprecations
|
||||
}
|
||||
@@ -112,7 +148,7 @@ private fun deprecationByOverridden(root: CallableMemberDescriptor): Deprecation
|
||||
|
||||
visited.add(node)
|
||||
|
||||
val deprecationsByAnnotation = node.getDeprecationsByAnnotation()
|
||||
val deprecationsByAnnotation = node.getOwnDeprecations()
|
||||
val overriddenDescriptors = node.original.overriddenDescriptors
|
||||
when {
|
||||
deprecationsByAnnotation.isNotEmpty() -> {
|
||||
@@ -135,11 +171,11 @@ private fun deprecationByOverridden(root: CallableMemberDescriptor): Deprecation
|
||||
return DeprecatedByOverridden(deprecations)
|
||||
}
|
||||
|
||||
private fun DeclarationDescriptor.getDeprecationsByAnnotation(): List<Deprecation> {
|
||||
private fun DeclarationDescriptor.getOwnDeprecations(): List<Deprecation> {
|
||||
if (this is TypeAliasConstructorDescriptor) {
|
||||
// Constructor of type alias has no annotations by itself, all its annotations come from the aliased constructor
|
||||
// and from the typealias declaration
|
||||
return underlyingConstructorDescriptor.getDeprecationsByAnnotation() + typeAliasDescriptor.getDeprecationsByAnnotation()
|
||||
return underlyingConstructorDescriptor.getOwnDeprecations() + typeAliasDescriptor.getOwnDeprecations()
|
||||
}
|
||||
|
||||
val result = SmartList<Deprecation>()
|
||||
@@ -150,6 +186,13 @@ private fun DeclarationDescriptor.getDeprecationsByAnnotation(): List<Deprecatio
|
||||
if (annotation != null) {
|
||||
result.add(DeprecatedByAnnotation(annotation, target))
|
||||
}
|
||||
|
||||
if (target is DeserializedMemberDescriptor) {
|
||||
val sinceKotlinInfo = target.sinceKotlinInfo
|
||||
if (sinceKotlinInfo != null) {
|
||||
result.add(DeprecatedBySinceKotlinInfo(sinceKotlinInfo, target))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addUseSiteTargetedDeprecationIfPresent(annotatedDescriptor: DeclarationDescriptor, useSiteTarget: AnnotationUseSiteTarget?) {
|
||||
@@ -182,14 +225,24 @@ private fun DeclarationDescriptor.getDeprecationsByAnnotation(): List<Deprecatio
|
||||
return result.distinct()
|
||||
}
|
||||
|
||||
internal fun createDeprecationDiagnostic(element: PsiElement, deprecation: Deprecation): Diagnostic {
|
||||
internal fun createDeprecationDiagnostic(
|
||||
element: PsiElement, deprecation: Deprecation, languageVersionSettings: LanguageVersionSettings
|
||||
): Diagnostic {
|
||||
val targetOriginal = deprecation.target.original
|
||||
val diagnosticFactory = when (deprecation.deprecationLevel) {
|
||||
WARNING -> Errors.DEPRECATION
|
||||
ERROR -> Errors.DEPRECATION_ERROR
|
||||
HIDDEN -> Errors.DEPRECATION_ERROR
|
||||
if (deprecation is DeprecatedBySinceKotlinInfo) {
|
||||
val factory = when (deprecation.deprecationLevel) {
|
||||
WARNING -> Errors.SINCE_KOTLIN_INFO_DEPRECATION
|
||||
ERROR, HIDDEN -> Errors.SINCE_KOTLIN_INFO_DEPRECATION_ERROR
|
||||
}
|
||||
return factory.on(element, targetOriginal, deprecation.sinceKotlinVersion,
|
||||
languageVersionSettings.languageVersion to deprecation.message)
|
||||
}
|
||||
return diagnosticFactory.on(element, targetOriginal, deprecation.message ?: "")
|
||||
|
||||
val factory = when (deprecation.deprecationLevel) {
|
||||
WARNING -> Errors.DEPRECATION
|
||||
ERROR, HIDDEN -> Errors.DEPRECATION_ERROR
|
||||
}
|
||||
return factory.on(element, targetOriginal, deprecation.message ?: "")
|
||||
}
|
||||
|
||||
// values from kotlin.DeprecationLevel
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.config
|
||||
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.SinceKotlinInfo
|
||||
|
||||
class ApiVersion private constructor(
|
||||
private val version: MavenComparableVersion,
|
||||
val versionString: String
|
||||
@@ -38,6 +40,10 @@ class ApiVersion private constructor(
|
||||
@JvmStatic
|
||||
fun createByLanguageVersion(version: LanguageVersion): ApiVersion = parse(version.versionString)!!
|
||||
|
||||
@JvmStatic
|
||||
fun createBySinceKotlinInfo(sinceKotlinInfo: SinceKotlinInfo): ApiVersion =
|
||||
sinceKotlinInfo.version.let { version -> parse(version.asString()) ?: error("Could not parse version: $version") }
|
||||
|
||||
fun parse(versionString: String): ApiVersion? = try {
|
||||
ApiVersion(MavenComparableVersion(versionString), versionString)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user