[FIR] Report when typealias expansion contains deprecated type

#KT-57843 Fixed
This commit is contained in:
Kirill Rakhman
2023-04-06 11:56:10 +02:00
committed by Space Team
parent 206fad4256
commit 1f29490118
13 changed files with 124 additions and 42 deletions
@@ -313,6 +313,24 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.TYPEALIAS_EXPANSION_DEPRECATION_ERROR) { firDiagnostic ->
TypealiasExpansionDeprecationErrorImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a),
firSymbolBuilder.buildSymbol(firDiagnostic.b),
firDiagnostic.c,
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.TYPEALIAS_EXPANSION_DEPRECATION) { firDiagnostic ->
TypealiasExpansionDeprecationImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a),
firSymbolBuilder.buildSymbol(firDiagnostic.b),
firDiagnostic.c,
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.API_NOT_AVAILABLE) { firDiagnostic ->
ApiNotAvailableImpl(
firDiagnostic.a,
@@ -259,6 +259,20 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val message: String
}
abstract class TypealiasExpansionDeprecationError : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = TypealiasExpansionDeprecationError::class
abstract val alias: KtSymbol
abstract val reference: KtSymbol
abstract val message: String
}
abstract class TypealiasExpansionDeprecation : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = TypealiasExpansionDeprecation::class
abstract val alias: KtSymbol
abstract val reference: KtSymbol
abstract val message: String
}
abstract class ApiNotAvailable : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = ApiNotAvailable::class
abstract val sinceKotlinVersion: ApiVersion
@@ -297,6 +297,22 @@ internal class DeprecationImpl(
override val token: KtLifetimeToken,
) : KtFirDiagnostic.Deprecation(), KtAbstractFirDiagnostic<PsiElement>
internal class TypealiasExpansionDeprecationErrorImpl(
override val alias: KtSymbol,
override val reference: KtSymbol,
override val message: String,
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
) : KtFirDiagnostic.TypealiasExpansionDeprecationError(), KtAbstractFirDiagnostic<PsiElement>
internal class TypealiasExpansionDeprecationImpl(
override val alias: KtSymbol,
override val reference: KtSymbol,
override val message: String,
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
) : KtFirDiagnostic.TypealiasExpansionDeprecation(), KtAbstractFirDiagnostic<PsiElement>
internal class ApiNotAvailableImpl(
override val sinceKotlinVersion: ApiVersion,
override val currentVersion: ApiVersion,
@@ -123,6 +123,17 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
parameter<String>("message")
}
val TYPEALIAS_EXPANSION_DEPRECATION_ERROR by error<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) {
parameter<Symbol>("alias")
parameter<Symbol>("reference")
parameter<String>("message")
}
val TYPEALIAS_EXPANSION_DEPRECATION by warning<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) {
parameter<Symbol>("alias")
parameter<Symbol>("reference")
parameter<String>("message")
}
val API_NOT_AVAILABLE by error<PsiElement>(PositioningStrategy.SELECTOR_BY_QUALIFIED) {
parameter<ApiVersion>("sinceKotlinVersion")
parameter<ApiVersion>("currentVersion")
@@ -155,6 +155,8 @@ object FirErrors {
val NO_THIS by error0<PsiElement>()
val DEPRECATION_ERROR by error2<PsiElement, FirBasedSymbol<*>, String>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val DEPRECATION by warning2<PsiElement, FirBasedSymbol<*>, String>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val TYPEALIAS_EXPANSION_DEPRECATION_ERROR by error3<PsiElement, FirBasedSymbol<*>, FirBasedSymbol<*>, String>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val TYPEALIAS_EXPANSION_DEPRECATION by warning3<PsiElement, FirBasedSymbol<*>, FirBasedSymbol<*>, String>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val API_NOT_AVAILABLE by error2<PsiElement, ApiVersion, ApiVersion>(SourceElementPositioningStrategies.SELECTOR_BY_QUALIFIED)
val UNRESOLVED_REFERENCE_WRONG_RECEIVER by error1<PsiElement, Collection<FirBasedSymbol<*>>>()
val UNRESOLVED_IMPORT by error1<PsiElement, String>(SourceElementPositioningStrategies.IMPORT_LAST_NAME)
@@ -291,6 +291,6 @@ object FirImportsChecker : FirFileChecker() {
if (importedFqName.isRoot || importedFqName.shortName().asString().isEmpty()) return
val classId = (import as? FirResolvedImport)?.resolvedParentClassId ?: ClassId.topLevel(importedFqName)
val classLike: FirRegularClassSymbol = classId.resolveToClass(context) ?: return
FirDeprecationChecker.reportApiStatusIfNeeded(import.source, classLike, null, context, reporter)
FirDeprecationChecker.reportApiStatusIfNeeded(import.source, classLike, context, reporter)
}
}
@@ -14,11 +14,11 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
object FirDeprecatedQualifierChecker : FirResolvedQualifierChecker() {
override fun check(expression: FirResolvedQualifier, context: CheckerContext, reporter: DiagnosticReporter) {
expression.nonFatalDiagnostics.filterIsInstance<ConeDeprecated>().forEach { diagnostic ->
FirDeprecationChecker.reportApiStatus(diagnostic.source, diagnostic.symbol, diagnostic.deprecationInfo, reporter, context)
FirDeprecationChecker.reportApiStatus(diagnostic.source, diagnostic.symbol, null, diagnostic.deprecationInfo, reporter, context)
}
if (expression.resolvedToCompanionObject) {
val companionSymbol = (expression.symbol as? FirRegularClassSymbol)?.companionObjectSymbol ?: return
FirDeprecationChecker.reportApiStatusIfNeeded(expression.source, companionSymbol, null, context, reporter)
FirDeprecationChecker.reportApiStatusIfNeeded(expression.source, companionSymbol, context, reporter)
}
}
}
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.resolved
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.deprecation.DeprecationLevelValue
@@ -43,26 +44,28 @@ object FirDeprecationChecker : FirBasicExpressionChecker() {
if (expression is FirDelegatedConstructorCall) {
// Report deprecations on the constructor itself, not on the declaring class as that will be handled by FirDeprecatedTypeChecker
val constructorOnlyDeprecation = referencedSymbol.getDeprecation(context.session, expression) ?: return
reportApiStatus(resolvedReference.source, referencedSymbol, constructorOnlyDeprecation, reporter, context)
reportApiStatus(resolvedReference.source, referencedSymbol, null, constructorOnlyDeprecation, reporter, context)
} else {
reportApiStatusIfNeeded(resolvedReference.source, referencedSymbol, expression, context, reporter)
reportApiStatusIfNeeded(resolvedReference.source, referencedSymbol, context, reporter, callSite = expression)
}
}
internal fun reportApiStatusIfNeeded(
source: KtSourceElement?,
referencedSymbol: FirBasedSymbol<*>,
callSite: FirElement?,
context: CheckerContext,
reporter: DiagnosticReporter
reporter: DiagnosticReporter,
typealiasSymbol: FirTypeAliasSymbol? = null,
callSite: FirElement? = null,
) {
val deprecation = getWorstDeprecation(callSite, referencedSymbol, context) ?: return
reportApiStatus(source, referencedSymbol, deprecation, reporter, context)
reportApiStatus(source, referencedSymbol, typealiasSymbol, deprecation, reporter, context)
}
internal fun reportApiStatus(
source: KtSourceElement?,
referencedSymbol: FirBasedSymbol<*>,
typealiasSymbol: FirTypeAliasSymbol?,
deprecationInfo: DeprecationInfo,
reporter: DiagnosticReporter,
context: CheckerContext,
@@ -70,22 +73,31 @@ object FirDeprecationChecker : FirBasicExpressionChecker() {
if (deprecationInfo is FutureApiDeprecationInfo) {
reportApiNotAvailable(source, deprecationInfo, reporter, context)
} else {
reportDeprecation(source, referencedSymbol, deprecationInfo, reporter, context)
reportDeprecation(source, referencedSymbol, typealiasSymbol, deprecationInfo, reporter, context)
}
}
private fun reportDeprecation(
source: KtSourceElement?,
referencedSymbol: FirBasedSymbol<*>,
typealiasSymbol: FirTypeAliasSymbol?,
deprecationInfo: DeprecationInfo,
reporter: DiagnosticReporter,
context: CheckerContext
) {
val diagnostic = when (deprecationInfo.deprecationLevel) {
DeprecationLevelValue.ERROR, DeprecationLevelValue.HIDDEN -> FirErrors.DEPRECATION_ERROR
DeprecationLevelValue.WARNING -> FirErrors.DEPRECATION
if (typealiasSymbol == null) {
val diagnostic = when (deprecationInfo.deprecationLevel) {
DeprecationLevelValue.ERROR, DeprecationLevelValue.HIDDEN -> FirErrors.DEPRECATION_ERROR
DeprecationLevelValue.WARNING -> FirErrors.DEPRECATION
}
reporter.reportOn(source, diagnostic, referencedSymbol, deprecationInfo.message ?: "", context)
} else {
val diagnostic = when (deprecationInfo.deprecationLevel) {
DeprecationLevelValue.ERROR, DeprecationLevelValue.HIDDEN -> FirErrors.TYPEALIAS_EXPANSION_DEPRECATION_ERROR
DeprecationLevelValue.WARNING -> FirErrors.TYPEALIAS_EXPANSION_DEPRECATION
}
reporter.reportOn(source, diagnostic, typealiasSymbol, referencedSymbol, deprecationInfo.message ?: "", context)
}
reporter.reportOn(source, diagnostic, referencedSymbol, deprecationInfo.message ?: "", context)
}
private fun reportApiNotAvailable(
@@ -6,21 +6,45 @@
package org.jetbrains.kotlin.fir.analysis.checkers.type
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirDeprecationChecker
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeAliasSymbol
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
import org.jetbrains.kotlin.fir.types.*
object FirDeprecatedTypeChecker : FirTypeRefChecker() {
override fun check(typeRef: FirTypeRef, context: CheckerContext, reporter: DiagnosticReporter) {
val source = typeRef.source ?: return
if (source.kind is KtFakeSourceElementKind) return
val resolved = typeRef.coneTypeSafe<ConeClassLikeType>() ?: return
val symbol = resolved.lookupTag.toSymbol(context.session) ?: return
FirDeprecationChecker.reportApiStatusIfNeeded(source, symbol, null, context, reporter)
val resolved = typeRef.coneTypeSafe<ConeClassLikeType>() ?: return
checkType(resolved, null, source, context, reporter)
}
@OptIn(SymbolInternals::class)
private fun checkType(
type: ConeClassLikeType,
typeAliasSymbol: FirTypeAliasSymbol?,
source: KtSourceElement,
context: CheckerContext,
reporter: DiagnosticReporter
) {
val symbol = type.lookupTag.toSymbol(context.session) ?: return
FirDeprecationChecker.reportApiStatusIfNeeded(source, symbol, context, reporter, typealiasSymbol = typeAliasSymbol)
if (symbol is FirTypeAliasSymbol) {
val typeAlias = symbol.fir
typeAlias.lazyResolveToPhase(FirResolvePhase.TYPES)
typeAlias.expandedTypeRef.coneType.forEachType {
if (it is ConeClassLikeType) checkType(it, symbol, source, context, reporter)
}
}
}
}
@@ -524,6 +524,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.THROWABLE_TYPE_MI
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TOO_MANY_ARGUMENTS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TOO_MANY_CHARACTERS_IN_CHARACTER_LITERAL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TOPLEVEL_TYPEALIASES_ONLY
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPEALIAS_EXPANSION_DEPRECATION
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPEALIAS_EXPANSION_DEPRECATION_ERROR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPEALIAS_SHOULD_EXPAND_TO_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPECHECKER_HAS_RUN_INTO_RECURSIVE_PROBLEM
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.TYPE_ARGUMENTS_NOT_ALLOWED
@@ -660,6 +662,9 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
map.put(VAL_OR_VAR_ON_SECONDARY_CONSTRUCTOR_PARAMETER, "''{0}'' on secondary constructor parameter is not allowed", TO_STRING)
map.put(DEPRECATION, "''{0}'' is deprecated. {1}", SYMBOL, STRING)
map.put(DEPRECATION_ERROR, "''{0}'' is deprecated. {1}", SYMBOL, STRING)
map.put(TYPEALIAS_EXPANSION_DEPRECATION, "''{0}'' uses ''{1}'', which is deprecated. {2}", SYMBOL, SYMBOL, STRING)
map.put(TYPEALIAS_EXPANSION_DEPRECATION_ERROR, "''{0}'' uses ''{1}'', which is an error. {2}", SYMBOL, SYMBOL, STRING)
map.put(
API_NOT_AVAILABLE,
"This declaration is only available since Kotlin {0} and cannot be used with the specified API version {1}",
@@ -10,7 +10,7 @@ typealias DeprecatedClassAlias = <!DEPRECATION!>DeprecatedClass<!>
typealias WithDeprecatedCtorAlias = WithDeprecatedCtor
typealias ArrayListOfDeprecatedClass = ArrayList<<!DEPRECATION!>DeprecatedClass<!>>
class Test1 : DeprecatedClassAlias()
class Test1 : <!TYPEALIAS_EXPANSION_DEPRECATION!>DeprecatedClassAlias<!>()
class Test2 : <!DEPRECATION!>WithDeprecatedCtorAlias<!>()
@@ -1,21 +0,0 @@
@Deprecated("")
class Foo
@Deprecated("", level = DeprecationLevel.ERROR)
class Err
typealias Test1 = <!DEPRECATION!>Foo<!>
typealias Test2 = List<<!DEPRECATION!>Foo<!>>
typealias Test3 = List<Test2>
typealias TestErr1 = <!DEPRECATION_ERROR!>Err<!>
typealias TestErr2 = List<<!DEPRECATION_ERROR!>Err<!>>
typealias TestErr3 = List<TestErr2>
fun use1(b: Test1) = b
fun use2(b: Test2) = b
fun use3(b: Test3) = b
fun useErr1(b: TestErr1) = b
fun useErr2(b: TestErr2) = b
fun useErr3(b: TestErr3) = b
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
@Deprecated("")
class Foo