From 91883f76e5a6dd5faf1c607f1d4446499eb93b78 Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Thu, 20 Feb 2025 09:07:03 +0100 Subject: [PATCH] Added translationUnitForInference --- .../de/fraunhofer/aisec/cpg/ScopeManager.kt | 21 ++++++++++ .../aisec/cpg/frontends/Language.kt | 39 ++++++++++++++++++- .../aisec/cpg/passes/TypeResolver.kt | 1 + .../aisec/cpg/passes/inference/PassHelper.kt | 9 +++-- 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt index 5c5b9654826..50960e341c8 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 @@ -868,6 +868,27 @@ class ScopeManager : ScopeProvider { return symbols.singleOrNull() } + + /** + * Returns the [TranslationUnitDeclaration] that should be used for inference, especially for + * global declarations. + * + * @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) + } + } } /** 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 2d8eeba3180..d0476208f1e 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 @@ -30,13 +30,20 @@ import com.fasterxml.jackson.core.JsonGenerator import com.fasterxml.jackson.databind.JsonSerializer import com.fasterxml.jackson.databind.SerializerProvider import com.fasterxml.jackson.databind.annotation.JsonSerialize -import de.fraunhofer.aisec.cpg.* +import de.fraunhofer.aisec.cpg.CallResolutionResult +import de.fraunhofer.aisec.cpg.SignatureResult +import de.fraunhofer.aisec.cpg.TranslationContext +import de.fraunhofer.aisec.cpg.ancestors import de.fraunhofer.aisec.cpg.graph.Name import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.graph.OverlayNode import de.fraunhofer.aisec.cpg.graph.declarations.Declaration import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.NamespaceDeclaration +import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration import de.fraunhofer.aisec.cpg.graph.edges.ast.TemplateArguments +import de.fraunhofer.aisec.cpg.graph.scopes.GlobalScope +import de.fraunhofer.aisec.cpg.graph.scopes.Scope import de.fraunhofer.aisec.cpg.graph.statements.expressions.BinaryOperator import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference @@ -44,6 +51,7 @@ import de.fraunhofer.aisec.cpg.graph.types.* 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.inference.Inference import java.io.File import kotlin.reflect.KClass import kotlin.reflect.full.primaryConstructor @@ -388,6 +396,35 @@ abstract class Language> : Node() { ref.candidates.singleOrNull() } } + + /** + * There are some cases where our [Inference] system needs to place declarations, e.g., a + * [NamespaceDeclaration] in the [GlobalScope]. The issue with that is that the [Scope.astNode] + * of the global scope is always the last parsed [TranslationUnitDeclaration] and we might end + * up adding the declaration to some random translation unit, where it does not really belong. + * + * Therefore, we give the language a chance to return a [TranslationUnitDeclaration] where the + * declaration should be placed. If the language does not override this function, the default + * implementation will return the first [TranslationUnitDeclaration] in + * [TranslationContext.currentComponent]. + * + * But languages might choose to take the information of [TypeToInfer] and [source] and create a + * specific [TranslationUnitDeclaration], e.g., for each namespace that is inferred globally or + * try to put all inferred declarations into one specific (inferred) new translation unit. + * + * @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 { + val tu = source.ctx?.currentComponent?.translationUnits?.firstOrNull() + if (tu == null) { + throw TranslationException( + "No translation unit found that should be used for inference" + ) + } + + return tu + } } /** 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 f027becd370..97c5705f2bd 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 @@ -49,6 +49,7 @@ open class TypeResolver(ctx: TranslationContext) : ComponentPass(ctx) { lateinit var walker: SubgraphWalker.ScopedWalker override fun accept(component: Component) { + ctx.currentComponent = component resolveFirstOrderTypes() refreshNames() 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 660b7d9760d..9fd781dbaf6 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 @@ -83,7 +83,7 @@ fun Pass<*>.tryNamespaceInference(name: Name, source: Node): NamespaceDeclaratio holder = tryScopeInference(parentName, source) } - return (holder ?: source.translationUnit ?: scopeManager.globalScope?.astNode) + return (holder ?: scopeManager.translationUnitForInference(source)) ?.startInference(ctx) ?.inferNamespaceDeclaration(name, null, source) } @@ -133,7 +133,7 @@ internal fun Pass<*>.tryRecordInference(type: Type, source: Node): RecordDeclara } val record = - (holder ?: source.translationUnit ?: scopeManager.globalScope?.astNode) + (holder ?: scopeManager.translationUnitForInference(source)) ?.startInference(ctx) ?.inferRecordDeclaration(type, kind, source) @@ -197,7 +197,10 @@ internal fun Pass<*>.tryVariableInference(ref: Reference): VariableDeclaration? } else if (ref.language is HasGlobalVariables) { // We can try to infer a possible global variable (at top-level), if the language // supports this - scopeManager.globalScope?.astNode?.startInference(this.ctx)?.inferVariableDeclaration(ref) + scopeManager + .translationUnitForInference(ref) + ?.startInference(this.ctx) + ?.inferVariableDeclaration(ref) } else { // Nothing to infer null