Skip to content

Commit

Permalink
Added translationUnitForInference
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto committed Feb 20, 2025
1 parent 457d092 commit 91883f7
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 4 deletions.
21 changes: 21 additions & 0 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 <TypeToInfer : Node> 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<TypeToInfer>(source)
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,28 @@ 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
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
Expand Down Expand Up @@ -388,6 +396,35 @@ abstract class Language<T : LanguageFrontend<*, *>> : 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 <TypeToInfer : Node> 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"

Check warning on line 422 in cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt

View check run for this annotation

Codecov / codecov/patch

cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/frontends/Language.kt#L421-L422

Added lines #L421 - L422 were not covered by tests
)
}

return tu
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<NamespaceDeclaration>(source))
?.startInference(ctx)
?.inferNamespaceDeclaration(name, null, source)
}
Expand Down Expand Up @@ -133,7 +133,7 @@ internal fun Pass<*>.tryRecordInference(type: Type, source: Node): RecordDeclara
}

val record =
(holder ?: source.translationUnit ?: scopeManager.globalScope?.astNode)
(holder ?: scopeManager.translationUnitForInference<RecordDeclaration>(source))
?.startInference(ctx)
?.inferRecordDeclaration(type, kind, source)

Expand Down Expand Up @@ -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<VariableDeclaration>(ref)
?.startInference(this.ctx)
?.inferVariableDeclaration(ref)
} else {
// Nothing to infer
null
Expand Down

0 comments on commit 91883f7

Please sign in to comment.