[JS FIR] Fix FIR NON_EXPORTABLE_TYPE diagnostic bugs
- use a declaration source if a parameter source is null; - ignore constructor return type to avoid double-checking of the class itself; - ignore constructor type parameters obtained from the class declaration; - ignore enum entries property (it is not checked in K1 at all); - fix exportability check for function types; ^KT-63089 Fixed
This commit is contained in:
committed by
Space Team
parent
6615b77213
commit
862be5a787
+45
-23
@@ -13,15 +13,17 @@ import org.jetbrains.kotlin.diagnostics.reportOn
|
|||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.FirSession
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.getAnnotationFirstArgument
|
import org.jetbrains.kotlin.fir.analysis.checkers.getAnnotationFirstArgument
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.isTopLevel
|
import org.jetbrains.kotlin.fir.analysis.checkers.isTopLevel
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.js.FirJsErrors
|
||||||
import org.jetbrains.kotlin.fir.analysis.js.checkers.isEffectivelyExternal
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.js.checkers.isExportedObject
|
import org.jetbrains.kotlin.fir.analysis.js.checkers.isExportedObject
|
||||||
import org.jetbrains.kotlin.fir.analysis.js.checkers.sanitizeName
|
import org.jetbrains.kotlin.fir.analysis.js.checkers.sanitizeName
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
|
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
|
||||||
|
import org.jetbrains.kotlin.fir.isEnumEntries
|
||||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.js.common.RESERVED_KEYWORDS
|
import org.jetbrains.kotlin.js.common.RESERVED_KEYWORDS
|
||||||
import org.jetbrains.kotlin.js.common.SPECIAL_KEYWORDS
|
import org.jetbrains.kotlin.js.common.SPECIAL_KEYWORDS
|
||||||
@@ -34,9 +36,13 @@ object FirJsExportDeclarationChecker : FirBasicDeclarationChecker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun checkTypeParameter(typeParameter: FirTypeParameterRef) {
|
fun checkTypeParameter(typeParameter: FirTypeParameterRef) {
|
||||||
|
if (typeParameter is FirConstructedClassTypeParameterRef) {
|
||||||
|
return
|
||||||
|
}
|
||||||
for (upperBound in typeParameter.symbol.resolvedBounds) {
|
for (upperBound in typeParameter.symbol.resolvedBounds) {
|
||||||
if (!upperBound.type.isExportable(context.session)) {
|
if (!upperBound.type.isExportable(context.session)) {
|
||||||
reporter.reportOn(typeParameter.source, FirJsErrors.NON_EXPORTABLE_TYPE, "upper bound", upperBound.type, context)
|
val source = upperBound.source ?: typeParameter.source ?: declaration.source
|
||||||
|
reporter.reportOn(source, FirJsErrors.NON_EXPORTABLE_TYPE, "upper bound", upperBound.type, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -44,7 +50,8 @@ object FirJsExportDeclarationChecker : FirBasicDeclarationChecker() {
|
|||||||
fun checkValueParameter(valueParameter: FirValueParameter) {
|
fun checkValueParameter(valueParameter: FirValueParameter) {
|
||||||
val type = valueParameter.returnTypeRef.coneType
|
val type = valueParameter.returnTypeRef.coneType
|
||||||
if (!type.isExportable(context.session)) {
|
if (!type.isExportable(context.session)) {
|
||||||
reporter.reportOn(valueParameter.source, FirJsErrors.NON_EXPORTABLE_TYPE, "parameter", type, context)
|
val source = valueParameter.source ?: declaration.source
|
||||||
|
reporter.reportOn(source, FirJsErrors.NON_EXPORTABLE_TYPE, "parameter", type, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,8 +98,8 @@ object FirJsExportDeclarationChecker : FirBasicDeclarationChecker() {
|
|||||||
|
|
||||||
val returnType = declaration.returnTypeRef.coneType
|
val returnType = declaration.returnTypeRef.coneType
|
||||||
|
|
||||||
if (!returnType.isExportable(context.session)) {
|
if (declaration !is FirConstructor && !returnType.isExportableReturn(context.session)) {
|
||||||
reporter.reportOn(declaration.source, FirJsErrors.NON_EXPORTABLE_TYPE, "return type", returnType, context)
|
reporter.reportOn(declaration.source, FirJsErrors.NON_EXPORTABLE_TYPE, "return", returnType, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,10 +113,11 @@ object FirJsExportDeclarationChecker : FirBasicDeclarationChecker() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val containingClass = declaration.getContainingClassSymbol(context.session) as? FirClassSymbol<*>
|
||||||
|
val enumEntriesProperty = containingClass?.let(declaration::isEnumEntries) ?: false
|
||||||
val returnType = declaration.returnTypeRef.coneType
|
val returnType = declaration.returnTypeRef.coneType
|
||||||
|
if (!enumEntriesProperty && !returnType.isExportable(context.session)) {
|
||||||
if (!returnType.isExportable(context.session)) {
|
reporter.reportOn(declaration.source, FirJsErrors.NON_EXPORTABLE_TYPE, "property", returnType, context)
|
||||||
reporter.reportOn(declaration.source, FirJsErrors.NON_EXPORTABLE_TYPE, "return type", returnType, context)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,29 +162,43 @@ object FirJsExportDeclarationChecker : FirBasicDeclarationChecker() {
|
|||||||
else -> typeParameters.any { it.symbol.isReified }
|
else -> typeParameters.any { it.symbol.isReified }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ConeKotlinType.isExportableReturn(session: FirSession, currentlyProcessed: MutableSet<ConeKotlinType> = mutableSetOf()) =
|
private fun ConeKotlinType.isExportableReturn(session: FirSession, currentlyProcessed: MutableSet<ConeKotlinType> = hashSetOf()) =
|
||||||
isUnit || isExportable(session, currentlyProcessed)
|
isUnit || isExportable(session, currentlyProcessed)
|
||||||
|
|
||||||
|
private fun ConeKotlinType.isExportableTypeArguments(
|
||||||
|
session: FirSession,
|
||||||
|
currentlyProcessed: MutableSet<ConeKotlinType>,
|
||||||
|
isFunctionType: Boolean
|
||||||
|
): Boolean {
|
||||||
|
if (typeArguments.isEmpty()) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for (i in 0 until typeArguments.lastIndex) {
|
||||||
|
if (typeArguments[i].type?.isExportable(session, currentlyProcessed) != true) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val isLastExportable = if (isFunctionType) {
|
||||||
|
typeArguments.last().type?.isExportableReturn(session, currentlyProcessed)
|
||||||
|
} else {
|
||||||
|
typeArguments.last().type?.isExportable(session, currentlyProcessed)
|
||||||
|
}
|
||||||
|
return isLastExportable == true
|
||||||
|
}
|
||||||
|
|
||||||
private fun ConeKotlinType.isExportable(
|
private fun ConeKotlinType.isExportable(
|
||||||
session: FirSession,
|
session: FirSession,
|
||||||
currentlyProcessed: MutableSet<ConeKotlinType> = mutableSetOf(),
|
currentlyProcessed: MutableSet<ConeKotlinType> = hashSetOf(),
|
||||||
): Boolean {
|
): Boolean {
|
||||||
if (!currentlyProcessed.add(this)) {
|
if (!currentlyProcessed.add(this)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
currentlyProcessed.add(this)
|
val isFunctionType = isBasicFunctionType(session)
|
||||||
val hasNonExportableArgument = typeArguments.any { it.type?.isExportable(session, currentlyProcessed) != true }
|
val isExportableArgs = isExportableTypeArguments(session, currentlyProcessed, isFunctionType)
|
||||||
|
|
||||||
if (hasNonExportableArgument) {
|
|
||||||
currentlyProcessed.remove(this)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
currentlyProcessed.remove(this)
|
currentlyProcessed.remove(this)
|
||||||
|
if (isFunctionType || !isExportableArgs) {
|
||||||
if (isBasicFunctionType(session)) {
|
return isExportableArgs
|
||||||
typeArguments.lastOrNull()?.type?.isExportableReturn(session, currentlyProcessed)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val nonNullable = withNullability(ConeNullability.NOT_NULL, session.typeContext)
|
val nonNullable = withNullability(ConeNullability.NOT_NULL, session.typeContext)
|
||||||
@@ -187,9 +209,9 @@ object FirJsExportDeclarationChecker : FirBasicDeclarationChecker() {
|
|||||||
return when {
|
return when {
|
||||||
isPrimitiveExportableType -> true
|
isPrimitiveExportableType -> true
|
||||||
@OptIn(SymbolInternals::class)
|
@OptIn(SymbolInternals::class)
|
||||||
symbol?.fir is FirMemberDeclaration -> false
|
symbol?.fir !is FirMemberDeclaration -> false
|
||||||
isEnum -> true
|
isEnum -> true
|
||||||
else -> symbol?.isEffectivelyExternal(session) == true || symbol?.isExportedObject(session) == true
|
else -> symbol.isEffectivelyExternal(session) || symbol.isExportedObject(session)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.fir.renderer.FirRenderer
|
|||||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertyAccessorSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertyAccessorSymbol
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.fir.types.builder.*
|
import org.jetbrains.kotlin.fir.types.builder.*
|
||||||
@@ -262,6 +263,9 @@ fun <T> List<T>.smartPlus(other: List<T>): List<T> = when {
|
|||||||
|
|
||||||
// Source element may be missing if the class came from a library
|
// Source element may be missing if the class came from a library
|
||||||
fun FirVariable.isEnumEntries(containingClass: FirClass) = isStatic && name == StandardNames.ENUM_ENTRIES && containingClass.isEnumClass
|
fun FirVariable.isEnumEntries(containingClass: FirClass) = isStatic && name == StandardNames.ENUM_ENTRIES && containingClass.isEnumClass
|
||||||
|
fun FirVariable.isEnumEntries(containingClassSymbol: FirClassSymbol<*>): Boolean {
|
||||||
|
return isStatic && name == StandardNames.ENUM_ENTRIES && containingClassSymbol.isEnumClass
|
||||||
|
}
|
||||||
|
|
||||||
val FirExpression.isArraySet: Boolean
|
val FirExpression.isArraySet: Boolean
|
||||||
get() {
|
get() {
|
||||||
|
|||||||
Reference in New Issue
Block a user