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 266ac9604f6..ba3781213b3 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 @@ -813,8 +813,10 @@ class ScopeManager : ScopeProvider { * * @return the declaration, or null if it does not exist */ - fun getRecordForName(name: Name): RecordDeclaration? { - return lookupSymbolByName(name).filterIsInstance().singleOrNull() + fun getRecordForName(name: Name, language: Language<*>?): RecordDeclaration? { + return lookupSymbolByName(name, language) + .filterIsInstance() + .singleOrNull() } fun typedefFor(alias: Name, scope: Scope? = currentScope): Type? { @@ -874,6 +876,18 @@ class ScopeManager : ScopeProvider { override val scope: Scope? get() = currentScope + /** + * A convenience function to call [lookupSymbolByName] with the properties of [node]. The + * arguments [scope] and [predicate] are forwarded. + */ + fun lookupSymbolByNameOfNode( + node: Node, + scope: Scope? = node.scope, + predicate: ((Declaration) -> Boolean)? = null, + ): List { + return lookupSymbolByName(node.name, node.language, node.location, scope, predicate) + } + /** * This function tries to convert a [Node.name] into a [Symbol] and then performs a lookup of * this symbol. This can either be an "unqualified lookup" if [name] is not qualified or a @@ -885,12 +899,13 @@ class ScopeManager : ScopeProvider { * function overloading. But it will only return list of declarations within the same scope; the * list cannot be spread across different scopes. * - * This means that as soon one or more declarations for the symbol are found in a "local" scope, - * these shadow all other occurrences of the same / symbol in a "higher" scope and only the ones - * from the lower ones will be returned. + * This means that as soon one or more declarations (of the matching [language]) for the symbol + * are found in a "local" scope, these shadow all other occurrences of the same / symbol in a + * "higher" scope and only the ones from the lower ones will be returned. */ fun lookupSymbolByName( name: Name, + language: Language<*>?, location: PhysicalLocation? = null, startScope: Scope? = currentScope, predicate: ((Declaration) -> Boolean)? = null, @@ -900,14 +915,25 @@ class ScopeManager : ScopeProvider { // We need to differentiate between a qualified and unqualified lookup. We have a qualified // lookup, if the scope is not null. In this case we need to stay within the specified scope val list = - if (scope != null) { - scope.lookupSymbol(n.localName, thisScopeOnly = true, predicate = predicate) - } else { + when { + scope != null -> { + scope + .lookupSymbol( + n.localName, + languageOnly = language, + thisScopeOnly = true, + predicate = predicate + ) + .toMutableList() + } + else -> { // Otherwise, we can look up the symbol alone (without any FQN) starting from // the startScope - startScope?.lookupSymbol(n.localName, predicate = predicate) + startScope + ?.lookupSymbol(n.localName, languageOnly = language, predicate = predicate) + ?.toMutableList() ?: mutableListOf() } - ?.toMutableList() ?: return listOf() + } // If we have both the definition and the declaration of a function declaration in our list, // we chose only the definition @@ -932,9 +958,15 @@ class ScopeManager : ScopeProvider { * It is important to know that the lookup needs to be unique, so if multiple declarations match * this symbol, a warning is triggered and null is returned. */ - fun lookupUniqueTypeSymbolByName(name: Name, startScope: Scope?): DeclaresType? { + fun lookupUniqueTypeSymbolByName( + name: Name, + language: Language<*>?, + startScope: Scope? + ): DeclaresType? { var symbols = - lookupSymbolByName(name = name, startScope = startScope) { it is DeclaresType } + lookupSymbolByName(name = name, language = language, startScope = startScope) { + it is DeclaresType + } .filterIsInstance() // We need to have a single match, otherwise we have an ambiguous type, and we cannot diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt index 3069f741952..8dfbd68a64c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TypeManager.kt @@ -380,5 +380,5 @@ fun Reference.nameIsType(): Type? { } // Lastly, check if the reference contains a symbol that points to type (declaration) - return scopeManager.lookupUniqueTypeSymbolByName(name, scope)?.declaredType + return scopeManager.lookupUniqueTypeSymbolByName(name, language, scope)?.declaredType } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/Scope.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/Scope.kt index e128ecd1125..30ee7dcd164 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/Scope.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/Scope.kt @@ -26,6 +26,7 @@ package de.fraunhofer.aisec.cpg.graph.scopes import com.fasterxml.jackson.annotation.JsonBackReference +import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.Node.Companion.TO_STRING_STYLE @@ -131,6 +132,7 @@ abstract class Scope( */ fun lookupSymbol( symbol: Symbol, + languageOnly: Language<*>? = null, thisScopeOnly: Boolean = false, replaceImports: Boolean = true, predicate: ((Declaration) -> Boolean)? = null @@ -138,12 +140,7 @@ abstract class Scope( // First, try to look for the symbol in the current scope (unless we have a predefined // search scope). In the latter case we also need to restrict the lookup to the search scope var modifiedScoped = this.predefinedLookupScopes[symbol]?.targetScope - var scope: Scope? = - if (modifiedScoped != null) { - modifiedScoped - } else { - this - } + var scope: Scope? = modifiedScoped ?: this var list: MutableList? = null @@ -163,9 +160,14 @@ abstract class Scope( list.replaceImports(symbol) } + // Filter according to the language + if (languageOnly != null) { + list.removeIf { it.language != languageOnly } + } + // Filter the list according to the predicate, if we have any if (predicate != null) { - list = list.filter(predicate).toMutableList() + list.removeIf { !predicate.invoke(it) } } // If we have a hit, we can break the loop diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolver.kt index 4c61ebc1827..50530e3ecbb 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolver.kt @@ -222,7 +222,7 @@ class ImportResolver(ctx: TranslationContext) : ComponentPass(ctx) { for (part in parts) { var namespaces = scopeManager - .lookupSymbolByName(part, import.location, import.scope) + .lookupSymbolByName(part, import.language, import.location, import.scope) .filterIsInstance() // We are only interested in "leaf" namespace declarations, meaning that they do not @@ -271,7 +271,13 @@ class ImportResolver(ctx: TranslationContext) : ComponentPass(ctx) { // Let's do some importing. We need to import either a wildcard if (import.wildcardImport) { - val list = scopeManager.lookupSymbolByName(import.import, import.location, scope) + val list = + scopeManager.lookupSymbolByName( + import.import, + import.language, + import.location, + scope + ) val symbol = list.singleOrNull() if (symbol != null) { // In this case, the symbol must point to a name scope @@ -284,7 +290,7 @@ class ImportResolver(ctx: TranslationContext) : ComponentPass(ctx) { // or a symbol directly val list = scopeManager - .lookupSymbolByName(import.import, import.location, scope) + .lookupSymbolByName(import.import, import.language, import.location, scope) .toMutableList() import.importedSymbols = mutableMapOf(import.symbol to list) } 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 42722b74f7a..fdc7f0edca3 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 @@ -204,7 +204,7 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { // Find a list of candidate symbols. Currently, this is only used the in the "next-gen" call // resolution, but in future this will also be used in resolving regular references. - ref.candidates = scopeManager.lookupSymbolByName(ref.name, ref.location).toSet() + ref.candidates = scopeManager.lookupSymbolByNameOfNode(ref).toSet() // Preparation for a future without legacy call resolving. Taking the first candidate is not // ideal since we are running into an issue with function pointers here (see workaround @@ -590,7 +590,9 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { var candidates = mutableSetOf() val records = possibleContainingTypes.mapNotNull { it.root.recordDeclaration }.toSet() for (record in records) { - candidates.addAll(ctx.scopeManager.lookupSymbolByName(record.name.fqn(symbol))) + candidates.addAll( + ctx.scopeManager.lookupSymbolByName(record.name.fqn(symbol), record.language) + ) } // Find invokes by supertypes @@ -700,7 +702,11 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { listOf() } else { val firstLevelCandidates = - possibleTypes.map { scopeManager.lookupSymbolByName(it.name.fqn(name)) }.flatten() + possibleTypes + .map { record -> + scopeManager.lookupSymbolByName(record.name.fqn(name), record.language) + } + .flatten() // C++ does not allow overloading at different hierarchy levels. If we find a // FunctionDeclaration with the same name as the function in the CallExpression we have diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TypeResolver.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TypeResolver.kt index e4345e98d2d..ce33e5205e0 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TypeResolver.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TypeResolver.kt @@ -104,7 +104,8 @@ open class TypeResolver(ctx: TranslationContext) : ComponentPass(ctx) { // filter for nodes that implement DeclaresType, because otherwise we will get a lot of // constructor declarations and such with the same name. It seems this is ok since most // languages will prefer structs/classes over functions when resolving types. - var declares = scopeManager.lookupUniqueTypeSymbolByName(type.name, type.scope) + var declares = + scopeManager.lookupUniqueTypeSymbolByName(type.name, type.language, type.scope) // If we did not find any declaration, we can try to infer a record declaration for it if (declares == null) { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt index 3fb62a3bfe1..c612ed53b8b 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/scopes/ScopeManagerTest.kt @@ -100,7 +100,7 @@ internal class ScopeManagerTest : BaseTest() { // resolve symbol val call = frontend.newCallExpression(frontend.newReference("A::func1"), "A::func1", false) - val func = final.lookupSymbolByName(call.callee!!.name).firstOrNull() + val func = final.lookupSymbolByNameOfNode(call.callee).firstOrNull() assertEquals(func1, func) } diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/DeclarationHandler.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/DeclarationHandler.kt index 18a6e92bf64..deb8f280f44 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/DeclarationHandler.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/DeclarationHandler.kt @@ -76,7 +76,7 @@ class DeclarationHandler(frontend: GoLanguageFrontend) : // TODO: this will only find methods within the current translation unit. // this is a limitation that we have for C++ as well - val record = frontend.scopeManager.getRecordForName(recordName) + val record = frontend.scopeManager.getRecordForName(recordName, language) // now this gets a little bit hacky, we will add it to the record declaration // this is strictly speaking not 100 % true, since the method property edge is diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt index 77e3cc32ac4..e4fe1773c0c 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/GoExtraPass.kt @@ -389,7 +389,7 @@ class GoExtraPass(ctx: TranslationContext) : ComponentPass(ctx) { // Try to see if we already know about this namespace somehow val namespace = - scopeManager.lookupSymbolByName(import.name, null).filter { + scopeManager.lookupSymbolByNameOfNode(import).filter { it is NamespaceDeclaration && it.path == import.importURL } diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/DeclarationHandler.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/DeclarationHandler.kt index df86cdf4c59..f1288ab1832 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/DeclarationHandler.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/DeclarationHandler.kt @@ -194,7 +194,7 @@ class DeclarationHandler(lang: LLVMIRLanguageFrontend) : } // try to see, if the struct already exists as a record declaration - var record = frontend.scopeManager.getRecordForName(Name(name)) + var record = frontend.scopeManager.getRecordForName(Name(name), language) // if yes, return it if (record != null) { diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/ExpressionHandler.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/ExpressionHandler.kt index 6071ee35b5a..c884e666d96 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/ExpressionHandler.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/ExpressionHandler.kt @@ -441,7 +441,7 @@ class ExpressionHandler(lang: LLVMIRLanguageFrontend) : var record = (baseType as? ObjectType)?.recordDeclaration if (record == null) { - record = frontend.scopeManager.getRecordForName(baseType.name) + record = frontend.scopeManager.getRecordForName(baseType.name, language) if (record != null) { (baseType as? ObjectType)?.recordDeclaration = record } diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt index 35e3163ac2b..9031e45c49e 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontend.kt @@ -245,7 +245,7 @@ class LLVMIRLanguageFrontend(language: Language, ctx: Tr /** Determines if a struct with [name] exists in the scope. */ fun isKnownStructTypeName(name: String): Boolean { - return this.scopeManager.getRecordForName(Name(name)) != null + return this.scopeManager.getRecordForName(Name(name), language) != null } fun getOperandValueAtIndex(instr: LLVMValueRef, idx: Int): Expression { diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/PythonAddDeclarationsPass.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/PythonAddDeclarationsPass.kt index 0b939057548..1b19b1ab3d0 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/PythonAddDeclarationsPass.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/PythonAddDeclarationsPass.kt @@ -26,6 +26,8 @@ package de.fraunhofer.aisec.cpg.passes import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.frontends.Language +import de.fraunhofer.aisec.cpg.frontends.python.PythonLanguage import de.fraunhofer.aisec.cpg.frontends.python.PythonLanguageFrontend import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.Declaration @@ -44,7 +46,7 @@ import de.fraunhofer.aisec.cpg.passes.configuration.RequiredFrontend @ExecuteBefore(ImportResolver::class) @ExecuteBefore(SymbolResolver::class) @RequiredFrontend(PythonLanguageFrontend::class) -class PythonAddDeclarationsPass(ctx: TranslationContext) : ComponentPass(ctx) { +class PythonAddDeclarationsPass(ctx: TranslationContext) : ComponentPass(ctx), LanguageProvider { override fun cleanup() { // nothing to do } @@ -96,11 +98,9 @@ class PythonAddDeclarationsPass(ctx: TranslationContext) : ComponentPass(ctx) { // - to look for a local symbol, unless // - a global keyword is present for this symbol and scope if (targetScope != null) { - scopeManager.lookupSymbolByName(ref.name, ref.location, targetScope) + scopeManager.lookupSymbolByNameOfNode(ref, targetScope) } else { - scopeManager.lookupSymbolByName(ref.name, ref.location) { - it.scope == scopeManager.currentScope - } + scopeManager.lookupSymbolByNameOfNode(ref) { it.scope == scopeManager.currentScope } } // Nothing to create @@ -203,4 +203,7 @@ class PythonAddDeclarationsPass(ctx: TranslationContext) : ComponentPass(ctx) { } } } + + override val language: Language<*>? + get() = ctx.config.languages.firstOrNull { it is PythonLanguage } }