Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moving hasSignature to language #1360

Merged
merged 1 commit into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading