diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt index 75aadb2644..5933753448 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt @@ -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 @@ -185,8 +189,7 @@ abstract class Language> : 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) { @@ -221,6 +224,49 @@ abstract class Language> : 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, + expressions: List? = 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 + } + } } /** diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt index 2089b14720..b27d2dd259 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/FunctionDeclaration.kt @@ -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 @@ -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 @@ -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, targetExpressions: List? = 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 { diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt index 95d4a92b9f..bdb686ca4b 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/DeclarationHandler.kt @@ -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