diff --git a/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/codyze/compliance/SecurityGoalTest.kt b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/codyze/compliance/SecurityGoalTest.kt index 32ac0523d4c..3a5731eed9b 100644 --- a/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/codyze/compliance/SecurityGoalTest.kt +++ b/codyze-compliance/src/test/kotlin/de/fraunhofer/aisec/codyze/compliance/SecurityGoalTest.kt @@ -25,12 +25,9 @@ */ package de.fraunhofer.aisec.codyze.compliance -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationManager import de.fraunhofer.aisec.cpg.TranslationResult -import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.graph.Component import de.fraunhofer.aisec.cpg.graph.Name import kotlin.io.path.Path @@ -75,11 +72,7 @@ class SecurityGoalTest { val result = TranslationResult( translationManager = TranslationManager.builder().build(), - TranslationContext( - config = TranslationConfiguration.builder().build(), - scopeManager = ScopeManager(), - typeManager = TypeManager(), - ), + TranslationContext(), ) val auth = Component().also { it.name = Name("auth") } result.components += auth diff --git a/cpg-concepts/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/concepts/memory/DynamicLoading.kt b/cpg-concepts/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/concepts/memory/DynamicLoading.kt index 20411f0dc06..cbbe3613276 100644 --- a/cpg-concepts/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/concepts/memory/DynamicLoading.kt +++ b/cpg-concepts/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/concepts/memory/DynamicLoading.kt @@ -26,6 +26,7 @@ package de.fraunhofer.aisec.cpg.graph.concepts.memory import de.fraunhofer.aisec.cpg.graph.Component +import de.fraunhofer.aisec.cpg.graph.ContextProvider import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.concepts.Concept import de.fraunhofer.aisec.cpg.graph.concepts.arch.OperatingSystemArchitecture @@ -61,6 +62,7 @@ abstract class DynamicLoadingOperation( * The [underlyingNode] is most likely a function call and [what] can point to a [Component] * representing the library. */ +@Suppress("CONTEXT_RECEIVERS_DEPRECATED") class LoadLibrary( underlyingNode: Node, concept: Concept, @@ -84,6 +86,7 @@ class LoadLibrary( os = os, ) { + context(ContextProvider) /** Looks up symbol candidates for [symbol] in the [LoadLibrary.what]. */ fun findSymbol(symbol: Symbol?): List { if (symbol == null) { 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 8c54b997234..54c0fda6903 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 @@ -53,13 +53,20 @@ import org.slf4j.LoggerFactory * than adding the declaration to the node itself. This ensures that all declarations are properly * registered in the scope map and can be resolved later. */ -class ScopeManager : ScopeProvider { +class ScopeManager(override var ctx: TranslationContext) : ScopeProvider, ContextProvider { + + /** + * The top-most scope in the scope tree. This is the root of the tree and is not associated with + * any particular AST node. + */ + val globalScope: GlobalScope = GlobalScope(ctx) + /** * A map associating each CPG node with its scope. The key type is intentionally a nullable - * [Node] because the [GlobalScope] is not associated to a CPG node when it is first created. It - * is later associated using the [resetToGlobal] function. + * [Node] because the [GlobalScope] is not associated to a CPG node. */ - private val scopeMap: MutableMap = IdentityHashMap() + private val scopeMap: MutableMap = + IdentityHashMap().also { it[null] = globalScope } /** * A lookup map for each [NameScope] and its associated FQN (as a [Name]). This is mainly needed @@ -69,10 +76,6 @@ class ScopeManager : ScopeProvider { */ private val nameScopeMap: MutableMap = mutableMapOf() - /** The currently active scope. */ - var currentScope: Scope? = null - private set - /** True, if the scope manager is currently in a [FunctionScope]. */ val isInFunction: Boolean get() = this.firstScopeOrNull { it is FunctionScope } != null @@ -81,8 +84,12 @@ class ScopeManager : ScopeProvider { val isInRecord: Boolean get() = this.firstScopeOrNull { it is RecordScope } != null - val globalScope: GlobalScope? - get() = scopeMap[null] as? GlobalScope + /** + * The currently active scope. When the [ScopeManager] is initialized, this is set to the global + * scope + */ + var currentScope: Scope = globalScope + private set /** The current function, according to the scope that is currently active. */ val currentFunction: FunctionDeclaration? @@ -90,7 +97,7 @@ class ScopeManager : ScopeProvider { /** The current block, according to the scope that is currently active. */ val currentBlock: Block? - get() = currentScope?.astNode as? Block ?: currentScope?.astNode?.firstParentOrNull() + get() = currentScope.astNode as? Block ?: currentScope.astNode?.firstParentOrNull() /** * The current method in the active scope tree, this ensures that 'this' keywords are mapped @@ -111,10 +118,6 @@ class ScopeManager : ScopeProvider { return if (namedScope is NameScope) namedScope.name else null } - init { - pushScope(GlobalScope()) - } - companion object { private val LOGGER = LoggerFactory.getLogger(ScopeManager::class.java) } @@ -126,7 +129,7 @@ class ScopeManager : ScopeProvider { * @param toMerge The scope managers to merge into this one */ fun mergeFrom(toMerge: Collection) { - val globalScopes = toMerge.mapNotNull { it.globalScope } + val globalScopes = toMerge.map { it.globalScope } val currGlobalScope = scopeMap[null] if (currGlobalScope !is GlobalScope) { LOGGER.error("Scope for null node is not a GlobalScope or is null") @@ -203,10 +206,10 @@ class ScopeManager : ScopeProvider { nameScopeMap[name] = scope } } - currentScope?.let { - it.children.add(scope) - scope.parent = it - } + + currentScope.children.add(scope) + scope.parent = currentScope + currentScope = scope } @@ -223,11 +226,9 @@ class ScopeManager : ScopeProvider { * is currently in-scope. */ fun enterScope(nodeToScope: Node) { - var newScope: Scope? = null - // check, if the node does not have an entry in the scope map if (!scopeMap.containsKey(nodeToScope)) { - newScope = + val newScope = when (nodeToScope) { is WhileStatement, is DoStatement, @@ -239,11 +240,11 @@ class ScopeManager : ScopeProvider { is IfStatement, is CatchClause, is CollectionComprehension, - is Block -> LocalScope(nodeToScope) - is FunctionDeclaration -> FunctionScope(nodeToScope) - is RecordDeclaration -> RecordScope(nodeToScope) - is TemplateDeclaration -> TemplateScope(nodeToScope) - is TranslationUnitDeclaration -> FileScope(nodeToScope) + is Block -> LocalScope(ctx, nodeToScope) + is FunctionDeclaration -> FunctionScope(ctx, nodeToScope) + is RecordDeclaration -> RecordScope(ctx, nodeToScope) + is TemplateDeclaration -> TemplateScope(ctx, nodeToScope) + is TranslationUnitDeclaration -> FileScope(ctx, nodeToScope) is NamespaceDeclaration -> newNamespaceIfNecessary(nodeToScope) else -> { LOGGER.error( @@ -253,14 +254,18 @@ class ScopeManager : ScopeProvider { return } } + + if (newScope != null) { + // push the new scope + pushScope(newScope) + newScope.scopedName = currentNamespace?.toString() + } } - // push the new scope - if (newScope != null) { - pushScope(newScope) - newScope.scopedName = currentNamespace?.toString() - } else { - currentScope = scopeMap[nodeToScope] + // We need to check again because of the newNamespaceIfNecessary function + var existing = scopeMap[nodeToScope] + if (existing != null) { + currentScope = existing } } @@ -281,7 +286,9 @@ class ScopeManager : ScopeProvider { */ private fun newNamespaceIfNecessary(nodeToScope: NamespaceDeclaration): NamespaceScope? { val existingScope = - filterScopes { it is NamespaceScope && it.name == nodeToScope.name }.firstOrNull() + filterScopes { it is NamespaceScope && it.name == nodeToScope.name } + .filterIsInstance() + .firstOrNull() return if (existingScope != null) { // update the AST node to this namespace declaration @@ -291,11 +298,9 @@ class ScopeManager : ScopeProvider { // scope scopeMap[nodeToScope] = existingScope - // do NOT return a new name scope, but rather return null, so enterScope knows that it - // does not need to push a new scope null } else { - NamespaceScope(nodeToScope) + NamespaceScope(ctx, nodeToScope) } } @@ -336,7 +341,7 @@ class ScopeManager : ScopeProvider { } // go back to the parent of the scope we just left - currentScope = leaveScope.parent + currentScope = leaveScope.parent ?: globalScope return leaveScope } @@ -347,7 +352,7 @@ class ScopeManager : ScopeProvider { * @param declaration the declaration to add */ fun addDeclaration(declaration: Declaration) { - currentScope?.addSymbol(declaration.symbol, declaration) + currentScope.addSymbol(declaration.symbol, declaration) } /** @@ -359,16 +364,16 @@ class ScopeManager : ScopeProvider { * @param predicate the search predicate */ @JvmOverloads - fun firstScopeOrNull(searchScope: Scope? = currentScope, predicate: Predicate): Scope? { + fun firstScopeOrNull(searchScope: Scope = currentScope, predicate: Predicate): Scope? { // start at searchScope - var scope = searchScope + var scope: Scope? = searchScope while (scope != null) { if (predicate.test(scope)) { return scope } - // go up-wards in the scope tree + // go upwards in the scope tree scope = scope.parent } @@ -382,7 +387,7 @@ class ScopeManager : ScopeProvider { * @param searchScope the scope to start the search in */ inline fun firstScopeIsInstanceOrNull( - searchScope: Scope? = currentScope + searchScope: Scope = currentScope ): T? { return this.firstScopeOrNull(searchScope) { it is T } as? T } @@ -422,7 +427,7 @@ class ScopeManager : ScopeProvider { fun getLabelStatement(labelString: String?): LabelStatement? { if (labelString == null) return null var labelStatement: LabelStatement? - var searchScope = currentScope + var searchScope: Scope? = currentScope while (searchScope != null) { labelStatement = searchScope.labelStatements[labelString] if (labelStatement != null) { @@ -439,19 +444,17 @@ class ScopeManager : ScopeProvider { */ fun resetToGlobal(declaration: TranslationUnitDeclaration?) { val global = this.globalScope - if (global != null) { - // update the AST node to this translation unit declaration - global.astNode = declaration - currentScope = global - } + // update the AST node to this translation unit declaration + global.astNode = declaration + currentScope = global } /** * Adds typedefs to a [Scope]. The language frontend needs to decide on the scope of the * typedef. Most likely, typedefs are global. Therefore, the [GlobalScope] is set as default. */ - fun addTypedef(typedef: TypedefDeclaration, scope: Scope? = globalScope) { - scope?.addTypedef(typedef) + fun addTypedef(typedef: TypedefDeclaration, scope: Scope = globalScope) { + scope.addTypedef(typedef) } /** @@ -516,6 +519,13 @@ class ScopeManager : ScopeProvider { // First, we need to check, whether we have some kind of scoping. if (scopeName != null) { + // This is a rather ugly hack, but we need to check whether the scopeName is the same as + // the current scope. This is unfortunately necessary since "toType()" returns a type + // with an FQN that is scoped to the current scope and we need to change that :( + if (scopeName == scope?.name) { + return ScopeExtraction(scope, name) + } + // We need to check, whether we have an alias for the name's parent in this file val scope = lookupScopeByName(scopeName, language, scope) @@ -608,9 +618,13 @@ class ScopeManager : ScopeProvider { */ @PleaseBeCareful internal fun jumpTo(scope: Scope?): Scope? { - val oldScope = currentScope - currentScope = scope - return oldScope + return if (scope != null) { + val oldScope = currentScope + currentScope = scope + return oldScope + } else { + null + } } /** @@ -641,8 +655,12 @@ class ScopeManager : ScopeProvider { .singleOrNull() } - fun typedefFor(alias: Name, scope: Scope? = currentScope): Type? { - var current = scope + fun typedefFor( + alias: Name, + scope: Scope? = currentScope, + prefix: Name? = currentNamespace, + ): Type? { + var current: Scope? = scope // We need to build a path from the current scope to the top most one. This ensures us that // a local definition overwrites / shadows one that was there on a higher scope. @@ -655,16 +673,8 @@ class ScopeManager : ScopeProvider { // all this happens. // // This process has several steps: - // First, do a quick local lookup, to see if we have a typedef our current scope - // (only do this if the name is not qualified) - if (!alias.isQualified() && current == scope) { - val decl = current.typedefs[alias] - if (decl != null) { - return decl.type - } - } - // Next, try to look up the name either by its FQN (if it is qualified) or make it + // First, try to look up the name either by its FQN (if it is qualified) or make it // qualified based on the current namespace val key = current.typedefs.keys.firstOrNull { @@ -677,7 +687,7 @@ class ScopeManager : ScopeProvider { } else { // Otherwise, we want to make an FQN out of it using the current // namespace - currentNamespace?.fqn(lookupName.localName) ?: lookupName + prefix?.fqn(lookupName.localName) ?: lookupName } it.lastPartsMatch(lookupName) @@ -686,6 +696,15 @@ class ScopeManager : ScopeProvider { return current.typedefs[key]?.type } + // Next, do a local lookup, to see if we have a typedef our current scope + // (only do this if the name is not qualified) + if (!alias.isQualified()) { + val decl = current.typedefs[alias] + if (decl != null) { + return decl.type + } + } + current = current.parent } @@ -693,7 +712,7 @@ class ScopeManager : ScopeProvider { } /** Returns the current scope for the [ScopeProvider] interface. */ - override val scope: Scope? + override val scope: Scope get() = currentScope /** @@ -702,7 +721,7 @@ class ScopeManager : ScopeProvider { */ fun lookupSymbolByNodeName( node: Node, - scope: Scope? = node.scope, + scope: Scope = node.scope ?: currentScope, replaceImports: Boolean = true, predicate: ((Declaration) -> Boolean)? = null, ): List { @@ -722,7 +741,7 @@ class ScopeManager : ScopeProvider { */ inline fun lookupSymbolByNodeNameOfType( node: Node, - scope: Scope? = node.scope, + scope: Scope = node.scope ?: currentScope, replaceImports: Boolean = true, ): List { return lookupSymbolByName(node.name, node.language, node.location, scope, replaceImports) { @@ -849,17 +868,8 @@ class ScopeManager : ScopeProvider { * @param TypeToInfer the type of the node that should be inferred * @param source the source that was responsible for the inference */ - fun translationUnitForInference( - source: Node - ): TranslationUnitDeclaration? { - // TODO(oxisto): This workaround is needed because it seems that not all types have a proper - // context :(. In this case we need to fall back to the global scope's astNode, which can - // be error-prone in a multi-language scenario. - return if (source.ctx == null) { - globalScope?.astNode as? TranslationUnitDeclaration - } else { - source.language.translationUnitForInference(source) - } + fun translationUnitForInference(source: Node): TranslationUnitDeclaration { + return source.language.translationUnitForInference(source) } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt index 264358a4902..2ec3a39ae85 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationConfiguration.kt @@ -736,7 +736,7 @@ val KClass>.frontend: KClass> get() { // Instantiate a temporary object of the language class val instance = - constructors.firstOrNull()?.call(EmptyTranslationContext) + constructors.firstOrNull()?.call() ?: throw IllegalArgumentException( "Could not instantiate temporary object of language class ${this.simpleName}" ) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt index 616ed09ee71..4570a4f050d 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationContext.kt @@ -41,14 +41,6 @@ open class TranslationContext( /** The configuration for this translation. */ val config: TranslationConfiguration = TranslationConfiguration.builder().build(), - /** - * The scope manager which comprises the complete translation result. In case of sequential - * parsing, this scope manager is passed to the individual frontends one after another. In case - * of sequential parsing, individual scope managers will be passed to each language frontend - * (through individual contexts) and then finally merged into a final one. - */ - val scopeManager: ScopeManager = ScopeManager(), - /** * The type manager is responsible for managing type information. Currently, we have one * instance of a [TypeManager] for the overall [TranslationResult]. @@ -60,6 +52,14 @@ open class TranslationContext( * the [TranslationResult.finalCtx] this may either be null or the last component analyzed. */ var currentComponent: Component? = null, +) { + /** + * The scope manager which comprises the complete translation result. In case of sequential + * parsing, this scope manager is passed to the individual frontends one after another. In case + * of sequential parsing, individual scope managers will be passed to each language frontend + * (through individual contexts) and then finally merged into a final one. + */ + val scopeManager: ScopeManager = ScopeManager(this) /** * Set of files, that are available for additional analysis. They are not the primary subjects @@ -70,15 +70,15 @@ open class TranslationContext( * The frontend can decide to add some of the contained files to [importedSources] which will * get them translated into the final graph by the [TranslationManager]. */ - var additionalSources: MutableSet = mutableSetOf(), + var additionalSources: MutableSet = mutableSetOf() /** * The additional sources from the [additionalSources] chosen to be analyzed along with the code * under analysis. The language frontends are supposed to fill this list, e.g. by analyzing the * import statements of the analyzed code and deciding which sources contain relevant symbols. */ - var importedSources: MutableSet = mutableSetOf(), -) { + var importedSources: MutableSet = mutableSetOf() + /** * The set of languages available in this translation context. We store this information here * because we want to ensure that we only have one instance of a language per @@ -101,7 +101,7 @@ open class TranslationContext( private fun createAvailableLanguages(): List> { // We need to initialize the available languages out of the context return config.languages.mapNotNull { - val language = it.constructors.firstOrNull()?.call(this) + val language = it.constructors.firstOrNull()?.call() if (language == null) { log.error("Could not create language instance for {}", it.simpleName) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt index d15ccb4d2ad..c3c31d41e26 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationManager.kt @@ -76,7 +76,7 @@ private constructor( var executedFrontends = setOf>() // Build a new global translation context - val ctx = TranslationContext(config, ScopeManager(), TypeManager()) + val ctx = TranslationContext(config) // Build a new translation result val result = TranslationResult(this, ctx) @@ -155,7 +155,6 @@ private constructor( for (sc in ctx.config.softwareComponents.keys) { val component = Component() - component.ctx = ctx component.name = Name(sc) result.addComponent(component) @@ -287,7 +286,6 @@ private constructor( var component = result.components.firstOrNull { it.name == compName } if (component == null) { component = Component() - component.ctx = ctx component.name = compName result.addComponent(component) ctx.config.topLevels.put(includePath.name, includePath.toFile()) @@ -357,13 +355,7 @@ private constructor( // Build a new translation context for this parallel parsing process. We need to do this // until we can use a single scope manager concurrently. We can re-use the global // configuration and type manager. - val ctx = - TranslationContext( - globalCtx.config, - ScopeManager(), - globalCtx.typeManager, - component, - ) + val ctx = TranslationContext(globalCtx.config, globalCtx.typeManager, component) parallelContexts.add(ctx) val future = @@ -407,14 +399,6 @@ private constructor( // We want to merge everything into the final scope manager of the result globalCtx.scopeManager.mergeFrom(parallelContexts.map { it.scopeManager }) - - // We also need to update all types that point to one of the "old" global scopes - // TODO(oxisto): This is really messy and instead we should have ONE global scope - // and individual file scopes beneath it - var newGlobalScope = globalCtx.scopeManager.globalScope - globalCtx.typeManager.firstOrderTypes.updateGlobalScope(newGlobalScope) - globalCtx.typeManager.secondOrderTypes.updateGlobalScope(newGlobalScope) - b.stop() log.info("Parallel parsing completed") @@ -505,9 +489,6 @@ private constructor( return if (language != null) { try { - // Make sure, that our simple types are also known to the type manager - language.builtInTypes.values.forEach { ctx.typeManager.registerType(it) } - // Return a new language frontend language.newFrontend(ctx) } catch (e: Exception) { @@ -581,5 +562,7 @@ private fun MutableList.updateGlobalScope(newGlobalScope: GlobalScope?) { if (type.scope is GlobalScope) { type.scope = newGlobalScope } + + type.secondOrderTypes.updateGlobalScope(newGlobalScope) } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt index cbb8a386378..3e8d6affbe8 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/TranslationResult.kt @@ -57,7 +57,7 @@ class TranslationResult( * dedicated [ScopeManager] each). This property will contain the final, merged context. */ var finalCtx: TranslationContext, -) : Node(), StatisticsHolder { +) : Node(), StatisticsHolder, ContextProvider { @Relationship("COMPONENTS") val componentEdges = astEdgesOf() /** @@ -93,11 +93,6 @@ class TranslationResult( val isCancelled: Boolean get() = translationManager.isCancelled() - override var ctx: TranslationContext? = null - get() { - return finalCtx - } - /** * Checks if only a single software component has been analyzed and returns its translation * units. For multiple software components, it aggregates the results. @@ -177,6 +172,9 @@ class TranslationResult( } set(_) {} + override val ctx: TranslationContext + get() = finalCtx + companion object { const val SOURCE_LOCATIONS_TO_FRONTEND = "sourceLocationsToFrontend" const val DEFAULT_APPLICATION_NAME = "application" 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 393a01f1262..6498152cb7a 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 @@ -33,6 +33,7 @@ import de.fraunhofer.aisec.cpg.graph.scopes.Scope import de.fraunhofer.aisec.cpg.graph.scopes.TemplateScope import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference import de.fraunhofer.aisec.cpg.graph.types.* +import de.fraunhofer.aisec.cpg.helpers.identitySetOf import de.fraunhofer.aisec.cpg.passes.Pass import de.fraunhofer.aisec.cpg.passes.Pass.Companion.log import de.fraunhofer.aisec.cpg.passes.ResolveCallExpressionAmbiguityPass @@ -57,8 +58,8 @@ class TypeManager { MutableMap> = ConcurrentHashMap() - val firstOrderTypes = mutableListOf() - val secondOrderTypes = mutableListOf() + val allFirstOrderTypes = identitySetOf() + val resolvedTypes = identitySetOf() /** * @param recordDeclaration that is instantiated by a template containing parameterizedtypes @@ -193,24 +194,9 @@ class TypeManager { return parameterizedType } - inline fun registerType(t: T): T { - // Skip as they should be unique to each class and not globally unique - if (t is ParameterizedType) { - return t - } - - if (t.isFirstOrderType) { - synchronized(firstOrderTypes) { firstOrderTypes.add(t) } - } else if (t is SecondOrderType) { - synchronized(secondOrderTypes) { secondOrderTypes.add(t) } - } - - return t - } - /** Checks, whether a [Type] with the given [name] exists. */ fun typeExists(name: CharSequence): Boolean { - return firstOrderTypes.any { type: Type -> type.root.name == name } + return resolvedTypes.any { type: Type -> type.root.name == name } } fun resolvePossibleTypedef(alias: Type, scopeManager: ScopeManager): Type { @@ -233,7 +219,7 @@ class TypeManager { return primitiveType } - return firstOrderTypes.firstOrNull { + return resolvedTypes.firstOrNull { (it.typeOrigin == Type.Origin.RESOLVED || it.typeOrigin == Type.Origin.GUESSED) && it.root.name == fqn && if (generics != null) { diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Handler.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Handler.kt index 6cdebab524c..0289bddb6a6 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Handler.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Handler.kt @@ -50,10 +50,10 @@ abstract class Handler by frontend, ScopeProvider by frontend, NamespaceProvider by frontend, - ContextProvider by frontend, RawNodeTypeProvider { protected val map = HashMap, HandlerInterface>() private val typeOfT: Class<*>? 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 6a471ade533..8014d332b09 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 @@ -36,6 +36,7 @@ import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.ancestors import de.fraunhofer.aisec.cpg.evaluation.ValueEvaluator import de.fraunhofer.aisec.cpg.graph.Component +import de.fraunhofer.aisec.cpg.graph.ContextProvider import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.OverlayNode @@ -61,6 +62,8 @@ import java.io.File import kotlin.reflect.KClass import kotlin.reflect.full.primaryConstructor import org.neo4j.ogm.annotation.Transient +import org.slf4j.Logger +import org.slf4j.LoggerFactory /** * [CastResult] is the result of the function [Language.tryCast] and describes whether a cast of one @@ -92,7 +95,7 @@ data class ImplicitCast(override var depthDistance: Int) : CastResult(depthDista * persisted in the final graph (database) and each node links to its corresponding language using * the [Node.language] property. */ -abstract class Language> : Node { +abstract class Language>() : Node() { /** The file extensions without the dot */ abstract val fileExtensions: List @@ -129,8 +132,9 @@ abstract class Language> : Node { /** The standard evaluator to be used with this language. */ @Transient @DoNotPersist open val evaluator: ValueEvaluator = ValueEvaluator() - constructor(ctx: TranslationContext? = null) : super() { - this.ctx = ctx + init { + this.language = this + this.name = Name(this::class.simpleName ?: EMPTY_NAME) } /** @@ -168,11 +172,6 @@ abstract class Language> : Node { return result } - init { - this.language = this - this::class.simpleName?.let { this.name = Name(it) } - } - private fun arithmeticOpTypePropagation(lhs: Type, rhs: Type): Type { return when { lhs is FloatingPointType && rhs !is FloatingPointType && rhs is NumericType -> lhs @@ -319,6 +318,7 @@ abstract class Language> : Node { * the best. The ranking is determined by the [CastResult.depthDistance] of all cast results * in the signature results. */ + context(ContextProvider) open fun bestViableResolution( result: CallResolutionResult ): Pair, CallResolutionResult.SuccessKind> { @@ -353,7 +353,7 @@ abstract class Language> : Node { null, source, false, - source.ctx!!, + ctx, null, needsExactMatch = true, ) @@ -428,13 +428,20 @@ abstract class Language> : Node { * @param TypeToInfer the type of the node that should be inferred * @param source the source that was responsible for the inference */ + context(ContextProvider) fun translationUnitForInference(source: Node): TranslationUnitDeclaration { // The easiest way to identify the current component would be traversing the AST, but that // does not work for types. But types have a scope and the scope (should) have the // connection to the AST. We add several fallbacks here to make sure that we have a // component. val component = - source.scope?.astNode?.component ?: source.component ?: source.ctx?.currentComponent + if (source !is Type) { + source.component + ?: this@ContextProvider.ctx.currentComponent + ?: source.scope?.astNode?.component + } else { + this@ContextProvider.ctx.currentComponent ?: source.scope?.astNode?.component + } if (component == null) { val msg = "No suitable component found that should be used for inference. " + @@ -453,6 +460,10 @@ abstract class Language> : Node { return tu } + + companion object { + @JvmStatic protected val log: Logger = LoggerFactory.getLogger(Language::class.java) + } } /** @@ -495,8 +506,7 @@ object NoLanguage : Language() { * * @property languages A list of languages that are part of this composite language definition. */ -class MultipleLanguages(ctx: TranslationContext, val languages: Set>) : - Language(ctx) { +class MultipleLanguages(val languages: Set>) : Language() { override val fileExtensions = languages.flatMap { it.fileExtensions } override val frontend: KClass = Nothing::class override val builtInTypes: Map = mapOf() @@ -512,7 +522,7 @@ fun Node.multiLanguage(): Language<*> { return if (languages.size == 1) { languages.single() } else if (languages.size > 1) { - MultipleLanguages(ctx!!, languages = languages) + MultipleLanguages(languages = languages) } else { UnknownLanguage } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt index 09050a6d160..d7503672a07 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageFrontend.kt @@ -49,9 +49,6 @@ abstract class LanguageFrontend( * The translation context, which contains all necessary managers used in this frontend parsing * process. Note, that different contexts could be passed to frontends, e.g., in parallel * parsing to supply different managers to different frontends. - * - * TODO(oxisto): once we address https://github.com/Fraunhofer-AISEC/cpg/issues/2109 we can - * remove this parameter and use the context from the language */ final override var ctx: TranslationContext, @@ -61,9 +58,9 @@ abstract class LanguageFrontend( ProcessedListener(), CodeAndLocationProvider, LanguageProvider, + ContextProvider, ScopeProvider, NamespaceProvider, - ContextProvider, RawNodeTypeProvider { val scopeManager: ScopeManager = ctx.scopeManager val typeManager: TypeManager = ctx.typeManager diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt index 2f95c52abf8..1fe1cc05bb2 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTraits.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.frontends -import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.HasOperatorCode import de.fraunhofer.aisec.cpg.graph.HasOverloadedOperation @@ -116,10 +115,9 @@ interface HasSuperClasses : LanguageTrait { */ val superClassKeyword: String - fun handleSuperExpression( + fun SymbolResolver.handleSuperExpression( memberExpression: MemberExpression, curClass: RecordDeclaration, - scopeManager: ScopeManager, ): Boolean } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Component.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Component.kt index d0ddcbcdf48..dd9c680b194 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Component.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Component.kt @@ -34,7 +34,6 @@ import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.passes.ImportDependencies import de.fraunhofer.aisec.cpg.passes.ImportResolver -import de.fraunhofer.aisec.cpg.persistence.DoNotPersist import de.fraunhofer.aisec.cpg.processing.strategy.Strategy import java.io.File import org.neo4j.ogm.annotation.Relationship @@ -70,11 +69,10 @@ open class Component : Node() { * Returns the top-level directory of this component according to * [TranslationConfiguration.topLevels] */ - @DoNotPersist - val topLevel: File? - get() { - return ctx?.config?.topLevels?.get(this.name.localName) - } + context(ContextProvider) + fun topLevel(): File? { + return this@ContextProvider.ctx.config.topLevels[this.name.localName] + } /** * All points where unknown data may enter this application, e.g., the main method, or other diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationBuilder.kt index 03087ba372c..e0f9b0283f7 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationBuilder.kt @@ -125,6 +125,7 @@ fun MetadataProvider.newOperatorDeclaration( * requires an appropriate [MetadataProvider], such as a [LanguageFrontend] as an additional * prepended argument. */ +context(ContextProvider) @JvmOverloads fun MetadataProvider.newConstructorDeclaration( name: CharSequence?, @@ -136,6 +137,7 @@ fun MetadataProvider.newConstructorDeclaration( node.applyMetadata(this, name, rawNode, defaultNamespace = recordDeclaration?.name) node.recordDeclaration = recordDeclaration + node.type = recordDeclaration?.toType() ?: unknownType() log(node) return node @@ -193,6 +195,7 @@ fun MetadataProvider.newVariableDeclaration( * an appropriate [MetadataProvider], such as a [LanguageFrontend] as an additional prepended * argument. */ +context(ContextProvider) @JvmOverloads fun LanguageProvider.newTupleDeclaration( elements: List, diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt index 82b5c402d4f..2475260932f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilder.kt @@ -611,7 +611,6 @@ fun MetadataProvider.newProblemType(rawNode: Any? = null): ProblemType { fun Literal.duplicate(implicit: Boolean): Literal { val duplicate = Literal() - duplicate.ctx = this.ctx duplicate.language = this.language duplicate.value = this.value duplicate.type = this.type @@ -658,7 +657,6 @@ fun Literal.duplicate(implicit: Boolean): Literal { fun TypeExpression.duplicate(implicit: Boolean): TypeExpression { val duplicate = TypeExpression() - duplicate.ctx = this.ctx duplicate.name = this.name.clone() duplicate.language = this.language duplicate.type = this.type diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt index bf9aa005db2..89919a66dfa 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt @@ -28,8 +28,6 @@ package de.fraunhofer.aisec.cpg.graph import com.fasterxml.jackson.annotation.JsonBackReference import com.fasterxml.jackson.annotation.JsonIgnore import de.fraunhofer.aisec.cpg.PopulatedByPass -import de.fraunhofer.aisec.cpg.TranslationContext -import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.Handler import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.frontends.UnknownLanguage @@ -63,21 +61,8 @@ import org.slf4j.Logger import org.slf4j.LoggerFactory /** The base class for all graph objects that are going to be persisted in the database. */ -abstract class Node : - IVisitable, - Persistable, - LanguageProvider, - ScopeProvider, - ContextProvider, - HasNameAndLocation, - HasScope { - /** - * Because we are updating type information in the properties of the node, we need a reference - * to managers such as the [TypeManager] instance which is responsible for this particular node. - * All managers are bundled in [TranslationContext]. It is set in [Node.applyMetadata] when a - * [ContextProvider] is provided. - */ - @get:JsonIgnore @Transient override var ctx: TranslationContext? = null +abstract class Node() : + IVisitable, Persistable, LanguageProvider, ScopeProvider, HasNameAndLocation, HasScope { /** This property holds the full name using our new [Name] class. */ @Convert(NameConverter::class) override var name: Name = Name(EMPTY_NAME) @@ -372,10 +357,11 @@ abstract class Node : * Works similar to [apply] but before executing [block], it enters the scope for this object and * afterward leaves the scope again. */ +context(ContextProvider) inline fun T.applyWithScope(block: T.() -> Unit): T { return this.apply { - ctx?.scopeManager?.enterScope(this) + (this@ContextProvider).ctx.scopeManager.enterScope(this) block() - ctx?.scopeManager?.leaveScope(this) + (this@ContextProvider).ctx.scopeManager.leaveScope(this) } } 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 6045897639f..9da63aa91db 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 @@ -107,23 +107,15 @@ fun Node.applyMetadata( doNotPrependNamespace: Boolean = false, defaultNamespace: Name? = null, ) { - // We definitely need a context provider, because otherwise we cannot set the context and the - // node cannot access necessary information about the current translation context it lives in. - this.ctx = - (provider as? ContextProvider)?.ctx - ?: throw TranslationException( - "Trying to create a node without a ContextProvider. This will fail." - ) - // We try to set the code and especially the location as soon as possible because the hashCode // implementation of the Node class relies on it. Otherwise, we could have a problem that the // location is not yet set, but the node is put into a hashmap. In this case the hashCode is // calculated based on an empty location and if we would later set the location, we would have a // mismatch. Each language frontend and also each handler implements CodeAndLocationProvider, so // calling a node builder from these should already set the location. - if (provider is CodeAndLocationProvider<*> && rawNode != null) { + if (provider is ContextProvider && provider is CodeAndLocationProvider<*> && rawNode != null) { @Suppress("UNCHECKED_CAST") - setCodeAndLocation(provider as CodeAndLocationProvider, rawNode) + with(provider) { setCodeAndLocation(provider as CodeAndLocationProvider, rawNode) } } if (provider is LanguageProvider) { @@ -177,7 +169,7 @@ fun LanguageProvider.newName( } else if (name.contains(language.namespaceDelimiter)) { // Let's check, if this is an FQN as string / char sequence by any chance. Then we need // to parse the name. In the future, we might drop compatibility for this - language.parseName(name) + parseName(name) } else { // Otherwise, a local name is supplied. Some nodes only want a local name. In this case, // we create a new name with the supplied (local) name and set the parent to null. @@ -234,7 +226,7 @@ fun NamespaceProvider.fqn(localName: String): Name { } interface ContextProvider : MetadataProvider { - val ctx: TranslationContext? + val ctx: TranslationContext } /** @@ -282,7 +274,7 @@ fun T.codeAndLocationFrom(other: Node): T { * code/location to the statement rather than the expression, after it comes back from the * expression handler. */ -context(CodeAndLocationProvider) +context(CodeAndLocationProvider, ContextProvider) fun T.codeAndLocationFromOtherRawNode(rawNode: AstNode?): T { rawNode?.let { setCodeAndLocation(this@CodeAndLocationProvider, it) } return this @@ -374,11 +366,12 @@ fun T.codeAndLocationFromChildren( * This internal function sets the code and location according to the [CodeAndLocationProvider]. * This also performs some checks, e.g., if the config disabled setting the code. */ +context(ContextProvider) private fun Node.setCodeAndLocation( provider: CodeAndLocationProvider, rawNode: AstNode, ) { - if (this.ctx?.config?.codeInNodes == true) { + if (this@ContextProvider.ctx.config.codeInNodes == true) { // only set code, if it's not already set or empty val code = provider.codeOf(rawNode) if (code != null) { 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 e8ff3b326c8..25268d77e74 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 @@ -50,41 +50,23 @@ fun LanguageProvider.incompleteType(): Type { } /** Returns a [PointerType] that describes an array reference to the current type. */ -context(ContextProvider) fun Type.array(): Type { - val c = - (this@ContextProvider).ctx - ?: throw TranslationException( - "Could not create type: translation context not available" - ) val type = this.reference(PointerType.PointerOrigin.ARRAY) - return c.typeManager.registerType(type) + return type } /** Returns a [PointerType] that describes a pointer reference to the current type. */ -context(ContextProvider) fun Type.pointer(): Type { - val c = - (this@ContextProvider).ctx - ?: throw TranslationException( - "Could not create type: translation context not available" - ) val type = this.reference(PointerType.PointerOrigin.POINTER) - return c.typeManager.registerType(type) + return type } -context(ContextProvider) fun Type.ref(): Type { - val c = - (this@ContextProvider).ctx - ?: throw TranslationException( - "Could not create type: translation context not available" - ) val type = ReferenceType(this) - return c.typeManager.registerType(type) + return type } /** @@ -106,13 +88,6 @@ fun LanguageProvider.objectType( return builtIn } - // Otherwise, we need to create a new type and register it at the type manager - val c = - (this as? ContextProvider)?.ctx - ?: throw TranslationException( - "Could not create type: translation context not available" - ) - // 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) @@ -121,8 +96,7 @@ fun LanguageProvider.objectType( // creating them and resolve them later. type.applyMetadata(this, name, rawNode = rawNode, doNotPrependNamespace = true) - // Piping it through register type will ensure that we know the type and can resolve it later - return c.typeManager.registerType(type) + return type } /** @@ -137,9 +111,9 @@ fun LanguageProvider.objectType( * does a check, whether it is a known built-in type. */ fun LanguageProvider.primitiveType(name: CharSequence): Type { - return language?.getSimpleTypeOf(name.toString()) + return language.getSimpleTypeOf(name.toString()) ?: throw TranslationException( - "Cannot find primitive type $name in language ${language?.name}. This is either an error in the language frontend or the language definition is missing a type definition." + "Cannot find primitive type $name in language ${language.name}. This is either an error in the language frontend or the language definition is missing a type definition." ) } @@ -148,5 +122,5 @@ fun LanguageProvider.primitiveType(name: CharSequence): Type { * [LanguageProvider]. */ fun LanguageProvider.isPrimitive(type: Type): Boolean { - return language?.primitiveTypeNames?.contains(type.typeName) == true + return language.primitiveTypeNames?.contains(type.typeName) == true } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt index dee0f469841..3a2c5d29e43 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt @@ -23,6 +23,8 @@ * \______/ \__| \______/ * */ +@file:Suppress("CONTEXT_RECEIVERS_DEPRECATED") + package de.fraunhofer.aisec.cpg.graph.builder import de.fraunhofer.aisec.cpg.* @@ -34,7 +36,7 @@ import de.fraunhofer.aisec.cpg.graph.scopes.RecordScope import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.CollectionComprehension -import de.fraunhofer.aisec.cpg.graph.types.FunctionType +import de.fraunhofer.aisec.cpg.graph.types.FunctionType.Companion.computeType import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.graph.types.UnknownType import de.fraunhofer.aisec.cpg.passes.executePass @@ -49,7 +51,6 @@ fun LanguageFrontend<*, *>.translationResult( val node = TranslationResult(TranslationManager.builder().config(ctx.config).build(), ctx) val component = Component() component.name = Name(DEFAULT_APPLICATION_NAME) - component.ctx = this.ctx node.addComponent(component) init(node) @@ -181,7 +182,7 @@ fun LanguageFrontend<*, *>.function( } // Make sure that our function has the correct type - node.type = FunctionType.computeType(node) + node.type = with(node) { computeType(node) } scopeManager.enterScope(node) init?.let { it(node) } @@ -206,7 +207,7 @@ fun LanguageFrontend<*, *>.method( ): MethodDeclaration { val node = newMethodDeclaration(name) node.returnTypes = listOf(returnType) - node.type = FunctionType.computeType(node) + node.type = with(node) { computeType(node) } scopeManager.enterScope(node) if (init != null) { 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 cb494bcaf0a..98c11880639 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 @@ -36,6 +36,7 @@ import de.fraunhofer.aisec.cpg.graph.statements.* 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.HasSecondaryTypeEdge import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.persistence.DoNotPersist import java.util.* @@ -43,9 +44,10 @@ import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.Relationship /** Represents the declaration or definition of a function. */ -open class FunctionDeclaration : ValueDeclaration(), DeclarationHolder, EOGStarterHolder { +open class FunctionDeclaration : + ValueDeclaration(), DeclarationHolder, EOGStarterHolder, HasSecondaryTypeEdge { @Relationship("BODY") var bodyEdge = astOptionalEdgeOf() - /** The function body. Usualfly a [Block]. */ + /** The function body. Usually a [Block]. */ var body by unwrapping(FunctionDeclaration::bodyEdge) /** The list of function parameters. */ @@ -200,6 +202,9 @@ open class FunctionDeclaration : ValueDeclaration(), DeclarationHolder, EOGStart return this.body?.cyclomaticComplexity ?: 0 } + override val secondaryTypes: List + get() = returnTypes + throwsTypes + signatureTypes + companion object { const val WHITESPACE = " " const val BRACKET_LEFT = "(" diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt index 23440b37c2c..4857b3c845a 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/RecordDeclaration.kt @@ -31,6 +31,7 @@ import de.fraunhofer.aisec.cpg.graph.edges.ast.astEdgesOf import de.fraunhofer.aisec.cpg.graph.edges.unwrapping import de.fraunhofer.aisec.cpg.graph.statements.Statement import de.fraunhofer.aisec.cpg.graph.types.DeclaresType +import de.fraunhofer.aisec.cpg.graph.types.HasSecondaryTypeEdge import de.fraunhofer.aisec.cpg.graph.types.ObjectType import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.persistence.DoNotPersist @@ -40,7 +41,12 @@ import org.neo4j.ogm.annotation.Transient /** Represents a C++ union/struct/class or Java class */ open class RecordDeclaration : - Declaration(), DeclarationHolder, StatementHolder, EOGStarterHolder, DeclaresType { + Declaration(), + DeclarationHolder, + StatementHolder, + EOGStarterHolder, + DeclaresType, + HasSecondaryTypeEdge { /** The kind, i.e. struct, class, union or enum. */ var kind: String? = null @@ -225,4 +231,7 @@ open class RecordDeclaration : override val declaredType: Type get() = toType() + + override val secondaryTypes: List + get() = superTypes } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TypedefDeclaration.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TypedefDeclaration.kt index f8aac316b51..56d6ade9e5d 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TypedefDeclaration.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TypedefDeclaration.kt @@ -25,18 +25,19 @@ */ package de.fraunhofer.aisec.cpg.graph.declarations +import de.fraunhofer.aisec.cpg.graph.types.HasSecondaryTypeEdge import de.fraunhofer.aisec.cpg.graph.types.Type -import de.fraunhofer.aisec.cpg.graph.types.UnknownType +import de.fraunhofer.aisec.cpg.graph.unknownType import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder /** Represents a type alias definition as found in C/C++: `typedef unsigned long ulong;` */ -class TypedefDeclaration : Declaration() /*, DeclaresType*/ { +class TypedefDeclaration : Declaration() /*, DeclaresType*/, HasSecondaryTypeEdge { /** The already existing type that is to be aliased */ - var type: Type = UnknownType.getUnknownType(null) + var type: Type = unknownType() /** The newly created alias to be defined */ - var alias: Type = UnknownType.getUnknownType(null) + var alias: Type = unknownType() override fun equals(other: Any?): Boolean { if (this === other) return true @@ -53,6 +54,9 @@ class TypedefDeclaration : Declaration() /*, DeclaresType*/ { .toString() } + override val secondaryTypes: List + get() = listOf(alias) + /*override val declaredType: Type get() = alias*/ } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/FileScope.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/FileScope.kt index e5b99432a88..05b5a111430 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/FileScope.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/FileScope.kt @@ -25,6 +25,7 @@ */ package de.fraunhofer.aisec.cpg.graph.scopes +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration /** @@ -33,4 +34,4 @@ import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration * * The only supported AST node is a [TranslationUnitDeclaration]. */ -class FileScope(astNode: TranslationUnitDeclaration?) : Scope(astNode) +class FileScope(ctx: TranslationContext, astNode: TranslationUnitDeclaration) : Scope(ctx, astNode) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/FunctionScope.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/FunctionScope.kt index 40bbe7fc911..9a04869e3c3 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/FunctionScope.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/FunctionScope.kt @@ -25,6 +25,14 @@ */ package de.fraunhofer.aisec.cpg.graph.scopes +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.ParameterDeclaration -class FunctionScope(astNode: FunctionDeclaration) : Scope(astNode) +/** + * Represents a scope that is only visible in the current function. This is usually used to hold + * [ParameterDeclaration] nodes, but in some languages such as Python, all variables that are inside + * the function (also the body) are inside the function scope. In other languages, such as C++, the + * variables of the function body would be in a [LocalScope] of the [FunctionDeclaration.body]. + */ +class FunctionScope(ctx: TranslationContext, astNode: FunctionDeclaration) : Scope(ctx, astNode) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/GlobalScope.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/GlobalScope.kt index 96f61a60096..0f62b4800cc 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/GlobalScope.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/GlobalScope.kt @@ -26,6 +26,7 @@ package de.fraunhofer.aisec.cpg.graph.scopes import de.fraunhofer.aisec.cpg.ScopeManager +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationManager import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.graph.nodes @@ -37,7 +38,7 @@ import de.fraunhofer.aisec.cpg.graph.nodes * scope that is restricted to a translation unit, i.e. C++ while still maintaining a unique list of * global variables. */ -class GlobalScope : Scope(null) { +class GlobalScope(ctx: TranslationContext) : Scope(ctx, null) { /** * Because the way we currently handle parallel parsing in [TranslationManager.parseParallel], diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/LocalScope.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/LocalScope.kt index 3dcd26cf0ec..fc90088acd3 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/LocalScope.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/LocalScope.kt @@ -25,10 +25,11 @@ */ package de.fraunhofer.aisec.cpg.graph.scopes +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.Node /** * Scope of validity associated to the local statement. Variables declared inside this statement are * not visible outside. */ -class LocalScope(astNode: Node) : Scope(astNode) {} +class LocalScope(ctx: TranslationContext, astNode: Node) : Scope(ctx, astNode) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/NameScope.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/NameScope.kt index d3109f8a582..67d1ecb8fef 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/NameScope.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/NameScope.kt @@ -25,7 +25,7 @@ */ package de.fraunhofer.aisec.cpg.graph.scopes -import de.fraunhofer.aisec.cpg.graph.Name +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.Node /** @@ -33,11 +33,11 @@ import de.fraunhofer.aisec.cpg.graph.Node * declared in it. This could be a package or other structural elements, like a class. In the first * case, the derived [NamespaceScope], in the latter case, the derived [RecordScope] should be used. */ -sealed class NameScope(node: Node?) : Scope(node) { +sealed class NameScope(ctx: TranslationContext, node: Node) : Scope(ctx, node) { init { astNode = node // Set the name so that we can use it as a namespace later - name = node?.name ?: Name(EMPTY_NAME) + name = node.name } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/NamespaceScope.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/NamespaceScope.kt index e6cad6e33a2..a20d1a4a5ad 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/NamespaceScope.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/NamespaceScope.kt @@ -25,6 +25,8 @@ */ package de.fraunhofer.aisec.cpg.graph.scopes +import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.graph.ContextProvider import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.declarations.ImportDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.NamespaceDeclaration @@ -38,7 +40,8 @@ import org.neo4j.ogm.annotation.Relationship * namespace. This scope is special in a way that it will only exist once (per [GlobalScope]) and * contains all symbols declared in this namespace, even if they are spread across multiple files. */ -class NamespaceScope(astNode: NamespaceDeclaration) : NameScope(astNode) { +class NamespaceScope(ctx: TranslationContext, astNode: NamespaceDeclaration) : + NameScope(ctx, astNode) { /** * This is the mirror property to [Scope.importedScopeEdges]. It specifies which other [Scope]s @@ -54,12 +57,15 @@ class NamespaceScope(astNode: NamespaceDeclaration) : NameScope(astNode) { /** Virtual property for accessing [importedScopeEdges] without property edges. */ val importedBy: MutableSet by unwrappingIncoming(NamespaceScope::importedByEdges) + context(ContextProvider) override fun addSymbol(symbol: Symbol, declaration: Declaration) { super.addSymbol(symbol, declaration) // Update imported symbols of dependent scopes for (edge in importedByEdges) { - edge.declaration?.let { ctx?.scopeManager?.updateImportedSymbols(it) } + edge.declaration?.let { + this@ContextProvider.ctx.scopeManager.updateImportedSymbols(it) + } } } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/RecordScope.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/RecordScope.kt index 61961475f25..f5f339fd689 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/RecordScope.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/RecordScope.kt @@ -25,8 +25,8 @@ */ package de.fraunhofer.aisec.cpg.graph.scopes -import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration /** Represents the scope of a record or class, most likely created by a [RecordDeclaration]. */ -class RecordScope(node: Node? = null) : NameScope(node) +class RecordScope(ctx: TranslationContext, node: RecordDeclaration) : NameScope(ctx, node) 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 cdcd700b332..377c4268e90 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 @@ -27,8 +27,10 @@ package de.fraunhofer.aisec.cpg.graph.scopes import com.fasterxml.jackson.annotation.JsonBackReference import de.fraunhofer.aisec.cpg.PopulatedByPass +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.HasImplicitReceiver import de.fraunhofer.aisec.cpg.frontends.Language +import de.fraunhofer.aisec.cpg.graph.ContextProvider import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.Declaration @@ -61,9 +63,10 @@ typealias SymbolMap = MutableMap> */ @NodeEntity sealed class Scope( + ctx: TranslationContext, @Relationship(value = "SCOPE", direction = Relationship.Direction.INCOMING) @JsonBackReference - open var astNode: Node? + open var astNode: Node?, ) : Node() { /** FQN Name currently valid */ @@ -131,6 +134,7 @@ sealed class Scope( } /** Adds a [declaration] with the defined [symbol]. */ + context(ContextProvider) open fun addSymbol(symbol: Symbol, declaration: Declaration) { if ( declaration is ImportDeclaration && diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/TemplateScope.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/TemplateScope.kt index 48e64cf4268..f43b1291b68 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/TemplateScope.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/TemplateScope.kt @@ -25,6 +25,11 @@ */ package de.fraunhofer.aisec.cpg.graph.scopes +import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.graph.Node -class TemplateScope(node: Node) : Scope(node) +/** + * Represents a scope that is only visible in the current template. This is usually to hold template + * parameters. + */ +class TemplateScope(ctx: TranslationContext, node: Node) : Scope(ctx, node) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt index 7793b85cddb..4bec1ff9fe2 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/CallExpression.kt @@ -47,7 +47,11 @@ import org.neo4j.ogm.annotation.Relationship * and is connected via the INVOKES edge to its [FunctionDeclaration]. */ open class CallExpression : - Expression(), HasOverloadedOperation, HasType.TypeObserver, ArgumentHolder { + Expression(), + HasOverloadedOperation, + HasType.TypeObserver, + ArgumentHolder, + HasSecondaryTypeEdge { /** * Connection to its [FunctionDeclaration]. This will be populated by the [SymbolResolver]. This * will have an effect on the [type] @@ -286,4 +290,7 @@ open class CallExpression : // TODO: Not sure if we can add the template, templateParameters, templateInstantiation fields // here override fun hashCode() = Objects.hash(super.hashCode(), arguments) + + override val secondaryTypes: List + get() = signature } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt index c6c81d51ae8..ed1ec59eba2 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/statements/expressions/ConstructExpression.kt @@ -81,7 +81,7 @@ class ConstructExpression : CallExpression() { set(value) { field = value if (value != null && this.type is UnknownType) { - type = objectType(value.name) + // type = objectType(value.name) } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/AutoType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/AutoType.kt index 2dcca508e35..7f9d399ec67 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/AutoType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/AutoType.kt @@ -30,12 +30,12 @@ import de.fraunhofer.aisec.cpg.graph.unknownType /** * This type represents a [Type] that uses auto-inference (usually from an initializer) to determine - * it's actual type. It is commonly used in dynamically typed languages or in languages that have a + * its actual type. It is commonly used in dynamically typed languages or in languages that have a * special keyword, such as `auto` in C++. * * Note: This is intentionally a distinct type and not the [UnknownType]. */ -class AutoType(override var language: Language<*>) : Type("auto", language) { +class AutoType(language: Language<*>) : Type("auto", language) { override fun reference(pointer: PointerType.PointerOrigin?): Type { return PointerType(this, pointer) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionPointerType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionPointerType.kt index 094f73218ae..c35e7283974 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionPointerType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionPointerType.kt @@ -26,7 +26,7 @@ package de.fraunhofer.aisec.cpg.graph.types import de.fraunhofer.aisec.cpg.frontends.Language -import de.fraunhofer.aisec.cpg.frontends.UnknownLanguage +import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder @@ -51,22 +51,11 @@ class FunctionPointerType : Type { parameters: List = listOf(), language: Language<*>, returnType: Type = UnknownType.getUnknownType(language), - ) : super(EMPTY_NAME, language) { + ) : super(Name(EMPTY_NAME), language) { this.parameters = parameters this.returnType = returnType } - constructor( - type: Type, - parameters: List = listOf(), - language: Language<*> = UnknownLanguage, - returnType: Type = UnknownType.getUnknownType(language), - ) : super(type) { - this.parameters = parameters - this.returnType = returnType - this.language = language - } - override fun reference(pointer: PointerOrigin?): PointerType { return PointerType(this, pointer) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt index 1d669daa6a6..b283fc1aada 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/FunctionType.kt @@ -26,7 +26,7 @@ package de.fraunhofer.aisec.cpg.graph.types import de.fraunhofer.aisec.cpg.frontends.Language -import de.fraunhofer.aisec.cpg.frontends.TranslationException +import de.fraunhofer.aisec.cpg.graph.ContextProvider import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.unknownType @@ -43,7 +43,7 @@ constructor( var parameters: List = listOf(), var returnTypes: List = listOf(), language: Language<*>, -) : Type(typeName, language) { +) : Type(typeName, language), HasSecondaryTypeEdge { override fun reference(pointer: PointerType.PointerOrigin?): Type { // TODO(oxisto): In the future, we actually could just remove the FunctionPointerType @@ -59,12 +59,15 @@ constructor( return unknownType() } + override val secondaryTypes: List + get() = parameters + returnTypes + companion object { /** * This helper function computes a [FunctionType] out of an existing [FunctionDeclaration]. */ @JvmStatic - fun computeType(func: FunctionDeclaration): FunctionType { + fun ContextProvider.computeType(func: FunctionDeclaration): FunctionType { val type = FunctionType( func.signature, @@ -73,8 +76,7 @@ constructor( func.language, ) - val c = func.ctx ?: throw TranslationException("context not available") - return c.typeManager.registerType(type) + return type } } } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasSecondaryTypeEdge.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasSecondaryTypeEdge.kt index a4a47c667a8..6e5016cc834 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasSecondaryTypeEdge.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasSecondaryTypeEdge.kt @@ -25,16 +25,13 @@ */ package de.fraunhofer.aisec.cpg.graph.types -import de.fraunhofer.aisec.cpg.graph.declarations.TypeParameterDeclaration import de.fraunhofer.aisec.cpg.passes.TypeResolver /** - * The [TypeResolver] needs to be aware of all outgoing edges to types in order to merge equal types - * to the same node. For the primary type edge, this is achieved through the [HasType] interface. If - * a node has additional type edges (e.g. [TypeParameterDeclaration.default]) the node must - * implement the [updateType] method, so that the current type is always replaced with the merged - * one. + * The [TypeResolver] needs to be aware of all outgoing edges to types in order to resolve them. If + * a node has more than one edge to a type, we need to know about it. */ -fun interface HasSecondaryTypeEdge { - fun updateType(typeState: Collection) +interface HasSecondaryTypeEdge { + + val secondaryTypes: List } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt index 90a18e7095f..afc1b24bc6c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/HasType.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.graph.types -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.ValueDeclaration @@ -42,7 +41,7 @@ import de.fraunhofer.aisec.cpg.graph.unknownType * implementations of this class, an [Expression] and a [ValueDeclaration]. All other nodes with * types should derive from these two base classes. */ -interface HasType : ContextProvider, LanguageProvider { +interface HasType : LanguageProvider { /** * This property refers to the *definite* [Type] that the [Node] has. If you are unsure about diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ObjectType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ObjectType.kt index ca54940ac83..8c7b64f035b 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ObjectType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ObjectType.kt @@ -38,7 +38,7 @@ import org.neo4j.ogm.annotation.Relationship * This is the main type in the Type system. ObjectTypes describe objects, as instances of a class. * This also includes primitive data types. */ -open class ObjectType : Type { +open class ObjectType : Type, HasSecondaryTypeEdge { /** * Reference from the [ObjectType] to its class ([RecordDeclaration]), only if the class is * available. This is set by the [TypeResolver]. @@ -67,17 +67,6 @@ open class ObjectType : Type { this.language = language } - constructor( - type: Type?, - generics: List, - primitive: Boolean, - language: Language<*>, - ) : super(type) { - this.language = language - this.generics = generics - isPrimitive = primitive - } - /** Empty default constructor for use in Neo4J persistence. */ constructor() : super() { isPrimitive = false @@ -109,4 +98,7 @@ open class ObjectType : Type { } override fun hashCode() = Objects.hash(super.hashCode(), generics, isPrimitive) + + override val secondaryTypes: List + get() = generics } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ParameterizedType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ParameterizedType.kt index 6e921373826..89771ea439a 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ParameterizedType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ParameterizedType.kt @@ -33,13 +33,7 @@ import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin * generics in the graph are represented as [ParameterizedType] nodes. */ class ParameterizedType : Type { - constructor(type: Type) : super(type) { - language = type.language - } - - constructor(typeName: String?, language: Language<*>) : super(typeName) { - this.language = language - } + constructor(typeName: CharSequence, language: Language<*>) : super(typeName, language) override fun reference(pointer: PointerOrigin?): Type { return PointerType(this, pointer) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/PointerType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/PointerType.kt index e94f3e3c920..3e7d08f4739 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/PointerType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/PointerType.kt @@ -35,7 +35,7 @@ import org.neo4j.ogm.annotation.Relationship * is no such pointer concept. */ class PointerType : Type, SecondOrderType { - @Relationship(value = "ELEMENT_TYPE") override lateinit var elementType: Type + @Relationship(value = "ELEMENT_TYPE") override var elementType: Type enum class PointerOrigin { POINTER, @@ -45,30 +45,20 @@ class PointerType : Type, SecondOrderType { var pointerOrigin: PointerOrigin? = null private set - constructor() : super() - - constructor(elementType: Type, pointerOrigin: PointerOrigin?) : super() { - language = elementType.language - name = - if (pointerOrigin == PointerOrigin.ARRAY) { - elementType.name.append("[]") - } else { - elementType.name.append("*") - } - this.pointerOrigin = pointerOrigin - this.elementType = elementType - } - - constructor(type: Type?, elementType: Type, pointerOrigin: PointerOrigin?) : super(type) { - language = elementType.language - name = - if (pointerOrigin == PointerOrigin.ARRAY) { - elementType.name.append("[]") - } else { - elementType.name.append("*") - } + constructor( + elementType: Type, + pointerOrigin: PointerOrigin?, + ) : super( + if (pointerOrigin == PointerOrigin.ARRAY) { + elementType.name.append("[]") + } else { + elementType.name.append("*") + }, + elementType.language, + ) { this.pointerOrigin = pointerOrigin this.elementType = elementType + this.elementType.secondOrderTypes += this } /** diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ReferenceType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ReferenceType.kt index 3ed69cfe7f0..dd3e3a1b008 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ReferenceType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/ReferenceType.kt @@ -45,12 +45,7 @@ class ReferenceType : Type, SecondOrderType { language = reference.language name = reference.name.append("&") this.elementType = reference - } - - constructor(type: Type, reference: Type) : super(type) { - language = reference.language - name = reference.name.append("&") - this.elementType = reference + this.elementType.secondOrderTypes += this } /** diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/TupleType.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/TupleType.kt index cb4eee97086..18c2a0f1331 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/TupleType.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/TupleType.kt @@ -32,7 +32,7 @@ import de.fraunhofer.aisec.cpg.graph.unknownType * Represents a tuple of types. Primarily used in resolving function calls with multiple return * values. */ -class TupleType(types: List) : Type() { +class TupleType(types: List) : Type(), HasSecondaryTypeEdge { var types: List = listOf() set(value) { field = value @@ -50,4 +50,7 @@ class TupleType(types: List) : Type() { override fun dereference(): Type { return unknownType() } + + override val secondaryTypes: List + get() = types } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/Type.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/Type.kt index 39a1f9c853a..35a9d8c81e4 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/Type.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/types/Type.kt @@ -36,6 +36,7 @@ import de.fraunhofer.aisec.cpg.graph.parseName import de.fraunhofer.aisec.cpg.graph.types.PointerType.PointerOrigin import de.fraunhofer.aisec.cpg.passes.TypeHierarchyResolver import de.fraunhofer.aisec.cpg.passes.TypeResolver +import de.fraunhofer.aisec.cpg.persistence.DoNotPersist import java.util.* import org.apache.commons.lang3.builder.ToStringBuilder import org.neo4j.ogm.annotation.NodeEntity @@ -75,43 +76,33 @@ abstract class Type : Node { open var typeOrigin: Origin? = null + /** + * The list of second-order types based on this type. An example might be a [PointerType], whose + * [PointerType.elementType] is this type. + */ + @DoNotPersist val secondOrderTypes = mutableListOf() + /** * This points to the [DeclaresType] node (most likely a [Declaration]), that declares this * type. At some point this should replace [ObjectType.recordDeclaration]. */ @PopulatedByPass(TypeResolver::class) var declaredFrom: DeclaresType? = null - constructor() { + constructor() : super() { name = Name(EMPTY_NAME, null, language) } - constructor(typeName: String?) { - name = language.parseName(typeName ?: UNKNOWN_TYPE_STRING) - typeOrigin = Origin.UNRESOLVED - } - - constructor(type: Type?) { - type?.name?.let { name = it.clone() } - typeOrigin = type?.typeOrigin - } - - constructor(typeName: CharSequence, language: Language<*>) { + constructor(typeName: CharSequence, language: Language<*>) : this() { + this.language = language name = if (this is FunctionType) { Name(typeName.toString(), null, language) } else { - language.parseName(typeName) + parseName(typeName) } - this.language = language typeOrigin = Origin.UNRESOLVED } - constructor(fullTypeName: Name, language: Language<*>) { - name = fullTypeName.clone() - typeOrigin = Origin.UNRESOLVED - this.language = language - } - /** Type Origin describes where the Type information came from */ enum class Origin { RESOLVED, @@ -141,7 +132,9 @@ abstract class Type : Node { */ abstract fun dereference(): Type - open fun refreshNames() {} + open fun refreshNames() { + secondOrderTypes.forEach { it.refreshNames() } + } @get:JsonIgnore var root: Type diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalker.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalker.kt index adcf47155ad..0ccc56bd758 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalker.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/SubgraphWalker.kt @@ -437,7 +437,6 @@ fun SubgraphWalker.ScopedWalker.replace(parent: Node?, old: Expression, new: Exp } private fun CallExpression.duplicateTo(call: CallExpression, callee: Reference) { - call.ctx = this.ctx call.language = this.language call.scope = this.scope call.argumentEdges.clear() diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt index bb1a4eb3dcf..1c7797cd51f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/CXXCallResolverHelper.kt @@ -155,7 +155,6 @@ fun signatureWithImplicitCastTransformation( val funcType = functionSignature[i] if (callType?.isPrimitive == true && funcType.isPrimitive && callType != funcType) { val implicitCast = CastExpression() - implicitCast.ctx = call.ctx implicitCast.isImplicit = true implicitCast.castType = funcType implicitCast.language = funcType.language diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt index 8989c7c9a99..597fd0a90af 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt @@ -256,7 +256,7 @@ class DFGPass(ctx: TranslationContext) : ComponentPass(ctx) { functionSummaries: DFGFunctionSummaries, ) { if (node.isInferred) { - val summaryExists = functionSummaries.addFlowsToFunctionDeclaration(node) + val summaryExists = with(functionSummaries) { addFlowsToFunctionDeclaration(node) } if (!summaryExists) { // If the function is inferred, we connect all parameters to the function diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt index 226375489be..f233ca5241c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt @@ -43,7 +43,6 @@ import de.fraunhofer.aisec.cpg.passes.configuration.ExecuteLate import de.fraunhofer.aisec.cpg.passes.configuration.RequiredFrontend import de.fraunhofer.aisec.cpg.passes.configuration.RequiresLanguageTrait import de.fraunhofer.aisec.cpg.processing.strategy.Strategy -import de.fraunhofer.aisec.cpg.processing.strategy.Strategy.TRANSLATION_UNITS_LEAST_IMPORTS import java.util.concurrent.CompletableFuture import java.util.function.Consumer import kotlin.reflect.KClass 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 85c7554e469..0e641a0f9b9 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 @@ -320,7 +320,7 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { base is Reference && base.name.localName == language.superClassKeyword ) { - language.handleSuperExpression(current, record, scopeManager) + with(language) { handleSuperExpression(current, record) } } // Handle a possible overloaded operator->. If we find an overloaded operator, this inserts @@ -626,7 +626,7 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) { template.parameters.size - constructExpression.templateArguments.size if (defaultDifference <= template.parameterDefaults.size) { // Check if predefined template value is used as default in next value - addRecursiveDefaultTemplateArgs(constructExpression, template, scopeManager) + addRecursiveDefaultTemplateArgs(constructExpression, template) // Add missing defaults val missingNewParams: List = diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TemplateCallResolverHelper.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TemplateCallResolverHelper.kt index b8d33f77e59..d2593ca40df 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TemplateCallResolverHelper.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/TemplateCallResolverHelper.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.* @@ -48,10 +47,9 @@ import de.fraunhofer.aisec.cpg.graph.types.apply * @param constructExpression * @param template */ -fun addRecursiveDefaultTemplateArgs( +fun SymbolResolver.addRecursiveDefaultTemplateArgs( constructExpression: ConstructExpression, template: RecordTemplateDeclaration, - scopeManager: ScopeManager, ) { var templateParameters: Int do { @@ -74,7 +72,6 @@ fun addRecursiveDefaultTemplateArgs( constructExpression, templateParametersExplicitInitialization, templateParameterRealDefaultInitialization, - scopeManager, ) } while (templateParameters != constructExpression.templateArguments.size) } @@ -113,12 +110,11 @@ fun handleExplicitTemplateParameters( * @param templateParameterRealDefaultInitialization mapping of template parameter to its real * default (no recursive) */ -fun applyMissingParams( +fun SymbolResolver.applyMissingParams( template: RecordTemplateDeclaration, constructExpression: ConstructExpression, templateParametersExplicitInitialization: Map, templateParameterRealDefaultInitialization: Map, - scopeManager: ScopeManager, ) { with(constructExpression) { val missingParams: List = 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 97c5705f2bd..5fab214d183 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 @@ -23,6 +23,8 @@ * \______/ \__| \______/ * */ +@file:Suppress("CONTEXT_RECEIVERS_DEPRECATED") + package de.fraunhofer.aisec.cpg.passes import de.fraunhofer.aisec.cpg.ScopeManager @@ -30,14 +32,19 @@ import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration +import de.fraunhofer.aisec.cpg.graph.scopes.GlobalScope import de.fraunhofer.aisec.cpg.graph.types.DeclaresType +import de.fraunhofer.aisec.cpg.graph.types.HasSecondaryTypeEdge +import de.fraunhofer.aisec.cpg.graph.types.HasType import de.fraunhofer.aisec.cpg.graph.types.ObjectType import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.graph.types.recordDeclaration -import de.fraunhofer.aisec.cpg.helpers.Benchmark import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker +import de.fraunhofer.aisec.cpg.helpers.identitySetOf import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn import de.fraunhofer.aisec.cpg.passes.inference.tryRecordInference +import de.fraunhofer.aisec.cpg.processing.strategy.Strategy +import kotlin.collections.plusAssign /** * The purpose of this [Pass] is to establish a relationship between [Type] nodes (more specifically @@ -50,20 +57,48 @@ open class TypeResolver(ctx: TranslationContext) : ComponentPass(ctx) { override fun accept(component: Component) { ctx.currentComponent = component - resolveFirstOrderTypes() - refreshNames() + walker = SubgraphWalker.ScopedWalker(scopeManager, strategy = Strategy::AST_FORWARD) + walker.registerHandler { handleNode(it) } + walker.iterate(component) + } - val b = - Benchmark( - TypeResolver::class.java, - "Updating imported symbols for ${component.imports.size} imports", - ) - b.stop() + /** + * This function is called for each [Node] in the component. It checks if the node has a type or + * declares a type. If so, it tries to resolve the type using [resolveType]. It also checks for + * secondary type edges (see [HasSecondaryTypeEdge] and resolves them as well. + * + * @param node The node to handle. + */ + private fun handleNode(node: Node) { + if (node is HasType) { + var type = node.type.root + handleType(type) + } else if (node is DeclaresType) { + handleType(node.declaredType) + } + + if (node is HasSecondaryTypeEdge) { + node.secondaryTypes.forEach { handleType(it) } + } } - private fun refreshNames() { - for (type in typeManager.secondOrderTypes) { - type.refreshNames() + /** + * This function is called for each [Type] in the component. It checks if the type is + * unresolved. If so, it tries to resolve the type using [resolveType]. It also checks for + * secondary type edges (see [HasSecondaryTypeEdge] and resolves them as well. + * + * @param type The type to handle. + */ + private fun handleType(type: Type) { + if ( + type is ObjectType && type.typeOrigin == Type.Origin.UNRESOLVED || + type.typeOrigin == Type.Origin.GUESSED + ) { + resolveType(type) + } + + if (type is HasSecondaryTypeEdge) { + type.secondaryTypes.filter { it.root != type }.forEach { handleType(it.root) } } } @@ -88,6 +123,10 @@ open class TypeResolver(ctx: TranslationContext) : ComponentPass(ctx) { * [Type.typeOrigin] to [Type.Origin.RESOLVED]. */ fun resolveType(type: Type): Boolean { + // Because we still have multiple "global scopes" (one per parallel context), we need to + // make sure they all point to the final global scope + type.updateGlobalScope() + // Check for a possible typedef var target = scopeManager.typedefFor(type.name, type.scope) if (target != null) { @@ -102,6 +141,8 @@ open class TypeResolver(ctx: TranslationContext) : ComponentPass(ctx) { type.declaredFrom = originDeclares type.recordDeclaration = originDeclares type.typeOrigin = Type.Origin.RESOLVED + typeManager.resolvedTypes += type + return true } @@ -127,9 +168,12 @@ open class TypeResolver(ctx: TranslationContext) : ComponentPass(ctx) { declaredType.name, ) type.name = declaredType.name + type.refreshNames() type.declaredFrom = declares type.recordDeclaration = declares as? RecordDeclaration type.typeOrigin = Type.Origin.RESOLVED + typeManager.resolvedTypes += type + if (declaredType.superTypes.contains(type)) log.warn( "Removing type {} from the list of its own supertypes. This would create a type cycle that is not allowed.", @@ -148,6 +192,7 @@ open class TypeResolver(ctx: TranslationContext) : ComponentPass(ctx) { } } ) + return true } @@ -158,9 +203,30 @@ open class TypeResolver(ctx: TranslationContext) : ComponentPass(ctx) { // Nothing to do } - /** Resolves all types in [TypeManager.firstOrderTypes] using [resolveType]. */ - fun resolveFirstOrderTypes() { - for (type in typeManager.firstOrderTypes.sortedBy { it.name }) { + /** Resolves all types in [TypeManager.resolvedTypes] using [resolveType]. */ + fun resolveFirstOrderTypes(comp: Component) { + // Let's find all of our types! + val allTypes = identitySetOf() + comp.nodes.forEach { + if (it is HasType) { + var type = it.type.root + if (type is HasSecondaryTypeEdge) { + allTypes += type.secondaryTypes.map { it.root } + } + allTypes += type + } else if (it is DeclaresType) { + allTypes += it.declaredType + } + + if (it is HasSecondaryTypeEdge) { + allTypes += it.secondaryTypes.map { it.root } + } + } + + // Gather them in the type manager, just so we have them + typeManager.allFirstOrderTypes += allTypes + + for (type in allTypes) { if ( type is ObjectType && type.typeOrigin == Type.Origin.UNRESOLVED || type.typeOrigin == Type.Origin.GUESSED @@ -170,3 +236,15 @@ open class TypeResolver(ctx: TranslationContext) : ComponentPass(ctx) { } } } + +/** + * This helper function sets the [Type.scope] to the current [ScopeManager.globalScope] if it has a + * [GlobalScope]. This is necessary because the parallel parsing introduces multiple global scopes. + */ +context(ContextProvider) +private fun Type.updateGlobalScope() { + if (scope is GlobalScope) { + scope = ctx.scopeManager.globalScope + secondOrderTypes.forEach { it.updateGlobalScope() } + } +} 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 7ebfe849e32..e2eb242b538 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 @@ -32,6 +32,7 @@ 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.SignatureMatches +import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.CastNotPossible import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.* @@ -39,6 +40,7 @@ import de.fraunhofer.aisec.cpg.graph.parseName import de.fraunhofer.aisec.cpg.graph.types.Type import de.fraunhofer.aisec.cpg.graph.unknownType import de.fraunhofer.aisec.cpg.matchesSignature +import de.fraunhofer.aisec.cpg.passes.DFGPass import de.fraunhofer.aisec.cpg.tryCast import java.io.File import org.slf4j.Logger @@ -46,8 +48,9 @@ import org.slf4j.LoggerFactory /** * If the user of the library registers one or multiple DFG-function summary files (via - * [Builder.registerFunctionSummaries]), this class is responsible for parsing the files, caching - * the result and adding the respective DFG summaries to the [FunctionDeclaration]. + * [TranslationConfiguration.Builder.registerFunctionSummaries]), this class is responsible for + * parsing the files, caching the result and adding the respective DFG summaries to the + * [FunctionDeclaration]. */ class DFGFunctionSummaries { private constructor() @@ -91,7 +94,7 @@ class DFGFunctionSummaries { * Adds the DFG edges to the [functionDeclaration] depending on the function summaries which are * kept in this object. If no suitable entry was found, this method returns `false`. */ - fun addFlowsToFunctionDeclaration(functionDeclaration: FunctionDeclaration): Boolean { + fun DFGPass.addFlowsToFunctionDeclaration(functionDeclaration: FunctionDeclaration): Boolean { val dfgEntries = findFunctionDeclarationEntry(functionDeclaration) ?: return false applyDfgEntryToFunctionDeclaration(functionDeclaration, dfgEntries) return true @@ -111,13 +114,14 @@ class DFGFunctionSummaries { * This method returns the list of [DFGEntry] for the "best match" or `null` if no entry * matches. */ - private fun findFunctionDeclarationEntry(functionDecl: FunctionDeclaration): List? { + private fun DFGPass.findFunctionDeclarationEntry( + functionDecl: FunctionDeclaration + ): List? { if (functionToDFGEntryMap.isEmpty()) return null 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. diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt index aeb2470128b..e58b5e59a99 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/Inference.kt @@ -42,6 +42,7 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference import de.fraunhofer.aisec.cpg.graph.statements.expressions.TypeExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.UnaryOperator import de.fraunhofer.aisec.cpg.graph.types.* +import de.fraunhofer.aisec.cpg.graph.types.FunctionType.Companion.computeType import de.fraunhofer.aisec.cpg.helpers.Util.debugWithFileLocation import de.fraunhofer.aisec.cpg.helpers.Util.errorWithFileLocation import java.util.* @@ -134,7 +135,7 @@ class Inference internal constructor(val start: Node, override val ctx: Translat inferred.returnTypes = listOf(returnType) } - inferred.type = FunctionType.computeType(inferred) + inferred.type = computeType(inferred) debugWithFileLocation( hint, diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/PassHelper.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/PassHelper.kt index 9fd781dbaf6..7622313f4b1 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/PassHelper.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/PassHelper.kt @@ -134,14 +134,14 @@ internal fun Pass<*>.tryRecordInference(type: Type, source: Node): RecordDeclara val record = (holder ?: scopeManager.translationUnitForInference(source)) - ?.startInference(ctx) + .startInference(ctx) ?.inferRecordDeclaration(type, kind, source) // Update the type's record. Because types are only unique per scope, we potentially need to // update multiple type nodes, i.e., all type nodes whose FQN match the inferred record. We only // need to do this if we are NOT in the type resolver if (this !is TypeResolver && record != null) { - typeManager.firstOrderTypes + typeManager.resolvedTypes .filter { it.name == record.name } .forEach { it.recordDeclaration = record } } @@ -401,7 +401,7 @@ internal fun Pass<*>.tryMethodInference( if (inferGlobalFunction) { var currentTU = - scopeManager.currentScope?.globalScope?.astNode as? TranslationUnitDeclaration + scopeManager.currentScope.globalScope?.astNode as? TranslationUnitDeclaration return listOfNotNull(currentTU?.inferFunction(call, ctx = ctx)) } diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/persistence/Common.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/persistence/Common.kt index 8b78a9a4552..4582e08cb71 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/persistence/Common.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/persistence/Common.kt @@ -172,10 +172,12 @@ val KClass<*>.labels: Set } internal val kClassType = KClass::class.createType(listOf(KTypeProjection.STAR)) -internal val nodeType = Node::class.createType() +internal val persistableType = Persistable::class.createType() internal val collectionType = Collection::class.createType(listOf(KTypeProjection.STAR)) -internal val collectionOfNodeType = - Collection::class.createType(listOf(KTypeProjection(variance = KVariance.OUT, type = nodeType))) +internal val collectionOfPersistableType = + Collection::class.createType( + listOf(KTypeProjection(variance = KVariance.OUT, type = persistableType)) + ) internal val edgeCollectionType = EdgeCollection::class.createType(listOf(KTypeProjection.STAR, KTypeProjection.STAR)) internal val mapType = Map::class.createType(listOf(KTypeProjection.STAR, KTypeProjection.STAR)) @@ -268,7 +270,7 @@ private fun isSimpleProperty(property: KProperty1): Boolean returnType.isSubtypeOf(kClassType) -> false returnType.isSubtypeOf(collectionType) -> false returnType.isSubtypeOf(mapType) -> false - returnType.isSubtypeOf(nodeType) -> false + returnType.isSubtypeOf(persistableType) -> false (returnType.javaType as? Class<*>)?.isInterface == true -> false else -> true } @@ -300,8 +302,8 @@ private fun isRelationship(property: KProperty1): Boolean { property.javaField?.getAnnotation(Relationship::class.java)?.direction == INCOMING -> false property.visibility == KVisibility.PRIVATE -> false returnType.isSubtypeOf(edgeCollectionType) -> true - returnType.isSubtypeOf(collectionOfNodeType) -> true - returnType.isSubtypeOf(nodeType) -> true + returnType.isSubtypeOf(collectionOfPersistableType) -> true + returnType.isSubtypeOf(persistableType) -> true else -> false } } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/ExclusionTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/ExclusionTest.kt index 7c34879b3a0..4131c015bca 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/ExclusionTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/ExclusionTest.kt @@ -40,7 +40,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull -class TestFileLanguage(ctx: TranslationContext) : TestLanguage(ctx) { +class TestFileLanguage() : TestLanguage() { override val fileExtensions: List get() = listOf("file") @@ -50,13 +50,8 @@ class TestFileLanguage(ctx: TranslationContext) : TestLanguage(ctx) { /** Just a test frontend that "reads" a file and returns an empty [TranslationUnitDeclaration]. */ class TestFileLanguageFrontend( - ctx: TranslationContext = - TranslationContext( - TranslationConfiguration.builder().build(), - ScopeManager(), - TypeManager(), - ), - language: Language = TestFileLanguage(ctx), + ctx: TranslationContext = TranslationContext(TranslationConfiguration.builder().build()), + language: Language = TestFileLanguage(), ) : TestLanguageFrontend(ctx, language) { override fun parse(file: File): TranslationUnitDeclaration { return newTranslationUnitDeclaration(file.name) 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 04b33d726d4..b40dd003950 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 @@ -88,32 +88,25 @@ class DFGFunctionSummariesTest { // 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) - } + var recordDecl = + startInference(ctx)?.inferRecordDeclaration(listType) + listType.recordDeclaration = recordDecl + recordDecl?.addSuperClass(objectType) + listType.superTypes.add(objectType) val specialListType = t("test.SpecialList") - ctx?.let { - val recordDecl = - startInference(it)?.inferRecordDeclaration(specialListType) - specialListType.recordDeclaration = recordDecl - recordDecl?.addSuperClass(listType) - specialListType.superTypes.add(listType) - } + recordDecl = + startInference(ctx)?.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) - } + recordDecl = + startInference(ctx)?.inferRecordDeclaration(verySpecialListType) + verySpecialListType.recordDeclaration = recordDecl + recordDecl?.addSuperClass(specialListType) + verySpecialListType.superTypes.add(listType) } function("main", t("int")) { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/InferenceTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/InferenceTest.kt index 43f867a1058..f31136e8884 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/InferenceTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/InferenceTest.kt @@ -98,17 +98,12 @@ class InferenceTest { @Test fun testUnaryOperatorReturnType() { - val tu = - GraphExamples.getInferenceUnaryOperatorReturnType() - .components - .firstOrNull() - ?.translationUnits - ?.firstOrNull() - assertNotNull(tu) - with(tu) { + val result = GraphExamples.getInferenceUnaryOperatorReturnType() + assertNotNull(result) + with(result) { val longType = assertResolvedType("long") - val bar = tu.functions["bar"] + val bar = functions["bar"] assertNotNull(bar) assertEquals(longType, bar.returnTypes.singleOrNull()) @@ -117,18 +112,13 @@ class InferenceTest { @Test fun testTupleTypeReturnType() { - val tu = - GraphExamples.getInferenceTupleReturnType() - .components - .firstOrNull() - ?.translationUnits - ?.firstOrNull() - assertNotNull(tu) - with(tu) { + val result = GraphExamples.getInferenceTupleReturnType() + assertNotNull(result) + with(result) { val fooType = assertResolvedType("Foo") val barType = assertResolvedType("Bar") - val bar = tu.functions["bar"] + val bar = functions["bar"] assertNotNull(bar) assertEquals(listOf(fooType, barType), bar.returnTypes) @@ -137,22 +127,17 @@ class InferenceTest { @Test fun testBinaryOperatorReturnType() { - val tu = - GraphExamples.getInferenceBinaryOperatorReturnType() - .components - .firstOrNull() - ?.translationUnits - ?.firstOrNull() - assertNotNull(tu) - with(tu) { + val result = GraphExamples.getInferenceBinaryOperatorReturnType() + assertNotNull(result) + with(result) { val intType = assertResolvedType("int") val longType = assertResolvedType("long") - val bar = tu.functions["bar"] + val bar = functions["bar"] assertNotNull(bar) assertEquals(intType, bar.returnTypes.singleOrNull()) - val baz = tu.functions["baz"] + val baz = functions["baz"] assertNotNull(baz) assertEquals(longType, baz.returnTypes.singleOrNull()) } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTest.kt index ee090563a15..01bd225bdc7 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/LanguageTest.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.frontends -import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationManager @@ -74,17 +73,16 @@ class LanguageTest { @Test fun testMultiLanguage() { - class OtherLanguage(ctx: TranslationContext) : TestLanguage(ctx) + class OtherLanguage(ctx: TranslationContext) : TestLanguage() val ctx = TranslationContext( config = TranslationConfiguration.builder().build(), - scopeManager = ScopeManager(), typeManager = TypeManager(), ) val otherLanguage = OtherLanguage(ctx) - val testLanguage = TestLanguage(ctx) + val testLanguage = TestLanguage() val result = TranslationResult( @@ -96,7 +94,6 @@ class LanguageTest { with(TestLanguageFrontend(language = otherLanguage)) { val tu = newTranslationUnitDeclaration("tu-language-other") val comp = Component() - comp.ctx = this.ctx comp.addTranslationUnit(tu) comp } @@ -106,7 +103,6 @@ class LanguageTest { with(TestLanguageFrontend(language = testLanguage)) { val tu = newTranslationUnitDeclaration("tu-language-test") val comp = Component() - comp.ctx = this.ctx comp.addTranslationUnit(tu) comp } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilderTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilderTest.kt index b8a2423bc01..16693fe7221 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilderTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/ExpressionBuilderTest.kt @@ -25,14 +25,13 @@ */ package de.fraunhofer.aisec.cpg.graph +import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend import de.fraunhofer.aisec.cpg.graph.builder.plus import de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration import de.fraunhofer.aisec.cpg.graph.edges.flows.CallingContextIn import de.fraunhofer.aisec.cpg.graph.edges.flows.ContextSensitiveDataflow import de.fraunhofer.aisec.cpg.graph.edges.flows.FieldDataflowGranularity import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression -import de.fraunhofer.aisec.cpg.graph.statements.expressions.Literal -import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -40,35 +39,39 @@ import kotlin.test.assertTrue class ExpressionBuilderTest { @Test fun testDuplicateWithDFGProperties() { - val node1 = Literal() - val node2 = Reference() - val granularity = FieldDataflowGranularity(FieldDeclaration()) - val callingContextIn = CallingContextIn(CallExpression()) - node1.prevDFGEdges.addContextSensitive(node2, granularity, callingContextIn) + with(TestLanguageFrontend()) { + val node1 = newLiteral(1) + val node2 = newReference("node2") + val granularity = FieldDataflowGranularity(FieldDeclaration()) + val callingContextIn = CallingContextIn(CallExpression()) + node1.prevDFGEdges.addContextSensitive(node2, granularity, callingContextIn) - val clone = node1.duplicate(false) - val clonedPrevDFG = clone.prevDFGEdges.single() - assertTrue(clonedPrevDFG is ContextSensitiveDataflow) - assertEquals(callingContextIn, clonedPrevDFG.callingContext) - assertEquals(granularity, clonedPrevDFG.granularity) + val clone = node1.duplicate(false) + val clonedPrevDFG = clone.prevDFGEdges.single() + assertTrue(clonedPrevDFG is ContextSensitiveDataflow) + assertEquals(callingContextIn, clonedPrevDFG.callingContext) + assertEquals(granularity, clonedPrevDFG.granularity) - assertEquals(setOf(node1, clone), node2.nextDFG) + assertEquals(setOf(node1, clone), node2.nextDFG) + } } @Test fun testDuplicateWithDFGProperties2() { - val node1 = Literal() - val node2 = Reference() - val granularity = FieldDataflowGranularity(FieldDeclaration()) - val callingContextIn = CallingContextIn(CallExpression()) - node1.nextDFGEdges.addContextSensitive(node2, granularity, callingContextIn) + with(TestLanguageFrontend()) { + val node1 = newLiteral(1) + val node2 = newReference("node2") + val granularity = FieldDataflowGranularity(FieldDeclaration()) + val callingContextIn = CallingContextIn(CallExpression()) + node1.nextDFGEdges.addContextSensitive(node2, granularity, callingContextIn) - val clone = node1.duplicate(false) - val clonedPrevDFG = clone.nextDFGEdges.single() - assertTrue(clonedPrevDFG is ContextSensitiveDataflow) - assertEquals(callingContextIn, clonedPrevDFG.callingContext) - assertEquals(granularity, clonedPrevDFG.granularity) + val clone = node1.duplicate(false) + val clonedPrevDFG = clone.nextDFGEdges.single() + assertTrue(clonedPrevDFG is ContextSensitiveDataflow) + assertEquals(callingContextIn, clonedPrevDFG.callingContext) + assertEquals(granularity, clonedPrevDFG.granularity) - assertEquals(setOf(node1, clone), node2.prevDFG) + assertEquals(setOf(node1, clone), node2.prevDFG) + } } } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/StatementBuilderTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/StatementBuilderTest.kt index 088e3420261..4f967ccdab1 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/StatementBuilderTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/StatementBuilderTest.kt @@ -25,10 +25,8 @@ */ package de.fraunhofer.aisec.cpg.graph -import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.TranslationContext -import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend import de.fraunhofer.aisec.cpg.graph.builder.translationResult import de.fraunhofer.aisec.cpg.graph.scopes.GlobalScope @@ -42,12 +40,7 @@ class StatementBuilderTest { fun testNewLookupScopeStatement() { val frontend = TestLanguageFrontend( - ctx = - TranslationContext( - TranslationConfiguration.builder().defaultPasses().build(), - ScopeManager(), - TypeManager(), - ) + ctx = TranslationContext(TranslationConfiguration.builder().defaultPasses().build()) ) val result = frontend.build { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TupleDeclarationTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TupleDeclarationTest.kt index 89aa3bda030..f9ffd05b257 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TupleDeclarationTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/declarations/TupleDeclarationTest.kt @@ -47,12 +47,7 @@ class TupleDeclarationTest { fun testTopLevelTuple() { with( TestLanguageFrontend( - ctx = - TranslationContext( - TranslationConfiguration.builder().defaultPasses().build(), - ScopeManager(), - TypeManager(), - ) + ctx = TranslationContext(TranslationConfiguration.builder().defaultPasses().build()) ) ) { val result = build { @@ -134,12 +129,7 @@ class TupleDeclarationTest { fun testFunctionLevelTuple() { with( TestLanguageFrontend( - ctx = - TranslationContext( - TranslationConfiguration.builder().defaultPasses().build(), - ScopeManager(), - TypeManager(), - ) + ctx = TranslationContext(TranslationConfiguration.builder().defaultPasses().build()) ) ) { val result = build { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/ScopeTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/ScopeTest.kt index ae1b56910b0..95c55e2228f 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/ScopeTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/ScopeTest.kt @@ -25,43 +25,43 @@ */ package de.fraunhofer.aisec.cpg.graph.scopes -import de.fraunhofer.aisec.cpg.graph.Name -import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration -import de.fraunhofer.aisec.cpg.graph.statements.LookupScopeStatement -import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block +import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend +import de.fraunhofer.aisec.cpg.graph.newBlock +import de.fraunhofer.aisec.cpg.graph.newLookupScopeStatement +import de.fraunhofer.aisec.cpg.graph.newVariableDeclaration import kotlin.test.Test import kotlin.test.assertEquals class ScopeTest { @Test fun testLookup() { - // some mock variable declarations, global and local - var globalA = VariableDeclaration() - globalA.name = Name("a") - var localA = VariableDeclaration() - localA.name = Name("a") + with(TestLanguageFrontend()) { + // some mock variable declarations, global and local + var globalA = newVariableDeclaration("a") + var localA = newVariableDeclaration("a") - // two scopes, global and local - val globalScope = GlobalScope() - globalScope.addSymbol("a", globalA) - val scope = LocalScope(Block()) - scope.parent = globalScope - scope.addSymbol("a", localA) + // two scopes, global and local + val globalScope = GlobalScope(ctx) + globalScope.addSymbol("a", globalA) + val scope = LocalScope(ctx, newBlock()) + scope.parent = globalScope + scope.addSymbol("a", localA) - // if we try to resolve "a" now, this should point to the local A since we start there and - // move upwards - var result = scope.lookupSymbol("a") - assertEquals(listOf(localA), result) + // if we try to resolve "a" now, this should point to the local A since we start there + // and + // move upwards + var result = scope.lookupSymbol("a") + assertEquals(listOf(localA), result) - // now, we pretend to have a lookup scope modifier for a symbol, e.g. through "global" in - // Python - var stmt = LookupScopeStatement() - stmt.targetScope = globalScope - stmt.symbols = listOf("a") - scope.predefinedLookupScopes["a"] = stmt + // now, we pretend to have a lookup scope modifier for a symbol, e.g. through "global" + // in + // Python + var stmt = newLookupScopeStatement(listOf("a"), targetScope = globalScope) + scope.predefinedLookupScopes["a"] = stmt - // let's try the lookup again, this time it should point to the global A - result = scope.lookupSymbol("a") - assertEquals(listOf(globalA), result) + // let's try the lookup again, this time it should point to the global A + result = scope.lookupSymbol("a") + assertEquals(listOf(globalA), result) + } } } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypePropagationTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypePropagationTest.kt index 8294f44fb54..2655474a584 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypePropagationTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/graph/types/TypePropagationTest.kt @@ -42,14 +42,8 @@ class TypePropagationTest { fun testBinopTypePropagation() { val frontend = TestLanguageFrontend( - ctx = - TranslationContext( - TranslationConfiguration.builder().defaultPasses().build(), - ScopeManager(), - TypeManager(), - ) + ctx = TranslationContext(TranslationConfiguration.builder().defaultPasses().build()) ) - val result = frontend.build { translationResult { @@ -93,12 +87,7 @@ class TypePropagationTest { fun testAssignTypePropagation() { val frontend = TestLanguageFrontend( - ctx = - TranslationContext( - TranslationConfiguration.builder().defaultPasses().build(), - ScopeManager(), - TypeManager(), - ) + ctx = TranslationContext(TranslationConfiguration.builder().defaultPasses().build()) ) /** @@ -175,12 +164,7 @@ class TypePropagationTest { fun testNewPropagation() { val frontend = TestLanguageFrontend( - ctx = - TranslationContext( - TranslationConfiguration.builder().defaultPasses().build(), - ScopeManager(), - TypeManager(), - ) + ctx = TranslationContext(TranslationConfiguration.builder().defaultPasses().build()) ) /** @@ -239,12 +223,7 @@ class TypePropagationTest { fun testComplexPropagation() { val frontend = TestLanguageFrontend( - ctx = - TranslationContext( - TranslationConfiguration.builder().defaultPasses().build(), - ScopeManager(), - TypeManager(), - ) + ctx = TranslationContext(TranslationConfiguration.builder().defaultPasses().build()) ) /** @@ -298,7 +277,7 @@ class TypePropagationTest { method("doSomething") } function("create", t("BaseClass").pointer().pointer()) { - param("flip", t("bool")) + param("flip", t("boolean")) body { declare { variable("b", t("BaseClass").pointer()) } ref("b") assign @@ -334,7 +313,7 @@ class TypePropagationTest { } function("main", t("int")) { body { - declare { variable("random", t("bool")) } + declare { variable("random", t("boolean")) } declare { variable("b", t("BaseClass").pointer()) { call("create") { ref("random") } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/ExtensionsTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/ExtensionsTest.kt index 41ad33d50e0..95a129701e1 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/ExtensionsTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/ExtensionsTest.kt @@ -27,9 +27,9 @@ package de.fraunhofer.aisec.cpg.helpers import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.frontends.TestLanguage +import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend import de.fraunhofer.aisec.cpg.frontends.testFrontend import de.fraunhofer.aisec.cpg.graph.* -import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.applyWithScope import de.fraunhofer.aisec.cpg.graph.builder.body import de.fraunhofer.aisec.cpg.graph.builder.declare @@ -39,8 +39,7 @@ import de.fraunhofer.aisec.cpg.graph.builder.translationResult import de.fraunhofer.aisec.cpg.graph.builder.translationUnit import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration import de.fraunhofer.aisec.cpg.graph.problems -import de.fraunhofer.aisec.cpg.graph.statements.DeclarationStatement -import de.fraunhofer.aisec.cpg.graph.statements.expressions.CollectionComprehension +import de.fraunhofer.aisec.cpg.graph.scopes.GlobalScope import de.fraunhofer.aisec.cpg.graph.statements.expressions.ProblemExpression import de.fraunhofer.aisec.cpg.graph.variables import de.fraunhofer.aisec.cpg.test.BaseTest @@ -48,7 +47,6 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs import kotlin.test.assertNotNull -import kotlin.test.assertNull internal class ExtensionsTest : BaseTest() { val problemDeclText = "This is a problem declaration." @@ -86,17 +84,18 @@ internal class ExtensionsTest : BaseTest() { } @Test - fun testApplyWithScopeWithoutCtxAndScopeManager() { - val collectionComprehension = - CollectionComprehension().applyWithScope { - val varA = VariableDeclaration() - varA.name = Name("a") - val declarationStatement = DeclarationStatement() - declarationStatement.addDeclaration(varA) - this.statement = declarationStatement - } - val varA = collectionComprehension.variables["a"] - assertIs(varA) - assertNull(varA.scope) + fun testApplyWithScope() { + with(TestLanguageFrontend()) { + val collectionComprehension = + newCollectionComprehension().applyWithScope { + val varA = newVariableDeclaration("a") + val declarationStatement = newDeclarationStatement() + declarationStatement.addDeclaration(varA) + this.statement = declarationStatement + } + val varA = collectionComprehension.variables["a"] + assertIs(varA) + assertIs(varA.scope) + } } } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolverTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolverTest.kt index 35158a97a95..aa03972b5c5 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolverTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ImportResolverTest.kt @@ -25,10 +25,8 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.TranslationContext -import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.builder.translationResult @@ -47,12 +45,7 @@ class ImportResolverTest { fun testImportOrderResolve() { val frontend = TestLanguageFrontend( - ctx = - TranslationContext( - TranslationConfiguration.builder().defaultPasses().build(), - ScopeManager(), - TypeManager(), - ) + ctx = TranslationContext(TranslationConfiguration.builder().defaultPasses().build()) ) var result = frontend.build { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceTest.kt index 9d49cbcc8d9..172b7b53464 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ReplaceTest.kt @@ -25,10 +25,8 @@ */ package de.fraunhofer.aisec.cpg.passes -import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.TranslationContext -import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.StructTestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguage import de.fraunhofer.aisec.cpg.frontends.TestLanguageFrontend @@ -41,7 +39,7 @@ class ReplaceTest { @ReplacePass(EvaluationOrderGraphPass::class, ReplaceTestLanguage::class, ReplacedPass::class) class ReplaceTestLanguageFrontend : TestLanguageFrontend() - class ReplaceTestLanguage(ctx: TranslationContext) : TestLanguage(ctx) { + class ReplaceTestLanguage() : TestLanguage() { override val frontend: KClass get() = ReplaceTestLanguageFrontend::class @@ -56,7 +54,7 @@ class ReplaceTest { fun testReplaceAnnotation() { val config = TranslationConfiguration.builder().registerLanguage().build() - val ctx = TranslationContext(config, ScopeManager(), TypeManager()) + val ctx = TranslationContext(config) assertContains(config.replacedPasses.values, ReplacedPass::class) assertContains( @@ -65,7 +63,7 @@ class ReplaceTest { ) val cls = - checkForReplacement(EvaluationOrderGraphPass::class, ReplaceTestLanguage(ctx), config) + checkForReplacement(EvaluationOrderGraphPass::class, ReplaceTestLanguage(), config) assertEquals(ReplacedPass::class, cls) } @@ -76,7 +74,7 @@ class ReplaceTest { .replacePass() .replacePass() .build() - val ctx = TranslationContext(config, ScopeManager(), TypeManager()) + val ctx = TranslationContext(config) assertContains(config.replacedPasses.values, ReplacedPass::class) assertContains( @@ -84,13 +82,13 @@ class ReplaceTest { Pair(EvaluationOrderGraphPass::class, StructTestLanguage::class), ) - var cls = checkForReplacement(EvaluationOrderGraphPass::class, TestLanguage(ctx), config) + var cls = checkForReplacement(EvaluationOrderGraphPass::class, TestLanguage(), config) assertEquals(EvaluationOrderGraphPass::class, cls) - cls = checkForReplacement(EvaluationOrderGraphPass::class, StructTestLanguage(ctx), config) + cls = checkForReplacement(EvaluationOrderGraphPass::class, StructTestLanguage(), config) assertEquals(ReplacedPass::class, cls) - cls = checkForReplacement(EvaluationOrderGraphPass::class, ReplaceTestLanguage(ctx), config) + cls = checkForReplacement(EvaluationOrderGraphPass::class, ReplaceTestLanguage(), config) assertEquals(ReplacedPass::class, cls) } } diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolverTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolverTest.kt index fc98ff1add7..a32e49e49a6 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolverTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/SymbolResolverTest.kt @@ -43,35 +43,37 @@ class SymbolResolverTest { fun testCombinedVariableAndCallResolution() { val result = GraphExamples.getCombinedVariableAndCallTest() - val type = result.records["TestClass"]?.toType() - assertNotNull(type) + with(result) { + val type = result.records["TestClass"]?.toType() + assertNotNull(type) - val method1 = result.methods["method1"] - assertNotNull(method1) + val method1 = result.methods["method1"] + assertNotNull(method1) - val method2 = result.methods["method2"] - assertNotNull(method2) + val method2 = result.methods["method2"] + assertNotNull(method2) - val constructor = result.methods["TestClass"] - assertNotNull(constructor) + val constructor = result.methods["TestClass"] + assertNotNull(constructor) - val variable = method2.variables["variable"] - assertEquals(type, variable?.type) + val variable = method2.variables["variable"] + assertEquals(type, variable?.type) - val ref = method2.refs["variable"] - assertEquals(type, ref?.type) + val ref = method2.refs["variable"] + assertEquals(type, ref?.type) - val callmethod1 = method2.calls["method1"] - assertIs(callmethod1) - assertRefersTo(callmethod1.base, method2.receiver) - assertInvokes(callmethod1, method1) + val callmethod1 = method2.calls["method1"] + assertIs(callmethod1) + assertRefersTo(callmethod1.base, method2.receiver) + assertInvokes(callmethod1, method1) - val callmethod2 = method2.calls["method2"] - assertInvokes(callmethod2, method2) + val callmethod2 = method2.calls["method2"] + assertInvokes(callmethod2, method2) - val construct = method1.calls { it is ConstructExpression }.firstOrNull() - assertNotNull(construct) - assertInvokes(construct, constructor) + val construct = method1.calls { it is ConstructExpression }.firstOrNull() + assertNotNull(construct) + assertInvokes(construct, constructor) + } } @Test 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 056e4ed30d7..b025fc2e53b 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 @@ -44,89 +44,101 @@ internal class ScopeManagerTest : BaseTest() { @Test fun testMerge() { val tm = TypeManager() - val s1 = ScopeManager() - val ctx = TranslationContext(config, s1, tm) - val language = TestLanguageWithColon(ctx) - val frontend1 = TestLanguageFrontend(ctx, language) - with(frontend1) { - val tu1 = frontend1.newTranslationUnitDeclaration("f1.cpp", null) - s1.resetToGlobal(tu1) - - // build a namespace declaration in f1.cpp with the namespace A - val namespaceA1 = frontend1.newNamespaceDeclaration("A") - s1.enterScope(namespaceA1) - - val func1 = frontend1.newFunctionDeclaration("func1") - s1.addDeclaration(func1) - namespaceA1.declarations += func1 - - s1.leaveScope(namespaceA1) - s1.addDeclaration(namespaceA1) - tu1.declarations += namespaceA1 - - val s2 = ScopeManager() - val frontend2 = TestLanguageFrontend(TranslationContext(config, s2, tm), language) - val tu2 = frontend2.newTranslationUnitDeclaration("f1.cpp", null) - s2.resetToGlobal(tu2) - - // and do the same in the other file - val namespaceA2 = frontend2.newNamespaceDeclaration("A") - s2.enterScope(namespaceA2) - - val func2 = frontend2.newFunctionDeclaration("func2") - s2.addDeclaration(func2) - namespaceA2.declarations += func2 - - s2.leaveScope(namespaceA2) - s2.addDeclaration(namespaceA2) - tu2.declarations += namespaceA2 - - // merge the two scopes. this replicates the behaviour of parseParallel - val final = ScopeManager() - final.mergeFrom(listOf(s1, s2)) - - // in the final scope manager, there should only be one NameScope "A" - val scopes = final.filterScopes { it.name.toString() == "A" } - assertEquals(1, scopes.size) - - val scopeA = scopes.firstOrNull() as? NameScope - assertNotNull(scopeA) - - // should also be able to look up via the FQN - assertEquals(scopeA, final.lookupScope(parseName("A"))) - - // and it should contain both functions from the different file in the same namespace - assertContains(scopeA.symbols["func1"] ?: listOf(), func1) - assertContains(scopeA.symbols["func2"] ?: listOf(), func2) - - // finally, test whether our two namespace declarations are pointing to the same - // NameScope - assertEquals(scopeA, final.lookupScope(namespaceA1)) - assertEquals(scopeA, final.lookupScope(namespaceA2)) - - // in the final scope manager, the global scope should not be any of the merged scope - // managers' original global scopes - assertFalse(listOf(s1, s2).map { it.globalScope }.contains(final.globalScope)) - - // resolve symbol - val func = - final - .lookupSymbolByName( - name = parseName("A::func1"), - language = language, - startScope = final.globalScope, - ) - .firstOrNull() - - assertEquals(func1, func) - } + + val rootCtx = TranslationContext(config, tm) + val language = TestLanguageWithColon() + + val ctx1 = TranslationContext(config, tm) + val s1 = ctx1.scopeManager + + val frontend1 = TestLanguageFrontend(ctx1, language) + val (func1, namespaceA1) = + with(frontend1) { + val tu1 = frontend1.newTranslationUnitDeclaration("f1.cpp", null) + s1.resetToGlobal(tu1) + + // build a namespace declaration in f1.cpp with the namespace A + val namespaceA1 = frontend1.newNamespaceDeclaration("A") + s1.enterScope(namespaceA1) + + val func1 = frontend1.newFunctionDeclaration("func1") + s1.addDeclaration(func1) + namespaceA1.declarations += func1 + + s1.leaveScope(namespaceA1) + s1.addDeclaration(namespaceA1) + tu1.declarations += namespaceA1 + Pair(func1, namespaceA1) + } + + val ctx2 = TranslationContext(config, tm) + val s2 = ctx2.scopeManager + val frontend2 = TestLanguageFrontend(ctx2, language) + val (func2, namespaceA2) = + with(frontend2) { + val tu2 = frontend2.newTranslationUnitDeclaration("f1.cpp", null) + s2.resetToGlobal(tu2) + + // and do the same in the other file + val namespaceA2 = frontend2.newNamespaceDeclaration("A") + s2.enterScope(namespaceA2) + + val func2 = frontend2.newFunctionDeclaration("func2") + s2.addDeclaration(func2) + namespaceA2.declarations += func2 + + s2.leaveScope(namespaceA2) + s2.addDeclaration(namespaceA2) + tu2.declarations += namespaceA2 + Pair(func2, namespaceA2) + } + + // merge the two scopes. this replicates the behaviour of parseParallel + val final = rootCtx.scopeManager + final.mergeFrom(listOf(s1, s2)) + + // in the final scope manager, there should only be one NameScope "A" + val scopes = final.filterScopes { it.name.toString() == "A" } + assertEquals(1, scopes.size) + + val scopeA = scopes.firstOrNull() as? NameScope + assertNotNull(scopeA) + + // should also be able to look up via the FQN + assertEquals(scopeA, final.lookupScope(parseName("A", delimiter = "::"))) + + // and it should contain both functions from the different file in the same namespace + assertContains(scopeA.symbols["func1"] ?: listOf(), func1) + assertContains(scopeA.symbols["func2"] ?: listOf(), func2) + + // finally, test whether our two namespace declarations are pointing to the same + // NameScope + assertEquals(scopeA, final.lookupScope(namespaceA1)) + assertEquals(scopeA, final.lookupScope(namespaceA2)) + + // in the final scope manager, the global scope should not be any of the merged scope + // managers' original global scopes + assertNotSame(s1.globalScope, final.globalScope) + assertNotSame(s2.globalScope, final.globalScope) + + // resolve symbol + val func = + final + .lookupSymbolByName( + name = parseName("A::func1", delimiter = "::"), + language = language, + startScope = final.globalScope, + ) + .firstOrNull() + + assertEquals(func1, func) } @Test fun testScopeFQN() { - val s = ScopeManager() - val ctx = TranslationContext(config, s, TypeManager()) - val frontend = TestLanguageFrontend(ctx, TestLanguageWithColon(ctx)) + val ctx = TranslationContext(config) + val s = ctx.scopeManager + val frontend = TestLanguageFrontend(ctx, TestLanguageWithColon()) with(frontend) { val tu = frontend.newTranslationUnitDeclaration("file.cpp", null) s.resetToGlobal(tu) @@ -161,8 +173,7 @@ internal class ScopeManagerTest : BaseTest() { @Test fun testMatchesSignature() { - val s = ScopeManager() - val frontend = TestLanguageFrontend(TranslationContext(config, s, TypeManager())) + val frontend = TestLanguageFrontend(TranslationContext(config)) with(frontend) { val method = newMethodDeclaration("testMethod").apply { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/persistence/TestCommon.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/persistence/TestCommon.kt index 92944e1b11e..96001c756df 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/persistence/TestCommon.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/persistence/TestCommon.kt @@ -73,6 +73,7 @@ class TestCommon { "PDG", "RETURN_TYPES", "SCOPE", + "SECONDARY_TYPES", "SIGNATURE_TYPES", "THROWS_TYPES", "TYPE", diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/processing/VisitorTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/processing/VisitorTest.kt index 8bb8aaf7ac2..b41d41d8c23 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/processing/VisitorTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/processing/VisitorTest.kt @@ -26,12 +26,9 @@ package de.fraunhofer.aisec.cpg.processing import de.fraunhofer.aisec.cpg.GraphExamples -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.TranslationManager import de.fraunhofer.aisec.cpg.TranslationResult -import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.Node @@ -145,12 +142,7 @@ class VisitorTest : BaseTest() { val tr = TranslationResult( translationManager = TranslationManager.builder().build(), - finalCtx = - TranslationContext( - config = TranslationConfiguration.builder().build(), - scopeManager = ScopeManager(), - typeManager = TypeManager(), - ), + finalCtx = TranslationContext(), ) tr.components += component1 tr.components += component2 diff --git a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt index 97cbb0e1ee1..323273f317b 100644 --- a/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt +++ b/cpg-core/src/testFixtures/kotlin/de/fraunhofer/aisec/cpg/frontends/TestLanguage.kt @@ -26,15 +26,13 @@ package de.fraunhofer.aisec.cpg.frontends import de.fraunhofer.aisec.cpg.* -import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.declarations.ProblemDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration -import de.fraunhofer.aisec.cpg.graph.statements.expressions.ProblemExpression import de.fraunhofer.aisec.cpg.graph.types.* import de.fraunhofer.aisec.cpg.graph.unknownType import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation import java.io.File -import java.util.function.Supplier import kotlin.reflect.KClass import kotlin.test.assertNotNull @@ -42,7 +40,7 @@ import kotlin.test.assertNotNull * This is a variant of the test language with `::` as a [namespaceDelimiter] to simulate languages * like C++. */ -open class TestLanguageWithColon(ctx: TranslationContext) : TestLanguage(ctx) { +open class TestLanguageWithColon() : TestLanguage() { override val namespaceDelimiter: String get() = "::" } @@ -51,8 +49,7 @@ open class TestLanguageWithColon(ctx: TranslationContext) : TestLanguage(ctx) { * This is a test language that can be used for unit test, where we need a language but do not have * a specific one. */ -open class TestLanguage(ctx: TranslationContext) : - Language(ctx), HasImplicitReceiver { +open class TestLanguage : Language(), HasImplicitReceiver { override val fileExtensions: List = listOf() override val frontend: KClass = TestLanguageFrontend::class override val compoundAssignmentOperators = @@ -74,11 +71,9 @@ open class TestLanguage(ctx: TranslationContext) : get() = "this" } -class ClassTestLanguage(ctx: TranslationContext) : - TestLanguage(ctx), HasClasses, HasDefaultArguments +class ClassTestLanguage() : TestLanguage(), HasClasses, HasDefaultArguments -class StructTestLanguage(ctx: TranslationContext) : - TestLanguageWithColon(ctx), HasStructs, HasClasses, HasDefaultArguments +class StructTestLanguage() : TestLanguageWithColon(), HasStructs, HasClasses, HasDefaultArguments /** * Creates a new [TestLanguageFrontend] with the given configuration [builder]. @@ -98,20 +93,15 @@ fun testFrontend(builder: (TranslationConfiguration.Builder) -> Unit): TestLangu * chance to configure a specific subclass of it, e.g., [TestLanguageWithColon]. */ fun testFrontend(config: TranslationConfiguration): TestLanguageFrontend { - val ctx = TranslationContext(config, ScopeManager(), TypeManager()) + val ctx = TranslationContext(config) val language = ctx.availableLanguage() assertNotNull(language) return TestLanguageFrontend(ctx, language) } open class TestLanguageFrontend( - ctx: TranslationContext = - TranslationContext( - TranslationConfiguration.builder().build(), - ScopeManager(), - TypeManager(), - ), - language: Language = TestLanguage(ctx), + ctx: TranslationContext = TranslationContext(TranslationConfiguration.builder().build()), + language: Language = TestLanguage(), ) : LanguageFrontend(ctx, language) { override fun parse(file: File): TranslationUnitDeclaration { TODO("Not yet implemented") @@ -136,4 +126,4 @@ open class TestLanguageFrontend( } class TestHandler(frontend: TestLanguageFrontend) : - Handler(Supplier { ProblemExpression() }, frontend) + Handler(::ProblemDeclaration, frontend) 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 a660b0dcc02..9128d1b18b7 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 @@ -332,6 +332,6 @@ fun assertLiteralValue(expected: T, expr: Expression?, message: Strin fun ContextProvider.assertResolvedType(fqn: String): Type { var type = - ctx?.typeManager?.lookupResolvedType(fqn, language = (this as? LanguageProvider)?.language) + ctx.typeManager.lookupResolvedType(fqn, language = (this as? LanguageProvider)?.language) return assertNotNull(type) } diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt index 1732f500ea8..4a1c1498611 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CLanguage.kt @@ -26,7 +26,6 @@ package de.fraunhofer.aisec.cpg.frontends.cxx import com.fasterxml.jackson.annotation.JsonIgnore -import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.types.* import kotlin.reflect.KClass @@ -35,8 +34,8 @@ import org.neo4j.ogm.annotation.Transient const val CONST = "const" /** The C language. */ -open class CLanguage(ctx: TranslationContext) : - Language(ctx), +open class CLanguage : + Language(), HasStructs, HasFunctionPointers, HasQualifier, diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt index 5c48b142a62..37a06566fc9 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CPPLanguage.kt @@ -29,6 +29,7 @@ import de.fraunhofer.aisec.cpg.CallResolutionResult import de.fraunhofer.aisec.cpg.SignatureMatches import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.* +import de.fraunhofer.aisec.cpg.graph.ContextProvider import de.fraunhofer.aisec.cpg.graph.HasOverloadedOperation import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.declarations.* @@ -47,8 +48,9 @@ import kotlin.reflect.KClass import org.neo4j.ogm.annotation.Transient /** The C++ language. */ -open class CPPLanguage(ctx: TranslationContext) : - CLanguage(ctx), +@Suppress("CONTEXT_RECEIVERS_DEPRECATED") +open class CPPLanguage() : + CLanguage(), HasDefaultArguments, HasTemplates, HasStructs, @@ -200,6 +202,7 @@ open class CPPLanguage(ctx: TranslationContext) : return CastNotPossible } + context(ContextProvider) override fun bestViableResolution( result: CallResolutionResult ): Pair, CallResolutionResult.SuccessKind> { diff --git a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt index c82cc383af8..d820cd20fb2 100644 --- a/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt +++ b/cpg-language-cxx/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXLanguageFrontend.kt @@ -156,7 +156,7 @@ open class CXXLanguageFrontend(ctx: TranslationContext, language: Language = ArrayList() - val topLevel = ctx.currentComponent?.topLevel + val topLevel = ctx.currentComponent?.topLevel() if (topLevel != null) { includeLocations.add(topLevel.toPath().toAbsolutePath()) } @@ -205,7 +205,7 @@ open class CXXLanguageFrontend(ctx: TranslationContext, language: Language() - ctx.currentComponent?.topLevel?.let { + ctx.currentComponent?.topLevel()?.let { includePaths.add(it.toPath().toAbsolutePath().toString()) } @@ -554,9 +554,9 @@ open class CXXLanguageFrontend(ctx: TranslationContext, language: Language().filter { it !is ConstructorDeclaration } assertFalse(methods.isEmpty()) methods.forEach { - val scope = scopeManager.lookupScope(it) + val scope = ctx.scopeManager.lookupScope(it) assertSame(it, scope!!.astNode) } @@ -68,7 +67,7 @@ internal class ScopeManagerTest : BaseTest() { // this is necessary, since the constructor was probably created as a function declaration // which later gets 'upgraded' to a constructor declaration. constructors.forEach { - val scope = scopeManager.lookupScope(it) + val scope = ctx.scopeManager.lookupScope(it) assertSame(it, scope!!.astNode) } } diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt index 9987d0431c6..5ca42f4d4a1 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/templates/FunctionTemplateTest.kt @@ -96,6 +96,9 @@ internal class FunctionTemplateTest : BaseTest() { val language = result.finalCtx.availableLanguage() assertNotNull(language) + val ctx = result.finalCtx + assertNotNull(ctx) + // This test checks the structure of FunctionTemplates without the TemplateExpansionPass val functionTemplateDecl = result.allChildren()[0] @@ -199,6 +202,9 @@ internal class FunctionTemplateTest : BaseTest() { val language = result.finalCtx.availableLanguage() assertNotNull(language) + val ctx = result.finalCtx + assertNotNull(ctx) + val templateDeclaration = findByUniquePredicate(result.allChildren()) { t: FunctionTemplateDeclaration -> @@ -339,6 +345,9 @@ internal class FunctionTemplateTest : BaseTest() { val language = result.finalCtx.availableLanguage() assertNotNull(language) + val ctx = result.finalCtx + assertNotNull(ctx) + val templateDeclaration = findByUniquePredicate(result.allChildren()) { t: FunctionTemplateDeclaration -> diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypeTests.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypeTests.kt index dd40766c102..abedf868e74 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypeTests.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypeTests.kt @@ -37,14 +37,7 @@ import kotlin.test.* internal class TypeTests : BaseTest() { @Test fun reference() { - val language = - CPPLanguage( - TranslationContext( - TranslationConfiguration.builder().build(), - ScopeManager(), - TypeManager(), - ) - ) + val language = CPPLanguage() val objectType: Type = IntegerType("int", 32, language, NumericType.Modifier.SIGNED) val pointerType: Type = PointerType(objectType, PointerType.PointerOrigin.POINTER) @@ -85,14 +78,7 @@ internal class TypeTests : BaseTest() { @Test fun dereference() { - val language = - CPPLanguage( - TranslationContext( - TranslationConfiguration.builder().build(), - ScopeManager(), - TypeManager(), - ) - ) + val language = CPPLanguage() val objectType: Type = IntegerType("int", 32, language, NumericType.Modifier.SIGNED) val pointerType: Type = PointerType(objectType, PointerType.PointerOrigin.POINTER) @@ -130,31 +116,27 @@ internal class TypeTests : BaseTest() { @Throws(Exception::class) fun testFunctionPointerTypes() { val topLevel = Path.of("src", "test", "resources", "types") - val tu = - analyzeAndGetFirstTU( - listOf(topLevel.resolve("fptr_type.cpp").toFile()), - topLevel, - true, - ) { + val result = + analyze(listOf(topLevel.resolve("fptr_type.cpp").toFile()), topLevel, true) { it.registerLanguage() } - val language = tu.ctx?.availableLanguage() + val language = result.finalCtx.availableLanguage() assertNotNull(language) val noParamType = FunctionPointerType(emptyList(), language, IncompleteType(language)) val oneParamType = FunctionPointerType( - listOf(tu.primitiveType("int")), + listOf(result.primitiveType("int")), language, IncompleteType(language), ) val twoParamType = FunctionPointerType( - listOf(tu.primitiveType("int"), tu.primitiveType("unsigned long int")), + listOf(result.primitiveType("int"), result.primitiveType("unsigned long int")), language, IntegerType("int", 32, language, NumericType.Modifier.SIGNED), ) - val variables = tu.variables + val variables = result.variables val localTwoParam = findByUniqueName(variables, "local_two_param") assertNotNull(localTwoParam) assertEquals(twoParamType, localTwoParam.type) @@ -183,14 +165,9 @@ internal class TypeTests : BaseTest() { @Throws(Exception::class) @Test fun testCommonTypeTestCpp() { - val ctx = - TranslationContext( - TranslationConfiguration.builder().build(), - ScopeManager(), - TypeManager(), - ) + val ctx = TranslationContext() - with(CXXLanguageFrontend(ctx, CPPLanguage(ctx))) { + with(CXXLanguageFrontend(ctx, CPPLanguage())) { val topLevel = Path.of("src", "test", "resources", "compiling", "hierarchy", "multistep") val result = diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypedefTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypedefTest.kt index ec74319bca6..e6e62575f0a 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypedefTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/types/TypedefTest.kt @@ -46,33 +46,29 @@ internal class TypedefTest : BaseTest() { @Test @Throws(Exception::class) fun testSingle() { - val tu = - analyzeAndGetFirstTU( - listOf(topLevel.resolve("typedefs.cpp").toFile()), - topLevel, - true, - ) { + val result = + analyze(listOf(topLevel.resolve("typedefs.cpp").toFile()), topLevel, true) { it.registerLanguage() } - with(tu) { + with(result) { // normal type - val l1 = tu.variables["l1"] - val l2 = tu.variables["l2"] + val l1 = variables["l1"] + val l2 = variables["l2"] assertEquals(l1?.type, l2?.type) // pointer - val longptr1 = tu.variables["longptr1"] - val longptr2 = tu.variables["longptr2"] + val longptr1 = variables["longptr1"] + val longptr2 = variables["longptr2"] assertEquals(longptr1?.type, longptr2?.type) // array - val arr1 = tu.variables["arr1"] - val arr2 = tu.variables["arr2"] + val arr1 = variables["arr1"] + val arr2 = variables["arr2"] assertEquals(arr1?.type, arr2?.type) // function pointer - val uintfp1 = tu.variables["uintfp1"] - val uintfp2 = tu.variables["uintfp2"] + val uintfp1 = variables["uintfp1"] + val uintfp2 = variables["uintfp2"] val fpType = uintfp1?.type as? FunctionPointerType assertNotNull(fpType) @@ -82,7 +78,7 @@ internal class TypedefTest : BaseTest() { assertEquals(NumericType.Modifier.UNSIGNED, returnType.modifier) assertEquals(uintfp1.type, uintfp2?.type) - val type = tu.ctx?.scopeManager?.typedefFor(Name("test")) + val type = finalCtx.scopeManager.typedefFor(Name("test")) assertIs(type) assertLocalName("uint8_t", type) } @@ -132,36 +128,33 @@ internal class TypedefTest : BaseTest() { @Test @Throws(Exception::class) fun testMultiple() { - val tu = - analyzeAndGetFirstTU( - listOf(topLevel.resolve("typedefs.cpp").toFile()), - topLevel, - true, - ) { + val result = + analyze(listOf(topLevel.resolve("typedefs.cpp").toFile()), topLevel, true) { it.registerLanguage() } - with(tu) { + with(result) { // simple type - val i1 = tu.variables["i1"] - val i2 = tu.variables["i2"] + val i1 = variables["i1"] + val i2 = variables["i2"] assertEquals(i1?.type, i2?.type) // array - val a1 = tu.variables["a1"] - val a2 = tu.variables["a2"] + val a1 = variables["a1"] + val a2 = variables["a2"] assertEquals(a1?.type, a2?.type) // pointer - val intPtr1 = tu.variables["intPtr1"] - val intPtr2 = tu.variables["intPtr2"] + val intPtr1 = variables["intPtr1"] + val intPtr2 = variables["intPtr2"] assertEquals(intPtr1?.type, intPtr2?.type) // function pointer - val fPtr1 = tu.variables["intFptr1"] - val fPtr2 = tu.variables["intFptr2"] + val fPtr1 = variables["intFptr1"] + val fPtr2 = variables["intFptr2"] assertEquals(fPtr1?.type, fPtr2?.type) - val type = tu.ctx?.scopeManager?.typedefFor(Name("type_B")) + val type = + finalCtx.scopeManager.typedefFor(Name("type_B"), finalCtx.scopeManager.globalScope) assertLocalName("template_class_A", type) assertIs(type) assertEquals(listOf(primitiveType("int"), primitiveType("int")), type.generics) 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 1517ddef039..f2d703a81d7 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 @@ -42,7 +42,6 @@ import de.fraunhofer.aisec.cpg.test.* import java.io.File import java.nio.file.Path import java.util.function.Consumer -import kotlin.Throws import kotlin.test.* internal class CXXLanguageFrontendTest : BaseTest() { @@ -50,12 +49,12 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Throws(Exception::class) fun testForEach() { val file = File("src/test/resources/cxx/foreachstmt.cpp") - val tu = - analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + val result = + analyze(listOf(file), file.parentFile.toPath(), true) { it.registerLanguage() } - with(tu) { - val main = tu.functions["main"] + with(result) { + val main = functions["main"] assertNotNull(main) val decl = main @@ -123,12 +122,12 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Throws(Exception::class) fun testTypeId() { val file = File("src/test/resources/typeidexpr.cpp") - val tu = - analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + val result = + analyze(listOf(file), file.parentFile.toPath(), true) { it.registerLanguage() } - val main = tu.functions["main"] - with(tu) { + val main = result.functions["main"] + with(result) { assertNotNull(main) val funcDecl = main @@ -164,12 +163,12 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Throws(Exception::class) fun testCast() { val file = File("src/test/resources/cxx/castexpr.cpp") - val tu = - analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + val result = + analyze(listOf(file), file.parentFile.toPath(), true) { it.registerLanguage() } - with(tu) { - val main = tu.functions["main"] + with(result) { + val main = functions["main"] assertNotNull(main) val e = main.variables["e"] @@ -210,12 +209,12 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Throws(Exception::class) fun testArrays() { val file = File("src/test/resources/cxx/arrays.cpp") - val tu = - analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + val result = + analyze(listOf(file), file.parentFile.toPath(), true) { it.registerLanguage() } - val main = tu.functions["main"] - with(tu) { + with(result) { + val main = functions["main"] assertNotNull(main) val statement = main.body as Block @@ -364,11 +363,14 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Throws(Exception::class) fun testDeclarationStatement() { val file = File("src/test/resources/cxx/declstmt.cpp") - val tu = - analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + val result = + analyze(listOf(file), file.parentFile.toPath(), true) { it.registerLanguage() } - with(tu) { + with(result) { + val tu = result.components.firstOrNull()?.translationUnits?.firstOrNull() + assertNotNull(tu) + val function = tu.declarations(0) val statements = function?.statements assertNotNull(statements) @@ -591,12 +593,12 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Throws(Exception::class) fun testBinaryOperator() { val file = File("src/test/resources/cxx/binaryoperator.cpp") - val tu = - analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + val result = + analyze(listOf(file), file.parentFile.toPath(), true) { it.registerLanguage() } - val main = tu.functions["main"] + val main = result.functions["main"] assertNotNull(main) val statements = main.statements @@ -640,7 +642,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { // syntactically no different from the previous ones val stmt = statements[4] as DeclarationStatement val decl = stmt.singleDeclaration as VariableDeclaration - with(tu) { assertEquals(objectType("std::string").pointer(), decl.type) } + with(result) { assertEquals(objectType("std::string").pointer(), decl.type) } assertLocalName("notMultiplication", decl) assertTrue(decl.initializer is BinaryOperator) @@ -656,12 +658,15 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Throws(Exception::class) fun testRecordDeclaration() { val file = File("src/test/resources/cxx/recordstmt.cpp") - val tu = - analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + val result = + analyze(listOf(file), file.parentFile.toPath(), true) { it.registerLanguage() } - val language = tu.ctx?.availableLanguage() + val language = result.finalCtx.availableLanguage() assertNotNull(language) + + val tu = result.components.firstOrNull()?.translationUnits?.firstOrNull() + assertNotNull(tu) assertEquals(language, tu.language) val recordDeclaration = tu.records.firstOrNull() @@ -847,10 +852,12 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Throws(Exception::class) fun testInitListExpression() { val file = File("src/test/resources/initlistexpression.cpp") - val tu = - analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + val result = + analyze(listOf(file), file.parentFile.toPath(), true) { it.registerLanguage() } + val tu = result.components.firstOrNull()?.translationUnits?.firstOrNull() + assertNotNull(tu) // x y = { 1, 2 }; val y = tu.declarations(1) @@ -872,7 +879,7 @@ internal class CXXLanguageFrontendTest : BaseTest() { // int z[] = { 2, 3, 4 }; val z = tu.declarations(2) assertNotNull(z) - with(tu) { assertEquals(primitiveType("int").array(), z.type) } + with(result) { assertEquals(primitiveType("int").array(), z.type) } initializer = z.initializer assertNotNull(initializer) @@ -886,12 +893,15 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Throws(Exception::class) fun testObjectCreation() { val file = File("src/test/resources/cxx/objcreation.cpp") - val tu = - analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + val result = + analyze(listOf(file), file.parentFile.toPath(), true) { it.registerLanguage() } - assertNotNull(tu) - with(tu) { + assertNotNull(result) + with(result) { + val tu = components.firstOrNull()?.translationUnits?.firstOrNull() + assertNotNull(tu) + // get the main method val main = tu.declarations(3) val statement = main!!.body as Block @@ -1402,15 +1412,15 @@ internal class CXXLanguageFrontendTest : BaseTest() { @Throws(Exception::class) fun testTypedef() { val file = File("src/test/resources/c/typedef_in_header/main.c") - val tu = - analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) { + val result = + analyze(listOf(file), file.parentFile.toPath(), true) { it.registerLanguage() } - with(tu) { - val typedefs = tu.ctx?.scopeManager?.typedefFor(Name("MyStruct")) + with(result) { + val typedefs = result.finalCtx.scopeManager.typedefFor(Name("MyStruct")) assertLocalName("__myStruct", typedefs) - val main = tu.functions["main"] + val main = result.functions["main"] assertNotNull(main) val call = main.calls.firstOrNull() diff --git a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXSymbolConfigurationTest.kt b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXSymbolConfigurationTest.kt index 8deb590ed00..7a6d2bcd151 100644 --- a/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXSymbolConfigurationTest.kt +++ b/cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/cxx/CXXSymbolConfigurationTest.kt @@ -26,7 +26,6 @@ package de.fraunhofer.aisec.cpg.frontends.cxx import de.fraunhofer.aisec.cpg.* -import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator @@ -43,16 +42,11 @@ internal class CXXSymbolConfigurationTest : BaseTest() { @Test @Throws(TranslationException::class) fun testWithoutSymbols() { - val ctx = - TranslationContext( - TranslationConfiguration.builder().build(), - ScopeManager(), - TypeManager(), - ) + val ctx = TranslationContext() // parse without symbols val tu = - CXXLanguageFrontend(ctx, CPPLanguage(ctx)).parse(File("src/test/resources/symbols.cpp")) + CXXLanguageFrontend(ctx, CPPLanguage()).parse(File("src/test/resources/symbols.cpp")) val main = tu.functions["main"] assertNotNull(main) @@ -86,9 +80,9 @@ internal class CXXSymbolConfigurationTest : BaseTest() { .build() // let's try with symbol definitions - val ctx = TranslationContext(config, ScopeManager(), TypeManager()) + val ctx = TranslationContext(config) val tu = - CXXLanguageFrontend(ctx, CPPLanguage(ctx)).parse(File("src/test/resources/symbols.cpp")) + CXXLanguageFrontend(ctx, CPPLanguage()).parse(File("src/test/resources/symbols.cpp")) val main = tu.functions["main"] assertNotNull(main) 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 f8a900b740e..bce6f802c6b 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 @@ -51,12 +51,13 @@ class DeclarationHandler(frontend: GoLanguageFrontend) : if (recv != null) { val recvField = recv.list.firstOrNull() val recordType = recvField?.type?.let { frontend.typeOf(it) } ?: unknownType() + // The record type be an unqualified type, so we need to use the current + // namespace to make a FQN out of it + val fqnRecord = + frontend.scopeManager.currentNamespace.fqn(recordType.root.name.localName) val method = - newMethodDeclaration( - Name(funcDecl.name.name, recordType.root.name), - rawNode = funcDecl, - ) + newMethodDeclaration(Name(funcDecl.name.name, fqnRecord), rawNode = funcDecl) // The name of the Go receiver is optional. In fact, if the name is not // specified we probably do not need any receiver variable at all, @@ -72,12 +73,10 @@ class DeclarationHandler(frontend: GoLanguageFrontend) : } if (recordType !is UnknownType) { - val recordName = recordType.root.name - // 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.lookupScope(recordName)?.astNode as? RecordDeclaration + frontend.scopeManager.lookupScope(fqnRecord)?.astNode as? RecordDeclaration // 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/frontends/golang/GoLanguage.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt index 87c62b2dd2e..b7067a4a7ff 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguage.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.frontends.golang -import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.declarations.ParameterDeclaration import de.fraunhofer.aisec.cpg.graph.primitiveType @@ -36,8 +35,8 @@ import de.fraunhofer.aisec.cpg.graph.unknownType import org.neo4j.ogm.annotation.Transient /** The Go language. */ -class GoLanguage(ctx: TranslationContext) : - Language(ctx), +class GoLanguage : + Language(), HasShortCircuitOperators, HasGenerics, HasStructs, diff --git a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt index 8360aeae12e..8ab378531bd 100644 --- a/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt +++ b/cpg-language-go/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontend.kt @@ -148,7 +148,7 @@ class GoLanguageFrontend(ctx: TranslationContext, language: Language ctx.currentComponent?.topLevel + ctx.currentComponent?.topLevel() != null -> ctx.currentComponent?.topLevel() else -> file.parentFile }!! @@ -252,16 +252,9 @@ class GoLanguageFrontend(ctx: TranslationContext, language: Language { - val name: String = - if (isBuiltinType(type.name)) { - // Definitely not an FQN type - type.name - } else { - // FQN'ize this name (with the current file) - "${currentFile?.name?.name}.${type.name}" // this.File.Name.Name - } - - objectType(name) + // Just return the name here, the type resolver will resolve this correctly to + // an FQN later + objectType(type.name) } is GoStandardLibrary.Ast.SelectorExpr -> { // This is a FQN type @@ -383,7 +376,7 @@ class GoLanguageFrontend(ctx: TranslationContext, language: Language() } - assertNotNull(tu) + assertNotNull(result) - with(tu) { + with(result) { val values = mapOf( "zeroShift" to Pair(0, assertResolvedType("int")), @@ -304,7 +304,7 @@ class DeclarationTest { "onehundredandfive" to Pair(105, assertResolvedType("int")), ) values.forEach { - val variable = tu.variables[it.key] + val variable = variables[it.key] assertNotNull(variable, "variable \"${it.key}\" not found") assertEquals(it.value.first, variable.evaluate(), "${it.key} does not match") assertEquals(it.value.second, variable.type, "${it.key} has the wrong type") diff --git a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt index 2b349be66c0..36bdcb79156 100644 --- a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt +++ b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/GoLanguageFrontendTest.kt @@ -96,77 +96,83 @@ class GoLanguageFrontendTest : BaseTest() { @Test fun testConstruct() { val topLevel = Path.of("src", "test", "resources", "golang") - val tu = - analyzeAndGetFirstTU( - listOf(topLevel.resolve("construct.go").toFile()), - topLevel, - true, - ) { + val result = + analyze(listOf(topLevel.resolve("construct.go").toFile()), topLevel, true) { it.registerLanguage() } - assertNotNull(tu) + assertNotNull(result) - val p = tu.namespaces["p"] - assertNotNull(p) + with(result) { + val p = namespaces["p"] + assertNotNull(p) - val myStruct = p.records["p.MyStruct"] - assertNotNull(myStruct) + val myStruct = p.records["p.MyStruct"] + assertNotNull(myStruct) - val main = p.functions["main"] - assertNotNull(main) + val main = p.functions["main"] + assertNotNull(main) - val body = main.body as? Block - assertNotNull(body) + val body = main.body as? Block + assertNotNull(body) - var decl = main.variables["o"] - assertNotNull(decl) + var decl = main.variables["o"] + assertNotNull(decl) - val new = assertIs(decl.firstAssignment) - with(tu) { assertEquals(assertResolvedType("p.MyStruct").pointer(), new.type) } + val new = assertIs(decl.firstAssignment) + with(result) { assertEquals(assertResolvedType("p.MyStruct").pointer(), new.type) } - val construct = new.initializer as? ConstructExpression - assertNotNull(construct) - assertEquals(myStruct, construct.instantiates) + val construct = new.initializer as? ConstructExpression + assertNotNull(construct) + assertEquals(myStruct, construct.instantiates) - // make array + // make array - decl = main.variables["a"] - assertNotNull(decl) + decl = main.variables["a"] + assertNotNull(decl) - var make = assertIs(decl.firstAssignment) - assertNotNull(make) - with(tu) { assertEquals(tu.primitiveType("int").array(), make.type) } + var make = assertIs(decl.firstAssignment) + assertNotNull(make) + assertEquals(primitiveType("int").array(), make.type) - assertTrue(make is NewArrayExpression) + assertTrue(make is NewArrayExpression) - val dimension = make.dimensions.firstOrNull() as? Literal<*> - assertNotNull(dimension) - assertEquals(5, dimension.value) + val dimension = make.dimensions.firstOrNull() as? Literal<*> + assertNotNull(dimension) + assertEquals(5, dimension.value) - // make map + // make map - decl = main.variables["m"] - assertNotNull(decl) + decl = main.variables["m"] + assertNotNull(decl) - make = assertIs(decl.firstAssignment) - assertNotNull(make) - assertTrue(make is ConstructExpression) - // TODO: Maps can have dedicated types and parsing them as a generic here is only a - // temporary solution. This should be fixed in the future. - assertEquals( - tu.objectType("map", listOf(tu.primitiveType("string"), tu.primitiveType("string"))), - make.type, - ) + make = assertIs(decl.firstAssignment) + assertNotNull(make) + assertTrue(make is ConstructExpression) - // make channel + // TODO: Maps can have dedicated types and parsing them as a generic here is only a + // temporary solution. This should be fixed in the future. + assertEquals( + objectType("map", listOf(primitiveType("string"), primitiveType("string"))).also { + it.scope = finalCtx.scopeManager.globalScope + }, + make.type, + ) + + // make channel - decl = main.variables["ch"] - assertNotNull(decl) + decl = main.variables["ch"] + assertNotNull(decl) - make = assertIs(decl.firstAssignment) - assertNotNull(make) - assertTrue(make is ConstructExpression) - assertEquals(tu.objectType("chan", listOf(tu.primitiveType("int"))), make.type) + make = assertIs(decl.firstAssignment) + assertNotNull(make) + assertTrue(make is ConstructExpression) + assertEquals( + objectType("chan", listOf(primitiveType("int"))).also { + it.scope = finalCtx.scopeManager.globalScope + }, + make.type, + ) + } } @Test @@ -680,60 +686,60 @@ class GoLanguageFrontendTest : BaseTest() { } assertNotNull(result) - val app = result.components["application"] - assertNotNull(app) + with(result) { + val app = result.components["application"] + assertNotNull(app) - val tus = app.translationUnits + val tus = app.translationUnits - // fetch the function declaration from the struct TU - val tu2 = tus[1] + // fetch the function declaration from the struct TU + val tu2 = tus[1] - val p2 = tu2.namespaces["p"] - assertNotNull(p2) + val p2 = tu2.namespaces["p"] + assertNotNull(p2) - val myOtherFunc = p2.methods["myOtherFunc"] - assertNotNull(myOtherFunc) - assertFalse(myOtherFunc.isImplicit) + val myOtherFunc = p2.methods["myOtherFunc"] + assertNotNull(myOtherFunc) + assertFalse(myOtherFunc.isImplicit) - val newMyStruct = p2.functions["NewMyStruct"] - assertNotNull(newMyStruct) + val newMyStruct = p2.functions["NewMyStruct"] + assertNotNull(newMyStruct) - // and compare it with the call TU - val tu = tus[0] + // and compare it with the call TU + val tu = tus[0] - val p = tu.namespaces["p"] - assertNotNull(p) + val p = tu.namespaces["p"] + assertNotNull(p) - val main = p.functions["main"] - assertNotNull(main) + val main = p.functions["main"] + assertNotNull(main) - val body = main.body as? Block - assertNotNull(body) + val body = main.body as? Block + assertNotNull(body) - val c = body.variables["c"] + val c = body.variables["c"] - assertNotNull(c) - with(tu) { + assertNotNull(c) // type will be inferred from the function declaration assertEquals(assertResolvedType("p.MyStruct").pointer(), c.type) - } - val newMyStructCall = assertIs(c.firstAssignment) - assertInvokes(newMyStructCall, newMyStruct) + val newMyStructCall = assertIs(c.firstAssignment) + assertInvokes(newMyStructCall, newMyStruct) - val call = tu.calls["myOtherFunc"] as? MemberCallExpression - assertNotNull(call) + val call = tu.calls["myOtherFunc"] as? MemberCallExpression + assertNotNull(call) - val base = call.base as? Reference - assertNotNull(base) - assertRefersTo(base, c) + val base = call.base as? Reference + assertNotNull(base) + assertRefersTo(base, c) - val myOtherFuncCall = tu.calls["myOtherFunc"] - assertNotNull(myOtherFuncCall) - assertInvokes(myOtherFuncCall, myOtherFunc) + val myOtherFuncCall = tu.calls["myOtherFunc"] + assertNotNull(myOtherFuncCall) + assertInvokes(myOtherFuncCall, myOtherFunc) - val go = main.calls["go"] - assertNotNull(go) + val go = main.calls["go"] + assertNotNull(go) + } } @Test diff --git a/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/TypeTest.kt b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/TypeTest.kt new file mode 100644 index 00000000000..15c76943b70 --- /dev/null +++ b/cpg-language-go/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/golang/TypeTest.kt @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2025, Fraunhofer AISEC. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $$$$$$\ $$$$$$$\ $$$$$$\ + * $$ __$$\ $$ __$$\ $$ __$$\ + * $$ / \__|$$ | $$ |$$ / \__| + * $$ | $$$$$$$ |$$ |$$$$\ + * $$ | $$ ____/ $$ |\_$$ | + * $$ | $$\ $$ | $$ | $$ | + * \$$$$$ |$$ | \$$$$$ | + * \______/ \__| \______/ + * + */ +package de.fraunhofer.aisec.cpg.frontends.golang + +import de.fraunhofer.aisec.cpg.test.analyze +import java.nio.file.Path +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class TypeTest { + @Test + fun testTypeNameConfusion() { + val topLevel = Path.of("src", "test", "resources", "golang") + val result = + analyze(listOf(topLevel.resolve("type_loop.go").toFile()), topLevel, true) { + it.registerLanguage() + } + assertNotNull(result) + + val types = result.finalCtx.typeManager.resolvedTypes + assertEquals(3, types.size) + assertEquals( + listOf("fmt.fmt", "fmt.some", "fmt.some"), + types.map { it.name.toString() }.sorted(), + ) + } +} diff --git a/cpg-language-go/src/test/resources/golang/type_loop.go b/cpg-language-go/src/test/resources/golang/type_loop.go new file mode 100644 index 00000000000..04365e422a5 --- /dev/null +++ b/cpg-language-go/src/test/resources/golang/type_loop.go @@ -0,0 +1,8 @@ +package fmt + +type some struct { +} + +type fmt struct { + some +} diff --git a/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ini/IniFileFrontend.kt b/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ini/IniFileFrontend.kt index 053d9dd7f2b..7efbd62866a 100644 --- a/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ini/IniFileFrontend.kt +++ b/cpg-language-ini/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ini/IniFileFrontend.kt @@ -91,7 +91,7 @@ class IniFileFrontend(ctx: TranslationContext, language: Language(ctx) { +class IniFileLanguage : Language() { override val fileExtensions = listOf("ini", "conf") override val namespaceDelimiter: String = "." // no such thing diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/DeclarationHandler.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/DeclarationHandler.kt index cd0b2859125..01927f68bfc 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/DeclarationHandler.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/DeclarationHandler.kt @@ -164,7 +164,7 @@ open class DeclarationHandler(lang: JavaLanguageFrontend) : ) { // create the receiver val receiver = - this.newVariableDeclaration("this", recordDeclaration?.toType() ?: unknownType(), false) + newVariableDeclaration("this", recordDeclaration?.toType() ?: unknownType(), false) .implicit("this") frontend.scopeManager.addDeclaration(receiver) functionDeclaration.receiver = receiver diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt index 4a27ad2e47e..6769a1dd023 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/ExpressionHandler.kt @@ -39,7 +39,7 @@ import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration import de.fraunhofer.aisec.cpg.graph.statements.DeclarationStatement import de.fraunhofer.aisec.cpg.graph.statements.Statement import de.fraunhofer.aisec.cpg.graph.statements.expressions.* -import de.fraunhofer.aisec.cpg.graph.types.FunctionType +import de.fraunhofer.aisec.cpg.graph.types.FunctionType.Companion.computeType import de.fraunhofer.aisec.cpg.graph.types.Type import java.util.function.Supplier import kotlin.collections.set @@ -65,7 +65,7 @@ class ExpressionHandler(lang: JavaLanguageFrontend) : // TODO: We cannot easily identify the signature of the lambda // val type = lambdaExpr.calculateResolvedType() - val functionType = FunctionType.computeType(anonymousFunction) + val functionType = computeType(anonymousFunction) anonymousFunction.type = functionType anonymousFunction.body = frontend.statementHandler.handle(lambdaExpr.body) frontend.scopeManager.leaveScope(anonymousFunction) diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaCallResolverHelper.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaCallResolverHelper.kt index 895682a6bb7..f7c06e0c151 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaCallResolverHelper.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaCallResolverHelper.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.frontends.java -import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression @@ -34,107 +33,99 @@ import de.fraunhofer.aisec.cpg.graph.types.HasType import de.fraunhofer.aisec.cpg.graph.types.recordDeclaration import de.fraunhofer.aisec.cpg.graph.unknownType import de.fraunhofer.aisec.cpg.helpers.Util +import de.fraunhofer.aisec.cpg.passes.SymbolResolver import de.fraunhofer.aisec.cpg.passes.SymbolResolver.Companion.LOGGER -class JavaCallResolverHelper { - - companion object { - /** - * Handle expressions in the form of `super.property` or `ClassName.super.call()`, - * conforming to JLS13 §15.12.1. - * - * This function basically sets the correct type of the [Reference] containing the "super" - * keyword. - * - * @param memberExpression The member expression that needs to be adjusted - * @param curClass The class containing the call - */ - fun handleSuperExpression( - memberExpression: MemberExpression, - curClass: RecordDeclaration, - scopeManager: ScopeManager, - ): Boolean { - // Because the "super" keyword still refers to "this" (but cast to another class), we - // still need to connect the super reference to the receiver of this method. - val func = scopeManager.currentFunction - if (func is MethodDeclaration) { - (memberExpression.base as Reference?)?.refersTo = func.receiver - } +/** + * Handle expressions in the form of `super.property` or `ClassName.super.call()`, conforming to + * JLS13 §15.12.1. + * + * This function basically sets the correct type of the [Reference] containing the "super" keyword. + * + * @param memberExpression The member expression that needs to be adjusted + * @param curClass The class containing the call + */ +fun SymbolResolver.handleSuperExpressionHelper( + memberExpression: MemberExpression, + curClass: RecordDeclaration, +): Boolean { + // Because the "super" keyword still refers to "this" (but cast to another class), we + // still need to connect the super reference to the receiver of this method. + val func = scopeManager.currentFunction + if (func is MethodDeclaration) { + (memberExpression.base as Reference?)?.refersTo = func.receiver + } - // In the next step we can "cast" the base to the correct type, by setting the base - var target: RecordDeclaration? = null + // In the next step we can "cast" the base to the correct type, by setting the base + var target: RecordDeclaration? = null - // In case the reference is just called "super", this is a direct superclass, either - // defined explicitly or java.lang.Object by default - if ( - memberExpression.base.name.toString() == - (memberExpression.language as? JavaLanguage)?.superClassKeyword - ) { - if (curClass.superClasses.isNotEmpty()) { - target = curClass.superClasses[0].root.recordDeclaration - } else { - Util.warnWithFileLocation( - memberExpression, - LOGGER, - "super call without direct superclass! Expected java.lang.Object to be present at least!", - ) - } - } else { - // BaseName.super.call(), might either be in order to specify an enclosing class or - // an interface that is implemented - target = handleSpecificSupertype(memberExpression, curClass) - } + // In case the reference is just called "super", this is a direct superclass, either + // defined explicitly or java.lang.Object by default + if ( + memberExpression.base.name.toString() == + (memberExpression.language as? JavaLanguage)?.superClassKeyword + ) { + if (curClass.superClasses.isNotEmpty()) { + target = curClass.superClasses[0].root.recordDeclaration + } else { + Util.warnWithFileLocation( + memberExpression, + LOGGER, + "super call without direct superclass! Expected java.lang.Object to be present at least!", + ) + } + } else { + // BaseName.super.call(), might either be in order to specify an enclosing class or + // an interface that is implemented + target = handleSpecificSupertype(memberExpression, curClass) + } - if (target != null) { - val superType = target.toType() - // Explicitly set the type of the call's base to the super type, basically "casting" - // the "this" object to the super class - memberExpression.base.type = superType + if (target != null) { + val superType = target.toType() + // Explicitly set the type of the call's base to the super type, basically "casting" + // the "this" object to the super class + memberExpression.base.type = superType - val refersTo = (memberExpression.base as? Reference)?.refersTo - if (refersTo is HasType) { - refersTo.type = superType - refersTo.assignedTypes = mutableSetOf(superType) - } + val refersTo = (memberExpression.base as? Reference)?.refersTo + if (refersTo is HasType) { + refersTo.type = superType + refersTo.assignedTypes = mutableSetOf(superType) + } - // Make sure that really only our super class is in the list of assigned types - memberExpression.base.assignedTypes = mutableSetOf(superType) + // Make sure that really only our super class is in the list of assigned types + memberExpression.base.assignedTypes = mutableSetOf(superType) - return true - } + return true + } - return false - } + return false +} - fun handleSpecificSupertype( - callee: MemberExpression, - curClass: RecordDeclaration, - ): RecordDeclaration? { - val baseName = callee.base.name.parent ?: return null +fun SymbolResolver.handleSpecificSupertype( + callee: MemberExpression, + curClass: RecordDeclaration, +): RecordDeclaration? { + val baseName = callee.base.name.parent ?: return null - val type = - callee.ctx?.typeManager?.lookupResolvedType(baseName.toString()) - ?: callee.unknownType() - if (type in curClass.implementedInterfaces) { - // Basename is an interface -> BaseName.super refers to BaseName itself - return type.recordDeclaration + val type = typeManager.lookupResolvedType(baseName.toString()) ?: callee.unknownType() + if (type in curClass.implementedInterfaces) { + // Basename is an interface -> BaseName.super refers to BaseName itself + return type.recordDeclaration + } else { + // BaseName refers to an enclosing class -> BaseName.super is BaseName's superclass + val base = type.recordDeclaration + if (base != null) { + if (base.superClasses.isNotEmpty()) { + return base.superClasses[0].root.recordDeclaration } else { - // BaseName refers to an enclosing class -> BaseName.super is BaseName's superclass - val base = type.recordDeclaration - if (base != null) { - if (base.superClasses.isNotEmpty()) { - return base.superClasses[0].root.recordDeclaration - } else { - Util.warnWithFileLocation( - callee, - LOGGER, - "super call without direct superclass! Expected java.lang.Object to be present at least!", - ) - } - } + Util.warnWithFileLocation( + callee, + LOGGER, + "super call without direct superclass! Expected java.lang.Object to be present at least!", + ) } - - return null } } + + return null } diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt index 50eb7f94322..b43e97bde1c 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguage.kt @@ -26,8 +26,6 @@ package de.fraunhofer.aisec.cpg.frontends.java import com.fasterxml.jackson.annotation.JsonIgnore -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration @@ -38,12 +36,13 @@ import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference import de.fraunhofer.aisec.cpg.graph.types.* +import de.fraunhofer.aisec.cpg.passes.SymbolResolver import kotlin.reflect.KClass import org.neo4j.ogm.annotation.Transient /** The Java language. */ -open class JavaLanguage(ctx: TranslationContext) : - Language(ctx), +open class JavaLanguage : + Language(), HasClasses, HasSuperClasses, HasGenerics, @@ -114,11 +113,10 @@ open class JavaLanguage(ctx: TranslationContext) : } else super.propagateTypeOfBinaryOperation(operation) } - override fun handleSuperExpression( + override fun SymbolResolver.handleSuperExpression( memberExpression: MemberExpression, curClass: RecordDeclaration, - scopeManager: ScopeManager, - ) = JavaCallResolverHelper.handleSuperExpression(memberExpression, curClass, scopeManager) + ) = handleSuperExpressionHelper(memberExpression, curClass) /** * This function handles some specifics of the Java language when choosing a reference target diff --git a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt index b90cf77fb75..01ea87c8d7e 100644 --- a/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt +++ b/cpg-language-java/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/java/JavaLanguageFrontend.kt @@ -547,7 +547,7 @@ open class JavaLanguageFrontend(ctx: TranslationContext, language: Language() } + val tu = result.components.firstOrNull()?.translationUnits?.firstOrNull() + assertNotNull(tu) + val declaration = tu.declarations[0] as? RecordDeclaration assertNotNull(declaration) @@ -187,7 +190,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { scope = firstCatch.scope assertNotNull(scope) - with(tu) { + with(result) { // first exception type was? resolved, so we can expect a FQN assertEquals( assertResolvedType("java.lang.NumberFormatException"), @@ -615,21 +618,23 @@ internal class JavaLanguageFrontendTest : BaseTest() { val tu = findByUniqueName(result.components.flatMap { it.translationUnits }, file1.toString()) - val namespace = tu.declarations(0) - assertNotNull(namespace) + with(result) { + val namespace = tu.declarations(0) + assertNotNull(namespace) - val record = namespace.declarations(0) - assertNotNull(record) + val record = namespace.declarations(0) + assertNotNull(record) - val constructor = record.constructors[0] - val op = constructor.bodyOrNull(0) - assertNotNull(op) + val constructor = record.constructors[0] + val op = constructor.bodyOrNull(0) + assertNotNull(op) - val lhs = op.lhs() - val receiver = (lhs?.base as? Reference)?.refersTo as? VariableDeclaration? - assertNotNull(receiver) - assertLocalName("this", receiver) - assertEquals(tu.assertResolvedType("my.Animal"), receiver.type) + val lhs = op.lhs() + val receiver = (lhs?.base as? Reference)?.refersTo as? VariableDeclaration? + assertNotNull(receiver) + assertLocalName("this", receiver) + assertEquals(assertResolvedType("my.Animal"), receiver.type) + } } @Test @@ -659,7 +664,7 @@ internal class JavaLanguageFrontendTest : BaseTest() { } } - class MyJavaLanguage(ctx: TranslationContext) : JavaLanguage(ctx) { + class MyJavaLanguage : JavaLanguage() { override val frontend = MyJavaLanguageFrontend::class } 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 8e028e948e4..720d2853894 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 @@ -121,7 +121,7 @@ internal class TypeTests : BaseTest() { val level2 = assertResolvedType("multistep.Level2") val unrelated = assertResolvedType("multistep.Unrelated") println( - result.finalCtx.typeManager.firstOrderTypes + result.finalCtx.typeManager.resolvedTypes .filter { it.typeName == "multistep.Root" } .map { it.superTypes } ) diff --git a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguage.kt b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguage.kt index 5b1966da57d..02487863951 100644 --- a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguage.kt +++ b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguage.kt @@ -25,12 +25,11 @@ */ package de.fraunhofer.aisec.cpg.frontends.jvm -import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.types.* import kotlin.reflect.KClass -class JVMLanguage(ctx: TranslationContext) : Language(ctx) { +class JVMLanguage : Language() { override val fileExtensions: List get() = listOf("class", "java", "jimple", "jar") diff --git a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguageFrontend.kt b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguageFrontend.kt index f3736d943a4..c1372a1df7b 100644 --- a/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguageFrontend.kt +++ b/cpg-language-jvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/jvm/JVMLanguageFrontend.kt @@ -78,7 +78,7 @@ class JVMLanguageFrontend( "class" -> { JavaView( JavaClassPathAnalysisInputLocation( - ctx.currentComponent?.topLevel?.path!!, + ctx.currentComponent?.topLevel()?.path!!, SourceType.Library, listOf( NopEliminator(), @@ -115,12 +115,14 @@ class JVMLanguageFrontend( } "java" -> { JavaView( - JavaSourcePathAnalysisInputLocation(ctx.currentComponent?.topLevel?.path!!) + JavaSourcePathAnalysisInputLocation( + ctx.currentComponent?.topLevel()?.path!! + ) ) } "jimple" -> { JimpleView( - JimpleAnalysisInputLocation(ctx.currentComponent?.topLevel?.toPath()!!) + JimpleAnalysisInputLocation(ctx.currentComponent?.topLevel()?.toPath()!!) ) } else -> { diff --git a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt index 773ec616e8d..c1e84c625bd 100644 --- a/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt +++ b/cpg-language-llvm/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguage.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.frontends.llvm -import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.types.FloatingPointType import de.fraunhofer.aisec.cpg.graph.types.IntegerType @@ -34,7 +33,7 @@ import kotlin.reflect.KClass import org.neo4j.ogm.annotation.Transient /** The LLVM IR language. */ -class LLVMIRLanguage(ctx: TranslationContext) : Language(ctx) { +class LLVMIRLanguage : Language() { override val fileExtensions = listOf("ll") override val namespaceDelimiter = "::" @Transient diff --git a/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt b/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt index 253beaf2843..1d0760adb6c 100644 --- a/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt +++ b/cpg-language-llvm/src/test/kotlin/de/fraunhofer/aisec/cpg/frontends/llvm/LLVMIRLanguageFrontendTest.kt @@ -25,10 +25,8 @@ */ package de.fraunhofer.aisec.cpg.frontends.llvm -import de.fraunhofer.aisec.cpg.ScopeManager import de.fraunhofer.aisec.cpg.TranslationConfiguration import de.fraunhofer.aisec.cpg.TranslationContext -import de.fraunhofer.aisec.cpg.TypeManager import de.fraunhofer.aisec.cpg.frontends.TranslationException import de.fraunhofer.aisec.cpg.graph.* import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration @@ -45,13 +43,8 @@ class LLVMIRLanguageFrontendTest { fun testExceptionBrokenFile() { val topLevel = Path.of("src", "test", "resources", "llvm") - val ctx = - TranslationContext( - TranslationConfiguration.builder().build(), - ScopeManager(), - TypeManager(), - ) - val frontend = LLVMIRLanguageFrontend(ctx, LLVMIRLanguage(ctx)) + val ctx = TranslationContext(TranslationConfiguration.builder().build()) + val frontend = LLVMIRLanguageFrontend(ctx, LLVMIRLanguage()) val exception = assertThrows { frontend.parse(topLevel.resolve("main-broken.ll").toFile()) @@ -63,13 +56,8 @@ class LLVMIRLanguageFrontendTest { fun test1() { val topLevel = Path.of("src", "test", "resources", "llvm") - val ctx = - TranslationContext( - TranslationConfiguration.builder().build(), - ScopeManager(), - TypeManager(), - ) - val frontend = LLVMIRLanguageFrontend(ctx, LLVMIRLanguage(ctx)) + val ctx = TranslationContext(TranslationConfiguration.builder().build()) + val frontend = LLVMIRLanguageFrontend(ctx, LLVMIRLanguage()) frontend.parse(topLevel.resolve("main.ll").toFile()) } diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/DeclarationHandler.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/DeclarationHandler.kt index bbca9dd12f5..9eab09da31d 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/DeclarationHandler.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/DeclarationHandler.kt @@ -36,7 +36,7 @@ import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.scopes.RecordScope import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression -import de.fraunhofer.aisec.cpg.graph.types.FunctionType +import de.fraunhofer.aisec.cpg.graph.types.FunctionType.Companion.computeType import de.fraunhofer.aisec.cpg.helpers.Util /** @@ -155,7 +155,7 @@ class DeclarationHandler(frontend: PythonLanguageFrontend) : } else { func.returnTypes = listOf(frontend.typeOf(s.returns)) } - func.type = FunctionType.computeType(func) + func.type = computeType(func) handleArguments(s.args, func, recordDeclaration) diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt index 7320e7eeeec..eccf12b782e 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguage.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.frontends.python -import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.evaluation.ValueEvaluator import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.HasOverloadedOperation @@ -47,8 +46,8 @@ import org.neo4j.ogm.annotation.Transient import org.neo4j.ogm.annotation.typeconversion.Convert /** The Python language. */ -class PythonLanguage(ctx: TranslationContext) : - Language(ctx), +class PythonLanguage : + Language(), HasShortCircuitOperators, HasOperatorOverloading, HasFunctionStyleConstruction, diff --git a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt index 7ac8a16bafb..318abddc815 100644 --- a/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt +++ b/cpg-language-python/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/python/PythonLanguageFrontend.kt @@ -301,7 +301,7 @@ class PythonLanguageFrontend(ctx: TranslationContext, language: Language() } - assertNotNull(tu) - with(tu) { - val p = tu.namespaces["literal"] + assertNotNull(result) + with(result) { + val p = namespaces["literal"] assertNotNull(p) assertLocalName("literal", p) @@ -1928,7 +1928,7 @@ class PythonFrontendTest : BaseTest() { } assertNotNull(result) - var myClass = result.finalCtx.typeManager.firstOrderTypes["MyClass"] + var myClass = result.finalCtx.typeManager.resolvedTypes["MyClass"] assertNotNull(myClass) assertNotNull(myClass.ancestors) } diff --git a/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/RubyLanguage.kt b/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/RubyLanguage.kt index 8301c00879d..8ca3bdb71f9 100644 --- a/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/RubyLanguage.kt +++ b/cpg-language-ruby/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/ruby/RubyLanguage.kt @@ -25,17 +25,16 @@ */ package de.fraunhofer.aisec.cpg.frontends.ruby -import de.fraunhofer.aisec.cpg.ScopeManager -import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.* import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression import de.fraunhofer.aisec.cpg.graph.types.* +import de.fraunhofer.aisec.cpg.passes.SymbolResolver import kotlin.reflect.KClass /** The Ruby Language */ -class RubyLanguage(ctx: TranslationContext) : - Language(ctx), +class RubyLanguage : + Language(), HasDefaultArguments, HasClasses, HasSuperClasses, @@ -76,10 +75,9 @@ class RubyLanguage(ctx: TranslationContext) : "^=", // Bitwise XOR assignment ) - override fun handleSuperExpression( + override fun SymbolResolver.handleSuperExpression( memberExpression: MemberExpression, curClass: RecordDeclaration, - scopeManager: ScopeManager, ): Boolean { TODO("Not yet implemented") } diff --git a/cpg-language-typescript/build.gradle.kts b/cpg-language-typescript/build.gradle.kts index db17b2cc5fc..ccf7992578b 100644 --- a/cpg-language-typescript/build.gradle.kts +++ b/cpg-language-typescript/build.gradle.kts @@ -43,7 +43,7 @@ publishing { } node { - download.set(findProperty("nodeDownload")?.toString()?.toBoolean() ?: false) + download.set(true) version.set("20.11.1") nodeProjectDir.set(file("${project.projectDir.resolve("src/main/nodejs")}")) } diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt index ad2ed24e2fa..7152b329629 100644 --- a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt +++ b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/JavaScriptLanguage.kt @@ -25,7 +25,6 @@ */ package de.fraunhofer.aisec.cpg.frontends.typescript -import de.fraunhofer.aisec.cpg.TranslationContext import de.fraunhofer.aisec.cpg.frontends.HasShortCircuitOperators import de.fraunhofer.aisec.cpg.frontends.Language import de.fraunhofer.aisec.cpg.graph.types.* @@ -33,8 +32,7 @@ import kotlin.reflect.KClass import org.neo4j.ogm.annotation.Transient /** The JavaScript language. */ -open class JavaScriptLanguage(ctx: TranslationContext) : - Language(ctx), HasShortCircuitOperators { +open class JavaScriptLanguage : Language(), HasShortCircuitOperators { override val fileExtensions = listOf("js", "jsx") override val namespaceDelimiter = "." @Transient diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeHandler.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeHandler.kt index b1dd08906d5..a298c5d800d 100644 --- a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeHandler.kt +++ b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeHandler.kt @@ -29,15 +29,12 @@ import de.fraunhofer.aisec.cpg.frontends.Handler import de.fraunhofer.aisec.cpg.graph.array import de.fraunhofer.aisec.cpg.graph.objectType import de.fraunhofer.aisec.cpg.graph.primitiveType +import de.fraunhofer.aisec.cpg.graph.types.ProblemType import de.fraunhofer.aisec.cpg.graph.types.Type -import de.fraunhofer.aisec.cpg.graph.types.UnknownType import de.fraunhofer.aisec.cpg.graph.unknownType class TypeHandler(frontend: TypeScriptLanguageFrontend) : - Handler( - { UnknownType.getUnknownType(frontend.language) }, - frontend, - ) { + Handler(::ProblemType, frontend) { init { map.put(TypeScriptNode::class.java, ::handleNode) diff --git a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguage.kt b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguage.kt index 3aada2ca733..9701ab31fd5 100644 --- a/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguage.kt +++ b/cpg-language-typescript/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/typescript/TypeScriptLanguage.kt @@ -25,9 +25,7 @@ */ package de.fraunhofer.aisec.cpg.frontends.typescript -import de.fraunhofer.aisec.cpg.TranslationContext - /** The TypeScript language. */ -class TypeScriptLanguage(ctx: TranslationContext) : JavaScriptLanguage(ctx) { +class TypeScriptLanguage : JavaScriptLanguage() { override val fileExtensions = listOf("ts", "tsx") } diff --git a/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg/persistence/Neo4J.kt b/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg/persistence/Neo4J.kt index 3d087d679ff..ec0b1dad23b 100644 --- a/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg/persistence/Neo4J.kt +++ b/cpg-neo4j/src/main/kotlin/de/fraunhofer/aisec/cpg/persistence/Neo4J.kt @@ -23,12 +23,13 @@ * \______/ \__| \______/ * */ +@file:Suppress("CONTEXT_RECEIVERS_DEPRECATED") + package de.fraunhofer.aisec.cpg.persistence import de.fraunhofer.aisec.cpg.TranslationResult import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.Persistable -import de.fraunhofer.aisec.cpg.graph.edges.Edge import de.fraunhofer.aisec.cpg.graph.edges.collections.EdgeCollection import de.fraunhofer.aisec.cpg.graph.nodes import de.fraunhofer.aisec.cpg.helpers.Benchmark @@ -169,31 +170,6 @@ private fun Collection.persist() { this.chunked(edgeChunkSize).map { chunk -> createRelationships(chunk) } } -context(Session) -private fun Collection>.persistEdgesOld() { - // Create an index for the "id" field of node, because we are "MATCH"ing on it in the edge - // creation. We need to wait for this to be finished - this@Session.executeWrite { tx -> - tx.run("CREATE INDEX IF NOT EXISTS FOR (n:Node) ON (n.id)").consume() - } - - this.chunked(edgeChunkSize).map { chunk -> - createRelationships( - chunk.flatMap { edge -> - // Since Neo4J does not support multiple labels on edges, but we do internally, we - // duplicate the edge for each label - edge.labels.map { label -> - mapOf( - "startId" to edge.start.id.toString(), - "endId" to edge.end.id.toString(), - "type" to label, - ) + edge.properties() - } - } - ) - } -} - /** * Creates relationships in a graph database based on provided properties. *