diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt index 24d4398feb..80e63ef0f2 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt @@ -598,7 +598,8 @@ class ScopeManager : ScopeProvider { */ @JvmOverloads fun resolveReference(ref: Reference, scope: Scope? = currentScope): ValueDeclaration? { - return resolve(scope) { + val s = extractScope(ref) ?: scope + return resolve(s) { if ( it.name.lastPartsMatch(ref.name) ) { // TODO: This place is likely to make things fail @@ -638,45 +639,51 @@ class ScopeManager : ScopeProvider { call: CallExpression, scope: Scope? = currentScope ): List { - val s = extractScope(call, scope) + val s = extractScope(call) ?: scope return resolve(s) { it.name.lastPartsMatch(call.name) && it.hasSignature(call.signature) } } - fun extractScope(node: Node, scope: Scope? = currentScope): Scope? { - var s = scope - + /** + * This function extracts scope information out of the [Node.name] of the given [Node]. It will + * return null, if the node does not contain any scope information (e.g. is not qualified). + * Otherwise, the scope pointing to the [Name.parent] of the [Node.name] will be returned. + * + * Note: Currently only *fully* qualified names are properly resolved. This function will + * probably return imprecise results for partially qualified names, e.g. if a name `A` inside + * `B` points to `A::B`, rather than to `A`. + */ + fun extractScope(node: Node): Scope? { // First, we need to check, whether we have some kind of scoping. - if (node.name.parent != null) { + if (node.name.isQualified()) { // extract the scope name, it is usually a name space, but could probably be something // else as well in other languages val scopeName = node.name.parent - // TODO: proper scope selection - - // this is a scoped call. we need to explicitly jump to that particular scope + // lookup the appropriate scope by name + // TODO(oxisto): We do not yet support relative scopes, e.g. "A" inside "B" should point + // to "A::B", not "A" val scopes = filterScopes { (it is NameScope && it.name == scopeName) } - s = - if (scopes.isEmpty()) { - Util.errorWithFileLocation( - node, - LOGGER, - "Could not find the scope $scopeName needed to resolve the call ${node.name}. Falling back to the default (current) scope" - ) - s - } else { - scopes[0] - } + return if (scopes.isEmpty()) { + Util.errorWithFileLocation( + node, + LOGGER, + "Could not find the scope $scopeName needed to resolve the call ${node.name}" + ) + null + } else { + scopes[0] + } } - return s + return null } /** * Directly jumps to a given scope. Returns the previous scope. Do not forget to set the scope * back to the old scope after performing the actions inside this scope. * - * Handle with care, here be dragons. Should not be exposed outside of the cpg-core module. + * Handle with care, here be dragons. Should not be exposed outside the cpg-core module. */ @PleaseBeCareful internal fun jumpTo(scope: Scope?): Scope? { 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 7e41af3b62..b6869a6fb2 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 @@ -39,7 +39,6 @@ import de.fraunhofer.aisec.cpg.passes.inference.inferMethod import de.fraunhofer.aisec.cpg.passes.inference.startInference import de.fraunhofer.aisec.cpg.passes.order.DependsOn import de.fraunhofer.aisec.cpg.processing.strategy.Strategy -import java.util.* import org.slf4j.Logger import org.slf4j.LoggerFactory @@ -167,17 +166,26 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { reference: Reference, type: FunctionPointerType ): ValueDeclaration { - val parent = reference.name.parent + var target = scopeManager.resolveReference(reference) - return handleUnknownFunction( - if (parent != null) { - reference.objectType(parent).recordDeclaration - } else { - null - }, - reference.name, - type - ) + // If we didn't find anything, we create a new function or method declaration + if (target == null) { + // Determine the scope where we want to start our inference + val scope = scopeManager.extractScope(reference) + + target = + (scope?.astNode ?: currentTU) + .startInference(ctx) + .createInferredFunctionDeclaration( + reference.name, + null, + false, + type.parameters, + type.returnType + ) + } + + return target } protected fun resolveReference(currentClass: RecordDeclaration?, current: Node?) { @@ -435,41 +443,6 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { } } - /** - * Generates a [MethodDeclaration] if the [declarationHolder] is a [RecordDeclaration] or a - * [FunctionDeclaration] if the [declarationHolder] is a [TranslationUnitDeclaration]. The - * resulting function/method has the signature and return type specified in [fctPtrType] and the - * specified [name]. - */ - protected fun handleUnknownFunction( - declarationHolder: RecordDeclaration?, - name: Name, - fctPtrType: FunctionPointerType - ): FunctionDeclaration { - // Try to find the function or method in the list of existing functions. - val target = - if (declarationHolder != null) { - declarationHolder.methods.firstOrNull { f -> - f.matches(name, fctPtrType.returnType, fctPtrType.parameters) - } - } else { - currentTU.functions.firstOrNull { f -> - f.matches(name, fctPtrType.returnType, fctPtrType.parameters) - } - } - // If we didn't find anything, we create a new function or method declaration - return target - ?: (declarationHolder ?: currentTU) - .startInference(ctx) - .createInferredFunctionDeclaration( - name, - null, - false, - fctPtrType.parameters, - fctPtrType.returnType - ) - } - protected fun resolve(node: Node?, currClass: RecordDeclaration?) { when (node) { is MemberExpression -> resolveMemberExpression(currClass, node) @@ -520,7 +493,7 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { // the TU. It is also a little bit redundant, since ScopeManager.resolveFunction // (which gets called before) already extracts the scope, but this information // gets lost. - val scope = scopeManager.extractScope(call, scopeManager.globalScope) + val scope = scopeManager.extractScope(call) ?: scopeManager.globalScope // We have two possible start points, a namespace declaration or a translation // unit. Nothing else is allowed (fow now)