Skip to content

Commit

Permalink
Moving hasSignature to language (#1360)
Browse files Browse the repository at this point in the history
This allows us to overload this specifically for specific languages.
  • Loading branch information
oxisto authored Nov 22, 2023
1 parent 613a684 commit 0ae68ad
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,14 @@ import de.fraunhofer.aisec.cpg.TranslationContext
import de.fraunhofer.aisec.cpg.ancestors
import de.fraunhofer.aisec.cpg.graph.Name
import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression
import de.fraunhofer.aisec.cpg.graph.types.*
import de.fraunhofer.aisec.cpg.graph.types.Type
import de.fraunhofer.aisec.cpg.graph.unknownType
import de.fraunhofer.aisec.cpg.isDerivedFrom
import java.io.File
import kotlin.reflect.KClass
import kotlin.reflect.full.primaryConstructor
Expand Down Expand Up @@ -185,8 +189,7 @@ abstract class Language<T : LanguageFrontend<*, *>> : Node() {
* enough.
*/
open fun shouldPropagateType(hasType: HasType, srcType: Type): Boolean {
val node = hasType as Node
var nodeType = hasType.type
val nodeType = hasType.type

// We only want to add certain types, in case we have a numeric type
if (nodeType is NumericType) {
Expand Down Expand Up @@ -221,6 +224,49 @@ abstract class Language<T : LanguageFrontend<*, *>> : Node() {
// Check, if super type (or its root) is in the list
return superType.root in superTypes
}

/**
* This function checks, if the two supplied signatures are equal. The usual use-case is
* comparing the signature arguments of a [CallExpression] (in [signature]) against the
* signature of a [FunctionDeclaration] (in [target]). Optionally, a list of [expressions]
* (e.g., the actual call arguments) can be supplied as a hint, these will be forwarded to other
* comparing functions, such as [isDerivedFrom].
*/
open fun hasSignature(
target: FunctionDeclaration,
signature: List<Type>,
expressions: List<Expression>? = null,
): Boolean {
val targetSignature = target.parameters
return if (
targetSignature.all { !it.isVariadic } && signature.size < targetSignature.size
) {
// TODO: So we don't consider arguments with default values (among others) but then, the
// SymbolResolver (or CXXCallResolverHelper) has a bunch of functions to consider it.
false
} else {
// signature is a collection of positional arguments, so the order must be preserved
for (i in targetSignature.indices) {
val declared = targetSignature[i]
if (declared.isVariadic) {
// Everything that follows is collected by this param, so the signature is
// fulfilled no matter what comes now
// FIXME: in Java, we could have overloading with different vararg types, in
// C++ we can't, as vararg types are not defined here anyways)
return true
}
val provided = signature[i]
val expression = expressions?.get(i)
if (!provided.isDerivedFrom(declared.type, expression, target)) {
return false
}
}

// Longer target signatures are only allowed with varargs. If we reach this point, no
// vararg has been encountered
signature.size == targetSignature.size
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
*/
package de.fraunhofer.aisec.cpg.graph.declarations

import de.fraunhofer.aisec.cpg.frontends.Language
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.edge.Properties
import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge
Expand All @@ -35,7 +36,6 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression
import de.fraunhofer.aisec.cpg.graph.types.Type
import de.fraunhofer.aisec.cpg.isDerivedFrom
import java.util.*
import org.apache.commons.lang3.builder.ToStringBuilder
import org.neo4j.ogm.annotation.Relationship
Expand Down Expand Up @@ -106,47 +106,26 @@ open class FunctionDeclaration : ValueDeclaration(), DeclarationHolder, Resoluti
}
})

fun hasSameSignature(targetFunctionDeclaration: FunctionDeclaration): Boolean {
return targetFunctionDeclaration.name.localName == name.localName &&
targetFunctionDeclaration.signatureTypes == signatureTypes
}

/**
* This function checks, if the supplied [CallExpression] has the same signature as the current
* [FunctionDeclaration].
*/
fun hasSignature(call: CallExpression): Boolean {
return hasSignature(call.signature, call.arguments)
}

// TODO: Documentation required. It's not completely clear what this method is supposed to do.
/**
* This function checks, if the two supplied signatures are equal. The usual use-case is
* comparing the signature arguments of a [CallExpression] (in [targetSignature]) against the
* current [FunctionDeclaration]. Optionally, a list of [targetExpressions] (e.g., the actual
* call arguments) can be supplied as a hint, these will be forwarded to other comparing
* functions, such as [Language.isDerivedFrom].
*/
fun hasSignature(
targetSignature: List<Type>,
targetExpressions: List<Expression>? = null
): Boolean {
val signature = parameters.sortedBy { it.argumentIndex }
// TODO: Why do we have to sort it here while we don't sort the list in signatureTypes?
return if (signature.all { !it.isVariadic } && targetSignature.size < signature.size) {
// TODO: So we don't consider arguments with default values (among others) but then, the
// SymbolResolver (or CXXCallResolverHelper) has a bunch of functions to consider it.
false
} else {
// signature is a collection of positional arguments, so the order must be preserved
for (i in signature.indices) {
val declared = signature[i]
if (declared.isVariadic) {
// Everything that follows is collected by this param, so the signature is
// fulfilled no matter what comes now
// FIXME: in Java, we could have overloading with different vararg types, in
// C++ we can't, as vararg types are not defined here anyways)
return true
}
val provided = targetSignature[i]
val expression = targetExpressions?.get(i)
if (!provided.isDerivedFrom(declared.type, expression, this)) {
return false
}
}
// Longer target signatures are only allowed with varargs. If we reach this point, no
// vararg has been encountered
targetSignature.size == signature.size
}
return this.language?.hasSignature(this, targetSignature, targetExpressions) ?: false
}

fun isOverrideCandidate(other: FunctionDeclaration): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,11 @@ class DeclarationHandler(lang: CXXLanguageFrontend) :
frontend.currentTU
?.declarations
?.filterIsInstance(FunctionDeclaration::class.java)
?.filter { !it.isDefinition && it.hasSameSignature(declaration) }
?.filter {
!it.isDefinition &&
it.name.lastPartsMatch(declaration.name) &&
it.hasSignature(declaration.signatureTypes)
}
?: listOf()
for (candidate in declarationCandidates) {
candidate.definition = declaration
Expand Down

0 comments on commit 0ae68ad

Please sign in to comment.