Skip to content

Commit

Permalink
Symbol resolver with EOG power
Browse files Browse the repository at this point in the history
Co-Authored-By: KuechA <[email protected]>
  • Loading branch information
oxisto and KuechA committed Sep 28, 2023
1 parent 6da9562 commit 3273ed3
Show file tree
Hide file tree
Showing 61 changed files with 1,242 additions and 1,055 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
package de.fraunhofer.aisec.cpg.analysis.fsm

import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.declarations.Declaration
import de.fraunhofer.aisec.cpg.graph.declarations.ParameterDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration
import de.fraunhofer.aisec.cpg.graph.edge.Properties
Expand Down Expand Up @@ -291,7 +290,8 @@ open class DFAOrderEvaluator(
if (
node is MemberCallExpression &&
node.base is Reference &&
consideredBases.contains((node.base as Reference).refersTo as Declaration)
(node.base as Reference).refersTo != null &&
consideredBases.contains((node.base as Reference).refersTo!!)
) {
allUsedBases.add((node.base as Reference).refersTo)
}
Expand Down
49 changes: 26 additions & 23 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/ScopeManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -615,20 +615,7 @@ class ScopeManager : ScopeProvider {
*/
@JvmOverloads
fun resolveReference(ref: Reference, startScope: Scope? = currentScope): ValueDeclaration? {
// Unfortunately, we still have an issue about duplicate declarations because header files
// are included multiple times, so we need to exclude the C++ frontend (for now).
val language = ref.language
val (scope, name) =
if (
language?.name?.localName != "CLanguage" &&
(language?.name?.localName != "CPPLanguage")
) {
// For all other languages, we can extract the scope information out of the name and
// start our search at the dedicated scope.
extractScope(ref, startScope)
} else {
Pair(scope, ref.name)
}
val (scope, name) = extractScope(ref, startScope)

// Try to resolve value declarations according to our criteria
return resolve<ValueDeclaration>(scope) {
Expand Down Expand Up @@ -686,13 +673,17 @@ class ScopeManager : ScopeProvider {
* This function extracts a possible scope out of a [Name], e.g. if the name is fully qualified.
* This also resolves possible name aliases (e.g. because of imports). It returns a pair of a
* scope (if found) as well as the name, which is possibly adjusted for the aliases.
*
* Note: Currently only *fully* qualified names are properly resolved. This function will
* probably return imprecise results for partially qualified names, e.g. if a name `A` inside
* `B` points to `A::B`, rather than to `A`.
*/
fun extractScope(node: Node, scope: Scope? = currentScope): Pair<Scope?, Name> {
var name: Name = node.name
var s = scope

// First, we need to check, whether we have some kind of scoping.
if (node.name.parent != null) {
if (node.name.isQualified()) {
// extract the scope name, it is usually a name space, but could probably be something
// else as well in other languages
var scopeName = node.name.parent
Expand All @@ -714,9 +705,9 @@ class ScopeManager : ScopeProvider {
Util.errorWithFileLocation(
node,
LOGGER,
"Could not find the scope $scopeName needed to resolve the call ${node.name}. Falling back to the default (current) scope"
"Could not find the scope $scopeName needed to resolve the call ${node.name}"
)
s
scope
} else {
scopes[0]
}
Expand All @@ -729,7 +720,7 @@ class ScopeManager : ScopeProvider {
* Directly jumps to a given scope. Returns the previous scope. Do not forget to set the scope
* back to the old scope after performing the actions inside this scope.
*
* Handle with care, here be dragons. Should not be exposed outside of the cpg-core module.
* Handle with care, here be dragons. Should not be exposed outside the cpg-core module.
*/
@PleaseBeCareful
internal fun jumpTo(scope: Scope?): Scope? {
Expand Down Expand Up @@ -774,24 +765,35 @@ class ScopeManager : ScopeProvider {
searchScope: Scope?,
stopIfFound: Boolean = false,
noinline predicate: (T) -> Boolean
): List<T> {
return resolve(T::class.java, searchScope, stopIfFound, predicate)
}

fun <T : Declaration> resolve(
klass: Class<T>,
searchScope: Scope?,
stopIfFound: Boolean = false,
predicate: (T) -> Boolean
): List<T> {
var scope = searchScope
val declarations = mutableListOf<T>()

while (scope != null) {
if (scope is ValueDeclarationScope) {
declarations.addAll(scope.valueDeclarations.filterIsInstance<T>().filter(predicate))
declarations.addAll(
scope.valueDeclarations.filterIsInstance(klass).filter(predicate)
)
}

if (scope is StructureDeclarationScope) {
var list = scope.structureDeclarations.filterIsInstance<T>().filter(predicate)
var list = scope.structureDeclarations.filterIsInstance(klass).filter(predicate)

// this was taken over from the old resolveStructureDeclaration.
// TODO(oxisto): why is this only when the list is empty?
if (list.isEmpty()) {
for (declaration in scope.structureDeclarations) {
if (declaration is RecordDeclaration) {
list = declaration.templates.filterIsInstance<T>().filter(predicate)
list = declaration.templates.filterIsInstance(klass).filter(predicate)
}
}
}
Expand Down Expand Up @@ -832,11 +834,12 @@ class ScopeManager : ScopeProvider {
/**
* Retrieves the [RecordDeclaration] for the given name in the given scope.
*
* @param scope the scope
* @param name the name
* * @param scope the scope. Default is [currentScope]
*
* @return the declaration, or null if it does not exist
*/
fun getRecordForName(scope: Scope, name: Name): RecordDeclaration? {
fun getRecordForName(name: Name, scope: Scope? = currentScope): RecordDeclaration? {
return resolve<RecordDeclaration>(scope, true) { it.name.lastPartsMatch(name) }
.firstOrNull()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,8 +444,7 @@ private constructor(
* This will register
* - [TypeHierarchyResolver]
* - [ImportResolver]
* - [VariableUsageResolver]
* - [CallResolver]
* - [SymbolResolver]
* - [DFGPass]
* - [EvaluationOrderGraphPass]
* - [TypeResolver]
Expand All @@ -457,8 +456,7 @@ private constructor(
fun defaultPasses(): Builder {
registerPass<TypeHierarchyResolver>()
registerPass<ImportResolver>()
registerPass<VariableUsageResolver>()
registerPass<CallResolver>() // creates CG
registerPass<SymbolResolver>()
registerPass<DFGPass>()
registerPass<EvaluationOrderGraphPass>() // creates EOG
registerPass<TypeResolver>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,13 @@ package de.fraunhofer.aisec.cpg.frontends

import de.fraunhofer.aisec.cpg.ScopeManager
import de.fraunhofer.aisec.cpg.TranslationContext
import de.fraunhofer.aisec.cpg.graph.Name
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression
import de.fraunhofer.aisec.cpg.graph.types.Type
import de.fraunhofer.aisec.cpg.passes.CallResolver
import java.util.regex.Pattern
import de.fraunhofer.aisec.cpg.passes.SymbolResolver

/**
* A language trait is a feature or trait that is common to a group of programming languages. Any
Expand Down Expand Up @@ -116,7 +114,7 @@ interface HasComplexCallResolution : LanguageTrait {
call: CallExpression,
ctx: TranslationContext,
currentTU: TranslationUnitDeclaration,
callResolver: CallResolver
callResolver: SymbolResolver
): List<FunctionDeclaration>

/**
Expand All @@ -131,7 +129,7 @@ interface HasComplexCallResolution : LanguageTrait {
fun refineInvocationCandidatesFromRecord(
recordDeclaration: RecordDeclaration,
call: CallExpression,
namePattern: Pattern,
name: String,
ctx: TranslationContext
): List<FunctionDeclaration>
}
Expand Down Expand Up @@ -170,7 +168,6 @@ interface HasSuperClasses : LanguageTrait {
callee: MemberExpression,
curClass: RecordDeclaration,
scopeManager: ScopeManager,
recordMap: Map<Name, RecordDeclaration>
): Boolean
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,11 @@ fun MetadataProvider.newCallExpression(
val node = CallExpression()
node.applyMetadata(this, fqn, rawNode, code, true)

// Set the call expression as resolution helper for the callee
if (callee is Reference) {
callee.resolutionHelper = node
}

node.callee = callee
node.template = template

Expand Down Expand Up @@ -331,6 +336,11 @@ fun MetadataProvider.newMemberCallExpression(
code,
)

// Set the call expression as resolution helper for the callee
if (callee is Reference) {
callee.resolutionHelper = node
}

node.callee = callee
node.isStatic = isStatic

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,12 @@ open class Node : IVisitable<Node>, Persistable, LanguageProvider, ScopeProvider
code == other.code &&
comment == other.comment &&
location == other.location &&
file == other.file &&
// We need to exclude "file" here, because in C++ the same header node can be
// imported in two different files and in this case, the "file" property will be
// different. Since want to squash those equal nodes, we will only consider all the
// other attributes, including "location" (which contains the *original* file
// location in the header file), but not "file".
// file == other.file &&
isImplicit == other.isImplicit
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,12 +438,12 @@ context(Holder<out Statement>)

fun LanguageFrontend<*, *>.memberCall(
localName: CharSequence,
member: Expression,
base: Expression,
isStatic: Boolean = false,
init: (MemberCallExpression.() -> Unit)? = null
): MemberCallExpression {
// Try to parse the name
val node = newMemberCallExpression(newMemberExpression(localName, member), isStatic)
val node = newMemberCallExpression(newMemberExpression(localName, base), isStatic)
if (init != null) {
init(node)
}
Expand Down Expand Up @@ -684,9 +684,9 @@ fun LanguageFrontend<*, *>.whileCondition(init: WhileStatement.() -> Expression)
}

/**
* <<<<<<< HEAD Configures the [DoStatement.condition] in the Fluent Node DSL of the nearest
* enclosing [DoStatement]. The [init] block can be used to create further sub-nodes as well as
* configuring the created node itself.
* Configures the [DoStatement.condition] in the Fluent Node DSL of the nearest enclosing
* [DoStatement]. The [init] block can be used to create further sub-nodes as well as configuring
* the created node itself.
*/
context(DoStatement)

Expand All @@ -695,13 +695,9 @@ fun LanguageFrontend<*, *>.whileCondition(init: DoStatement.() -> Expression): E
}

/**
* Creates a new [CompoundStatement] in the Fluent Node DSL and sets it to the
* [IfStatement.thenStatement] of the nearest enclosing [IfStatement]. The [init] block can be used
* to create further sub-nodes as well as configuring the created node itself.
* =======
* Creates a new [Block] in the Fluent Node DSL and sets it to the [IfStatement.thenStatement] of
* the nearest enclosing [IfStatement]. The [init] block can be used to create further sub-nodes as
* well as configuring the created node itself. >>>>>>> main
* well as configuring the created node itself.
*/
context(IfStatement)

Expand Down Expand Up @@ -748,9 +744,9 @@ fun LanguageFrontend<*, *>.loopBody(init: Block.() -> Unit): Block {
}

/**
* Creates a new [CompoundStatement] in the Fluent Node DSL and sets it to the
* [DoStatement.statement] of the nearest enclosing [DoStatement]. The [init] block can be used to
* create further sub-nodes as well as configuring the created node itself.
* Creates a new [Block] in the Fluent Node DSL and sets it to the [DoStatement.statement] of the
* nearest enclosing [WhileStatement]. The [init] block can be used to create further sub-nodes as
* well as configuring the created node itself.
*/
context(DoStatement)

Expand Down Expand Up @@ -1439,3 +1435,14 @@ private fun <T : Node> LanguageFrontend<*, *>.scopeIfNecessary(
scopeManager.leaveScope(node)
}
}

context(MethodDeclaration)

fun LanguageFrontend<*, *>.receiver(name: String, type: Type): VariableDeclaration {
val node = newVariableDeclaration(name, type)

this@MethodDeclaration.receiver = node
scopeManager.addDeclaration(node)

return node
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,16 @@ package de.fraunhofer.aisec.cpg.graph.declarations
import de.fraunhofer.aisec.cpg.graph.AST
import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge
import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdgeDelegate
import de.fraunhofer.aisec.cpg.graph.types.Type
import org.apache.commons.lang3.builder.ToStringBuilder
import org.neo4j.ogm.annotation.Relationship

class EnumDeclaration : Declaration() {
class EnumDeclaration : RecordDeclaration() {
@Relationship(value = "ENTRIES", direction = Relationship.Direction.OUTGOING)
@AST
var entryEdges: MutableList<PropertyEdge<EnumConstantDeclaration>> = ArrayList()

@Relationship(value = "SUPER_TYPES", direction = Relationship.Direction.OUTGOING)
var superTypeEdges: MutableList<PropertyEdge<Type>> = ArrayList()

@Relationship var superTypeDeclarations: Set<RecordDeclaration> = HashSet()

var entries by PropertyEdgeDelegate(EnumDeclaration::entryEdges)

var superTypes by PropertyEdgeDelegate(EnumDeclaration::superTypeEdges)

override fun toString(): String {
return ToStringBuilder(this, TO_STRING_STYLE)
.appendSuper(super.toString())
Expand Down
Loading

0 comments on commit 3273ed3

Please sign in to comment.