diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ResolvableExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ResolvableExpression.kt index 23d9a3af70..0d35e89491 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ResolvableExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ResolvableExpression.kt @@ -44,12 +44,13 @@ import org.neo4j.ogm.annotation.Relationship * example would be a [CallExpression], which is resolved to a [FunctionDeclaration]. but if * languages have the trait [HasOperatorOverloading], also operators, such as an [UnaryOperator] */ -abstract class ResolvableExpression : - Expression(), HasBase, HasType.TypeObserver { +abstract class ResolvableExpression : Expression(), HasType.TypeObserver { abstract val signature: List abstract val arguments: List? + abstract val resolutionBase: Expression? + /** * Connection to its [T]. This will be populated by the [SymbolResolver]. This will have an * effect on the [type] diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt index 6f9ad3f8ed..6213c53948 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/BinaryOperator.kt @@ -83,6 +83,9 @@ open class BinaryOperator : override val arguments: List? get() = listOf(rhs) + override val resolutionBase: Expression? + get() = lhs + private fun connectNewLhs(lhs: Expression) { lhs.registerTypeObserver(this) if (lhs is Reference && "=" == operatorCode) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt index 4f2e55ae5c..8e269a7815 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt @@ -255,9 +255,6 @@ open class CallExpression : ResolvableExpression(), Argumen // here override fun hashCode() = Objects.hash(super.hashCode(), arguments) - override val base: Expression? - get() = null - - override val operatorCode: String? + override val resolutionBase: Expression? get() = null } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/MemberCallExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/MemberCallExpression.kt index 588be27338..e4f61ada19 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/MemberCallExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/MemberCallExpression.kt @@ -74,4 +74,7 @@ class MemberCallExpression : CallExpression(), HasBase, HasOperatorCode { } override fun hashCode() = Objects.hash(super.hashCode(), base) + + override val resolutionBase: Expression? + get() = base } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt index 3342ca3f2a..05c789bbc2 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/UnaryOperator.kt @@ -71,7 +71,7 @@ class UnaryOperator : override val arguments: List? get() = null - override val base: Expression? + override val resolutionBase: Expression? get() = this private fun changeExpressionAccess() { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt index d3ac36fbd6..3b22ae8a5b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolver.kt @@ -437,23 +437,34 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { src: HasType, ) {} - override val base: Expression? + override val resolutionBase: Expression? get() = reference.base - override val operatorCode: String? - get() = "->" + /*override val operatorCode: String? + get() = "->"*/ } var op = resolveCalleeByName("operator->", emptySignature) .filterIsInstance() .singleOrNull() - // For now, we just re-direct the containing class, but we should actually model an - // implicit call to our operator in between + // We unfortunately, have no direct access to the AST parent, but this is a very good + // heuristic to get it + var parent = reference.base.prevEOG.singleOrNull() + if (op != null) { type = op.returnTypes.singleOrNull()?.root ?: unknownType() - // rather hacky - reference.name = type.root.name.fqn(reference.name.localName) + + // We need to insert a new call expression to our operator in between + val ref = + newMemberExpression(op.name, reference.base, operatorCode = ".") + .implicit(op.name.localName, location = reference.location) + ref.refersTo = op + var call = newMemberCallExpression(ref).codeAndLocationFrom(ref) + call.invokes = listOf(op) + + // Make the call our new base + reference.base = call } } @@ -937,13 +948,7 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { ): Pair, Type?> { val possibleTypes = mutableSetOf() var bestGuess: Type? = null - if (call is MemberCallExpression) { - call.base?.let { base -> - bestGuess = base.type - possibleTypes.add(base.type) - possibleTypes.addAll(base.assignedTypes) - } - } else if (call is CallExpression) { + if (call is CallExpression && call !is MemberCallExpression) { // This could be a C++ member call with an implicit this (which we do not create), so // let's add the current class to the possible list scopeManager.currentRecord?.toType()?.let { @@ -951,7 +956,7 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { possibleTypes.add(it) } } else { - call.base?.let { base -> + call.resolutionBase?.let { base -> bestGuess = base.type possibleTypes.add(base.type) possibleTypes.addAll(base.assignedTypes) diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXDeclarationTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXDeclarationTest.kt index 5b04d9c6a1..8b68fc2ded 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXDeclarationTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXDeclarationTest.kt @@ -30,6 +30,7 @@ import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block +import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberCallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.UnaryOperator import de.fraunhofer.aisec.cpg.graph.types.FunctionPointerType import de.fraunhofer.aisec.cpg.test.* @@ -259,14 +260,28 @@ class CXXDeclarationTest { var proxy = result.records["Proxy"] assertNotNull(proxy) + var op = proxy.operators["operator->"] + assertNotNull(op) + var data = result.records["Data"] assertNotNull(data) var size = data.fields["size"] assertNotNull(size) + val p = result.refs["p"] + assertNotNull(p) + assertEquals(proxy.toType(), p.type) + var sizeRef = result.memberExpressions["size"] assertNotNull(sizeRef) assertRefersTo(sizeRef, size) + + // we should now have an implicit call to our operator in-between "p" and "size" + val opCall = sizeRef.base + assertNotNull(opCall) + assertIs(opCall) + assertEquals(p, opCall.base) + assertInvokes(opCall, op) } }