mirror of
https://github.com/github/codeql.git
synced 2025-12-22 19:56:32 +01:00
Restore implicit wildcard types
The Kotlin compiler represents types like List<out CharSequence> internally as List<CharSequence> due to the fact that List's type parameter is covariant, and similarly Comparable<in CharSequence> where Comparable's type parameter is contravariant. However it restores use-site variance when emitting class files, so we must do the same thing for compatability with Java code. Note this is a partial solution because it will also add wildcards to Java .class files that *could* have a variance / wildcard but don't -- for example, a Java method could really take an invariant Comparable<CharSequence>, which is only achievable in Kotlin via the @JvmSuppressWildcards annotation. We also don't yet support @JvmSuppressWildcards given on a surrounding class or function.
This commit is contained in:
@@ -497,7 +497,9 @@ open class KotlinFileExtractor(
|
|||||||
else
|
else
|
||||||
null
|
null
|
||||||
} ?: vp.type
|
} ?: vp.type
|
||||||
val substitutedType = typeSubstitution?.let { it(maybeErasedType, TypeContext.OTHER, pluginContext) } ?: maybeErasedType
|
val typeWithWildcards = addJavaLoweringWildcards(maybeErasedType, true)
|
||||||
|
val substitutedType = typeSubstitution?.let { it(typeWithWildcards, TypeContext.OTHER, pluginContext) } ?: typeWithWildcards
|
||||||
|
|
||||||
val id = useValueParameter(vp, parent)
|
val id = useValueParameter(vp, parent)
|
||||||
if (extractTypeAccess) {
|
if (extractTypeAccess) {
|
||||||
extractTypeAccessRecursive(substitutedType, location, id, -1)
|
extractTypeAccessRecursive(substitutedType, location, id, -1)
|
||||||
@@ -704,7 +706,7 @@ open class KotlinFileExtractor(
|
|||||||
|
|
||||||
val paramsSignature = allParamTypes.joinToString(separator = ",", prefix = "(", postfix = ")") { it.javaResult.signature!! }
|
val paramsSignature = allParamTypes.joinToString(separator = ",", prefix = "(", postfix = ")") { it.javaResult.signature!! }
|
||||||
|
|
||||||
val adjustedReturnType = getAdjustedReturnType(f)
|
val adjustedReturnType = addJavaLoweringWildcards(getAdjustedReturnType(f), false)
|
||||||
val substReturnType = typeSubstitution?.let { it(adjustedReturnType, TypeContext.RETURN, pluginContext) } ?: adjustedReturnType
|
val substReturnType = typeSubstitution?.let { it(adjustedReturnType, TypeContext.RETURN, pluginContext) } ?: adjustedReturnType
|
||||||
|
|
||||||
val locId = locOverride ?: getLocation(f, classTypeArgsIncludingOuterClasses)
|
val locId = locOverride ?: getLocation(f, classTypeArgsIncludingOuterClasses)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import com.github.codeql.utils.versions.isRawType
|
|||||||
import com.semmle.extractor.java.OdasaOutput
|
import com.semmle.extractor.java.OdasaOutput
|
||||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||||
import org.jetbrains.kotlin.backend.common.ir.allOverridden
|
import org.jetbrains.kotlin.backend.common.ir.allOverridden
|
||||||
|
import org.jetbrains.kotlin.backend.common.ir.isFinalClass
|
||||||
import org.jetbrains.kotlin.backend.common.lower.parentsWithSelf
|
import org.jetbrains.kotlin.backend.common.lower.parentsWithSelf
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.getJvmNameFromAnnotation
|
import org.jetbrains.kotlin.backend.jvm.ir.getJvmNameFromAnnotation
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor
|
import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor
|
||||||
@@ -850,6 +851,49 @@ open class KotlinUsesExtractor(
|
|||||||
(f.name.asString() == "addAll" && overridesFunctionDefinedOn(f, "kotlin.collections", "MutableCollection")) ||
|
(f.name.asString() == "addAll" && overridesFunctionDefinedOn(f, "kotlin.collections", "MutableCollection")) ||
|
||||||
(f.name.asString() == "addAll" && overridesFunctionDefinedOn(f, "kotlin.collections", "MutableList"))
|
(f.name.asString() == "addAll" && overridesFunctionDefinedOn(f, "kotlin.collections", "MutableList"))
|
||||||
|
|
||||||
|
|
||||||
|
private fun wildcardAdditionAllowed(v: Variance, t: IrType, inParameterContext: Boolean) =
|
||||||
|
when {
|
||||||
|
t.hasAnnotation(FqName("kotlin.jvm.JvmWildcard")) -> true
|
||||||
|
!inParameterContext -> false // By default, wildcards are only automatically added for method parameters.
|
||||||
|
t.hasAnnotation(FqName("kotlin.jvm.JvmSuppressWildcards")) -> false
|
||||||
|
v == Variance.IN_VARIANCE -> !(t.isNullableAny() || t.isAny())
|
||||||
|
v == Variance.OUT_VARIANCE -> ((t as? IrSimpleType)?.classOrNull?.owner?.isFinalClass) != true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addJavaLoweringArgumentWildcards(p: IrTypeParameter, t: IrTypeArgument, inParameterContext: Boolean): IrTypeArgument =
|
||||||
|
(t as? IrTypeProjection)?.let {
|
||||||
|
val newBase = addJavaLoweringWildcards(it.type, inParameterContext)
|
||||||
|
val newVariance =
|
||||||
|
if (it.variance == Variance.INVARIANT &&
|
||||||
|
p.variance != Variance.INVARIANT &&
|
||||||
|
wildcardAdditionAllowed(p.variance, it.type, inParameterContext))
|
||||||
|
p.variance
|
||||||
|
else
|
||||||
|
it.variance
|
||||||
|
if (newBase !== it.type || newVariance != it.variance)
|
||||||
|
makeTypeProjection(newBase, newVariance)
|
||||||
|
else
|
||||||
|
null
|
||||||
|
} ?: t
|
||||||
|
|
||||||
|
fun addJavaLoweringWildcards(t: IrType, inParameterContext: Boolean): IrType =
|
||||||
|
(t as? IrSimpleType)?.let {
|
||||||
|
val typeParams = it.classOrNull?.owner?.typeParameters ?: return t
|
||||||
|
val newArgs = typeParams.zip(it.arguments).map { pair ->
|
||||||
|
addJavaLoweringArgumentWildcards(
|
||||||
|
pair.first,
|
||||||
|
pair.second,
|
||||||
|
inParameterContext
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return if (newArgs.zip(it.arguments).all { pair -> pair.first === pair.second })
|
||||||
|
t
|
||||||
|
else
|
||||||
|
it.toBuilder().also { builder -> builder.arguments = newArgs }.buildSimpleType()
|
||||||
|
} ?: t
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is the normal getFunctionLabel function to use. If you want
|
* This is the normal getFunctionLabel function to use. If you want
|
||||||
* to refer to the function in its source class then
|
* to refer to the function in its source class then
|
||||||
@@ -956,8 +1000,10 @@ open class KotlinUsesExtractor(
|
|||||||
// Collection.remove(Object) because Collection.remove(Collection::E) in the Kotlin universe.
|
// Collection.remove(Object) because Collection.remove(Collection::E) in the Kotlin universe.
|
||||||
// If this has happened, erase the type again to get the correct Java signature.
|
// If this has happened, erase the type again to get the correct Java signature.
|
||||||
val maybeAmendedForCollections = if (overridesCollectionsMethod) eraseCollectionsMethodParameterType(it.value.type, name, it.index) else it.value.type
|
val maybeAmendedForCollections = if (overridesCollectionsMethod) eraseCollectionsMethodParameterType(it.value.type, name, it.index) else it.value.type
|
||||||
|
// Add any wildcard types that the Kotlin compiler would add in the Java lowering of this function:
|
||||||
|
val withAddedWildcards = addJavaLoweringWildcards(maybeAmendedForCollections, true)
|
||||||
// Now substitute any class type parameters in:
|
// Now substitute any class type parameters in:
|
||||||
val maybeSubbed = maybeAmendedForCollections.substituteTypeAndArguments(substitutionMap, TypeContext.OTHER, pluginContext)
|
val maybeSubbed = withAddedWildcards.substituteTypeAndArguments(substitutionMap, TypeContext.OTHER, pluginContext)
|
||||||
// Finally, mimic the Java extractor's behaviour by naming functions with type parameters for their erased types;
|
// Finally, mimic the Java extractor's behaviour by naming functions with type parameters for their erased types;
|
||||||
// those without type parameters are named for the generic type.
|
// those without type parameters are named for the generic type.
|
||||||
val maybeErased = if (functionTypeParameters.isEmpty()) maybeSubbed else erase(maybeSubbed)
|
val maybeErased = if (functionTypeParameters.isEmpty()) maybeSubbed else erase(maybeSubbed)
|
||||||
@@ -969,6 +1015,8 @@ open class KotlinUsesExtractor(
|
|||||||
pluginContext.irBuiltIns.unitType
|
pluginContext.irBuiltIns.unitType
|
||||||
else
|
else
|
||||||
erase(returnType.substituteTypeAndArguments(substitutionMap, TypeContext.RETURN, pluginContext))
|
erase(returnType.substituteTypeAndArguments(substitutionMap, TypeContext.RETURN, pluginContext))
|
||||||
|
// Note that `addJavaLoweringWildcards` is not required here because the return type used to form the function
|
||||||
|
// label is always erased.
|
||||||
val returnTypeId = useType(labelReturnType, TypeContext.RETURN).javaResult.id
|
val returnTypeId = useType(labelReturnType, TypeContext.RETURN).javaResult.id
|
||||||
// This suffix is added to generic methods (and constructors) to match the Java extractor's behaviour.
|
// This suffix is added to generic methods (and constructors) to match the Java extractor's behaviour.
|
||||||
// Comments in that extractor indicates it didn't want the label of the callable to clash with the raw
|
// Comments in that extractor indicates it didn't want the label of the callable to clash with the raw
|
||||||
|
|||||||
Reference in New Issue
Block a user