mirror of
https://github.com/github/codeql.git
synced 2025-12-21 03:06:31 +01:00
Instantiated generic type substitution: substitute outer class parameters too
This commit is contained in:
committed by
Ian Lynagh
parent
fd495aa783
commit
3365f3972e
@@ -141,13 +141,10 @@ open class KotlinFileExtractor(
|
||||
extractVisibility(c, id, c.visibility)
|
||||
}
|
||||
|
||||
// `typeArgs` can be null to describe a raw generic type.
|
||||
// `argsIncludingOuterClasses` can be null to describe a raw generic type.
|
||||
// For non-generic types it will be zero-length list.
|
||||
fun extractClassInstance(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?): Label<out DbClassorinterface> {
|
||||
// For all purposes ignore type arguments relating to outer classes.
|
||||
val typeArgs = removeOuterClassTypeArgs(c, argsIncludingOuterClasses)
|
||||
|
||||
if (typeArgs?.isEmpty() == true) {
|
||||
if (argsIncludingOuterClasses?.isEmpty() == true) {
|
||||
logger.warn(Severity.ErrorSevere, "Instance without type arguments: " + c.name.asString())
|
||||
}
|
||||
|
||||
@@ -174,6 +171,7 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
}
|
||||
|
||||
val typeArgs = removeOuterClassTypeArgs(c, argsIncludingOuterClasses)
|
||||
if (typeArgs != null) {
|
||||
for ((idx, arg) in typeArgs.withIndex()) {
|
||||
val argId = getTypeArgumentLabel(arg).id
|
||||
@@ -187,7 +185,7 @@ open class KotlinFileExtractor(
|
||||
val unbound = useClassSource(c)
|
||||
tw.writeErasure(id, unbound)
|
||||
extractClassModifiers(c, id)
|
||||
extractClassSupertypes(c, id, if (typeArgs == null) ExtractSupertypesMode.Raw else ExtractSupertypesMode.Specialised(typeArgs))
|
||||
extractClassSupertypes(c, id, if (argsIncludingOuterClasses == null) ExtractSupertypesMode.Raw else ExtractSupertypesMode.Specialised(argsIncludingOuterClasses))
|
||||
|
||||
val locId = tw.getLocation(c)
|
||||
tw.writeHasLocation(id, locId)
|
||||
@@ -200,12 +198,12 @@ open class KotlinFileExtractor(
|
||||
|
||||
// `typeArgs` can be null to describe a raw generic type.
|
||||
// For non-generic types it will be zero-length list.
|
||||
fun extractMemberPrototypes(c: IrClass, typeArgs: List<IrTypeArgument>?, id: Label<out DbClassorinterface>) {
|
||||
fun extractMemberPrototypes(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?, id: Label<out DbClassorinterface>) {
|
||||
val typeParamSubstitution =
|
||||
when (typeArgs) {
|
||||
when (argsIncludingOuterClasses) {
|
||||
null -> { x: IrType, _: TypeContext, _: IrPluginContext -> x.toRawType() }
|
||||
else -> {
|
||||
c.typeParameters.map({ it.symbol }).zip(typeArgs).toMap().let {
|
||||
getTypeParametersInScope(c).map({ it.symbol }).zip(argsIncludingOuterClasses).toMap().let {
|
||||
{ x: IrType, useContext: TypeContext, pluginContext: IrPluginContext ->
|
||||
x.substituteTypeAndArguments(
|
||||
it,
|
||||
@@ -219,8 +217,8 @@ open class KotlinFileExtractor(
|
||||
|
||||
c.declarations.map {
|
||||
when(it) {
|
||||
is IrFunction -> extractFunctionIfReal(it, id, false, typeParamSubstitution, typeArgs)
|
||||
is IrProperty -> extractProperty(it, id, false, typeParamSubstitution, typeArgs)
|
||||
is IrFunction -> extractFunctionIfReal(it, id, false, typeParamSubstitution, argsIncludingOuterClasses)
|
||||
is IrProperty -> extractProperty(it, id, false, typeParamSubstitution, argsIncludingOuterClasses)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
@@ -445,13 +443,13 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
}
|
||||
|
||||
fun extractFunctionIfReal(f: IrFunction, parentId: Label<out DbReftype>, extractBody: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgs: List<IrTypeArgument>?) {
|
||||
fun extractFunctionIfReal(f: IrFunction, parentId: Label<out DbReftype>, extractBody: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) {
|
||||
if (f.origin == IrDeclarationOrigin.FAKE_OVERRIDE)
|
||||
return
|
||||
extractFunction(f, parentId, extractBody, typeSubstitution, classTypeArgs)
|
||||
extractFunction(f, parentId, extractBody, typeSubstitution, classTypeArgsIncludingOuterClasses)
|
||||
}
|
||||
|
||||
fun extractFunction(f: IrFunction, parentId: Label<out DbReftype>, extractBody: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgs: List<IrTypeArgument>?): Label<out DbCallable> {
|
||||
fun extractFunction(f: IrFunction, parentId: Label<out DbReftype>, extractBody: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?): Label<out DbCallable> {
|
||||
declarationStack.push(f)
|
||||
|
||||
getFunctionTypeParameters(f).mapIndexed { idx, it -> extractTypeParameter(it, idx) }
|
||||
@@ -462,7 +460,7 @@ open class KotlinFileExtractor(
|
||||
if (f.isLocalFunction())
|
||||
getLocallyVisibleFunctionLabels(f).function
|
||||
else
|
||||
useFunction<DbCallable>(f, parentId, classTypeArgs)
|
||||
useFunction<DbCallable>(f, parentId, classTypeArgsIncludingOuterClasses)
|
||||
|
||||
val sourceDeclaration =
|
||||
if (typeSubstitution != null)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.codeql
|
||||
|
||||
import com.github.codeql.utils.substituteTypeAndArguments
|
||||
import com.github.codeql.utils.substituteTypeArguments
|
||||
import com.semmle.extractor.java.OdasaOutput
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
@@ -142,15 +143,28 @@ open class KotlinUsesExtractor(
|
||||
} ?: argsIncludingOuterClasses
|
||||
}
|
||||
|
||||
// Gets nested inner classes starting at `c` and proceeding outwards to the innermost enclosing static class.
|
||||
// For example, for (java syntax) `class A { static class B { class C { class D { } } } }`,
|
||||
// `nonStaticParentsWithSelf(D)` = `[D, C, B]`.
|
||||
fun parentsWithTypeParametersInScope(c: IrClass): List<IrDeclarationParent> {
|
||||
val parentsList = c.parentsWithSelf.toList()
|
||||
val firstOuterClassIdx = parentsList.indexOfFirst { it is IrClass && !it.isInner }
|
||||
return if (firstOuterClassIdx == -1) parentsList else parentsList.subList(0, firstOuterClassIdx + 1)
|
||||
}
|
||||
|
||||
// Gets the type parameter symbols that are in scope for class `c` in Kotlin order (i.e. for
|
||||
// `class NotInScope<T> { static class OutermostInScope<A, B> { class QueryClass<C, D> { } } }`,
|
||||
// `getTypeParametersInScope(QueryClass)` = `[C, D, A, B]`.
|
||||
fun getTypeParametersInScope(c: IrClass) =
|
||||
parentsWithTypeParametersInScope(c).mapNotNull({ (it as? IrClass)?.typeParameters }).flatten()
|
||||
|
||||
// The Kotlin compiler internal representation of Outer<A, B>.Inner<C, D>.InnerInner<E, F> is InnerInner<E, F, C, D, A, B>. This function returns [A, B, C, D, E, F].
|
||||
fun orderTypeArgsLeftToRight(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?): List<IrTypeArgument>? {
|
||||
if(argsIncludingOuterClasses.isNullOrEmpty())
|
||||
return argsIncludingOuterClasses
|
||||
val ret = ArrayList<IrTypeArgument>()
|
||||
// Iterate over nested inner classes starting at `c`'s surrounding top-level or static nested class and ending at `c`, from the outermost inwards:
|
||||
val parentsList = c.parentsWithSelf.toList()
|
||||
val firstOuterClassIdx = parentsList.indexOfFirst { it is IrClass && !it.isInner }
|
||||
val truncatedParents = if (firstOuterClassIdx == -1) parentsList else parentsList.subList(0, firstOuterClassIdx + 1)
|
||||
val truncatedParents = parentsWithTypeParametersInScope(c)
|
||||
for(parent in truncatedParents.reversed()) {
|
||||
if(parent is IrClass) {
|
||||
val firstArgIdx = argsIncludingOuterClasses.size - (ret.size + parent.typeParameters.size)
|
||||
@@ -216,9 +230,6 @@ open class KotlinUsesExtractor(
|
||||
// `typeArgs` can be null to describe a raw generic type.
|
||||
// For non-generic types it will be zero-length list.
|
||||
fun addClassLabel(c: IrClass, argsIncludingOuterClasses: List<IrTypeArgument>?, inReceiverContext: Boolean = false): TypeResult<DbClassorinterface> {
|
||||
// For all purposes ignore type arguments relating to outer classes.
|
||||
val typeArgs = removeOuterClassTypeArgs(c, argsIncludingOuterClasses)
|
||||
|
||||
val classLabelResult = getClassLabel(c, argsIncludingOuterClasses)
|
||||
|
||||
var instanceSeenBefore = true
|
||||
@@ -229,7 +240,7 @@ open class KotlinUsesExtractor(
|
||||
extractClassLaterIfExternal(c)
|
||||
})
|
||||
|
||||
if (typeArgs == null || typeArgs.isNotEmpty()) {
|
||||
if (argsIncludingOuterClasses == null || argsIncludingOuterClasses.isNotEmpty()) {
|
||||
// If this is a generic type instantiation or a raw type then it has no
|
||||
// source entity, so we need to extract it here
|
||||
val extractorWithCSource by lazy { this.withSourceFileOfClass(c) }
|
||||
@@ -239,7 +250,7 @@ open class KotlinUsesExtractor(
|
||||
}
|
||||
|
||||
if (inReceiverContext && genericSpecialisationsExtracted.add(classLabelResult.classLabel)) {
|
||||
extractorWithCSource.extractMemberPrototypes(c, typeArgs, classLabel)
|
||||
extractorWithCSource.extractMemberPrototypes(c, argsIncludingOuterClasses, classLabel)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,6 +607,13 @@ class X {
|
||||
return getFunctionLabel(f.parent, getFunctionShortName(f), f.valueParameters, f.returnType, f.extensionReceiverParameter, getFunctionTypeParameters(f), classTypeArguments)
|
||||
}
|
||||
|
||||
fun getEnclosingClass(it: IrDeclarationParent): IrClass? =
|
||||
when(it) {
|
||||
is IrClass -> it
|
||||
is IrFunction -> getEnclosingClass(it.parent)
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun getFunctionLabel(
|
||||
parent: IrDeclarationParent,
|
||||
name: String,
|
||||
@@ -606,20 +624,21 @@ class X {
|
||||
classTypeArguments: List<IrTypeArgument>? = null
|
||||
): String {
|
||||
val parentId = useDeclarationParent(parent, false, classTypeArguments, true)
|
||||
return getFunctionLabel(parentId, name, parameters, returnType, extensionReceiverParameter, functionTypeParameters, classTypeArguments)
|
||||
return getFunctionLabel(getEnclosingClass(parent), parentId, name, parameters, returnType, extensionReceiverParameter, functionTypeParameters, classTypeArguments)
|
||||
}
|
||||
|
||||
fun getFunctionLabel(f: IrFunction, parentId: Label<out DbElement>, classTypeArguments: List<IrTypeArgument>?) =
|
||||
getFunctionLabel(parentId, getFunctionShortName(f), f.valueParameters, f.returnType, f.extensionReceiverParameter, getFunctionTypeParameters(f), classTypeArguments)
|
||||
fun getFunctionLabel(f: IrFunction, parentId: Label<out DbElement>, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) =
|
||||
getFunctionLabel(getEnclosingClass(f), parentId, getFunctionShortName(f), f.valueParameters, f.returnType, f.extensionReceiverParameter, getFunctionTypeParameters(f), classTypeArgsIncludingOuterClasses)
|
||||
|
||||
fun getFunctionLabel(
|
||||
enclosingClass: IrClass?,
|
||||
parentId: Label<out DbElement>,
|
||||
name: String,
|
||||
parameters: List<IrValueParameter>,
|
||||
returnType: IrType,
|
||||
extensionReceiverParameter: IrValueParameter?,
|
||||
functionTypeParameters: List<IrTypeParameter>,
|
||||
classTypeArguments: List<IrTypeArgument>?
|
||||
classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?
|
||||
): String {
|
||||
val allParams = if (extensionReceiverParameter == null) {
|
||||
parameters
|
||||
@@ -628,10 +647,20 @@ class X {
|
||||
params.addAll(parameters)
|
||||
params
|
||||
}
|
||||
|
||||
val substitutionMap = classTypeArgsIncludingOuterClasses?.let { notNullArgs ->
|
||||
if (notNullArgs.isEmpty())
|
||||
null
|
||||
else
|
||||
enclosingClass?.let { notNullClass ->
|
||||
getTypeParametersInScope(notNullClass).map({ it.symbol }).zip(notNullArgs).toMap()
|
||||
}
|
||||
}
|
||||
val getIdForFunctionLabel = { it: IrValueParameter ->
|
||||
// Mimic the Java extractor's behaviour: functions with type parameters are named for their erased types;
|
||||
// those without type parameters are named for the generic type.
|
||||
val maybeErased = if (functionTypeParameters.isEmpty()) it.type else erase(it.type)
|
||||
val maybeSubbed = it.type.substituteTypeAndArguments(substitutionMap, TypeContext.OTHER, pluginContext)
|
||||
val maybeErased = if (functionTypeParameters.isEmpty()) maybeSubbed else erase(maybeSubbed)
|
||||
"{${useType(maybeErased).javaResult.id}}"
|
||||
}
|
||||
val paramTypeIds = allParams.joinToString(separator = ",", transform = getIdForFunctionLabel)
|
||||
@@ -641,7 +670,7 @@ class X {
|
||||
// Comments in that extractor indicates it didn't want the label of the callable to clash with the raw
|
||||
// method (and presumably that disambiguation is never needed when the method belongs to a parameterized
|
||||
// instance of a generic class), but as of now I don't know when the raw method would be referred to.
|
||||
val typeArgSuffix = if (functionTypeParameters.isNotEmpty() && classTypeArguments.isNullOrEmpty()) "<${functionTypeParameters.size}>" else "";
|
||||
val typeArgSuffix = if (functionTypeParameters.isNotEmpty() && classTypeArgsIncludingOuterClasses.isNullOrEmpty()) "<${functionTypeParameters.size}>" else "";
|
||||
return "@\"callable;{$parentId}.$name($paramTypeIds){$returnTypeId}${typeArgSuffix}\""
|
||||
}
|
||||
|
||||
@@ -704,8 +733,8 @@ class X {
|
||||
}
|
||||
}
|
||||
|
||||
fun <T: DbCallable> useFunction(f: IrFunction, parentId: Label<out DbElement>, classTypeArguments: List<IrTypeArgument>?) =
|
||||
useFunctionCommon<T>(f, getFunctionLabel(f, parentId, classTypeArguments))
|
||||
fun <T: DbCallable> useFunction(f: IrFunction, parentId: Label<out DbElement>, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) =
|
||||
useFunctionCommon<T>(f, getFunctionLabel(f, parentId, classTypeArgsIncludingOuterClasses))
|
||||
|
||||
fun getTypeArgumentLabel(
|
||||
arg: IrTypeArgument
|
||||
|
||||
Reference in New Issue
Block a user