Repository: joernio/joern (scala)

Issue:
WARN CdgPass: Found CDG edge starting at BLOCK node
Sample command:
```scala
joern> importCode.cpp.fromString("""
       int foo()
       {
         int v1, v2 = 0;
         if((v1 = 1, v1 == 2) || v2 <= 3) return 1;
         return 0;
       }
       """) 
[...]
Adding default overlays to base CPG
2022-12-19 12:05:09.024 WARN CdgPass: Found CDG edge starting at BLOCK node. This is most likely caused by an invalid CFG. Method: foo number of outgoing CFG edges from BLOCK node: 2
2022-12-19 12:05:09.024 WARN CdgPass: Found CDG edge starting at BLOCK node. This is most likely caused by an invalid CFG. Method: foo number of outgoing CFG edges from BLOCK node: 2
2022-12-19 12:05:09.024 WARN CdgPass: Found CDG edge starting at BLOCK node. This is most likely caused by an invalid CFG. Method: foo number of outgoing CFG edges from BLOCK node: 2
The graph has been modified. You may want to use the `save` command to persist changes to disk.  All changes will also be saved collectively on exit
The graph has been modified. You may want to use the `save` command to persist changes to disk.  All changes will also be saved collectively on exit
res0: Cpg = Cpg (Graph [70 nodes])
```

Pull request description:
Fixes: https://github.com/joernio/joern/issues/2114

Also: removed remaining occurrences of default.semantics

Patch:
diff --git a/dataflowengineoss/README.md b/dataflowengineoss/README.md
index f92cb4449045..cfcd90fc95ba 100644
--- a/dataflowengineoss/README.md
+++ b/dataflowengineoss/README.md
@@ -2,7 +2,7 @@
 
 A taint-tracking system based on whole-program data-dependence 
 representation. External library calls can be defined by 
-semantic models (see `src/test/resources/default.semantics`)
+semantic models (see `io.joern.dataflowengineoss.DefaultSemantics`).
 
 Basic usage:
 
@@ -25,25 +25,14 @@ To begin using the data flow engine on the CPG, we need the following:
 ```scala
   // (1) Imports to extend CFG nodes
   import io.joern.dataflowengineoss.language.toExtendedCfgNode
-  import io.joern.dataflowengineoss.semanticsloader.{Parser, Semantics, FlowSemantic}
   import io.joern.dataflowengineoss.queryengine.{EngineContext, EngineConfig}
 
   import scala.util.{Failure, Success, Try}
   import scala.io.{BufferedSource, Source}
 
-  // (2) Parse in semantic models, if none given then external methods will be overtainted by default
-  val semanticsParser = new Parser()
-  val defaultSemantics: Try[BufferedSource] = Try(
-    Source.fromInputStream(getClass.getClassLoader.getResourceAsStream("default.semantics"))
-  )
-  val semantics: List[FlowSemantic] = defaultSemantics match {
-    case Failure(_)         => List()
-    case Success(semantics) => semanticsParser.parse(semantics.getLines().mkString("\n"))
-  }
-
-  // (3) Optional: Configure the engine
+  // (2) Optional: Configure the engine
   val engineConfig = EngineConfig(maxCallDepth = 2, initialTable = None, disableCacheUse = false)
 
-  // (4) Create execution context for the engine
-  implicit var context: EngineContext = EngineContext(Semantics.fromList(semantics), engineConfig)
+  // (3) Create execution context for the engine
+  implicit var context: EngineContext = EngineContext(config =  engineConfig)
 ```
\ No newline at end of file
diff --git a/dataflowengineoss/src/main/scala/io/joern/dataflowengineoss/DefaultSemantics.scala b/dataflowengineoss/src/main/scala/io/joern/dataflowengineoss/DefaultSemantics.scala
index f860bb5152d3..f0de26b4de29 100644
--- a/dataflowengineoss/src/main/scala/io/joern/dataflowengineoss/DefaultSemantics.scala
+++ b/dataflowengineoss/src/main/scala/io/joern/dataflowengineoss/DefaultSemantics.scala
@@ -3,6 +3,8 @@ package io.joern.dataflowengineoss
 import io.joern.dataflowengineoss.semanticsloader.{FlowSemantic, Semantics}
 import io.shiftleft.codepropertygraph.generated.Operators
 
