diff --git a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/AstCreator.scala b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/AstCreator.scala index 9c075c5802c8..750036276fab 100644 --- a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/AstCreator.scala +++ b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/AstCreator.scala @@ -85,7 +85,7 @@ class AstCreator( fileContent: Option[String], global: Global, val symbolSolver: JavaSymbolSolver, - keepTypeArguments: Boolean + protected val keepTypeArguments: Boolean )(implicit val withSchemaValidation: ValidationMode) extends AstCreatorBase(filename) with AstNodeBuilder[Node, AstCreator] diff --git a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForVarDeclAndAssignsCreator.scala b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForVarDeclAndAssignsCreator.scala index 37e0b7631597..827e9d1862ab 100644 --- a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForVarDeclAndAssignsCreator.scala +++ b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/astcreation/expressions/AstForVarDeclAndAssignsCreator.scala @@ -1,6 +1,7 @@ package io.joern.javasrc2cpg.astcreation.expressions import com.github.javaparser.ast.Node +import com.github.javaparser.ast.`type`.ClassOrInterfaceType import com.github.javaparser.ast.body.VariableDeclarator import com.github.javaparser.ast.expr.AssignExpr.Operator import com.github.javaparser.ast.expr.{AssignExpr, Expression, ObjectCreationExpr, VariableDeclarationExpr} @@ -104,11 +105,26 @@ trait AstForVarDeclAndAssignsCreator { this: AstCreator => } def astsForVariableDeclarator(variableDeclarator: VariableDeclarator, originNode: Node): Seq[Ast] = { + + val variableDeclaratorType = variableDeclarator.getType + // If generics are in the type name, we may be unable to resolve the type + val (variableTypeString, maybeTypeArgs) = variableDeclaratorType match { + case typ: ClassOrInterfaceType => + val typeParams = typ.getTypeArguments.toScala.map(_.asScala.flatMap(typeInfoCalc.fullName)) + (typ.getName.asString(), typeParams) + case _ => (variableDeclarator.getTypeAsString, None) + } + val typeFullName = tryWithSafeStackOverflow( scope - .lookupType(variableDeclarator.getTypeAsString, includeWildcards = false) + .lookupType(variableTypeString, includeWildcards = false) .orElse(typeInfoCalc.fullName(variableDeclarator.getType)) - ).toOption.flatten + ).toOption.flatten.map { typ => + maybeTypeArgs match { + case Some(typeArgs) if keepTypeArguments => s"$typ<${typeArgs.mkString(",")}>" + case _ => typ + } + } val (correspondingNode, localAst): (NewVariableNode, Option[Ast]) = scope.lookupVariable(variableDeclarator.getNameAsString).variableNode.map((_, None)).getOrElse { diff --git a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/VarDeclTests.scala b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/VarDeclTests.scala index 85cf07d447e5..b058a3db581a 100644 --- a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/VarDeclTests.scala +++ b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/VarDeclTests.scala @@ -2,6 +2,7 @@ package io.joern.javasrc2cpg.querying import io.joern.javasrc2cpg.Config import io.joern.javasrc2cpg.testfixtures.JavaSrcCode2CpgFixture +import io.shiftleft.codepropertygraph.generated.Operators import io.shiftleft.codepropertygraph.generated.nodes.{Block, Call, Local} import io.shiftleft.semanticcpg.language.* @@ -158,29 +159,53 @@ class VarDeclTests extends JavaSrcCode2CpgFixture { } "generics with 'keep type arguments' config" should { - val cpg = code(""" - |import java.util.ArrayList; - |import java.util.List; - |import java.util.HashMap; - | - |public class Main { - | public static void main(String[] args) { - | // Create a List of Strings - | List stringList = new ArrayList<>(); - | var stringIntMap = new HashMap(); - | } - |} - | - |""".stripMargin) - .withConfig(Config().withKeepTypeArguments(true)) - "show the fully qualified type arguments for `List`" in { + "show the fully qualified type arguments for stdlib `List and `Map` objects" in { + val cpg = code(""" + |import java.util.ArrayList; + |import java.util.List; + |import java.util.HashMap; + | + |public class Main { + | public static void main(String[] args) { + | // Create a List of Strings + | List stringList = new ArrayList<>(); + | var stringIntMap = new HashMap(); + | } + |} + | + |""".stripMargin) + .withConfig(Config().withKeepTypeArguments(true)) + cpg.identifier("stringList").typeFullName.head shouldBe "java.util.List" + cpg.identifier("stringIntMap").typeFullName.head shouldBe "java.util.HashMap" } - "show the fully qualified type arguments for `Map`" in { - cpg.identifier("stringIntMap").typeFullName.head shouldBe "java.util.HashMap" + "show the fully qualified names of external types" in { + val cpg = code(""" + |import org.apache.flink.streaming.api.datastream.DataStream; + |import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; + |import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer; + |import org.apache.flink.streaming.util.serialization.SimpleStringSchema; + | + |import java.util.Properties; + | + |public class FlinkKafkaExample { + | public static void main() throws Exception { + | Properties kafkaProps = new Properties(); + | SimpleStringSchema schema = new SimpleStringSchema(); + | FlinkKafkaProducer kafkaProducer = new FlinkKafkaProducer("kafka-topic", schema, kafkaProps); + | } + |} + |""".stripMargin).withConfig(Config().withKeepTypeArguments(true)) + + cpg.call + .codeExact("new FlinkKafkaProducer(\"kafka-topic\", schema, kafkaProps)") + .filterNot(_.name == Operators.alloc) + .map(_.methodFullName) + .head shouldBe "org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer.:(3)" } } + }