diff --git a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/io/dotgenerator/DotAstGeneratorTests.scala b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/io/dotgenerator/DotAstGeneratorTests.scala
index e504e951950e..15399533a725 100644
--- a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/io/dotgenerator/DotAstGeneratorTests.scala
+++ b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/io/dotgenerator/DotAstGeneratorTests.scala
@@ -32,7 +32,7 @@ class DotAstGeneratorTests extends CCodeToCpgSuite {
inside(cpg.method.name("my_func").dotAst.l) { case List(x) =>
x should (
startWith("digraph \"my_func\"") and
- include("""[label = <(CONTROL_STRUCTURE,if (y > 42),if (y > 42))5> ]""") and
+ include("""[label = if (y > 42)> ]""") and
endWith("}\n")
)
}
@@ -52,7 +52,7 @@ class DotAstGeneratorTests extends CCodeToCpgSuite {
"allow plotting sub trees of methods" in {
inside(cpg.method.ast.isControlStructure.code(".*y > 42.*").dotAst.l) { case List(x, _) =>
- x should (include("y > 42") and include("IDENTIFIER,y") and not include "x * 2")
+ x should (include("y > 42") and include("IDENTIFIER, 5
y") and not include "x * 2")
}
}
@@ -60,10 +60,8 @@ class DotAstGeneratorTests extends CCodeToCpgSuite {
inside(cpg.method.name("lemon").dotAst.l) { case List(x) =>
x should (
startWith("digraph \"lemon\"") and
- include("""[label = <(goog,goog("\"yes\""))18> ]""") and
- include(
- """[label = <(LITERAL,"\"yes\"",goog("\"yes\""))18> ]"""
- ) and
+ include("""[label = goog
goog("\"yes\"")> ]""") and
+ include("""[label = "\"yes\""> ]""") and
endWith("}\n")
)
}
diff --git a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/io/dotgenerator/DotCdgGeneratorTests.scala b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/io/dotgenerator/DotCdgGeneratorTests.scala
index 08778bd8ec6e..954e7035e676 100644
--- a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/io/dotgenerator/DotCdgGeneratorTests.scala
+++ b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/io/dotgenerator/DotCdgGeneratorTests.scala
@@ -19,9 +19,9 @@ class DotCdgGeneratorTests extends DataFlowCodeToCpgSuite {
inside(cpg.method.name("foo").dotCdg.l) { case List(x) =>
x should (
startWith("digraph \"foo\"") and
- include("""[label = <(<operator>.greaterThan,x > 8)3> ]""") and
- include("""[label = <(<operator>.assignment,z = a(x))4> ]""") and
- include("""[label = <(a,a(x))4> ]""") and
+ include("""[label = <operator>.greaterThan
x > 8> ]""") and
+ include("""[label = <operator>.assignment
z = a(x)> ]""") and
+ include("""[label = a
a(x)> ]""") and
endWith("}\n")
)
val lines = x.split("\n")
@@ -46,9 +46,9 @@ class DotCdgGeneratorTests extends DataFlowCodeToCpgSuite {
inside(cpg.method.name("foo").dotCdg.l) { case List(x) =>
x should (
startWith("digraph \"foo\"") and
- include("""[label = <(<operator>.greaterThan,x > 8)3> ]""") and
- include("""[label = <(<operator>.assignment,z = a(x))4> ]""") and
- include("""[label = <(a,a(x))4> ]""") and
+ include("""[label = <operator>.greaterThan
x > 8> ]""") and
+ include("""[label = <operator>.assignment
z = a(x)> ]""") and
+ include("""[label = a
a(x)> ]""") and
endWith("}\n")
)
val lines = x.split("\n")
diff --git a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/io/dotgenerator/DotCfgGeneratorTests.scala b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/io/dotgenerator/DotCfgGeneratorTests.scala
index 237944ea4d64..59b06936439c 100644
--- a/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/io/dotgenerator/DotCfgGeneratorTests.scala
+++ b/joern-cli/frontends/c2cpg/src/test/scala/io/joern/c2cpg/io/dotgenerator/DotCfgGeneratorTests.scala
@@ -21,7 +21,7 @@ class DotCfgGeneratorTests extends CCodeToCpgSuite {
inside(cpg.method.name("main").dotCfg.l) { case List(dotStr) =>
dotStr should (
startWith("digraph \"main\" {") and
- include("(<operator>.assignment,i = 0)") and
+ include("<operator>.assignment
i = 0") and
endWith("}\n")
)
}
diff --git a/semanticcpg/src/main/scala/io/shiftleft/semanticcpg/dotgenerator/DotSerializer.scala b/semanticcpg/src/main/scala/io/shiftleft/semanticcpg/dotgenerator/DotSerializer.scala
index 88029763adc2..ad5a14041616 100644
--- a/semanticcpg/src/main/scala/io/shiftleft/semanticcpg/dotgenerator/DotSerializer.scala
+++ b/semanticcpg/src/main/scala/io/shiftleft/semanticcpg/dotgenerator/DotSerializer.scala
@@ -4,10 +4,11 @@ import io.shiftleft.codepropertygraph.generated.PropertyNames
import io.shiftleft.codepropertygraph.generated.nodes._
import io.shiftleft.semanticcpg.language._
import io.shiftleft.semanticcpg.utils.MemberAccess
+import org.apache.commons.lang.StringUtils
-import java.util.Optional
import scala.collection.immutable.HashMap
import scala.collection.mutable
+import scala.jdk.OptionConverters.RichOptional
import scala.language.postfixOps
object DotSerializer {
@@ -36,6 +37,7 @@ object DotSerializer {
case Some(r) => namedGraphBegin(r)
case None => defaultGraphBegin()
}
+ sb.append("node [shape=\"rect\"];\n")
val nodeStrings = graph.vertices.map(nodeToDot)
val edgeStrings = graph.edges.map(e => edgeToDot(e, withEdgeTypes))
val subgraphStrings = graph.subgraph.zipWithIndex.map { case ((subgraph, nodes), idx) =>
@@ -61,23 +63,29 @@ object DotSerializer {
}
private def stringRepr(vertex: StoredNode): String = {
- val maybeLineNo: Optional[AnyRef] = vertex.propertyOption(PropertyNames.LINE_NUMBER)
- escape(vertex match {
- case call: Call => (call.name, call.code).toString
- case expr: Expression => (expr.label, expr.code, toCfgNode(expr).code).toString
- case method: Method => (method.label, method.name).toString
- case ret: MethodReturn => (ret.label, ret.typeFullName).toString
- case param: MethodParameterIn => ("PARAM", param.code).toString
- case local: Local => (local.label, s"${local.code}: ${local.typeFullName}").toString
- case target: JumpTarget => (target.label, target.name).toString
- case modifier: Modifier => (modifier.label, modifier.modifierType).toString()
- case annoAssign: AnnotationParameterAssign => (annoAssign.label, annoAssign.code).toString()
- case annoParam: AnnotationParameter => (annoParam.label, annoParam.code).toString()
- case typ: Type => (typ.label, typ.name).toString()
- case typeDecl: TypeDecl => (typeDecl.label, typeDecl.name).toString()
- case member: Member => (member.label, member.name).toString()
- case _ => ""
- }) + (if (maybeLineNo.isPresent) s"${maybeLineNo.get()}" else "")
+ val lineOpt = vertex.propertyOption(PropertyNames.LINE_NUMBER).toScala.map(_.toString)
+ val list = (vertex match {
+ case call: Call => List(call.label, call.name, call.code)
+ case identifier: Identifier => List(identifier.label, identifier.name)
+ case literal: Literal => List(literal.label, literal.code)
+ case local: Local => List(local.label, local.name, local.typeFullName)
+ case method: Method => List(method.label, method.name)
+ case expr: Expression => List(expr.label, expr.code, toCfgNode(expr).code)
+ case ret: MethodReturn => List(ret.label)
+ case typeDecl: TypeDecl => List(typeDecl.label, typeDecl.name)
+ case param: MethodParameterIn => List("PARAM", param.name)
+ case target: JumpTarget => List(target.label, target.name)
+ case modifier: Modifier => List(modifier.label, modifier.modifierType)
+ case typ: Type => List(typ.label, typ.name)
+ case dec: DeclarationBase => List(dec.label, dec.name)
+ case others: AstNode => List(others.label, others.code)
+ }).map(StringUtils.normalizeSpace)
+ .map(escape)
+ (lineOpt match {
+ case Some(line) => s"${list.head}, $line" :: list.tail
+ case None => list
+ }).distinct // expr.code, toCfgNode(expr).code could be the same
+ .mkString("
")
}
private def toCfgNode(node: StoredNode): CfgNode = {