diff --git a/java/kotlin-extractor2/src/main/kotlin/entities/Expression.kt b/java/kotlin-extractor2/src/main/kotlin/entities/Expression.kt index b9d163d622de..15b8028a9f0a 100644 --- a/java/kotlin-extractor2/src/main/kotlin/entities/Expression.kt +++ b/java/kotlin-extractor2/src/main/kotlin/entities/Expression.kt @@ -344,7 +344,7 @@ private fun KotlinFileExtractor.extractExpression( extractExpression(e.baseExpression!!, callable, parent) } - is KtDotQualifiedExpression -> { + is KtQualifiedExpression -> { // We're propagating the extraction to the child, and then getting the qualifier from the parent of the // child. The selector could be many expression kind, such as KtCallExpression, KtReferenceExpression, // and each of those would need to look for the qualifier diff --git a/java/kotlin-extractor2/src/main/kotlin/entities/MethodCall.kt b/java/kotlin-extractor2/src/main/kotlin/entities/MethodCall.kt index aa98d8915ae0..00339634c3a6 100644 --- a/java/kotlin-extractor2/src/main/kotlin/entities/MethodCall.kt +++ b/java/kotlin-extractor2/src/main/kotlin/entities/MethodCall.kt @@ -6,8 +6,9 @@ import org.jetbrains.kotlin.analysis.api.resolution.symbol import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol import org.jetbrains.kotlin.analysis.api.types.KaType import org.jetbrains.kotlin.psi.KtCallExpression -import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.psi.KtQualifiedExpression +import org.jetbrains.kotlin.psi.KtSafeQualifiedExpression import org.jetbrains.kotlin.utils.mapToIndex context(KaSession) @@ -43,14 +44,15 @@ fun KotlinFileExtractor.extractMethodCall( .sortedBy { p -> p.first } .map { p -> p.second } - // TODO: fix getting the qualifier, we should handle safe qualified expressions too - val qualifier: KtExpression? = (call.parent as? KtDotQualifiedExpression)?.receiverExpression + val callQualifiedParent = call.parent as? KtQualifiedExpression + val qualifier = + if (callQualifiedParent?.selectorExpression == call) callQualifiedParent.receiverExpression else null val extensionReceiver = if (target.isExtension) qualifier else null val dispatchReceiver = if (!target.isExtension) qualifier else null val exprParent = stmtExprParent.expr(call, enclosingCallable) - extractRawMethodAccess( + val callId = extractRawMethodAccess( target, tw.getLocation(call), call.expressionType!!, @@ -62,6 +64,10 @@ fun KotlinFileExtractor.extractMethodCall( extensionReceiver, args ) + + if (call.parent is KtSafeQualifiedExpression) { + tw.writeKtSafeAccess(callId) + } } context(KaSession) @@ -138,7 +144,7 @@ private fun KotlinFileExtractor.extractRawMethodAccess( extractClassTypeArguments: Boolean = false, superQualifierSymbol: IrClassSymbol? = null */ -) { +): Label { /* OLD KE1: val callTarget = getCalleeRealOverrideTarget(syntacticCallTarget) val methodId = getCalleeMethodId(callTarget, drType, extractClassTypeArguments) @@ -229,6 +235,8 @@ private fun KotlinFileExtractor.extractRawMethodAccess( } val idxOffset = if (extensionReceiver != null) 1 else 0 extractCallValueArguments(id, valueArguments, enclosingStmt, enclosingCallable, idxOffset) + + return id } context(KaSession) diff --git a/java/ql/lib/config/semmlecode.dbscheme b/java/ql/lib/config/semmlecode.dbscheme index 186df68ece58..60a2bd1be314 100644 --- a/java/ql/lib/config/semmlecode.dbscheme +++ b/java/ql/lib/config/semmlecode.dbscheme @@ -1528,3 +1528,10 @@ ktFunctionOriginalNames( ktDataClasses( unique int id: @classorinterface ref ) + +// not all variables are qualifiable, but adding fieldaccess would be more involved: +@qualifiableaccess = @varaccess | @methodaccess; + +ktSafeAccess( + unique int id: @qualifiableaccess ref +) diff --git a/java/ql/lib/semmle/code/java/Expr.qll b/java/ql/lib/semmle/code/java/Expr.qll index 1862319e30bb..d4c57c1caa40 100644 --- a/java/ql/lib/semmle/code/java/Expr.qll +++ b/java/ql/lib/semmle/code/java/Expr.qll @@ -2360,6 +2360,9 @@ private module Qualifier { result = this.(FieldAccess).getQualifier() or result = this.(MethodCall).getQualifier() } + + /** Holds if this member access is a `?.` safe qualified expression in Kotlin. */ + predicate safeAccess() { ktSafeAccess(this) } } /**