diff --git a/java/kotlin-extractor/src/main/kotlin/ExternalDeclExtractor.kt b/java/kotlin-extractor/src/main/kotlin/ExternalDeclExtractor.kt index d036fe2e27a..61097aea593 100644 --- a/java/kotlin-extractor/src/main/kotlin/ExternalDeclExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/ExternalDeclExtractor.kt @@ -93,9 +93,9 @@ class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: Stri ftw.writeHasLocation(ftw.fileId, ftw.getWholeFileLocation()) ftw.writeCupackage(ftw.fileId, pkgId) - fileExtractor.extractClassSource(irDecl, !irDecl.isFileClass, false) + fileExtractor.extractClassSource(irDecl, extractDeclarations = !irDecl.isFileClass, extractStaticInitializer = false, extractPrivateMembers = false, extractFunctionBodies = false) } else { - fileExtractor.extractDeclaration(irDecl) + fileExtractor.extractDeclaration(irDecl, extractPrivateMembers = false, extractFunctionBodies = false) } } diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index c5f29a3eff7..9a5149a392a 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -74,7 +74,7 @@ open class KotlinFileExtractor( } } - file.declarations.map { extractDeclaration(it) } + file.declarations.map { extractDeclaration(it, extractPrivateMembers = true, extractFunctionBodies = true) } extractStaticInitializer(file, null) CommentExtractor(this, file, tw.fileId).extract() } @@ -91,20 +91,29 @@ open class KotlinFileExtractor( return false } - fun extractDeclaration(declaration: IrDeclaration) { + private fun shouldExtractDecl(declaration: IrDeclaration, extractPrivateMembers: Boolean) = + extractPrivateMembers || + when(declaration) { + is IrDeclarationWithVisibility -> declaration.visibility.let { it != DescriptorVisibilities.PRIVATE && it != DescriptorVisibilities.PRIVATE_TO_THIS } + else -> true + } + + fun extractDeclaration(declaration: IrDeclaration, extractPrivateMembers: Boolean, extractFunctionBodies: Boolean) { with("declaration", declaration) { + if (!shouldExtractDecl(declaration, extractPrivateMembers)) + return when (declaration) { is IrClass -> { if (isExternalDeclaration(declaration)) { extractExternalClassLater(declaration) } else { - extractClassSource(declaration, extractDeclarations = true, extractStaticInitializer = true) + extractClassSource(declaration, extractDeclarations = true, extractStaticInitializer = true, extractPrivateMembers = extractPrivateMembers, extractFunctionBodies = extractFunctionBodies) } } is IrFunction -> { val parentId = useDeclarationParent(declaration.parent, false)?.cast() if (parentId != null) { - extractFunction(declaration, parentId, true, null, listOf()) + extractFunction(declaration, parentId, extractBody = extractFunctionBodies, extractMethodAndParameterTypeAccesses = extractFunctionBodies, null, listOf()) } Unit } @@ -114,14 +123,14 @@ open class KotlinFileExtractor( is IrProperty -> { val parentId = useDeclarationParent(declaration.parent, false)?.cast() if (parentId != null) { - extractProperty(declaration, parentId, true, null, listOf()) + extractProperty(declaration, parentId, extractBackingField = true, extractFunctionBodies = extractFunctionBodies, null, listOf()) } Unit } is IrEnumEntry -> { val parentId = useDeclarationParent(declaration.parent, false)?.cast() if (parentId != null) { - extractEnumEntry(declaration, parentId) + extractEnumEntry(declaration, parentId, extractFunctionBodies) } Unit } @@ -320,7 +329,7 @@ open class KotlinFileExtractor( // `argsIncludingOuterClasses` can be null to describe a raw generic type. // For non-generic types it will be zero-length list. - fun extractMemberPrototypes(c: IrClass, argsIncludingOuterClasses: List?, id: Label) { + fun extractNonPrivateMemberPrototypes(c: IrClass, argsIncludingOuterClasses: List?, id: Label) { with("member prototypes", c) { val typeParamSubstitution = when (argsIncludingOuterClasses) { @@ -339,17 +348,19 @@ open class KotlinFileExtractor( } c.declarations.map { - when(it) { - is IrFunction -> extractFunction(it, id, false, typeParamSubstitution, argsIncludingOuterClasses) - is IrProperty -> extractProperty(it, id, false, typeParamSubstitution, argsIncludingOuterClasses) - else -> {} + if (shouldExtractDecl(it, false)) { + when(it) { + is IrFunction -> extractFunction(it, id, extractBody = false, extractMethodAndParameterTypeAccesses = false, typeParamSubstitution, argsIncludingOuterClasses) + is IrProperty -> extractProperty(it, id, extractBackingField = false, extractFunctionBodies = false, typeParamSubstitution, argsIncludingOuterClasses) + else -> {} + } } } } } private fun extractLocalTypeDeclStmt(c: IrClass, callable: Label, parent: Label, idx: Int) { - val id = extractClassSource(c, extractDeclarations = true, extractStaticInitializer = true).cast() + val id = extractClassSource(c, extractDeclarations = true, extractStaticInitializer = true, extractPrivateMembers = true, extractFunctionBodies = true).cast() extractLocalTypeDeclStmt(id, c, callable, parent, idx) } @@ -361,7 +372,7 @@ open class KotlinFileExtractor( tw.writeHasLocation(stmtId, locId) } - fun extractClassSource(c: IrClass, extractDeclarations: Boolean, extractStaticInitializer: Boolean): Label { + fun extractClassSource(c: IrClass, extractDeclarations: Boolean, extractStaticInitializer: Boolean, extractPrivateMembers: Boolean, extractFunctionBodies: Boolean): Label { with("class source", c) { DeclarationStackAdjuster(c).use { @@ -395,7 +406,7 @@ open class KotlinFileExtractor( c.typeParameters.mapIndexed { idx, param -> extractTypeParameter(param, idx) } if (extractDeclarations) { - c.declarations.map { extractDeclaration(it) } + c.declarations.map { extractDeclaration(it, extractPrivateMembers = extractPrivateMembers, extractFunctionBodies = extractFunctionBodies) } if (extractStaticInitializer) extractStaticInitializer(c, id) } @@ -442,7 +453,7 @@ open class KotlinFileExtractor( val instance = useCompanionObjectClassInstance(innerDeclaration) if (instance != null) { val type = useSimpleTypeClass(innerDeclaration, emptyList(), false) - tw.writeFields(instance.id, instance.name, type.javaResult.id, innerId, instance.id) + tw.writeFields(instance.id, instance.name, type.javaResult.id, parentId, instance.id) tw.writeFieldsKotlinType(instance.id, type.kotlinResult.id) tw.writeHasLocation(instance.id, innerLocId) addModifiers(instance.id, "public", "static", "final") @@ -500,11 +511,11 @@ open class KotlinFileExtractor( return FieldResult(instanceId, instanceName) } - private fun extractValueParameter(vp: IrValueParameter, parent: Label, idx: Int, typeSubstitution: TypeSubstitution?, parentSourceDeclaration: Label, classTypeArgsIncludingOuterClasses: List?): TypeResults { + private fun extractValueParameter(vp: IrValueParameter, parent: Label, idx: Int, typeSubstitution: TypeSubstitution?, parentSourceDeclaration: Label, classTypeArgsIncludingOuterClasses: List?, extractTypeAccess: Boolean): TypeResults { with("value parameter", vp) { val location = getLocation(vp, classTypeArgsIncludingOuterClasses) val id = useValueParameter(vp, parent) - if (!isExternalDeclaration(vp)) { + if (extractTypeAccess) { extractTypeAccessRecursive(vp.type, location, id, -1) } return extractValueParameter(id, vp.type, vp.name.asString(), location, parent, idx, typeSubstitution, useValueParameter(vp, parentSourceDeclaration), vp.isVararg) @@ -539,7 +550,7 @@ open class KotlinFileExtractor( classTypeArgsIncludingOuterClasses = listOf() ) val clinitId = tw.getLabelFor(clinitLabel) - val returnType = useType(pluginContext.irBuiltIns.unitType) + val returnType = useType(pluginContext.irBuiltIns.unitType, TypeContext.RETURN) tw.writeMethods(clinitId, "", "()", returnType.javaResult.id, parentId, clinitId) tw.writeMethodsKotlinType(clinitId, returnType.kotlinResult.id) @@ -665,7 +676,7 @@ open class KotlinFileExtractor( } } - fun extractFunction(f: IrFunction, parentId: Label, extractBody: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List?, idOverride: Label? = null): Label? { + fun extractFunction(f: IrFunction, parentId: Label, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List?, idOverride: Label? = null): Label? { if (isFake(f)) return null with("function", f) { @@ -678,7 +689,9 @@ open class KotlinFileExtractor( ?: if (f.isLocalFunction()) getLocallyVisibleFunctionLabels(f).function else - useFunction(f, parentId, classTypeArgsIncludingOuterClasses) + // If this is a class that would ordinarily be replaced by a Java equivalent (e.g. kotlin.Map -> java.util.Map), + // don't replace here, really extract the Kotlin version: + useFunction(f, parentId, classTypeArgsIncludingOuterClasses, noReplace = true) val sourceDeclaration = if (typeSubstitution != null) @@ -689,13 +702,13 @@ open class KotlinFileExtractor( val extReceiver = f.extensionReceiverParameter val idxOffset = if (extReceiver != null) 1 else 0 val paramTypes = f.valueParameters.mapIndexed { i, vp -> - extractValueParameter(vp, id, i + idxOffset, typeSubstitution, sourceDeclaration, classTypeArgsIncludingOuterClasses) + extractValueParameter(vp, id, i + idxOffset, typeSubstitution, sourceDeclaration, classTypeArgsIncludingOuterClasses, extractTypeAccess = extractMethodAndParameterTypeAccesses) } val allParamTypes = if (extReceiver != null) { val extendedType = useType(extReceiver.type) tw.writeKtExtensionFunctions(id.cast(), extendedType.javaResult.id, extendedType.kotlinResult.id) - val t = extractValueParameter(extReceiver, id, 0, null, sourceDeclaration, classTypeArgsIncludingOuterClasses) + val t = extractValueParameter(extReceiver, id, 0, null, sourceDeclaration, classTypeArgsIncludingOuterClasses, extractTypeAccess = extractMethodAndParameterTypeAccesses) listOf(t) + paramTypes } else { paramTypes @@ -724,7 +737,7 @@ open class KotlinFileExtractor( tw.writeMethods(methodId, shortName.nameInDB, "${shortName.nameInDB}$paramsSignature", returnType.javaResult.id, parentId, sourceDeclaration.cast()) tw.writeMethodsKotlinType(methodId, returnType.kotlinResult.id) - if (!isExternalDeclaration(f)) { + if (extractMethodAndParameterTypeAccesses) { extractTypeAccessRecursive(f.returnType, locId, id, -1) } @@ -791,14 +804,14 @@ open class KotlinFileExtractor( return id } - fun extractProperty(p: IrProperty, parentId: Label, extractBackingField: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgs: List?) { + fun extractProperty(p: IrProperty, parentId: Label, extractBackingField: Boolean, extractFunctionBodies: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List?) { with("property", p) { if (isFake(p)) return DeclarationStackAdjuster(p).use { - val id = useProperty(p, parentId) - val locId = getLocation(p, classTypeArgs) + val id = useProperty(p, parentId, classTypeArgsIncludingOuterClasses) + val locId = getLocation(p, classTypeArgsIncludingOuterClasses) tw.writeKtProperties(id, p.name.asString()) tw.writeHasLocation(id, locId) @@ -807,7 +820,7 @@ open class KotlinFileExtractor( val setter = p.setter if (getter != null) { - val getterId = extractFunction(getter, parentId, extractBackingField, typeSubstitution, classTypeArgs)?.cast() + val getterId = extractFunction(getter, parentId, extractBody = extractFunctionBodies, extractMethodAndParameterTypeAccesses = extractFunctionBodies, typeSubstitution, classTypeArgsIncludingOuterClasses)?.cast() if (getterId != null) { tw.writeKtPropertyGetters(id, getterId) } @@ -821,7 +834,7 @@ open class KotlinFileExtractor( if (!p.isVar) { logger.errorElement("!isVar property with a setter", p) } - val setterId = extractFunction(setter, parentId, extractBackingField, typeSubstitution, classTypeArgs)?.cast() + val setterId = extractFunction(setter, parentId, extractBody = extractFunctionBodies, extractMethodAndParameterTypeAccesses = extractFunctionBodies, typeSubstitution, classTypeArgsIncludingOuterClasses)?.cast() if (setterId != null) { tw.writeKtPropertySetters(id, setterId) } @@ -857,7 +870,7 @@ open class KotlinFileExtractor( } } - fun extractEnumEntry(ee: IrEnumEntry, parentId: Label) { + fun extractEnumEntry(ee: IrEnumEntry, parentId: Label, extractTypeAccess: Boolean) { with("enum entry", ee) { DeclarationStackAdjuster(ee).use { val id = useEnumEntry(ee) @@ -867,7 +880,7 @@ open class KotlinFileExtractor( val locId = tw.getLocation(ee) tw.writeHasLocation(id, locId) - if (!isExternalDeclaration(ee)) { + if (extractTypeAccess) { val fieldDeclarationId = tw.getFreshIdLabel() tw.writeFielddecls(fieldDeclarationId, parentId) tw.writeFieldDeclaredIn(id, fieldDeclarationId, 0) @@ -1341,6 +1354,23 @@ open class KotlinFileExtractor( return result } + private fun findTopLevelFunctionOrWarn(functionFilter: String, type: String, warnAgainstElement: IrElement): IrFunction? { + + val fn = pluginContext.referenceFunctions(FqName(functionFilter)) + .firstOrNull { it.owner.parentClassOrNull?.fqNameWhenAvailable?.asString() == type } + ?.owner + + if (fn != null) { + if (fn.parentClassOrNull != null) { + extractExternalClassLater(fn.parentAsClass) + } + } else { + logger.errorElement("Couldn't find JVM intrinsic function $functionFilter in $type", warnAgainstElement) + } + + return fn + } + val javaLangString by lazy { val result = pluginContext.referenceClass(FqName("java.lang.String"))?.owner result?.let { extractExternalClassLater(it) } @@ -1854,6 +1884,11 @@ open class KotlinFileExtractor( } } } + isFunction(target, "kotlin", "(some array type)", { isArrayType(it) }, "iterator") && c.origin == IrStatementOrigin.FOR_LOOP_ITERATOR -> { + findTopLevelFunctionOrWarn("kotlin.jvm.internal.iterator", "kotlin.jvm.internal.ArrayIteratorKt", c)?.let { iteratorFn -> + extractRawMethodAccess(iteratorFn, c, callable, parent, idx, enclosingStmt, listOf(c.dispatchReceiver), null, null, listOf((c.dispatchReceiver!!.type as IrSimpleType).arguments.first().typeOrNull!!)) + } + } isFunction(target, "kotlin", "(some array type)", { isArrayType(it) }, "get") && c.origin == IrStatementOrigin.GET_ARRAY_ELEMENT -> { val id = tw.getFreshIdLabel() val type = useType(c.type) @@ -3976,7 +4011,7 @@ open class KotlinFileExtractor( helper.extractParameterToFieldAssignmentInConstructor("", functionType, fieldId, 0, 1) // add implementation function - extractFunction(samMember, classId, false, null, null, ids.function) + extractFunction(samMember, classId, extractBody = false, extractMethodAndParameterTypeAccesses = true, null, null, ids.function) //body val blockId = tw.getFreshIdLabel() @@ -4152,7 +4187,7 @@ open class KotlinFileExtractor( val id = extractGeneratedClass(ids, superTypes, tw.getLocation(localFunction), localFunction) // Extract local function as a member - extractFunction(localFunction, id, true, null, listOf()) + extractFunction(localFunction, id, extractBody = true, extractMethodAndParameterTypeAccesses = true, null, listOf()) return id } diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index 2c58842e9e4..adaf5061e6c 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -4,21 +4,19 @@ import com.github.codeql.utils.* import com.github.codeql.utils.versions.isRawType import com.semmle.extractor.java.OdasaOutput import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext +import org.jetbrains.kotlin.backend.common.ir.allOverridden import org.jetbrains.kotlin.backend.common.lower.parentsWithSelf -import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap -import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.IrConst -import org.jetbrains.kotlin.ir.expressions.IrConstructorCall -import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl -import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection +import org.jetbrains.kotlin.ir.types.impl.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.util.OperatorNameConventions @@ -49,6 +47,41 @@ open class KotlinUsesExtractor( TypeResult(fakeKotlinType(), "", "") ) + private data class MethodKey(val className: FqName, val functionName: Name) + + private fun makeDescription(className: FqName, functionName: String) = MethodKey(className, Name.guessByFirstCharacter(functionName)) + + // This essentially mirrors SpecialBridgeMethods.kt, a backend pass which isn't easily available to our extractor. + private val specialFunctions = mapOf( + makeDescription(StandardNames.FqNames.collection, "") to "size", + makeDescription(FqName("java.util.Collection"), "") to "size", + makeDescription(StandardNames.FqNames.map, "") to "size", + makeDescription(FqName("java.util.Map"), "") to "size", + makeDescription(StandardNames.FqNames.charSequence.toSafe(), "") to "length", + makeDescription(FqName("java.lang.CharSequence"), "") to "length", + makeDescription(StandardNames.FqNames.map, "") to "keySet", + makeDescription(FqName("java.util.Map"), "") to "keySet", + makeDescription(StandardNames.FqNames.map, "") to "values", + makeDescription(FqName("java.util.Map"), "") to "values", + makeDescription(StandardNames.FqNames.map, "") to "entrySet", + makeDescription(FqName("java.util.Map"), "") to "entrySet" + ) + + private val specialFunctionShortNames = specialFunctions.keys.map { it.functionName }.toSet() + + fun getSpecialJvmName(f: IrFunction): String? { + if (specialFunctionShortNames.contains(f.name) && f is IrSimpleFunction) { + f.allOverridden(true).forEach { overriddenFunc -> + overriddenFunc.parentAsClass.fqNameWhenAvailable?.let { parentFqName -> + specialFunctions[MethodKey(parentFqName, f.name)]?.let { + return it + } + } + } + } + return null + } + fun getJvmName(container: IrAnnotationContainer): String? { for(a: IrConstructorCall in container.annotations) { val t = a.type @@ -67,7 +100,7 @@ open class KotlinUsesExtractor( } } } - return null + return (container as? IrFunction)?.let { getSpecialJvmName(container) } } @OptIn(kotlin.ExperimentalStdlibApi::class) // Annotation required by kotlin versions < 1.5 @@ -122,10 +155,7 @@ open class KotlinUsesExtractor( } fun getJavaEquivalentClass(c: IrClass) = - c.fqNameWhenAvailable?.toUnsafe() - ?.let { JavaToKotlinClassMap.mapKotlinToJava(it) } - ?.let { pluginContext.referenceClass(it.asSingleFqName()) } - ?.owner + getJavaEquivalentClassId(c)?.let { pluginContext.referenceClass(it.asSingleFqName()) }?.owner /** * Gets a KotlinFileExtractor based on this one, except it attributes locations to the file that declares the given class. @@ -380,7 +410,7 @@ open class KotlinUsesExtractor( if (inReceiverContext && globalExtensionState.genericSpecialisationsExtracted.add(classLabelResult.classLabel)) { val supertypeMode = if (argsIncludingOuterClasses == null) ExtractSupertypesMode.Raw else ExtractSupertypesMode.Specialised(argsIncludingOuterClasses) extractorWithCSource.extractClassSupertypes(c, classLabel, supertypeMode, true) - extractorWithCSource.extractMemberPrototypes(c, argsIncludingOuterClasses, classLabel) + extractorWithCSource.extractNonPrivateMemberPrototypes(c, argsIncludingOuterClasses, classLabel) } } @@ -696,8 +726,26 @@ open class KotlinUsesExtractor( } when (f) { - getter -> return FunctionNames(getJvmName(getter) ?: JvmAbi.getterName(propName), JvmAbi.getterName(propName)) - setter -> return FunctionNames(getJvmName(setter) ?: JvmAbi.setterName(propName), JvmAbi.setterName(propName)) + getter -> { + val defaultFunctionName = JvmAbi.getterName(propName) + val defaultDbName = if (getter.visibility == DescriptorVisibilities.PRIVATE && getter.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) { + // In JVM these functions don't exist, instead the backing field is accessed directly + defaultFunctionName + "\$private" + } else { + defaultFunctionName + } + return FunctionNames(getJvmName(getter) ?: defaultDbName, defaultFunctionName) + } + setter -> { + val defaultFunctionName = JvmAbi.setterName(propName) + val defaultDbName = if (setter.visibility == DescriptorVisibilities.PRIVATE && setter.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) { + // In JVM these functions don't exist, instead the backing field is accessed directly + defaultFunctionName + "\$private" + } else { + defaultFunctionName + } + return FunctionNames(getJvmName(setter) ?: defaultDbName, defaultFunctionName) + } else -> { logger.error( "Function has a corresponding property, but is neither the getter nor the setter" @@ -774,7 +822,9 @@ open class KotlinUsesExtractor( // The type parameters of the function. This does not include type parameters of enclosing classes. functionTypeParameters: List, // The type arguments of enclosing classes of the function. - classTypeArgsIncludingOuterClasses: List? + classTypeArgsIncludingOuterClasses: List?, + // The prefix used in the label. "callable", unless a property label is created, then it's "property". + prefix: String = "callable" ): String { val parentId = maybeParentId ?: useDeclarationParent(parent, false, classTypeArgsIncludingOuterClasses, true) val allParams = if (extensionReceiverParameter == null) { @@ -810,7 +860,7 @@ open class KotlinUsesExtractor( // 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() && classTypeArgsIncludingOuterClasses.isNullOrEmpty()) "<${functionTypeParameters.size}>" else ""; - return "@\"callable;{$parentId}.$name($paramTypeIds){$returnTypeId}${typeArgSuffix}\"" + return "@\"$prefix;{$parentId}.$name($paramTypeIds){$returnTypeId}${typeArgSuffix}\"" } protected fun IrFunction.isLocalFunction(): Boolean { @@ -867,17 +917,62 @@ open class KotlinUsesExtractor( return id } - fun useFunction(f: IrFunction, classTypeArgsIncludingOuterClasses: List? = null): Label { + // These are classes with Java equivalents, but whose methods don't all exist on those Java equivalents-- + // for example, the numeric classes define arithmetic functions (Int.plus, Long.or and so on) that lower to + // primitive arithmetic on the JVM, but which we extract as calls to reflect the source syntax more closely. + private val expectedMissingEquivalents = setOf( + "kotlin.Boolean", "kotlin.Byte", "kotlin.Char", "kotlin.Double", "kotlin.Float", "kotlin.Int", "kotlin.Long", "kotlin.Number", "kotlin.Short" + ) + + fun kotlinFunctionToJavaEquivalent(f: IrFunction, noReplace: Boolean) = + if (noReplace) + f + else + f.parentClassOrNull?.let { parentClass -> + getJavaEquivalentClass(parentClass)?.let { javaClass -> + if (javaClass != parentClass) + // Look for an exact type match... + javaClass.declarations.find { decl -> + decl is IrFunction && + decl.name == f.name && + decl.valueParameters.size == f.valueParameters.size && + // Note matching by classifier not the whole type so that generic arguments are allowed to differ, + // as they always will for method type parameters occurring in parameter types (e.g. toArray(T[] array) + // Differing only by nullability would also be insignificant if it came up. + decl.valueParameters.zip(f.valueParameters).all { p -> p.first.type.classifierOrNull == p.second.type.classifierOrNull } + } ?: + // Or if there is none, look for the only viable overload + javaClass.declarations.singleOrNull { decl -> + decl is IrFunction && + decl.name == f.name && + decl.valueParameters.size == f.valueParameters.size + } ?: + run { + val parentFqName = parentClass.fqNameWhenAvailable?.asString() + if (!expectedMissingEquivalents.contains(parentFqName)) { + logger.warn("Couldn't find a Java equivalent function to $parentFqName.${f.name} in ${javaClass.fqNameWhenAvailable}") + } + null + } + else + null + } + } as IrFunction? ?: f + + fun useFunction(f: IrFunction, classTypeArgsIncludingOuterClasses: List? = null, noReplace: Boolean = false): Label { if (f.isLocalFunction()) { val ids = getLocallyVisibleFunctionLabels(f) return ids.function.cast() } else { - return useFunctionCommon(f, getFunctionLabel(f, classTypeArgsIncludingOuterClasses)) + val realFunction = kotlinFunctionToJavaEquivalent(f, noReplace) + return useFunctionCommon(realFunction, getFunctionLabel(realFunction, classTypeArgsIncludingOuterClasses)) } } - fun useFunction(f: IrFunction, parentId: Label, classTypeArgsIncludingOuterClasses: List?) = - useFunctionCommon(f, getFunctionLabel(f, parentId, classTypeArgsIncludingOuterClasses)) + fun useFunction(f: IrFunction, parentId: Label, classTypeArgsIncludingOuterClasses: List?, noReplace: Boolean = false) = + kotlinFunctionToJavaEquivalent(f, noReplace).let { + useFunctionCommon(it, getFunctionLabel(it, parentId, classTypeArgsIncludingOuterClasses)) + } fun getTypeArgumentLabel( arg: IrTypeArgument @@ -941,6 +1036,9 @@ open class KotlinUsesExtractor( is IrFunction -> { "{${useFunction(parent)}}.$cls" } + is IrField -> { + "{${useField(parent)}}.$cls" + } else -> { if (pkg.isEmpty()) cls else "$pkg.$cls" } @@ -1111,15 +1209,20 @@ open class KotlinUsesExtractor( * `parent` is null. */ fun getValueParameterLabel(vp: IrValueParameter, parent: Label?): String { - val parentId = parent ?: useDeclarationParent(vp.parent, false) - val idx = vp.index + val declarationParent = vp.parent + val parentId = parent ?: useDeclarationParent(declarationParent, false) + + val idx = if (declarationParent is IrFunction && declarationParent.extensionReceiverParameter != null) + // For extension functions increase the index to match what the java extractor sees: + vp.index + 1 + else + vp.index + if (idx < 0) { - val p = vp.parent - if (p !is IrFunction || p.extensionReceiverParameter != vp) { - // We're not extracting this and this@TYPE parameters of functions: - logger.error("Unexpected negative index for parameter") - } + // We're not extracting this and this@TYPE parameters of functions: + logger.error("Unexpected negative index for parameter") } + return "@\"params;{$parentId};$idx\"" } @@ -1140,24 +1243,29 @@ open class KotlinUsesExtractor( if (parentId == null) { return null } else { - return getPropertyLabel(p, parentId) + return getPropertyLabel(p, parentId, null) } } - fun getPropertyLabel(p: IrProperty, parentId: Label) = - "@\"property;{$parentId};${p.name.asString()}\"" + private fun getPropertyLabel(p: IrProperty, parentId: Label, classTypeArgsIncludingOuterClasses: List?): String { + val getter = p.getter + val setter = p.setter - fun useProperty(p: IrProperty): Label? { - val label = getPropertyLabel(p) - if (label == null) { - return null + val func = getter ?: setter + val ext = func?.extensionReceiverParameter + + return if (ext == null) { + "@\"property;{$parentId};${p.name.asString()}\"" } else { - return tw.getLabelFor(label).also { extractPropertyLaterIfExternalFileMember(p) } + val returnType = getter?.returnType ?: setter?.valueParameters?.singleOrNull()?.type ?: pluginContext.irBuiltIns.unitType + val typeParams = getFunctionTypeParameters(func) + + getFunctionLabel(p.parent, parentId, p.name.asString(), listOf(), returnType, ext, typeParams, classTypeArgsIncludingOuterClasses, "property") } } - fun useProperty(p: IrProperty, parentId: Label): Label = - tw.getLabelFor(getPropertyLabel(p, parentId)).also { extractPropertyLaterIfExternalFileMember(p) } + fun useProperty(p: IrProperty, parentId: Label, classTypeArgsIncludingOuterClasses: List?): Label = + tw.getLabelFor(getPropertyLabel(p, parentId, classTypeArgsIncludingOuterClasses)).also { extractPropertyLaterIfExternalFileMember(p) } fun getEnumEntryLabel(ee: IrEnumEntry): String { val parentId = useDeclarationParent(ee.parent, false) diff --git a/java/kotlin-extractor/src/main/kotlin/utils/ClassNames.kt b/java/kotlin-extractor/src/main/kotlin/utils/ClassNames.kt index cdcdd535a2c..5a9713087eb 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/ClassNames.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/ClassNames.kt @@ -7,7 +7,9 @@ import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable import org.jetbrains.kotlin.ir.util.parentClassOrNull import org.jetbrains.kotlin.load.kotlin.JvmPackagePartSource @@ -84,4 +86,7 @@ fun getContainingClassOrSelf(decl: IrDeclaration): IrClass? { is IrClass -> decl else -> decl.parentClassOrNull } -} \ No newline at end of file +} + +fun getJavaEquivalentClassId(c: IrClass) = + c.fqNameWhenAvailable?.toUnsafe()?.let { JavaToKotlinClassMap.mapKotlinToJava(it) } \ No newline at end of file diff --git a/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt b/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt index 5e0d392ca64..b297e917de2 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt @@ -1,6 +1,7 @@ package com.github.codeql.utils import com.github.codeql.KotlinUsesExtractor +import com.github.codeql.getJavaEquivalentClassId import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor import org.jetbrains.kotlin.descriptors.ClassKind @@ -19,6 +20,7 @@ import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.types.impl.IrStarProjectionImpl import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection +import org.jetbrains.kotlin.ir.util.classId import org.jetbrains.kotlin.ir.util.constructedClassType import org.jetbrains.kotlin.ir.util.constructors import org.jetbrains.kotlin.ir.util.parentAsClass @@ -195,13 +197,25 @@ fun IrTypeArgument.withQuestionMark(b: Boolean): IrTypeArgument = typealias TypeSubstitution = (IrType, KotlinUsesExtractor.TypeContext, IrPluginContext) -> IrType +fun matchingTypeParameters(l: IrTypeParameter?, r: IrTypeParameter): Boolean { + if (l === r) + return true + if (l == null) + return false + // Special case: match List's E and MutableList's E, for example, because in the JVM lowering they will map to the same thing. + val lParent = l.parent as? IrClass ?: return false + val rParent = r.parent as? IrClass ?: return false + val lJavaId = getJavaEquivalentClassId(lParent) ?: lParent.classId + return (getJavaEquivalentClassId(rParent) ?: rParent.classId) == lJavaId && l.name == r.name +} + // Returns true if type is C where C is declared `class C { ... }` fun isUnspecialised(paramsContainer: IrTypeParametersContainer, args: List): Boolean { val unspecialisedHere = paramsContainer.typeParameters.zip(args).all { paramAndArg -> (paramAndArg.second as? IrTypeProjection)?.let { // Type arg refers to the class' own type parameter? it.variance == Variance.INVARIANT && - it.type.classifierOrNull?.owner === paramAndArg.first + matchingTypeParameters(it.type.classifierOrNull?.owner as? IrTypeParameter, paramAndArg.first) } ?: false } val remainingArgs = args.drop(paramsContainer.typeParameters.size) diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll index 7584acfab09..3aa228c5c9c 100644 --- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll @@ -143,6 +143,7 @@ private module Frameworks { private import semmle.code.java.frameworks.JMS private import semmle.code.java.frameworks.RabbitMQ private import semmle.code.java.regex.RegexFlowModels + private import semmle.code.java.frameworks.KotlinStdLib } private predicate sourceModelCsv(string row) { diff --git a/java/ql/lib/semmle/code/java/frameworks/KotlinStdLib.qll b/java/ql/lib/semmle/code/java/frameworks/KotlinStdLib.qll new file mode 100644 index 00000000000..63cdb87acdf --- /dev/null +++ b/java/ql/lib/semmle/code/java/frameworks/KotlinStdLib.qll @@ -0,0 +1,11 @@ +/** Definitions of taint steps in the KotlinStdLib framework */ + +import java +private import semmle.code.java.dataflow.ExternalFlow + +private class KotlinStdLibSummaryCsv extends SummaryModelCsv { + override predicate row(string row) { + row = + "kotlin.jvm.internal;ArrayIteratorKt;false;iterator;(Object[]);;Argument[0].ArrayElement;ReturnValue.Element;value" + } +} diff --git a/java/ql/test/kotlin/library-tests/classes/PrintAst.expected b/java/ql/test/kotlin/library-tests/classes/PrintAst.expected index 4cbec7bf704..0346a4e0292 100644 --- a/java/ql/test/kotlin/library-tests/classes/PrintAst.expected +++ b/java/ql/test/kotlin/library-tests/classes/PrintAst.expected @@ -578,7 +578,7 @@ generic_anonymous.kt: # 3| 1: [ExprStmt] ; # 3| 0: [ClassInstanceExpr] new (...) # 3| -3: [TypeAccess] Object -# 3| 5: [Method] getX +# 3| 5: [Method] getX$private # 3| 3: [TypeAccess] new Object(...) { ... } # 3| 0: [TypeAccess] T # 3| 5: [BlockStmt] { ... } @@ -590,8 +590,69 @@ generic_anonymous.kt: # 7| 5: [BlockStmt] { ... } # 7| 0: [ReturnStmt] return ... # 7| 0: [MethodAccess] getMember(...) -# 7| -1: [MethodAccess] getX(...) +# 7| -1: [MethodAccess] getX$private(...) # 7| -1: [ThisAccess] this +localClassField.kt: +# 0| [CompilationUnit] localClassField +# 1| 1: [Class] A +# 1| 1: [Constructor] A +# 1| 5: [BlockStmt] { ... } +# 1| 0: [SuperConstructorInvocationStmt] super(...) +# 1| 1: [BlockStmt] { ... } +# 2| 0: [ExprStmt] ; +# 2| 0: [KtInitializerAssignExpr] ...=... +# 2| 0: [VarAccess] x +# 7| 1: [ExprStmt] ; +# 7| 0: [KtInitializerAssignExpr] ...=... +# 7| 0: [VarAccess] y +# 2| 2: [Method] getX +# 2| 3: [TypeAccess] Object +# 2| 5: [BlockStmt] { ... } +# 2| 0: [ReturnStmt] return ... +# 2| 0: [VarAccess] this.x +# 2| -1: [ThisAccess] this +# 2| 2: [FieldDeclaration] Object x; +# 2| -1: [TypeAccess] Object +# 2| 0: [WhenExpr] when ... +# 2| 0: [WhenBranch] ... -> ... +# 2| 0: [BooleanLiteral] true +# 2| 1: [BlockStmt] { ... } +# 3| 0: [LocalTypeDeclStmt] class ... +# 3| 0: [LocalClass] L +# 3| 1: [Constructor] L +# 3| 5: [BlockStmt] { ... } +# 3| 0: [SuperConstructorInvocationStmt] super(...) +# 3| 1: [BlockStmt] { ... } +# 4| 1: [ExprStmt] ; +# 4| 0: [ClassInstanceExpr] new L(...) +# 4| -3: [TypeAccess] L +# 2| 1: [WhenBranch] ... -> ... +# 2| 0: [BooleanLiteral] true +# 5| 1: [BlockStmt] { ... } +# 7| 4: [Method] getY +# 7| 3: [TypeAccess] Object +# 7| 5: [BlockStmt] { ... } +# 7| 0: [ReturnStmt] return ... +# 7| 0: [VarAccess] this.y +# 7| -1: [ThisAccess] this +# 7| 4: [FieldDeclaration] Object y; +# 7| -1: [TypeAccess] Object +# 7| 0: [WhenExpr] when ... +# 7| 0: [WhenBranch] ... -> ... +# 7| 0: [BooleanLiteral] true +# 7| 1: [BlockStmt] { ... } +# 8| 0: [LocalTypeDeclStmt] class ... +# 8| 0: [LocalClass] L +# 8| 1: [Constructor] L +# 8| 5: [BlockStmt] { ... } +# 8| 0: [SuperConstructorInvocationStmt] super(...) +# 8| 1: [BlockStmt] { ... } +# 9| 1: [ExprStmt] ; +# 9| 0: [ClassInstanceExpr] new L(...) +# 9| -3: [TypeAccess] L +# 7| 1: [WhenBranch] ... -> ... +# 7| 0: [BooleanLiteral] true +# 10| 1: [BlockStmt] { ... } local_anonymous.kt: # 0| [CompilationUnit] local_anonymous # 3| 1: [Class] Class1 diff --git a/java/ql/test/kotlin/library-tests/classes/classes.expected b/java/ql/test/kotlin/library-tests/classes/classes.expected index 9f67caca2e1..2e3fd5a4305 100644 --- a/java/ql/test/kotlin/library-tests/classes/classes.expected +++ b/java/ql/test/kotlin/library-tests/classes/classes.expected @@ -37,6 +37,9 @@ | generic_anonymous.kt:0:0:0:0 | Generic_anonymousKt | Generic_anonymousKt | final, public | | generic_anonymous.kt:1:1:9:1 | Generic | Generic | final, private | | generic_anonymous.kt:3:19:5:3 | new Object(...) { ... } | | final, private | +| localClassField.kt:1:1:11:1 | A | A | final, public | +| localClassField.kt:3:9:3:19 | L | A$L | final, private | +| localClassField.kt:8:9:8:19 | L | A$L | final, private | | local_anonymous.kt:3:1:36:1 | Class1 | LocalAnonymous.Class1 | final, public | | local_anonymous.kt:5:16:7:9 | new Object(...) { ... } | | final, private | | local_anonymous.kt:11:9:11:24 | | Class1$ | final, private | diff --git a/java/ql/test/kotlin/library-tests/classes/ctorCalls.expected b/java/ql/test/kotlin/library-tests/classes/ctorCalls.expected index 691a1e84d4d..6567b235b6c 100644 --- a/java/ql/test/kotlin/library-tests/classes/ctorCalls.expected +++ b/java/ql/test/kotlin/library-tests/classes/ctorCalls.expected @@ -36,6 +36,9 @@ superCall | classes.kt:129:17:131:17 | super(...) | | generic_anonymous.kt:1:1:9:1 | super(...) | | generic_anonymous.kt:3:19:5:3 | super(...) | +| localClassField.kt:1:1:11:1 | super(...) | +| localClassField.kt:3:9:3:19 | super(...) | +| localClassField.kt:8:9:8:19 | super(...) | | local_anonymous.kt:3:1:36:1 | super(...) | | local_anonymous.kt:5:16:7:9 | super(...) | | local_anonymous.kt:11:9:11:24 | super(...) | diff --git a/java/ql/test/kotlin/library-tests/classes/genericExprTypes.expected b/java/ql/test/kotlin/library-tests/classes/genericExprTypes.expected index 1443bcd5e69..aa9514774a6 100644 --- a/java/ql/test/kotlin/library-tests/classes/genericExprTypes.expected +++ b/java/ql/test/kotlin/library-tests/classes/genericExprTypes.expected @@ -27,7 +27,7 @@ | generic_anonymous.kt:4:20:4:20 | Generic.this | Generic | | generic_anonymous.kt:4:20:4:20 | getT(...) | T | | generic_anonymous.kt:7:3:7:22 | T | T | -| generic_anonymous.kt:7:15:7:15 | getX(...) | new Object(...) { ... } | +| generic_anonymous.kt:7:15:7:15 | getX$private(...) | new Object(...) { ... } | | generic_anonymous.kt:7:15:7:15 | this | Generic | | generic_anonymous.kt:7:17:7:22 | getMember(...) | T | | generic_anonymous.kt:11:1:11:56 | String | String | diff --git a/java/ql/test/kotlin/library-tests/classes/localClass.expected b/java/ql/test/kotlin/library-tests/classes/localClass.expected index 53548310414..751b6649982 100644 --- a/java/ql/test/kotlin/library-tests/classes/localClass.expected +++ b/java/ql/test/kotlin/library-tests/classes/localClass.expected @@ -2,5 +2,7 @@ | classes.kt:118:9:123:9 | class ... | classes.kt:118:9:123:9 | | classes.kt:117:5:124:5 | fn2 | classes.kt:109:1:136:1 | C1 | | classes.kt:119:13:121:13 | class ... | classes.kt:119:13:121:13 | Local2 | classes.kt:118:9:123:9 | localFn | classes.kt:109:1:136:1 | C1 | | classes.kt:129:17:131:17 | class ... | classes.kt:129:17:131:17 | Local3 | classes.kt:128:13:133:13 | fn | classes.kt:127:16:134:9 | new Object(...) { ... } | +| localClassField.kt:3:9:3:19 | class ... | localClassField.kt:3:9:3:19 | L | localClassField.kt:1:1:11:1 | A | localClassField.kt:1:1:11:1 | A | +| localClassField.kt:8:9:8:19 | class ... | localClassField.kt:8:9:8:19 | L | localClassField.kt:1:1:11:1 | A | localClassField.kt:1:1:11:1 | A | | local_anonymous.kt:11:9:11:24 | class ... | local_anonymous.kt:11:9:11:24 | | local_anonymous.kt:10:5:13:5 | fn2 | local_anonymous.kt:3:1:36:1 | Class1 | | local_anonymous.kt:25:9:25:27 | class ... | local_anonymous.kt:25:9:25:27 | LocalClass | local_anonymous.kt:24:5:27:5 | fn5 | local_anonymous.kt:3:1:36:1 | Class1 | diff --git a/java/ql/test/kotlin/library-tests/classes/localClassField.kt b/java/ql/test/kotlin/library-tests/classes/localClassField.kt new file mode 100644 index 00000000000..f1db3f45a79 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/classes/localClassField.kt @@ -0,0 +1,11 @@ +class A { + val x = if (true) { + class L { } + L() + } else {} + + val y = if (true) { + class L { } + L() + } else {} +} diff --git a/java/ql/test/kotlin/library-tests/classes/superTypes.expected b/java/ql/test/kotlin/library-tests/classes/superTypes.expected index 3fa2b881183..f0b7f0ca3e9 100644 --- a/java/ql/test/kotlin/library-tests/classes/superTypes.expected +++ b/java/ql/test/kotlin/library-tests/classes/superTypes.expected @@ -49,6 +49,9 @@ | file:///SuperChain2.class:0:0:0:0 | SuperChain2 | file:///SuperChain1.class:0:0:0:0 | SuperChain1 | | generic_anonymous.kt:1:1:9:1 | Generic | file:///Object.class:0:0:0:0 | Object | | generic_anonymous.kt:3:19:5:3 | new Object(...) { ... } | file:///Object.class:0:0:0:0 | Object | +| localClassField.kt:1:1:11:1 | A | file:///Object.class:0:0:0:0 | Object | +| localClassField.kt:3:9:3:19 | L | file:///Object.class:0:0:0:0 | Object | +| localClassField.kt:8:9:8:19 | L | file:///Object.class:0:0:0:0 | Object | | local_anonymous.kt:3:1:36:1 | Class1 | file:///Object.class:0:0:0:0 | Object | | local_anonymous.kt:5:16:7:9 | new Object(...) { ... } | file:///Object.class:0:0:0:0 | Object | | local_anonymous.kt:11:9:11:24 | | file:///Object.class:0:0:0:0 | Object | diff --git a/java/ql/test/kotlin/library-tests/comments/comments.expected b/java/ql/test/kotlin/library-tests/comments/comments.expected index 10d2cb46e73..043a7bbdf73 100644 --- a/java/ql/test/kotlin/library-tests/comments/comments.expected +++ b/java/ql/test/kotlin/library-tests/comments/comments.expected @@ -12,7 +12,7 @@ commentOwners | comments.kt:4:1:11:3 | /**\n * A group of *members*.\n *\n * This class has no useful logic; it's just a documentation example.\n *\n * @property name the name of this group.\n * @constructor Creates an empty group.\n */ | comments.kt:12:1:31:1 | Group | | comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | comments.kt:17:5:17:46 | members | | comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | comments.kt:17:5:17:46 | members | -| comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | comments.kt:17:13:17:46 | getMembers | +| comments.kt:14:5:16:7 | /**\n * Members of this group.\n */ | comments.kt:17:13:17:46 | getMembers$private | | comments.kt:19:5:22:7 | /**\n * Adds a [member] to this group.\n * @return the new size of the group.\n */ | comments.kt:23:5:26:5 | add | | comments.kt:35:5:35:34 | /** Medium is in the middle */ | comments.kt:36:5:36:14 | Medium | | comments.kt:37:5:37:23 | /** This is high */ | comments.kt:38:5:38:11 | High | diff --git a/java/ql/test/kotlin/library-tests/companion_objects/companion_objects.expected b/java/ql/test/kotlin/library-tests/companion_objects/companion_objects.expected index b3a594cbc57..1766f462578 100644 --- a/java/ql/test/kotlin/library-tests/companion_objects/companion_objects.expected +++ b/java/ql/test/kotlin/library-tests/companion_objects/companion_objects.expected @@ -1,2 +1,2 @@ -| companion_objects.kt:1:1:6:1 | MyClass | companion_objects.kt:3:5:5:5 | MyClassCompanion | companion_objects.kt:3:5:5:5 | MyClassCompanion | final,public,static | -| companion_objects.kt:8:1:13:1 | MyInterface | companion_objects.kt:10:5:12:5 | MyInterfaceCompanion | companion_objects.kt:10:5:12:5 | MyInterfaceCompanion | final,public,static | +| companion_objects.kt:1:1:6:1 | MyClass | companion_objects.kt:3:5:5:5 | MyClassCompanion | companion_objects.kt:3:5:5:5 | MyClassCompanion | companion_objects.kt:1:1:6:1 | MyClass | final,public,static | +| companion_objects.kt:8:1:13:1 | MyInterface | companion_objects.kt:10:5:12:5 | MyInterfaceCompanion | companion_objects.kt:10:5:12:5 | MyInterfaceCompanion | companion_objects.kt:8:1:13:1 | MyInterface | final,public,static | diff --git a/java/ql/test/kotlin/library-tests/companion_objects/companion_objects.ql b/java/ql/test/kotlin/library-tests/companion_objects/companion_objects.ql index a170248eaae..a6df9bb27b6 100644 --- a/java/ql/test/kotlin/library-tests/companion_objects/companion_objects.ql +++ b/java/ql/test/kotlin/library-tests/companion_objects/companion_objects.ql @@ -5,4 +5,4 @@ where c.fromSource() and cco = c.getCompanionObject() and f = cco.getInstance() -select c, f, cco, concat(f.getAModifier().toString(), ",") +select c, f, cco, f.getDeclaringType(), concat(f.getAModifier().toString(), ",") diff --git a/java/ql/test/kotlin/library-tests/data-classes/callees.expected b/java/ql/test/kotlin/library-tests/data-classes/callees.expected index b2dd4085a2a..c4b66f2d3a0 100644 --- a/java/ql/test/kotlin/library-tests/data-classes/callees.expected +++ b/java/ql/test/kotlin/library-tests/data-classes/callees.expected @@ -5,4 +5,4 @@ | dc.kt:0:0:0:0 | times(...) | Int.times | | dc.kt:0:0:0:0 | toString(...) | Arrays.toString | | dc.kt:0:0:0:0 | toString(...) | Arrays.toString | -| dc.kt:1:1:1:71 | super(...) | Any.Any | +| dc.kt:1:1:1:71 | super(...) | Object.Object | diff --git a/java/ql/test/kotlin/library-tests/dataflow/foreach/C1.java b/java/ql/test/kotlin/library-tests/dataflow/foreach/C1.java new file mode 100644 index 00000000000..164f9bd1795 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/dataflow/foreach/C1.java @@ -0,0 +1,22 @@ +public final class C1 { + public final String taint(String t) { + return t; + } + + public final void sink(Object a) { + } + + public final void test() { + String[] l = new String[]{this.taint("a"), ""}; + this.sink(l); + this.sink(l[0]); + + for(int i = 0; i < l.length; i++) { + this.sink(l[i]); + } + + for (String s : l) { + this.sink(s); + } + } +} diff --git a/java/ql/test/kotlin/library-tests/dataflow/foreach/C2.kt b/java/ql/test/kotlin/library-tests/dataflow/foreach/C2.kt new file mode 100644 index 00000000000..7a98abaa110 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/dataflow/foreach/C2.kt @@ -0,0 +1,18 @@ +class C2 { + fun taint(t: String): String { + return t + } + + fun sink(a: Any?) {} + fun test() { + val l = arrayOf(taint("a"), "") + sink(l) + sink(l[0]) + for (i in l.indices) { + sink(l[i]) + } + for (s in l) { + sink(s) + } + } +} diff --git a/java/ql/test/kotlin/library-tests/dataflow/foreach/test.expected b/java/ql/test/kotlin/library-tests/dataflow/foreach/test.expected new file mode 100644 index 00000000000..f204c12ebe2 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/dataflow/foreach/test.expected @@ -0,0 +1,8 @@ +| C1.java:10:44:10:46 | "a" | C1.java:11:17:11:17 | l | +| C1.java:10:44:10:46 | "a" | C1.java:12:17:12:20 | ...[...] | +| C1.java:10:44:10:46 | "a" | C1.java:15:20:15:23 | ...[...] | +| C1.java:10:44:10:46 | "a" | C1.java:19:20:19:20 | s | +| C2.kt:8:32:8:32 | a | C2.kt:9:14:9:14 | l | +| C2.kt:8:32:8:32 | a | C2.kt:10:14:10:17 | ...[...] | +| C2.kt:8:32:8:32 | a | C2.kt:12:18:12:21 | ...[...] | +| C2.kt:8:32:8:32 | a | C2.kt:15:18:15:18 | s | diff --git a/java/ql/test/kotlin/library-tests/dataflow/foreach/test.ql b/java/ql/test/kotlin/library-tests/dataflow/foreach/test.ql new file mode 100644 index 00000000000..c14c0ca83d2 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/dataflow/foreach/test.ql @@ -0,0 +1,19 @@ +import java +import semmle.code.java.dataflow.TaintTracking +import semmle.code.java.dataflow.ExternalFlow + +class Conf extends TaintTracking::Configuration { + Conf() { this = "qltest:foreach-array-iterator" } + + override predicate isSource(DataFlow::Node n) { + n.asExpr().(Argument).getCall().getCallee().hasName("taint") + } + + override predicate isSink(DataFlow::Node n) { + n.asExpr().(Argument).getCall().getCallee().hasName("sink") + } +} + +from DataFlow::Node src, DataFlow::Node sink, Conf conf +where conf.hasFlow(src, sink) +select src, sink diff --git a/java/ql/test/kotlin/library-tests/exprs/exprs.expected b/java/ql/test/kotlin/library-tests/exprs/exprs.expected index 2d31bbf92cf..21548a78ed9 100644 --- a/java/ql/test/kotlin/library-tests/exprs/exprs.expected +++ b/java/ql/test/kotlin/library-tests/exprs/exprs.expected @@ -1717,86 +1717,6 @@ | exprs.kt:268:3:268:9 | updated | exprs.kt:261:1:270:1 | inPlaceOperators | VarAccess | | exprs.kt:268:3:268:14 | ...%=... | exprs.kt:261:1:270:1 | inPlaceOperators | AssignRemExpr | | exprs.kt:268:14:268:14 | 1 | exprs.kt:261:1:270:1 | inPlaceOperators | IntegerLiteral | -| file:///!unknown-binary-location/SomePredicate.class:0:0:0:0 | T | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/SomePredicate.class:0:0:0:0 | boolean | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function0.class:0:0:0:0 | R | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function1.class:0:0:0:0 | P1 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function1.class:0:0:0:0 | R | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function2.class:0:0:0:0 | P1 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function2.class:0:0:0:0 | P2 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function2.class:0:0:0:0 | R | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P1 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P2 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P3 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P4 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P5 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P6 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P7 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P8 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P9 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P10 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P11 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P12 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P13 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P14 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P15 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P16 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P17 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P18 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P19 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P20 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P21 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | P22 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function22.class:0:0:0:0 | R | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P1 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P2 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P3 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P4 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P5 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P6 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P7 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P8 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P9 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P10 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P11 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P12 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P13 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P14 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P15 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P16 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P17 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P18 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P19 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P20 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P21 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P22 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | P23 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function23.class:0:0:0:0 | R | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P1 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P2 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P3 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P4 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P5 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P6 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P7 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P8 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P9 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P10 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P11 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P12 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P13 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P14 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P15 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P16 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P17 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P18 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P19 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P20 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P21 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P22 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P23 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | P24 | file://:0:0:0:0 | | TypeAccess | -| file:///!unknown-binary-location/kotlin/Function24.class:0:0:0:0 | R | file://:0:0:0:0 | | TypeAccess | | funcExprs.kt:1:1:1:46 | Unit | file://:0:0:0:0 | | TypeAccess | | funcExprs.kt:1:26:1:37 | Function0 | file://:0:0:0:0 | | TypeAccess | | funcExprs.kt:1:26:1:37 | Integer | file://:0:0:0:0 | | TypeAccess | diff --git a/java/ql/test/kotlin/library-tests/extensions/A.java b/java/ql/test/kotlin/library-tests/extensions/A.java new file mode 100644 index 00000000000..acb3b819de5 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/extensions/A.java @@ -0,0 +1,5 @@ +class A { + void method() { + ExtensionsKt.someFun(new SomeClass(), ""); + } +} \ No newline at end of file diff --git a/java/ql/test/kotlin/library-tests/extensions/methodaccesses.expected b/java/ql/test/kotlin/library-tests/extensions/methodaccesses.expected index ba9440a9bc2..e2864ed1fdb 100644 --- a/java/ql/test/kotlin/library-tests/extensions/methodaccesses.expected +++ b/java/ql/test/kotlin/library-tests/extensions/methodaccesses.expected @@ -1,3 +1,5 @@ +| A.java:3:9:3:49 | someFun(...) | A.java:3:9:3:20 | ExtensionsKt | A.java:3:30:3:44 | new SomeClass(...) | +| A.java:3:9:3:49 | someFun(...) | A.java:3:9:3:20 | ExtensionsKt | A.java:3:47:3:48 | "" | | extensions.kt:21:17:21:38 | someClassMethod(...) | extensions.kt:21:5:21:15 | new SomeClass(...) | extensions.kt:21:34:21:36 | foo | | extensions.kt:22:17:22:30 | someFun(...) | extensions.kt:22:17:22:30 | ExtensionsKt | extensions.kt:22:5:22:15 | new SomeClass(...) | | extensions.kt:22:17:22:30 | someFun(...) | extensions.kt:22:17:22:30 | ExtensionsKt | extensions.kt:22:26:22:28 | foo | diff --git a/java/ql/test/kotlin/library-tests/extensions/methods.expected b/java/ql/test/kotlin/library-tests/extensions/methods.expected index 133477a047a..3c2e4114a78 100644 --- a/java/ql/test/kotlin/library-tests/extensions/methods.expected +++ b/java/ql/test/kotlin/library-tests/extensions/methods.expected @@ -1,3 +1,4 @@ +| A.java:2:10:2:15 | method | file://:0:0:0:0 | void | | extensions.kt:3:5:3:38 | someClassMethod | file://:0:0:0:0 | void | | extensions.kt:6:5:6:41 | anotherClassMethod | file://:0:0:0:0 | void | | extensions.kt:9:1:9:36 | someFun | file://:0:0:0:0 | void | diff --git a/java/ql/test/kotlin/library-tests/extensions/parameters.expected b/java/ql/test/kotlin/library-tests/extensions/parameters.expected index 8081534e885..5cb7e0986d3 100644 --- a/java/ql/test/kotlin/library-tests/extensions/parameters.expected +++ b/java/ql/test/kotlin/library-tests/extensions/parameters.expected @@ -1,7 +1,9 @@ parametersWithArgs | extensions.kt:3:25:3:34 | p1 | 0 | extensions.kt:21:34:21:36 | foo | | extensions.kt:6:28:6:37 | p1 | 0 | extensions.kt:25:40:25:42 | foo | +| extensions.kt:9:5:9:13 | | 0 | A.java:3:30:3:44 | new SomeClass(...) | | extensions.kt:9:5:9:13 | | 0 | extensions.kt:22:5:22:15 | new SomeClass(...) | +| extensions.kt:9:23:9:32 | p1 | 1 | A.java:3:47:3:48 | "" | | extensions.kt:9:23:9:32 | p1 | 1 | extensions.kt:22:26:22:28 | foo | | extensions.kt:10:5:10:16 | | 0 | extensions.kt:26:5:26:18 | new AnotherClass(...) | | extensions.kt:10:29:10:38 | p1 | 1 | extensions.kt:26:32:26:34 | foo | diff --git a/java/ql/test/kotlin/library-tests/inherited-callee/test.expected b/java/ql/test/kotlin/library-tests/inherited-callee/test.expected index 3f5cee5b85f..38f8f9f0666 100644 --- a/java/ql/test/kotlin/library-tests/inherited-callee/test.expected +++ b/java/ql/test/kotlin/library-tests/inherited-callee/test.expected @@ -3,8 +3,8 @@ | Test.java:25:5:25:16 | hashCode(...) | hashCode | Object | | Test.java:26:5:26:17 | inheritMe(...) | inheritMe | Test | | Test.java:28:5:28:34 | inheritedInterfaceMethodJ(...) | inheritedInterfaceMethodJ | ParentIf | -| Test.kt:23:7:23:16 | toString(...) | toString | Any | -| Test.kt:24:7:24:15 | equals(...) | equals | Any | -| Test.kt:25:7:25:16 | hashCode(...) | hashCode | Any | +| Test.kt:23:7:23:16 | toString(...) | toString | Object | +| Test.kt:24:7:24:15 | equals(...) | equals | Object | +| Test.kt:25:7:25:16 | hashCode(...) | hashCode | Object | | Test.kt:26:7:26:17 | inheritMe(...) | inheritMe | TestKt | | Test.kt:28:9:28:35 | inheritedInterfaceMethodK(...) | inheritedInterfaceMethodK | ParentIf | diff --git a/java/ql/test/kotlin/library-tests/java-map-methods/test.expected b/java/ql/test/kotlin/library-tests/java-map-methods/test.expected new file mode 100644 index 00000000000..0a646e52fe1 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/java-map-methods/test.expected @@ -0,0 +1,2 @@ +| Integer | +| Object | diff --git a/java/ql/test/kotlin/library-tests/java-map-methods/test.kt b/java/ql/test/kotlin/library-tests/java-map-methods/test.kt new file mode 100644 index 00000000000..9a40d3c29bf --- /dev/null +++ b/java/ql/test/kotlin/library-tests/java-map-methods/test.kt @@ -0,0 +1 @@ +fun test(m: Map) = m.getOrDefault(1, 2) diff --git a/java/ql/test/kotlin/library-tests/java-map-methods/test.ql b/java/ql/test/kotlin/library-tests/java-map-methods/test.ql new file mode 100644 index 00000000000..2694f59162d --- /dev/null +++ b/java/ql/test/kotlin/library-tests/java-map-methods/test.ql @@ -0,0 +1,4 @@ +import java + +from MethodAccess ma +select ma.getCallee().getAParameter().getType().toString() diff --git a/java/ql/test/kotlin/library-tests/methods/clinit.expected b/java/ql/test/kotlin/library-tests/methods/clinit.expected new file mode 100644 index 00000000000..8e4a65ab39a --- /dev/null +++ b/java/ql/test/kotlin/library-tests/methods/clinit.expected @@ -0,0 +1 @@ +| clinit.kt:0:0:0:0 | | file://:0:0:0:0 | void | diff --git a/java/ql/test/kotlin/library-tests/methods/clinit.kt b/java/ql/test/kotlin/library-tests/methods/clinit.kt new file mode 100644 index 00000000000..291dc7cad4b --- /dev/null +++ b/java/ql/test/kotlin/library-tests/methods/clinit.kt @@ -0,0 +1,3 @@ +package foo.bar + +var topLevelInt: Int = 0 \ No newline at end of file diff --git a/java/ql/test/kotlin/library-tests/methods/clinit.ql b/java/ql/test/kotlin/library-tests/methods/clinit.ql new file mode 100644 index 00000000000..11ea8515abb --- /dev/null +++ b/java/ql/test/kotlin/library-tests/methods/clinit.ql @@ -0,0 +1,5 @@ +import java + +from Method m +where m.getName() = "" +select m, m.getReturnType() diff --git a/java/ql/test/kotlin/library-tests/methods/exprs.expected b/java/ql/test/kotlin/library-tests/methods/exprs.expected index 7b3f15f512b..a08c7cda44e 100644 --- a/java/ql/test/kotlin/library-tests/methods/exprs.expected +++ b/java/ql/test/kotlin/library-tests/methods/exprs.expected @@ -1,3 +1,17 @@ +| clinit.kt:3:1:3:24 | ...=... | AssignExpr | +| clinit.kt:3:1:3:24 | ...=... | KtInitializerAssignExpr | +| clinit.kt:3:1:3:24 | | VarAccess | +| clinit.kt:3:1:3:24 | ClinitKt | TypeAccess | +| clinit.kt:3:1:3:24 | ClinitKt | TypeAccess | +| clinit.kt:3:1:3:24 | ClinitKt | TypeAccess | +| clinit.kt:3:1:3:24 | ClinitKt.topLevelInt | VarAccess | +| clinit.kt:3:1:3:24 | ClinitKt.topLevelInt | VarAccess | +| clinit.kt:3:1:3:24 | ClinitKt.topLevelInt | VarAccess | +| clinit.kt:3:1:3:24 | Unit | TypeAccess | +| clinit.kt:3:1:3:24 | int | TypeAccess | +| clinit.kt:3:1:3:24 | int | TypeAccess | +| clinit.kt:3:1:3:24 | int | TypeAccess | +| clinit.kt:3:24:3:24 | 0 | IntegerLiteral | | methods2.kt:4:1:5:1 | Unit | TypeAccess | | methods2.kt:4:26:4:31 | int | TypeAccess | | methods2.kt:4:34:4:39 | int | TypeAccess | diff --git a/java/ql/test/kotlin/library-tests/methods/methods.expected b/java/ql/test/kotlin/library-tests/methods/methods.expected index b48a979dc33..87832f2ed87 100644 --- a/java/ql/test/kotlin/library-tests/methods/methods.expected +++ b/java/ql/test/kotlin/library-tests/methods/methods.expected @@ -1,4 +1,7 @@ methods +| clinit.kt:0:0:0:0 | ClinitKt | clinit.kt:0:0:0:0 | | () | | +| clinit.kt:0:0:0:0 | ClinitKt | clinit.kt:3:1:3:24 | getTopLevelInt | getTopLevelInt() | public, static | +| clinit.kt:0:0:0:0 | ClinitKt | clinit.kt:3:1:3:24 | setTopLevelInt | setTopLevelInt(int) | public, static | | methods2.kt:0:0:0:0 | Methods2Kt | methods2.kt:4:1:5:1 | fooBarTopLevelMethod | fooBarTopLevelMethod(int,int) | public, static | | methods2.kt:7:1:10:1 | Class2 | methods2.kt:8:5:9:5 | fooBarClassMethod | fooBarClassMethod(int,int) | public | | methods3.kt:0:0:0:0 | Methods3Kt | methods3.kt:3:1:3:42 | fooBarTopLevelMethodExt | fooBarTopLevelMethodExt(int,int) | public, static | diff --git a/java/ql/test/kotlin/library-tests/methods/parameters.expected b/java/ql/test/kotlin/library-tests/methods/parameters.expected index 75f8eea234b..3e34c2c3b86 100644 --- a/java/ql/test/kotlin/library-tests/methods/parameters.expected +++ b/java/ql/test/kotlin/library-tests/methods/parameters.expected @@ -1,3 +1,4 @@ +| clinit.kt:3:1:3:24 | setTopLevelInt | clinit.kt:3:1:3:24 | | 0 | | methods2.kt:4:1:5:1 | fooBarTopLevelMethod | methods2.kt:4:26:4:31 | x | 0 | | methods2.kt:4:1:5:1 | fooBarTopLevelMethod | methods2.kt:4:34:4:39 | y | 1 | | methods2.kt:8:5:9:5 | fooBarClassMethod | methods2.kt:8:27:8:32 | x | 0 | diff --git a/java/ql/test/kotlin/library-tests/modifiers/modifiers.expected b/java/ql/test/kotlin/library-tests/modifiers/modifiers.expected index b499378e07c..7f56350444a 100644 --- a/java/ql/test/kotlin/library-tests/modifiers/modifiers.expected +++ b/java/ql/test/kotlin/library-tests/modifiers/modifiers.expected @@ -2,7 +2,7 @@ | modifiers.kt:1:6:25:1 | X | Constructor | public | | modifiers.kt:2:5:2:21 | a | Field | private | | modifiers.kt:2:5:2:21 | a | Property | private | -| modifiers.kt:2:13:2:21 | getA | Method | private | +| modifiers.kt:2:13:2:21 | getA$private | Method | private | | modifiers.kt:3:5:3:23 | b | Field | private | | modifiers.kt:3:5:3:23 | b | Property | protected | | modifiers.kt:3:15:3:23 | getB | Method | protected | diff --git a/java/ql/test/kotlin/library-tests/private-anonymous-types/other.kt b/java/ql/test/kotlin/library-tests/private-anonymous-types/other.kt new file mode 100644 index 00000000000..ace100f9173 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/private-anonymous-types/other.kt @@ -0,0 +1 @@ +class Ext : A("Hello") { } diff --git a/java/ql/test/kotlin/library-tests/private-anonymous-types/test.expected b/java/ql/test/kotlin/library-tests/private-anonymous-types/test.expected new file mode 100644 index 00000000000..4c6907f99d4 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/private-anonymous-types/test.expected @@ -0,0 +1,18 @@ +| file:///!unknown-binary-location/A.class:0:0:0:0 | A | file:///!unknown-binary-location/A.class:0:0:0:0 | A | +| file:///!unknown-binary-location/A.class:0:0:0:0 | A | file:///!unknown-binary-location/A.class:0:0:0:0 | getAnonType | +| file:///!unknown-binary-location/If.class:0:0:0:0 | If | file:///!unknown-binary-location/If.class:0:0:0:0 | getX | +| file:///!unknown-binary-location/If.class:0:0:0:0 | If | file:///!unknown-binary-location/If.class:0:0:0:0 | getX | +| other.kt:1:1:1:34 | Ext | other.kt:1:1:1:34 | Ext | +| test.kt:0:0:0:0 | TestKt | test.kt:19:1:19:38 | user | +| test.kt:1:1:5:1 | If | test.kt:3:3:3:11 | getX | +| test.kt:7:1:17:1 | A | test.kt:7:6:17:1 | A | +| test.kt:7:1:17:1 | A | test.kt:9:3:11:3 | anonType | +| test.kt:7:1:17:1 | A | test.kt:9:3:11:3 | getAnonType | +| test.kt:7:1:17:1 | A | test.kt:13:3:15:3 | privateAnonType | +| test.kt:7:1:17:1 | A | test.kt:13:11:15:3 | getPrivateAnonType$private | +| test.kt:9:18:11:3 | new If(...) { ... } | test.kt:9:18:11:3 | | +| test.kt:9:18:11:3 | new If(...) { ... } | test.kt:10:5:10:22 | x | +| test.kt:9:18:11:3 | new If(...) { ... } | test.kt:10:14:10:22 | getX | +| test.kt:13:33:15:3 | new If(...) { ... } | test.kt:13:33:15:3 | | +| test.kt:13:33:15:3 | new If(...) { ... } | test.kt:14:5:14:22 | x | +| test.kt:13:33:15:3 | new If(...) { ... } | test.kt:14:14:14:22 | getX | diff --git a/java/ql/test/kotlin/library-tests/private-anonymous-types/test.kt b/java/ql/test/kotlin/library-tests/private-anonymous-types/test.kt new file mode 100644 index 00000000000..b0d49124eac --- /dev/null +++ b/java/ql/test/kotlin/library-tests/private-anonymous-types/test.kt @@ -0,0 +1,19 @@ +interface If { + + val x : T + +} + +open class A(t: T) { + + val anonType = object : If { + override val x = t + } + + private val privateAnonType = object : If { + override val x = t + } + +} + +fun user(x: A) = x.anonType.x diff --git a/java/ql/test/kotlin/library-tests/private-anonymous-types/test.ql b/java/ql/test/kotlin/library-tests/private-anonymous-types/test.ql new file mode 100644 index 00000000000..74bbc59a7fd --- /dev/null +++ b/java/ql/test/kotlin/library-tests/private-anonymous-types/test.ql @@ -0,0 +1,5 @@ +import java + +from ClassOrInterface ci, Member m +where m = ci.getAMember() and ci.getSourceDeclaration().fromSource() +select ci, m diff --git a/java/ql/test/kotlin/library-tests/properties/properties.expected b/java/ql/test/kotlin/library-tests/properties/properties.expected index e1ebdabfd2a..05870c7b6e1 100644 --- a/java/ql/test/kotlin/library-tests/properties/properties.expected +++ b/java/ql/test/kotlin/library-tests/properties/properties.expected @@ -20,6 +20,8 @@ fieldDeclarations | properties.kt:38:5:38:34 | int internalProp; | properties.kt:38:5:38:34 | internalProp | 0 | | properties.kt:67:1:67:23 | int constVal; | properties.kt:67:1:67:23 | constVal | 0 | | properties.kt:70:5:70:16 | int prop; | properties.kt:70:5:70:16 | prop | 0 | +| properties.kt:84:5:84:29 | int data; | properties.kt:84:5:84:29 | data | 0 | +| properties.kt:92:5:93:18 | int data; | properties.kt:92:5:93:18 | data | 0 | #select | properties.kt:2:27:2:50 | constructorProp | properties.kt:2:27:2:50 | getConstructorProp | file://:0:0:0:0 | | properties.kt:2:27:2:50 | constructorProp | public | | properties.kt:2:53:2:83 | mutableConstructorProp | properties.kt:2:53:2:83 | getMutableConstructorProp | properties.kt:2:53:2:83 | setMutableConstructorProp | properties.kt:2:53:2:83 | mutableConstructorProp | public | @@ -40,9 +42,13 @@ fieldDeclarations | properties.kt:30:5:31:29 | overrideGetterUseField | properties.kt:31:13:31:29 | getOverrideGetterUseField | properties.kt:30:5:31:29 | setOverrideGetterUseField | properties.kt:30:5:31:29 | overrideGetterUseField | public | | properties.kt:32:5:33:29 | useField | properties.kt:33:13:33:29 | getUseField | file://:0:0:0:0 | | properties.kt:32:5:33:29 | useField | public | | properties.kt:34:5:34:36 | lateInitVar | properties.kt:34:14:34:36 | getLateInitVar | properties.kt:34:14:34:36 | setLateInitVar | properties.kt:34:5:34:36 | lateInitVar | public | -| properties.kt:35:5:35:32 | privateProp | properties.kt:35:13:35:32 | getPrivateProp | file://:0:0:0:0 | | properties.kt:35:5:35:32 | privateProp | private | +| properties.kt:35:5:35:32 | privateProp | properties.kt:35:13:35:32 | getPrivateProp$private | file://:0:0:0:0 | | properties.kt:35:5:35:32 | privateProp | private | | properties.kt:36:5:36:36 | protectedProp | properties.kt:36:15:36:36 | getProtectedProp | file://:0:0:0:0 | | properties.kt:36:5:36:36 | protectedProp | protected | | properties.kt:37:5:37:30 | publicProp | properties.kt:37:12:37:30 | getPublicProp | file://:0:0:0:0 | | properties.kt:37:5:37:30 | publicProp | public | | properties.kt:38:5:38:34 | internalProp | properties.kt:38:14:38:34 | getInternalProp | file://:0:0:0:0 | | properties.kt:38:5:38:34 | internalProp | internal | | properties.kt:67:1:67:23 | constVal | properties.kt:67:7:67:23 | getConstVal | file://:0:0:0:0 | | properties.kt:67:1:67:23 | constVal | public | | properties.kt:70:5:70:16 | prop | properties.kt:70:5:70:16 | getProp | file://:0:0:0:0 | | properties.kt:70:5:70:16 | prop | public | +| properties.kt:78:1:79:13 | x | properties.kt:79:5:79:13 | getX | file://:0:0:0:0 | | file://:0:0:0:0 | | public | +| properties.kt:80:1:81:13 | x | properties.kt:81:5:81:13 | getX | file://:0:0:0:0 | | file://:0:0:0:0 | | public | +| properties.kt:84:5:84:29 | data | properties.kt:84:13:84:29 | getData$private | properties.kt:84:13:84:29 | setData$private | properties.kt:84:5:84:29 | data | private | +| properties.kt:92:5:93:18 | data | properties.kt:93:9:93:18 | getData | properties.kt:92:13:93:18 | setData$private | properties.kt:92:5:93:18 | data | private | diff --git a/java/ql/test/kotlin/library-tests/properties/properties.kt b/java/ql/test/kotlin/library-tests/properties/properties.kt index b9f7073f636..bde7994f990 100644 --- a/java/ql/test/kotlin/library-tests/properties/properties.kt +++ b/java/ql/test/kotlin/library-tests/properties/properties.kt @@ -73,3 +73,26 @@ class C { println(c.prop) } } + + +val Int.x : Int + get() = 5 +val Double.x : Int + get() = 5 + +class A { + private var data: Int = 0 + + fun setData(p: Int) { + data = p + } +} + +class B { + private var data: Int = 5 + get() = 42 + + fun setData(p: Int) { + data = p + } +} diff --git a/java/ql/test/kotlin/library-tests/reflection/PrintAst.expected b/java/ql/test/kotlin/library-tests/reflection/PrintAst.expected index 3918bc031b6..13b6b6e63b2 100644 --- a/java/ql/test/kotlin/library-tests/reflection/PrintAst.expected +++ b/java/ql/test/kotlin/library-tests/reflection/PrintAst.expected @@ -11,7 +11,7 @@ reflection.kt: # 47| 0: [MethodAccess] get(...) # 47| -1: [ExtensionReceiverAccess] this # 47| 0: [SubExpr] ... - ... -# 47| 0: [MethodAccess] getLength(...) +# 47| 0: [MethodAccess] length(...) # 47| -1: [ExtensionReceiverAccess] this # 47| 1: [IntegerLiteral] 1 # 49| 2: [Method] fn2 diff --git a/java/ql/test/kotlin/library-tests/special-method-getters/test.expected b/java/ql/test/kotlin/library-tests/special-method-getters/test.expected new file mode 100644 index 00000000000..fdec7b0e605 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/special-method-getters/test.expected @@ -0,0 +1,9 @@ +| test.kt:1:84:1:89 | length(...) | length | +| test.kt:1:97:1:100 | size(...) | size | +| test.kt:1:108:1:111 | size(...) | size | +| test.kt:1:119:1:122 | keySet(...) | keySet | +| test.kt:1:124:1:127 | size(...) | size | +| test.kt:1:135:1:140 | values(...) | values | +| test.kt:1:142:1:145 | size(...) | size | +| test.kt:1:153:1:159 | entrySet(...) | entrySet | +| test.kt:1:161:1:164 | size(...) | size | diff --git a/java/ql/test/kotlin/library-tests/special-method-getters/test.kt b/java/ql/test/kotlin/library-tests/special-method-getters/test.kt new file mode 100644 index 00000000000..d83aee0f131 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/special-method-getters/test.kt @@ -0,0 +1 @@ +fun test(cs: CharSequence, col: Collection, map: Map) = cs.length + col.size + map.size + map.keys.size + map.values.size + map.entries.size diff --git a/java/ql/test/kotlin/library-tests/special-method-getters/test.ql b/java/ql/test/kotlin/library-tests/special-method-getters/test.ql new file mode 100644 index 00000000000..257b3344501 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/special-method-getters/test.ql @@ -0,0 +1,4 @@ +import java + +from MethodAccess ma +select ma, ma.getCallee().toString() diff --git a/java/ql/test/kotlin/library-tests/this/call.expected b/java/ql/test/kotlin/library-tests/this/call.expected index afb0a7ede60..59ca46c4a04 100644 --- a/java/ql/test/kotlin/library-tests/this/call.expected +++ b/java/ql/test/kotlin/library-tests/this/call.expected @@ -1,13 +1,13 @@ -| this.kt:2:1:58:1 | super(...) | Any | -| this.kt:3:5:53:5 | super(...) | Any | +| this.kt:2:1:58:1 | super(...) | Object | +| this.kt:3:5:53:5 | super(...) | Object | | this.kt:9:66:17:13 | ...->... | | -| this.kt:9:66:17:13 | super(...) | Any | +| this.kt:9:66:17:13 | super(...) | Object | | this.kt:12:82:16:17 | ...->... | | -| this.kt:12:82:16:17 | super(...) | Any | +| this.kt:12:82:16:17 | super(...) | Object | | this.kt:19:42:21:13 | ...->... | | -| this.kt:19:42:21:13 | super(...) | Any | +| this.kt:19:42:21:13 | super(...) | Object | | this.kt:23:30:25:13 | ...->... | | -| this.kt:23:30:25:13 | super(...) | Any | +| this.kt:23:30:25:13 | super(...) | Object | | this.kt:36:13:36:25 | topLevelFun(...) | topLevelFun | | this.kt:37:13:37:22 | outerFun(...) | outerFun | | this.kt:38:13:38:22 | innerFun(...) | innerFun | @@ -19,5 +19,5 @@ | this.kt:44:18:44:35 | topLevelInnerFun(...) | topLevelInnerFun | | this.kt:45:18:45:32 | outerInnerFun(...) | outerInnerFun | | this.kt:46:18:46:40 | topLevelOuterInnerFun(...) | topLevelOuterInnerFun | -| this.kt:64:1:65:1 | super(...) | Any | -| this.kt:67:1:68:1 | super(...) | Any | +| this.kt:64:1:65:1 | super(...) | Object | +| this.kt:67:1:68:1 | super(...) | Object | diff --git a/java/ql/test/kotlin/library-tests/types/types.expected b/java/ql/test/kotlin/library-tests/types/types.expected index 7802402a108..5fe5d97d0b2 100644 --- a/java/ql/test/kotlin/library-tests/types/types.expected +++ b/java/ql/test/kotlin/library-tests/types/types.expected @@ -1406,7 +1406,6 @@ | ListIterator | GenericType, Interface, ParameterizedType | | ListIterator<> | Interface, RawType | | ListIterator | Interface, ParameterizedType | -| ListIterator | Interface, ParameterizedType | | LoadOperation | GenericType, Interface, ParameterizedType | | LoadOperation<> | Interface, RawType | | LoadOperation | Interface, ParameterizedType |