Skip to content

Commit

Permalink
operator-> overload works
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Jun 30, 2024
1 parent 8398eac commit eecbd7b
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<T : FunctionDeclaration> :
Expression(), HasBase, HasType.TypeObserver {
abstract class ResolvableExpression<T : FunctionDeclaration> : Expression(), HasType.TypeObserver {
abstract val signature: List<Type>

abstract val arguments: List<Expression>?

abstract val resolutionBase: Expression?

/**
* Connection to its [T]. This will be populated by the [SymbolResolver]. This will have an
* effect on the [type]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ open class BinaryOperator :
override val arguments: List<Expression>?
get() = listOf(rhs)

override val resolutionBase: Expression?
get() = lhs

private fun connectNewLhs(lhs: Expression) {
lhs.registerTypeObserver(this)
if (lhs is Reference && "=" == operatorCode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,6 @@ open class CallExpression : ResolvableExpression<FunctionDeclaration>(), 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
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,7 @@ class MemberCallExpression : CallExpression(), HasBase, HasOperatorCode {
}

override fun hashCode() = Objects.hash(super.hashCode(), base)

override val resolutionBase: Expression?
get() = base
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class UnaryOperator :
override val arguments: List<Expression>?
get() = null

override val base: Expression?
override val resolutionBase: Expression?
get() = this

private fun changeExpressionAccess() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<OperatorDeclaration>()
.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
}
}

Expand Down Expand Up @@ -937,21 +948,15 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) {
): Pair<Set<Type>, Type?> {
val possibleTypes = mutableSetOf<Type>()
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 {
bestGuess = it
possibleTypes.add(it)
}
} else {
call.base?.let { base ->
call.resolutionBase?.let { base ->
bestGuess = base.type
possibleTypes.add(base.type)
possibleTypes.addAll(base.assignedTypes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*
Expand Down Expand Up @@ -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<MemberCallExpression>(opCall)
assertEquals(p, opCall.base)
assertInvokes(opCall, op)
}
}

0 comments on commit eecbd7b

Please sign in to comment.