+import scala.annotation.unused
+
 object DefaultSemantics {
 
   /** @return
@@ -148,6 +150,7 @@ object DefaultSemantics {
   /** @return
     *   procedure semantics for operators and common external Java calls only.
     */
+  @unused
   def javaSemantics(): Semantics = Semantics.fromList(operatorFlows ++ javaFlows)
 
 }
diff --git a/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForExpressionsCreator.scala b/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForExpressionsCreator.scala
index 8fed5d7fafb3..fa09dbdd2fc4 100644
--- a/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForExpressionsCreator.scala
+++ b/joern-cli/frontends/c2cpg/src/main/scala/io/joern/c2cpg/astcreation/AstForExpressionsCreator.scala
@@ -113,33 +113,31 @@ trait AstForExpressionsCreator { this: AstCreator =>
 
   private def astForUnaryExpression(unary: IASTUnaryExpression): Ast = {
     val operatorMethod = unary.getOperator match {
-      case IASTUnaryExpression.op_prefixIncr  => Operators.preIncrement
-      case IASTUnaryExpression.op_prefixDecr  => Operators.preDecrement
-      case IASTUnaryExpression.op_plus        => Operators.plus
-      case IASTUnaryExpression.op_minus       => Operators.minus
-      case IASTUnaryExpression.op_star        => Operators.indirection
-      case IASTUnaryExpression.op_amper       => Operators.addressOf
-      case IASTUnaryExpression.op_tilde       => Operators.not
-      case IASTUnaryExpression.op_not         => Operators.logicalNot
-      case IASTUnaryExpression.op_sizeof      => Operators.sizeOf
-      case IASTUnaryExpression.op_postFixIncr => Operators.postIncrement
-      case IASTUnaryExpression.op_postFixDecr => Operators.postDecrement
-      case IASTUnaryExpression.op_throw       => "<operator>.throw"
-      case IASTUnaryExpression.op_typeid      => "<operator>.typeOf"
-      case _                                  => "<operator>.unknown"
+      case IASTUnaryExpression.op_prefixIncr       => Operators.preIncrement
+      case IASTUnaryExpression.op_prefixDecr       => Operators.preDecrement
+      case IASTUnaryExpression.op_plus             => Operators.plus
+      case IASTUnaryExpression.op_minus            => Operators.minus
+      case IASTUnaryExpression.op_star             => Operators.indirection
+      case IASTUnaryExpression.op_amper            => Operators.addressOf
+      case IASTUnaryExpression.op_tilde            => Operators.not
+      case IASTUnaryExpression.op_not              => Operators.logicalNot
+      case IASTUnaryExpression.op_sizeof           => Operators.sizeOf
+      case IASTUnaryExpression.op_postFixIncr      => Operators.postIncrement
+      case IASTUnaryExpression.op_postFixDecr      => Operators.postDecrement
+      case IASTUnaryExpression.op_throw            => "<operator>.throw"
+      case IASTUnaryExpression.op_typeid           => "<operator>.typeOf"
+      case IASTUnaryExpression.op_bracketedPrimary => "<operator>.bracketedPrimary"
+      case _                                       => "<operator>.unknown"
     }
 
-    if (unary.getOperator == IASTUnaryExpression.op_bracketedPrimary) {
-      astForExpression(unary.getOperand)
+    if (
+      unary.getOperator == IASTUnaryExpression.op_bracketedPrimary &&
+      !unary.getOperand.isInstanceOf[IASTExpressionList]
+    ) {
+      nullSafeAst(unary.getOperand)
     } else {
       val cpgUnary = newCallNode(unary, operatorMethod, operatorMethod, DispatchTypes.STATIC_DISPATCH)
-      val operandExpr = unary.getOperand match {
-        // special handling for operand expression in brackets - we simply ignore the brackets
-        case opExpr: IASTUnaryExpression if opExpr.getOperator == IASTUnaryExpression.op_bracketedPrimary =>
-          opExpr.getOperand
-        case opExpr => opExpr
-      }
-      val operand = nullSafeAst(operandExpr)
+      val operand  = nullSafeAst(unary.getOperand)
       callAst(cpgUnary, List(operand))
     }
   }


Test patch:
diff --git a/benchmarks/src/test/resources/default.semantics b/benchmarks/src/test/resources/default.semantics
deleted file mode 120000
index 335ad86700cc..000000000000
--- a/benchmarks/src/test/resources/default.semantics
+++ /dev/null
@@ -1,1 +0,0 @@
-../../../../joern-cli/src/main/resources/default.semantics
\ No newline at end of file
diff --git a/benchmarks/src/test/scala/io/joern/benchmarks/testfixtures/BenchmarkFixture.scala b/benchmarks/src/test/scala/io/joern/benchmarks/testfixtures/BenchmarkFixture.scala
index 178c5e787787..93f8765672bc 100644
--- a/benchmarks/src/test/scala/io/joern/benchmarks/testfixtures/BenchmarkFixture.scala
+++ b/benchmarks/src/test/scala/io/joern/benchmarks/testfixtures/BenchmarkFixture.scala
@@ -6,6 +6,7 @@ import io.joern.dataflowengineoss.language._
 import io.joern.dataflowengineoss.layers.dataflows.{OssDataFlow, OssDataFlowOptions}
 import io.joern.dataflowengineoss.queryengine.EngineContext
 import io.joern.dataflowengineoss.semanticsloader.{Parser, Semantics}
+import io.joern.dataflowengineoss.DefaultSemantics
 import io.joern.javasrc2cpg.{JavaSrc2Cpg, Config => JavaSrcConfig}
 import io.joern.jimple2cpg.{Jimple2Cpg, Config => JimpleConfig}
 import io.shiftleft.codepropertygraph.generated.nodes.CfgNode
@@ -29,9 +30,8 @@ abstract class BenchmarkFixture(
     with Matchers
     with BeforeAndAfterAll {
 
-  val semanticsFile: String            = ProjectRoot.relativise("benchmarks/src/test/resources/default.semantics")
-  lazy val defaultSemantics: Semantics = Semantics.fromList(new Parser().parseFile(semanticsFile))
-  implicit val resolver: ICallResolver = NoResolve
+  lazy val defaultSemantics: Semantics           = DefaultSemantics()
+  implicit val resolver: ICallResolver           = NoResolve
   implicit lazy val engineContext: EngineContext = EngineContext(defaultSemantics)
 
   private lazy val targetFiles = getListOfFiles(ProjectRoot.relativise(constructTargetFilePath))
@@ -150,8 +150,7 @@ class BenchmarkCpgContext {
     }
     val context                          = new LayerCreatorContext(cpg.get)
     val options                          = new OssDataFlowOptions()
-    val semanticsFile: String            = ProjectRoot.relativise("benchmarks/src/test/resources/default.semantics")
-    lazy val defaultSemantics: Semantics = Semantics.fromList(new Parser().parseFile(semanticsFile))
+    lazy val defaultSemantics: Semantics = DefaultSemantics()
     new OssDataFlow(options)(defaultSemantics).run(context)
     cpg.get
   }
diff --git a/dataflowengineoss/src/test/resources/default.semantics b/dataflowengineoss/src/test/resources/default.semantics
deleted file mode 120000
index 335ad86700cc..000000000000
--- a/dataflowengineoss/src/test/resources/default.semantics
+++ /dev/null
@@ -1,1 +0,0 @@
-../../../../joern-cli/src/main/resources/default.semantics
\ No newline at end of file
diff --git a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/dataflow/DataFlowTests.scala b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/dataflow/DataFlowTests.scala
index 4cc61e6167f6..308eabe4ba6d 100644
--- a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/dataflow/DataFlowTests.scala
+++ b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/dataflow/DataFlowTests.scala
@@ -2160,4 +2160,41 @@ class DataFlowTestsWithCallDepth extends DataFlowCodeToCpgSuite {
       sink.reachableBy(src).method.name.toSet shouldBe Set("foo", "bar")
     }
   }
+
+  "DataFlowTest70" should {
+    val cpg = code("""
+        |int foo() {
+        |  int v1, v2 = 0;
+        |  if((v1 = 1, v2 == 2) || v2 <= 3) return v2;
+        |  return 0;
+        |}
+        |""".stripMargin)
+    "find flows" in {
+      val source = cpg.identifier("v2").l
+      val sink   = cpg.method("foo").methodReturn.l
+      sink.reachableByFlows(source).l.map(flowToResultPairs).toSet shouldBe Set(
+        List(
+          ("v2 == 2", Some(4)),
+          ("(v1 = 1, v2 == 2)", Some(4)),
+          ("(v1 = 1, v2 == 2) || v2 <= 3", Some(4)),
+          ("int", Some(2))
+        ),
+        List(
+          ("v2 = 0", Some(3)),
+          ("v2 == 2", Some(4)),
+          ("(v1 = 1, v2 == 2)", Some(4)),
+          ("(v1 = 1, v2 == 2) || v2 <= 3", Some(4)),
+          ("int", Some(2))
+        ),
+        List(
+          ("v2 <= 3", Some(4)),
+          ("(v1 = 1, v2 == 2)", Some(4)),
+          ("(v1 = 1, v2 == 2) || v2 <= 3", Some(4)),
+          ("int", Some(2))
+        ),
+        List(("return v2;", Some(4)), ("int", Some(2)))
+      )
+    }
+  }
+
 }
diff --git a/joern-cli/frontends/ghidra2cpg/src/test/scala/io/joern/ghidra2cpg/querying/x86/DataFlowTests.scala b/joern-cli/frontends/ghidra2cpg/src/test/scala/io/joern/ghidra2cpg/querying/x86/DataFlowTests.scala
index 62689843c8ee..ec369e163436 100644
--- a/joern-cli/frontends/ghidra2cpg/src/test/scala/io/joern/ghidra2cpg/querying/x86/DataFlowTests.scala
+++ b/joern-cli/frontends/ghidra2cpg/src/test/scala/io/joern/ghidra2cpg/querying/x86/DataFlowTests.scala
@@ -5,11 +5,11 @@ import io.shiftleft.codepropertygraph.Cpg
 import io.joern.dataflowengineoss.language._
 import io.joern.dataflowengineoss.layers.dataflows.{OssDataFlow, OssDataFlowOptions}
 import io.joern.dataflowengineoss.queryengine.EngineContext
-import io.joern.dataflowengineoss.semanticsloader.{Parser, Semantics}
+import io.joern.dataflowengineoss.semanticsloader.Semantics
+import io.joern.dataflowengineoss.DefaultSemantics
 import io.joern.x2cpg.layers.{Base, CallGraph, ControlFlow, TypeRelations}
-import io.shiftleft.semanticcpg.language.{ICallResolver, _}
+import io.shiftleft.semanticcpg.language._
 import io.shiftleft.semanticcpg.layers._
-import io.shiftleft.utils.ProjectRoot
 
 class DataFlowTests extends GhidraBinToCpgSuite {
 
@@ -30,9 +30,7 @@ class DataFlowTests extends GhidraBinToCpgSuite {
   }
 
   "The data flow should contain " in {
-    implicit val resolver: ICallResolver = NoResolve
-    val semanticsFilename    = ProjectRoot.relativise("dataflowengineoss/src/test/resources/default.semantics")
-    val semantics: Semantics = Semantics.fromList(new Parser().parseFile(semanticsFilename))
+    val semantics: Semantics            = DefaultSemantics()
     implicit val context: EngineContext = EngineContext(semantics)
 
     def source = cpg.method.name("dataflow").call.code("MOV EDX,EAX").argument.code("EAX")
diff --git a/querydb/src/test/resources/default.semantics b/querydb/src/test/resources/default.semantics
deleted file mode 120000
index 335ad86700cc..000000000000
--- a/querydb/src/test/resources/default.semantics
+++ /dev/null
@@ -1,1 +0,0 @@
-../../../../joern-cli/src/main/resources/default.semantics
\ No newline at end of file