From d6371a67596596e21d1a42e2af14d9c1b6aed886 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Mon, 2 Dec 2024 11:38:26 +0100 Subject: [PATCH 1/3] Restrict symbol resolving to language (#1857) * Restrict symbol resolving to language Currently, the symbol resolver runs wild and considers all nodes of all languages. When we have more instances where we have multiple languages in the same component, this craates a problem. This PR now only considers the same language as the ref we are looking for. * Correcty setting the python language * Moved language to lookupSymbolByName function * ++ * ++ * ++ * Update cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt * Fixed compile error * Fixed compile error * Addressed code review --- .../de/fraunhofer/aisec/cpg/ScopeManager.kt | 56 +++++++++++++++---- .../de/fraunhofer/aisec/cpg/TypeManager.kt | 2 +- .../aisec/cpg/graph/scopes/Scope.kt | 16 +++--- .../aisec/cpg/passes/ImportResolver.kt | 12 +++- .../aisec/cpg/passes/SymbolResolver.kt | 12 +++- .../aisec/cpg/passes/TypeResolver.kt | 3 +- .../cpg/passes/scopes/ScopeManagerTest.kt | 2 +- .../frontends/golang/DeclarationHandler.kt | 2 +- .../aisec/cpg/passes/GoExtraPass.kt | 2 +- .../cpg/frontends/llvm/DeclarationHandler.kt | 2 +- .../cpg/frontends/llvm/ExpressionHandler.kt | 2 +- .../frontends/llvm/LLVMIRLanguageFrontend.kt | 2 +- .../cpg/passes/PythonAddDeclarationsPass.kt | 13 +++-- 13 files changed, 88 insertions(+), 38 deletions(-) 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 266ac9604f..ba3781213b 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 3069f74195..8dfbd68a64 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 e128ecd112..30ee7dcd16 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 4c61ebc182..50530e3ecb 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 42722b74f7..fdc7f0edca 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 e4345e98d2..ce33e5205e 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 3fb62a3bfe..c612ed53b8 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 18a6e92bf6..deb8f280f4 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 77e3cc32ac..e4fe1773c0 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 df86cdf4c5..f1288ab183 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 6071ee35b5..c884e666d9 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 35e3163ac2..9031e45c49 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 0b93905754..1b19b1ab3d 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 } } From d728ab03d04ff22b40cabeec8ade59acd8ec216c Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Mon, 2 Dec 2024 13:30:58 +0100 Subject: [PATCH 2/3] Create one `Type` object per type reference (#1773) --- .github/workflows/build.yml | 10 +- .../de/fraunhofer/aisec/cpg/TypeManager.kt | 25 +--- .../fraunhofer/aisec/cpg/graph/NodeBuilder.kt | 4 +- .../fraunhofer/aisec/cpg/graph/TypeBuilder.kt | 38 ++---- .../passes/inference/DFGFunctionSummaries.kt | 121 ++++++++---------- .../enhancements/DFGFunctionSummariesTest.kt | 76 +++++------ .../de/fraunhofer/aisec/cpg/test/TestUtils.kt | 6 +- .../frontends/cxx/CXXLanguageFrontendTest.kt | 5 +- .../JavaExternalTypeHierarchyResolver.kt | 35 ++--- .../aisec/cpg/graph/types/TypeTests.kt | 32 +++-- 10 files changed, 153 insertions(+), 199 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 70030ee32a..785475de5c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -68,11 +68,19 @@ jobs: id: build env: VERSION: ${{ env.version }} + - name: Prepare report.xml for Codecov + run: | + # this is needed because codecov incorrectly reports lines that have no coverage information (good or bad) as a miss + # See https://github.com/codecov/feedback/issues/564 and https://github.com/Kotlin/kotlinx-kover/issues/699. + # Actually these lines should just not exist in the coverage XML file, since they are only structural elements, such + # as brackets. + cat cpg-all/build/reports/kover/report.xml | grep -v 'mi="0" ci="0" mb="0" cb="0"' > cpg-all/build/reports/kover/report-codecov.xml + rm cpg-all/build/reports/kover/report.xml - name: Upload Code Coverage uses: codecov/codecov-action@v5 with: fail_ci_if_error: true - files: ./cpg-all/build/reports/kover/report.xml + files: ./cpg-all/build/reports/kover/report-codecov.xml token: ${{ secrets.CODECOV_TOKEN }} verbose: true - name: Prepare test and coverage reports 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 8dfbd68a64..61b0d0f0be 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 @@ -57,8 +57,8 @@ class TypeManager { MutableMap> = ConcurrentHashMap() - val firstOrderTypes: MutableSet = ConcurrentHashMap.newKeySet() - val secondOrderTypes: MutableSet = ConcurrentHashMap.newKeySet() + val firstOrderTypes = mutableListOf() + val secondOrderTypes = mutableListOf() /** * @param recordDeclaration that is instantiated by a template containing parameterizedtypes @@ -200,26 +200,9 @@ class TypeManager { } if (t.isFirstOrderType) { - // Make sure we only ever return one unique object per type - if (!firstOrderTypes.add(t)) { - return firstOrderTypes.first { it == t && it is T } as T - } else { - log.trace( - "Registering unique first order type {}{}", - t.name, - if ((t as? ObjectType)?.generics?.isNotEmpty() == true) { - " with generics ${t.generics.joinToString(",", "[", "]") { it.name.toString() }}" - } else { - "" - } - ) - } + synchronized(firstOrderTypes) { firstOrderTypes.add(t) } } else if (t is SecondOrderType) { - if (!secondOrderTypes.add(t)) { - return secondOrderTypes.first { it == t && it is T } as T - } else { - log.trace("Registering unique second order type {}", t.name) - } + synchronized(secondOrderTypes) { secondOrderTypes.add(t) } } return t diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt index ac060fa20a..5a2e01a263 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/NodeBuilder.kt @@ -299,8 +299,8 @@ fun T.codeAndLocationFromOtherRawNode(rawNode: AstNode?): T * are between the child nodes. * * @param parentNode Used to extract the code for this node. - * @param newLineType The char(s) used to describe a new line, usually either "\n" or "\r\n". This - * is needed because the location block spanning the children usually comprises more than one + * @param lineBreakSequence The char(s) used to describe a new line, usually either "\n" or "\r\n". + * This is needed because the location block spanning the children usually comprises more than one * line. */ context(CodeAndLocationProvider) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/TypeBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/TypeBuilder.kt index a0a0fde85a..10f318283f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/TypeBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/TypeBuilder.kt @@ -113,34 +113,16 @@ fun LanguageProvider.objectType( "Could not create type: translation context not available" ) - val scope = c.scopeManager.currentScope - - synchronized(c.typeManager.firstOrderTypes) { - // We can try to look up the type by its name and return it, if it already exists. - var type = - c.typeManager.firstOrderTypes.firstOrNull { - it is ObjectType && - it.name == name && - it.scope == scope && - it.generics == generics && - it.language == language - } - if (type != null) { - return type - } - - // Otherwise, we either need to create the type because of the generics or because we do not - // know the type yet. - type = ObjectType(name, generics, false, language) - // Apply our usual metadata, such as scope, code, location, if we have any. Make sure only - // to refer by the local name because we will treat types as sort of references when - // creating them and resolve them later. - type.applyMetadata(this, name, rawNode = rawNode, localNameOnly = true) - - // Piping it through register type will ensure that in any case we return the one unique - // type object (per scope) for it. - return c.typeManager.registerType(type) - } + // Otherwise, we either need to create the type because of the generics or because we do not + // know the type yet. + var type = ObjectType(name, generics, false, language) + // Apply our usual metadata, such as scope, code, location, if we have any. Make sure only + // to refer by the local name because we will treat types as sort of references when + // creating them and resolve them later. + type.applyMetadata(this, name, rawNode = rawNode, localNameOnly = true) + + // Piping it through register type will ensure that we know the type and can resolve it later + return c.typeManager.registerType(type) } /** diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/DFGFunctionSummaries.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/DFGFunctionSummaries.kt index 61e58c87d8..873aa1d873 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/DFGFunctionSummaries.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/DFGFunctionSummaries.kt @@ -31,13 +31,10 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import com.fasterxml.jackson.module.kotlin.readValue import com.fasterxml.jackson.module.kotlin.registerKotlinModule import de.fraunhofer.aisec.cpg.IncompatibleSignature -import de.fraunhofer.aisec.cpg.ancestors +import de.fraunhofer.aisec.cpg.SignatureMatches import de.fraunhofer.aisec.cpg.frontends.CastNotPossible -import de.fraunhofer.aisec.cpg.graph.ContextProvider -import de.fraunhofer.aisec.cpg.graph.LanguageProvider import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.* -import de.fraunhofer.aisec.cpg.graph.objectType import de.fraunhofer.aisec.cpg.graph.parseName import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.graph.unknownType @@ -117,10 +114,10 @@ class DFGFunctionSummaries { private fun findFunctionDeclarationEntry(functionDecl: FunctionDeclaration): List? { if (functionToDFGEntryMap.isEmpty()) return null - val provider = functionDecl val language = functionDecl.language val languageName = language?.javaClass?.name val methodName = functionDecl.name + val typeManager = functionDecl.ctx?.typeManager ?: return null // The language and the method name have to match. If a signature is specified, it also has // to match to the one of the FunctionDeclaration, null indicates that we accept everything. @@ -131,7 +128,12 @@ class DFGFunctionSummaries { // Split the name if we have a FQN val entryMethodName = language.parseName(it.methodName) val entryRecord = - entryMethodName.parent?.let { provider.lookupType(entryMethodName.parent) } + entryMethodName.parent?.let { + typeManager.lookupResolvedType( + entryMethodName.parent, + language = language + ) + } methodName.lastPartsMatch( entryMethodName.localName ) && // The local name has to match @@ -148,7 +150,10 @@ class DFGFunctionSummaries { (it.signature == null || functionDecl.matchesSignature( it.signature.map { signatureType -> - provider.lookupType(signatureType) + typeManager.lookupResolvedType( + signatureType, + language = language + ) ?: functionDecl.unknownType() } ) != IncompatibleSignature) } else { @@ -184,57 +189,50 @@ class DFGFunctionSummaries { .map { Pair( language.parseName(it.methodName).parent?.let { it1 -> - functionDecl.objectType(it1) - }, + typeManager.lookupResolvedType(it1, language = language) + } ?: language.unknownType(), it ) } - var mostPreciseClassEntries = mutableListOf() - var mostPreciseType = typeEntryList.first().first - var superTypes = mostPreciseType?.ancestors?.map { it.type } ?: setOf() - for (typeEntry in typeEntryList) { - if (typeEntry.first == mostPreciseType) { - mostPreciseClassEntries.add(typeEntry.second) - } else if (typeEntry.first in superTypes) { - mostPreciseClassEntries.clear() - mostPreciseClassEntries.add(typeEntry.second) - mostPreciseType = typeEntry.first - superTypes = mostPreciseType?.ancestors?.map { it.type } ?: setOf() - } - } - val maxSignature = mostPreciseClassEntries.mapNotNull { it.signature?.size }.max() - if (mostPreciseClassEntries.size > 1) { - mostPreciseClassEntries = - mostPreciseClassEntries - .filter { it.signature?.size == maxSignature } - .toMutableList() - } - // Filter parameter types. We start with parameter 0 and continue. Let's hope we remove - // some entries here. - var argIndex = 0 - while (mostPreciseClassEntries.size > 1 && argIndex < maxSignature) { - mostPreciseType = - mostPreciseClassEntries.first().signature?.get(argIndex)?.let { - functionDecl.objectType(it) - } - superTypes = mostPreciseType?.ancestors?.map { it.type } ?: setOf() - val newMostPrecise = mutableListOf() - for (entry in mostPreciseClassEntries) { - val currentType = - entry.signature?.get(argIndex)?.let { functionDecl.objectType(it) } - if (currentType == mostPreciseType) { - newMostPrecise.add(entry) - } else if (currentType in superTypes) { - newMostPrecise.clear() - newMostPrecise.add(entry) - mostPreciseType = currentType - superTypes = mostPreciseType?.ancestors?.map { it.type } ?: setOf() + val uniqueTypes = typeEntryList.map { it.first }.distinct() + val targetType = + language.parseName(functionDecl.name).parent?.let { it1 -> + typeManager.lookupResolvedType(it1, language = language) + } ?: language.unknownType() + + var mostPreciseType = + uniqueTypes + .map { Pair(it, language?.tryCast(targetType, it)) } + .sortedBy { it.second?.depthDistance } + .firstOrNull() + ?.first + + var mostPreciseClassEntries = + typeEntryList.filter { it.first == mostPreciseType }.map { it.second } + + var signatureResults = + mostPreciseClassEntries + .map { + Pair( + it, + functionDecl.matchesSignature( + it.signature?.map { + typeManager.lookupResolvedType(it, language = language) + ?: language.unknownType() + } ?: listOf() + ) + ) } - } - argIndex++ - mostPreciseClassEntries = newMostPrecise - } - functionToDFGEntryMap[mostPreciseClassEntries.first()] + .filter { it.second is SignatureMatches } + .associate { it } + + val rankings = signatureResults.entries.map { Pair(it.value.ranking, it.key) } + + // Find the best (lowest) rank and find functions with the specific rank + val bestRanking = rankings.minBy { it.first }.first + val list = rankings.filter { it.first == bestRanking }.map { it.second } + + functionToDFGEntryMap[list.first()] } else { null } @@ -356,19 +354,4 @@ class DFGFunctionSummaries { val log: Logger = LoggerFactory.getLogger(DFGFunctionSummaries::class.java) } - - fun ContextProvider.lookupType(fqn: CharSequence): Type { - // Try to look up the type from the specified FQN string - var type = - ctx?.typeManager?.lookupResolvedType( - fqn.toString(), - language = (this as? LanguageProvider)?.language - ) - return if (type == null) { - log.warn("Could not find specified type $fqn. Using UnknownType") - this.unknownType() - } else { - type - } - } } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt index da08592c2f..68158c6137 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt @@ -84,46 +84,48 @@ class DFGFunctionSummariesTest { .build { translationResult { translationUnit("DfgInferredCall.c") { - function("main", t("int")) { - body { - // We need three types with a type hierarchy. - val objectType = t("test.Object") - val listType = t("test.List") - ctx?.let { - val recordDecl = - this@translationUnit.startInference(it) - ?.inferRecordDeclaration( - listType, - ) - listType.recordDeclaration = recordDecl - recordDecl?.addSuperClass(objectType) - listType.superTypes.add(objectType) - } + namespace("test") { + // We need three types with a type hierarchy. + val objectType = t("test.Object") + val listType = t("test.List") + ctx?.let { + val recordDecl = + startInference(it) + ?.inferRecordDeclaration( + listType, + ) + listType.recordDeclaration = recordDecl + recordDecl?.addSuperClass(objectType) + listType.superTypes.add(objectType) + } - val specialListType = t("test.SpecialList") - ctx?.let { - val recordDecl = - this@translationUnit.startInference(it) - ?.inferRecordDeclaration( - specialListType, - ) - specialListType.recordDeclaration = recordDecl - recordDecl?.addSuperClass(listType) - specialListType.superTypes.add(listType) - } + val specialListType = t("test.SpecialList") + ctx?.let { + val recordDecl = + startInference(it) + ?.inferRecordDeclaration( + specialListType, + ) + specialListType.recordDeclaration = recordDecl + recordDecl?.addSuperClass(listType) + specialListType.superTypes.add(listType) + } - val verySpecialListType = t("test.VerySpecialList") - ctx?.let { - val recordDecl = - this@translationUnit.startInference(it) - ?.inferRecordDeclaration( - specialListType, - ) - specialListType.recordDeclaration = recordDecl - recordDecl?.addSuperClass(listType) - specialListType.superTypes.add(listType) - } + val verySpecialListType = t("test.VerySpecialList") + ctx?.let { + val recordDecl = + startInference(it) + ?.inferRecordDeclaration( + verySpecialListType, + ) + verySpecialListType.recordDeclaration = recordDecl + recordDecl?.addSuperClass(specialListType) + verySpecialListType.superTypes.add(listType) + } + } + function("main", t("int")) { + body { memberCall("addAll", construct("test.VerySpecialList")) { literal(1, t("int")) construct("test.Object") diff --git a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/test/TestUtils.kt b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/test/TestUtils.kt index 04410153f9..21435dddbf 100644 --- a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/test/TestUtils.kt +++ b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/test/TestUtils.kt @@ -300,7 +300,7 @@ fun assertUsageOfMemberAndBase(usingNode: Node?, usedBase: Node?, usedMember: De } fun assertFullName(fqn: String, node: Node?, message: String? = null) { - assertNotNull(node) + assertNotNull(node, message) assertEquals(fqn, node.name.toString(), message) } @@ -317,8 +317,8 @@ fun assertLiteralValue(expected: T, expr: Expression?, message: Strin assertEquals(expected, assertIs>(expr).value, message) } -fun ContextProvider.assertResolvedType(fqn: String, generics: List? = null): Type { +fun ContextProvider.assertResolvedType(fqn: String): Type { var type = - ctx?.typeManager?.lookupResolvedType(fqn, generics, (this as? LanguageProvider)?.language) + ctx?.typeManager?.lookupResolvedType(fqn, language = (this as? LanguageProvider)?.language) return assertNotNull(type) } diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt index 4e6243fd57..8592f8d0f7 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontendTest.kt @@ -61,10 +61,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { val decl = main val ls = decl.variables["ls"] assertNotNull(ls) - assertEquals( - assertResolvedType("std::vector", listOf(assertResolvedType("int"))), - ls.type - ) + assertEquals(assertResolvedType("std::vector"), ls.type) assertLocalName("ls", ls) val forEachStatement = decl.forEachLoops.firstOrNull() diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt index 84a75a95b9..1bd43e1d1c 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/JavaExternalTypeHierarchyResolver.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.passes -import com.github.javaparser.resolution.UnsolvedSymbolException import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver @@ -71,30 +70,24 @@ class JavaExternalTypeHierarchyResolver(ctx: TranslationContext) : ComponentPass } // Iterate over all known types and add their (direct) supertypes. - for (t in typeManager.firstOrderTypes) { + var types = typeManager.firstOrderTypes.toList() + for (t in types) { val symbol = resolver.tryToSolveType(t.typeName) if (symbol.isSolved) { - try { - val resolvedSuperTypes = symbol.correspondingDeclaration.getAncestors(true) - for (anc in resolvedSuperTypes) { - // We need to try to resolve the type first in order to create weirdly - // scoped types - var superType = typeManager.lookupResolvedType(anc.qualifiedName) + val resolvedSuperTypes = symbol.correspondingDeclaration.getAncestors(true) + for (anc in resolvedSuperTypes) { + // We need to try to resolve the type first in order to create weirdly + // scoped types + var superType = typeManager.lookupResolvedType(anc.qualifiedName) - // Otherwise, we can create this in the global scope - if (superType == null) { - superType = provider.objectType(anc.qualifiedName) - superType.typeOrigin = Type.Origin.RESOLVED - } - - // Add all resolved supertypes to the type. - t.superTypes.add(superType) + // Otherwise, we can create this in the global scope + if (superType == null) { + superType = provider.objectType(anc.qualifiedName) + superType.typeOrigin = Type.Origin.RESOLVED } - } catch (e: UnsolvedSymbolException) { - // Even if the symbol itself is resolved, "getAncestors()" may throw exception. - LOGGER.warn( - "Could not resolve supertypes of ${symbol.correspondingDeclaration}" - ) + + // Add all resolved supertypes to the type. + t.superTypes.add(superType) } } } diff --git a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt index 5b608b175b..07b0c56130 100644 --- a/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt +++ b/cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypeTests.kt @@ -111,18 +111,20 @@ internal class TypeTests : BaseTest() { fun testCommonTypeTestJava() { val topLevel = Path.of("src", "test", "resources", "compiling", "hierarchy") val result = analyze("java", topLevel, true) { it.registerLanguage(JavaLanguage()) } - val root = assertNotNull(result.records["multistep.Root"]).toType() - val level0 = assertNotNull(result.records["multistep.Level0"]).toType() - val level1 = assertNotNull(result.records["multistep.Level1"]).toType() - val level1b = assertNotNull(result.records["multistep.Level1B"]).toType() - val level2 = assertNotNull(result.records["multistep.Level2"]).toType() - val unrelated = assertNotNull(result.records["multistep.Unrelated"]).toType() - println( - result.finalCtx.typeManager.firstOrderTypes - .filter { it.typeName == "multistep.Root" } - .map { it.superTypes } - ) - getCommonTypeTestGeneral(root, level0, level1, level1b, level2, unrelated) + with(result) { + val root = assertResolvedType("multistep.Root") + val level0 = assertResolvedType("multistep.Level0") + val level1 = assertResolvedType("multistep.Level1") + val level1b = assertResolvedType("multistep.Level1B") + val level2 = assertResolvedType("multistep.Level2") + val unrelated = assertResolvedType("multistep.Unrelated") + println( + result.finalCtx.typeManager.firstOrderTypes + .filter { it.typeName == "multistep.Root" } + .map { it.superTypes } + ) + getCommonTypeTestGeneral(root, level0, level1, level1b, level2, unrelated) + } } private fun getCommonTypeTestGeneral( @@ -169,7 +171,11 @@ internal class TypeTests : BaseTest() { // Check unrelated type behavior: No common root class for (t in listOf(root, level0, level1, level1b, level2)) { - assertFullName("java.lang.Object", setOf(unrelated, t).commonType) + assertFullName( + "java.lang.Object", + setOf(unrelated, t).commonType, + "${t.typeName} and ${unrelated.typeName} do not have a common type (java.lang.Object) which they should" + ) } } } From af0bcd990682e5e1131ede64701341ee1b618c62 Mon Sep 17 00:00:00 2001 From: Maximilian Kaul Date: Mon, 2 Dec 2024 14:21:26 +0100 Subject: [PATCH 3/3] improve kdoc by importing more stuff (#1877) --- .../frontend/configfiles/IniFileFrontend.kt | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontend/configfiles/IniFileFrontend.kt b/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontend/configfiles/IniFileFrontend.kt index 340f839806..bc2b4982f6 100644 --- a/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontend/configfiles/IniFileFrontend.kt +++ b/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontend/configfiles/IniFileFrontend.kt @@ -26,12 +26,16 @@ package de.fraunhofer.aisec.cpg.frontend.configfiles import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.frontends.Handler import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.* +import de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.NamespaceDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration +import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import de.fraunhofer.aisec.cpg.sarif.Region @@ -45,14 +49,13 @@ import org.ini4j.Profile * The INI file frontend. This frontend utilizes the [ini4j library](https://ini4j.sourceforge.net/) * to parse the config file. The result consists of * - a [TranslationUnitDeclaration] wrapping the entire result - * - a [de.fraunhofer.aisec.cpg.graph.declarations.NamespaceDeclaration] wrapping the INI file and - * thus preventing collisions with other symbols which might have the same name + * - a [NamespaceDeclaration] wrapping the INI file and thus preventing collisions with other + * symbols which might have the same name * - a [RecordDeclaration] per `Section` (a section refers to a block of INI values marked with a * line `[SectionName]`) - * - a [de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration] per entry in a section. The - * [de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration.name] matches the `entry`s `name` - * field and the [de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration.initializer] is set - * to a [statements.expressions.Literal] with the corresponding `entry`s `value`. + * - a [FieldDeclaration] per entry in a section. The [FieldDeclaration.name] matches the `entry`s + * `name` field and the [FieldDeclaration.initializer] is set to a [Literal] with the + * corresponding `entry`s `value`. * * Note: * - the "ini4j" library does not provide any super type for all nodes. Thus, the frontend accepts @@ -63,8 +66,7 @@ import org.ini4j.Profile * `.toString()` * - [locationOf] always returns `null` as the "ini4j" library does not provide any means of getting * a location given a node - * - [setComment] not implemented as this is not used (no - * [de.fraunhofer.aisec.cpg.frontends.Handler] pattern implemented) + * - [setComment] not implemented as this is not used (no [Handler] pattern implemented) * - Comments in general are not supported. */ class IniFileFrontend(language: Language, ctx: TranslationContext) : @@ -125,10 +127,8 @@ class IniFileFrontend(language: Language, ctx: TranslationConte } /** - * Translates an `MutableEntry` to a new - * [de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration] with the - * [de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration.initializer] being set to the - * `entry`s value. + * Translates an `MutableEntry` to a new [FieldDeclaration] with the + * [FieldDeclaration.initializer] being set to the `entry`s value. */ private fun handleEntry(entry: MutableMap.MutableEntry) { val field =