Skip to content

Commit

Permalink
Fixing record inference and making SymbolResolver deterministic aga…
Browse files Browse the repository at this point in the history
…in (#1476)
  • Loading branch information
oxisto authored Mar 27, 2024
1 parent c831147 commit 3c6411d
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,7 @@ class ScopeManager : ScopeProvider {
val scopes = filterScopes { (it is NameScope && it.name == scopeName) }
s =
if (scopes.isEmpty()) {
Util.errorWithFileLocation(
Util.warnWithFileLocation(
node,
LOGGER,
"Could not find the scope $scopeName needed to resolve the call ${node.name}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ import de.fraunhofer.aisec.cpg.graph.statements.SwitchStatement
import de.fraunhofer.aisec.cpg.graph.statements.expressions.*
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block
import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker
import de.fraunhofer.aisec.cpg.helpers.toIdentitySet
import de.fraunhofer.aisec.cpg.passes.EvaluationOrderGraphPass
import de.fraunhofer.aisec.cpg.passes.astParent

Expand Down Expand Up @@ -67,9 +66,9 @@ inline fun <reified T> Node?.allChildren(noinline predicate: ((T) -> Boolean)? =
* include retrieving it from either an individual [TranslationUnitDeclaration] or the complete
* [TranslationResult].
*/
val Node.allEOGStarters: Set<Node>
val Node.allEOGStarters: List<Node>
get() {
return this.allChildren<EOGStarterHolder>().flatMap { it.eogStarters }.toIdentitySet()
return this.allChildren<EOGStarterHolder>().flatMap { it.eogStarters }.distinct()
}

@JvmName("astNodes")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) {
for (tu in component.translationUnits) {
currentTU = tu
// gather all resolution start holders and their start nodes
val nodes = tu.allEOGStarters.toSet()
val nodes = tu.allEOGStarters

for (node in nodes) {
walker.iterate(node)
Expand Down Expand Up @@ -700,7 +700,7 @@ open class SymbolResolver(ctx: TranslationContext) : ComponentPass(ctx) {
if (root != null && record == null) {
record =
it.startInference(ctx)
?.inferRecordDeclaration(it, currentTU, locationHint = call)
?.inferRecordDeclaration(root, currentTU, locationHint = call)
// update the record declaration
root.recordDeclaration = record
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,18 +375,19 @@ class Inference internal constructor(val start: Node, override val ctx: Translat
)
return null
}
debugWithFileLocation(
locationHint,
log,
"Encountered an unknown record type ${type.typeName} during a call. We are going to infer that record"
)

return inferInScopeOf(currentTU) {
// This could be a class or a struct. We start with a class and may have to fine-tune
// this later.
val declaration = newRecordDeclaration(type.typeName, kind)
declaration.isInferred = true

debugWithFileLocation(
locationHint,
log,
"Inferred a new record declaration ${declaration.name} (${declaration.kind})"
)

// Update the type
type.recordDeclaration = declaration

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,30 @@ class GoLanguageFrontendTest : BaseTest() {
assertInvokes(printfCall, printf)
}

@Test
fun testPointerTypeInference() {
val topLevel = Path.of("src", "test", "resources", "golang")
val result =
analyze(
listOf(
topLevel.resolve("inference.go").toFile(),
),
topLevel,
true
) {
it.registerLanguage<GoLanguage>()
}
assertNotNull(result)

// There should be only a single one inferred method with that name
val queryDecl = result.methods("Query").singleOrNull()
assertNotNull(queryDecl)
assertTrue(queryDecl.isInferred)

val query = result.mcalls["Query"]
assertInvokes(query, queryDecl)
}

@Test
fun testQualifiedCallInMethod() {
val stdLib = Path.of("src", "test", "resources", "golang-std")
Expand Down
8 changes: 8 additions & 0 deletions cpg-language-go/src/test/resources/golang/inference.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package p

import "database/sql"

func doDB() {
var db *sql.DB
db.Query("SELECT * FROM table")
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,11 @@
package de.fraunhofer.aisec.cpg_vis_neo4j

import com.fasterxml.jackson.databind.ObjectMapper
import de.fraunhofer.aisec.cpg.*
import de.fraunhofer.aisec.cpg.graph.builder.*
import de.fraunhofer.aisec.cpg.TranslationManager
import de.fraunhofer.aisec.cpg.TranslationResult
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.functions
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.graph.types.*
import java.io.File
import java.nio.file.Files
import java.nio.file.Paths
Expand All @@ -41,56 +40,28 @@ import kotlin.reflect.jvm.javaField
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import org.junit.jupiter.api.Tag
import org.neo4j.ogm.annotation.Relationship
import org.neo4j.ogm.config.ObjectMapperFactory.objectMapper
import picocli.CommandLine

@Tag("integration")
class ApplicationTest {
private fun createTranslationResult(): Pair<Application, TranslationResult> {
val topLevel = Paths.get("src").resolve("test").resolve("resources").toAbsolutePath()
val path = topLevel.resolve("client.cpp").toAbsolutePath()

val cmd = CommandLine(Application::class.java)
cmd.parseArgs(path.toString())
val application = cmd.getCommand<Application>()

val translationConfiguration = application.setupTranslationConfiguration()
val translationResult =
TranslationManager.builder().config(translationConfiguration).build().analyze().get()
return application to translationResult
}

@Test
@Throws(InterruptedException::class)
fun testPush() {
val (application, translationResult) = createTranslationResult()

assertEquals(32, translationResult.functions.size)

application.pushToNeo4j(translationResult)

val sessionAndSessionFactoryPair = application.connect()

val session = sessionAndSessionFactoryPair.first
session.beginTransaction().use { transaction ->
val functions = session.loadAll(FunctionDeclaration::class.java)
assertNotNull(functions)
fun createTranslationResult(): Pair<Application, TranslationResult> {
val topLevel = Paths.get("src").resolve("test").resolve("resources").toAbsolutePath()
val path = topLevel.resolve("client.cpp").toAbsolutePath()

assertEquals(32, functions.size)
val cmd = CommandLine(Application::class.java)
cmd.parseArgs(path.toString())
val application = cmd.getCommand<Application>()

transaction.commit()
}
val translationConfiguration = application.setupTranslationConfiguration()
val translationResult =
TranslationManager.builder().config(translationConfiguration).build().analyze().get()
return application to translationResult
}

session.clear()
sessionAndSessionFactoryPair.second.close()
}
class ApplicationTest {

@Test
fun testSerializeCpgViaOGM() {
val (application, translationResult) = createTranslationResult()

assertEquals(32, translationResult.functions.size)

val (nodes, edges) = application.translateCPGToOGMBuilders(translationResult)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2024, 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_vis_neo4j

import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.functions
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull

class Neo4JTest {
@Test
@Throws(InterruptedException::class)
fun testPush() {
val (application, translationResult) = createTranslationResult()

assertEquals(32, translationResult.functions.size)

application.pushToNeo4j(translationResult)

val sessionAndSessionFactoryPair = application.connect()

val session = sessionAndSessionFactoryPair.first
session.beginTransaction().use { transaction ->
val functions = session.loadAll(FunctionDeclaration::class.java)
assertNotNull(functions)

assertEquals(32, functions.size)

transaction.commit()
}

session.clear()
sessionAndSessionFactoryPair.second.close()
}
}
15 changes: 15 additions & 0 deletions cpg-neo4j/src/test/resources/log4j2.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<Configuration status="WARN">
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss,SSS} %-5p %C{1} %m%n"/>
<ThresholdFilter level="DEBUG"/>
</Console>
</Appenders>
<Loggers>
<Logger level="DEBUG" name="de.fraunhofer.aisec"/>
<Logger level="ERROR" name="org.neo4j.ogm.drivers.bolt.response.BoltResponse" />
<Root level="INFO">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>

0 comments on commit 3c6411d

Please sign in to comment.