Skip to content

Commit 065865f

Browse files
committed
De-coupling variable declaration of AST placement
1 parent 07d2b30 commit 065865f

File tree

55 files changed

+519
-552
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+519
-552
lines changed

cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt

+39-78
Original file line numberDiff line numberDiff line change
@@ -343,47 +343,11 @@ class ScopeManager : ScopeProvider {
343343
/**
344344
* This function MUST be called when a language frontend first handles a [Declaration]. It adds
345345
* a declaration to the scope manager, taking into account the currently active scope.
346-
* Furthermore, it adds the declaration to the [de.fraunhofer.aisec.cpg.graph.DeclarationHolder]
347-
* that is associated with the current scope through [ValueDeclarationScope.addValueDeclaration]
348-
* and [StructureDeclarationScope.addStructureDeclaration].
349-
*
350-
* Setting [Scope.astNode] to false is useful, if you want to make sure a certain declaration is
351-
* visible within a scope, but is not directly part of the scope's AST. An example is the way
352-
* C/C++ handles unscoped enum constants. They are visible in the enclosing scope, e.g., a
353-
* translation unit, but they are added to the AST of their enum declaration, not the
354-
* translation unit. The enum declaration is then added to the translation unit.
355346
*
356347
* @param declaration the declaration to add
357-
* @param addToAST specifies, whether the declaration also gets added to the [Scope.astNode] of
358-
* the current scope (if it implements [DeclarationHolder]). Defaults to true.
359348
*/
360-
@JvmOverloads
361-
fun addDeclaration(declaration: Declaration?, addToAST: Boolean = true) {
362-
if (declaration != null) {
363-
// New stuff here
364-
currentScope?.addSymbol(declaration.symbol, declaration)
365-
}
366-
367-
// Legacy stuff here
368-
when (declaration) {
369-
is ProblemDeclaration,
370-
is IncludeDeclaration -> {
371-
// directly add problems and includes to the global scope
372-
this.globalScope?.addDeclaration(declaration, addToAST, this)
373-
}
374-
is ValueDeclaration -> {
375-
val scope = this.firstScopeIsInstanceOrNull<ValueDeclarationScope>()
376-
scope?.addDeclaration(declaration, addToAST, this)
377-
}
378-
is ImportDeclaration,
379-
is EnumDeclaration,
380-
is RecordDeclaration,
381-
is NamespaceDeclaration,
382-
is TemplateDeclaration -> {
383-
val scope = this.firstScopeIsInstanceOrNull<StructureDeclarationScope>()
384-
scope?.addDeclaration(declaration, addToAST, this)
385-
}
386-
}
349+
fun addDeclaration(declaration: Declaration) {
350+
currentScope?.addSymbol(declaration.symbol, declaration)
387351
}
388352

389353
/**
@@ -483,11 +447,10 @@ class ScopeManager : ScopeProvider {
483447
}
484448

485449
/**
486-
* Adds typedefs to a [ValueDeclarationScope]. The language frontend needs to decide on the
487-
* scope of the typedef. Most likely, typedefs are global. Therefore, the [GlobalScope] is set
488-
* as default.
450+
* Adds typedefs to a [Scope]. The language frontend needs to decide on the scope of the
451+
* typedef. Most likely, typedefs are global. Therefore, the [GlobalScope] is set as default.
489452
*/
490-
fun addTypedef(typedef: TypedefDeclaration, scope: ValueDeclarationScope? = globalScope) {
453+
fun addTypedef(typedef: TypedefDeclaration, scope: Scope? = globalScope) {
491454
scope?.addTypedef(typedef)
492455
}
493456

@@ -684,45 +647,43 @@ class ScopeManager : ScopeProvider {
684647
// We need to build a path from the current scope to the top most one. This ensures us that
685648
// a local definition overwrites / shadows one that was there on a higher scope.
686649
while (current != null) {
687-
if (current is ValueDeclarationScope) {
688-
// This is a little bit of a hack to support partial FQN resolution at least with
689-
// typedefs, but it's not really ideal.
690-
// And this also should be merged with the scope manager logic when resolving names.
691-
//
692-
// The better approach would be to harmonize the FQN of all types in one pass before
693-
// all this happens.
694-
//
695-
// This process has several steps:
696-
// First, do a quick local lookup, to see if we have a typedef our current scope
697-
// (only do this if the name is not qualified)
698-
if (!alias.isQualified() && current == scope) {
699-
val decl = current.typedefs[alias]
700-
if (decl != null) {
701-
return decl.type
702-
}
650+
// This is a little bit of a hack to support partial FQN resolution at least with
651+
// typedefs, but it's not really ideal.
652+
// And this also should be merged with the scope manager logic when resolving names.
653+
//
654+
// The better approach would be to harmonize the FQN of all types in one pass before
655+
// all this happens.
656+
//
657+
// This process has several steps:
658+
// First, do a quick local lookup, to see if we have a typedef our current scope
659+
// (only do this if the name is not qualified)
660+
if (!alias.isQualified() && current == scope) {
661+
val decl = current.typedefs[alias]
662+
if (decl != null) {
663+
return decl.type
703664
}
665+
}
704666

705-
// Next, try to look up the name either by its FQN (if it is qualified) or make it
706-
// qualified based on the current namespace
707-
val key =
708-
current.typedefs.keys.firstOrNull {
709-
var lookupName = alias
710-
711-
// If the lookup name is already a FQN, we can use the name directly
712-
lookupName =
713-
if (lookupName.isQualified()) {
714-
lookupName
715-
} else {
716-
// Otherwise, we want to make an FQN out of it using the current
717-
// namespace
718-
currentNamespace?.fqn(lookupName.localName) ?: lookupName
719-
}
720-
721-
it.lastPartsMatch(lookupName)
722-
}
723-
if (key != null) {
724-
return current.typedefs[key]?.type
667+
// Next, try to look up the name either by its FQN (if it is qualified) or make it
668+
// qualified based on the current namespace
669+
val key =
670+
current.typedefs.keys.firstOrNull {
671+
var lookupName = alias
672+
673+
// If the lookup name is already a FQN, we can use the name directly
674+
lookupName =
675+
if (lookupName.isQualified()) {
676+
lookupName
677+
} else {
678+
// Otherwise, we want to make an FQN out of it using the current
679+
// namespace
680+
currentNamespace?.fqn(lookupName.localName) ?: lookupName
681+
}
682+
683+
it.lastPartsMatch(lookupName)
725684
}
685+
if (key != null) {
686+
return current.typedefs[key]?.type
726687
}
727688

728689
current = current.parent

cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/DeclarationBuilder.kt

+1-6
Original file line numberDiff line numberDiff line change
@@ -455,15 +455,10 @@ fun MetadataProvider.newImportDeclaration(
455455
rawNode: Any? = null,
456456
): ImportDeclaration {
457457
val node = ImportDeclaration()
458-
node.applyMetadata(this, "", rawNode)
458+
node.applyMetadata(this, alias ?: import, rawNode, doNotPrependNamespace = true)
459459
node.import = import
460460
node.alias = alias
461461
node.style = style
462-
if (alias != null) {
463-
node.name = alias
464-
} else {
465-
node.name = import
466-
}
467462

468463
log(node)
469464
return node

cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/builder/Fluent.kt

+11-7
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ fun LanguageFrontend<*, *>.namespace(
9999
init(node)
100100
scopeManager.leaveScope(node)
101101
scopeManager.addDeclaration(node)
102+
addDeclaration(node)
103+
102104
return node
103105
}
104106

@@ -119,6 +121,7 @@ fun LanguageFrontend<*, *>.record(
119121
init(node)
120122
scopeManager.leaveScope(node)
121123
scopeManager.addDeclaration(node)
124+
addDeclaration(node)
122125

123126
return node
124127
}
@@ -142,6 +145,7 @@ fun LanguageFrontend<*, *>.field(
142145
}
143146

144147
scopeManager.addDeclaration(node)
148+
addDeclaration(node)
145149

146150
return node
147151
}
@@ -184,6 +188,7 @@ fun LanguageFrontend<*, *>.function(
184188
scopeManager.leaveScope(node)
185189

186190
scopeManager.addDeclaration(node)
191+
addDeclaration(node)
187192

188193
return node
189194
}
@@ -283,6 +288,7 @@ fun LanguageFrontend<*, *>.param(
283288
init?.let { it(node) }
284289

285290
scopeManager.addDeclaration(node)
291+
this@FunctionDeclaration.parameters += node
286292

287293
return node
288294
}
@@ -411,8 +417,7 @@ fun LanguageFrontend<*, *>.variable(
411417
val node = newVariableDeclaration(name, type)
412418
if (init != null) init(node)
413419

414-
declarationEdges += node
415-
420+
declarations += node
416421
scopeManager.addDeclaration(node)
417422

418423
return node
@@ -432,8 +437,7 @@ fun LanguageFrontend<*, *>.problemDecl(
432437
val node = newProblemDeclaration(problem = description, problemType = type)
433438
if (init != null) init(node)
434439

435-
declarationEdges += node
436-
440+
declarations += node
437441
scopeManager.addDeclaration(node)
438442

439443
return node
@@ -667,9 +671,9 @@ fun LanguageFrontend<*, *>.forInitializer(
667671

668672
initializerStatement = node
669673

670-
if (node.isSingleDeclaration()) {
671-
672-
scopeManager.addDeclaration(node.singleDeclaration, false)
674+
val single = node.singleDeclaration
675+
if (single != null) {
676+
scopeManager.addDeclaration(single)
673677
}
674678

675679
return node

cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/FunctionScope.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,4 @@ package de.fraunhofer.aisec.cpg.graph.scopes
2727

2828
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
2929

30-
class FunctionScope(astNode: FunctionDeclaration) : ValueDeclarationScope(astNode)
30+
class FunctionScope(astNode: FunctionDeclaration) : Scope(astNode)

cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/GlobalScope.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import de.fraunhofer.aisec.cpg.graph.nodes
3737
* scope that is restricted to a translation unit, i.e. C++ while still maintaining a unique list of
3838
* global variables.
3939
*/
40-
class GlobalScope : StructureDeclarationScope(null) {
40+
class GlobalScope : Scope(null) {
4141

4242
/**
4343
* Because the way we currently handle parallel parsing in [TranslationManager.parseParallel],

cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/LocalScope.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ import de.fraunhofer.aisec.cpg.graph.Node
3131
* Scope of validity associated to the local statement. Variables declared inside this statement are
3232
* not visible outside.
3333
*/
34-
class LocalScope(astNode: Node) : ValueDeclarationScope(astNode) {}
34+
class LocalScope(astNode: Node) : Scope(astNode) {}

cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/NameScope.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import de.fraunhofer.aisec.cpg.graph.Node
3333
* declared in it. This could be a package or other structural elements, like a class. In the first
3434
* case, the derived [NamespaceScope], in the latter case, the derived [RecordScope] should be used.
3535
*/
36-
sealed class NameScope(node: Node?) : StructureDeclarationScope(node) {
36+
sealed class NameScope(node: Node?) : Scope(node) {
3737

3838
init {
3939
astNode = node

cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/NamespaceScope.kt

+5-12
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
*/
2626
package de.fraunhofer.aisec.cpg.graph.scopes
2727

28-
import de.fraunhofer.aisec.cpg.ScopeManager
2928
import de.fraunhofer.aisec.cpg.graph.declarations.Declaration
3029
import de.fraunhofer.aisec.cpg.graph.declarations.ImportDeclaration
3130
import de.fraunhofer.aisec.cpg.graph.declarations.NamespaceDeclaration
@@ -45,8 +44,8 @@ class NamespaceScope(astNode: NamespaceDeclaration) : NameScope(astNode) {
4544
* This is the mirror property to [Scope.importedScopeEdges]. It specifies which other [Scope]s
4645
* are importing this namespace.
4746
*
48-
* This is used in [addDeclaration] to update the [ImportDeclaration.importedSymbols] once we
49-
* add a new symbol here, so that is it also visible in the scope of the [ImportDeclaration].
47+
* This is used in [addSymbol] to update the [ImportDeclaration.importedSymbols] once we add a
48+
* new symbol here, so that is it also visible in the scope of the [ImportDeclaration].
5049
*/
5150
@Relationship(value = "IMPORTS_SCOPE", direction = Relationship.Direction.INCOMING)
5251
val importedByEdges: Imports =
@@ -55,18 +54,12 @@ class NamespaceScope(astNode: NamespaceDeclaration) : NameScope(astNode) {
5554
/** Virtual property for accessing [importedScopeEdges] without property edges. */
5655
val importedBy: MutableSet<Scope> by unwrappingIncoming(NamespaceScope::importedByEdges)
5756

58-
override fun addDeclaration(
59-
declaration: Declaration,
60-
addToAST: Boolean,
61-
scopeManager: ScopeManager,
62-
) {
63-
val result = super.addDeclaration(declaration, addToAST, scopeManager)
57+
override fun addSymbol(symbol: Symbol, declaration: Declaration) {
58+
super.addSymbol(symbol, declaration)
6459

6560
// Update imported symbols of dependent scopes
6661
for (edge in importedByEdges) {
67-
edge.declaration?.let { scopeManager.updateImportedSymbols(it) }
62+
edge.declaration?.let { ctx?.scopeManager?.updateImportedSymbols(it) }
6863
}
69-
70-
return result
7164
}
7265
}

cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/scopes/Scope.kt

+19-10
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,11 @@ import com.fasterxml.jackson.annotation.JsonBackReference
2929
import de.fraunhofer.aisec.cpg.PopulatedByPass
3030
import de.fraunhofer.aisec.cpg.frontends.HasImplicitReceiver
3131
import de.fraunhofer.aisec.cpg.frontends.Language
32+
import de.fraunhofer.aisec.cpg.graph.Name
3233
import de.fraunhofer.aisec.cpg.graph.Node
3334
import de.fraunhofer.aisec.cpg.graph.declarations.Declaration
3435
import de.fraunhofer.aisec.cpg.graph.declarations.ImportDeclaration
36+
import de.fraunhofer.aisec.cpg.graph.declarations.TypedefDeclaration
3537
import de.fraunhofer.aisec.cpg.graph.edges.scopes.Import
3638
import de.fraunhofer.aisec.cpg.graph.edges.scopes.ImportStyle
3739
import de.fraunhofer.aisec.cpg.graph.edges.scopes.Imports
@@ -114,8 +116,22 @@ sealed class Scope(
114116
*/
115117
@Transient var predefinedLookupScopes: MutableMap<Symbol, LookupScopeStatement> = mutableMapOf()
116118

119+
/**
120+
* A map of typedefs keyed by their alias name. This is still needed as a bridge until we
121+
* completely redesign the alias / typedef system.
122+
*/
123+
@Transient val typedefs = mutableMapOf<Name, TypedefDeclaration>()
124+
125+
/**
126+
* Adds a [typedef] declaration to the scope. This is used to store typedefs in the scope, so
127+
* that they can be resolved later on.
128+
*/
129+
fun addTypedef(typedef: TypedefDeclaration) {
130+
typedefs[typedef.alias.name] = typedef
131+
}
132+
117133
/** Adds a [declaration] with the defined [symbol]. */
118-
fun addSymbol(symbol: Symbol, declaration: Declaration) {
134+
open fun addSymbol(symbol: Symbol, declaration: Declaration) {
119135
if (
120136
declaration is ImportDeclaration &&
121137
declaration.style == ImportStyle.IMPORT_ALL_SYMBOLS_FROM_NAMESPACE
@@ -229,7 +245,7 @@ sealed class Scope(
229245

230246
override fun hashCode(): Int {
231247
var result = astNode?.hashCode() ?: 0
232-
result = 31 * result + (name?.hashCode() ?: 0)
248+
result = 31 * result + name.hashCode()
233249
return result
234250
}
235251

@@ -251,19 +267,12 @@ sealed class Scope(
251267
override fun toString(): String {
252268
val builder = ToStringBuilder(this, TO_STRING_STYLE)
253269

254-
if (name?.isNotEmpty() == true) {
270+
if (name.isNotEmpty() == true) {
255271
builder.append("name", name)
256272
}
257273

258274
return builder.toString()
259275
}
260-
261-
fun addSymbols(other: MutableMap<Symbol, MutableSet<Declaration>>) {
262-
for ((key, value) in other.entries) {
263-
val list = this.symbols.computeIfAbsent(key) { mutableListOf() }
264-
list += value
265-
}
266-
}
267276
}
268277

269278
/**

0 commit comments

Comments
 (0)