From ba0b6c8f6f4d7e9cd27ea872080c45d6bc7e5438 Mon Sep 17 00:00:00 2001 From: Khemraj Rathore Date: Fri, 2 Jun 2023 20:58:37 +0530 Subject: [PATCH 1/2] fix - correct typeFullName for member with generic type, issue 105 --- .../joern/javasrc2cpg/passes/AstCreator.scala | 12 ++++++++++- .../javasrc2cpg/querying/MemberTests.scala | 21 +++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/passes/AstCreator.scala b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/passes/AstCreator.scala index 8b60aa740e9a..0de377b75424 100644 --- a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/passes/AstCreator.scala +++ b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/passes/AstCreator.scala @@ -726,10 +726,20 @@ class AstCreator(filename: String, javaParserAst: CompilationUnit, global: Globa private def astForFieldVariable(v: VariableDeclarator, fieldDeclaration: FieldDeclaration): Ast = { // TODO: Should be able to find expected type here val annotations = fieldDeclaration.getAnnotations + + // variable can be declared with generic type, so we need to get rid of the <> part of it + // Ex - private Consumer consumer; + // From Consumer we need to get to Consumer so splitting it by '<' + val typeFullName = typeInfoCalc .fullName(v.getType) - .orElse(scopeStack.lookupVariableType(v.getTypeAsString, wildcardFallback = true)) + .orElse( + scopeStack.lookupVariableType( + v.getTypeAsString.split("<").headOption.getOrElse(v.getTypeAsString), + wildcardFallback = true + ) + ) .getOrElse(s"${Defines.UnresolvedNamespace}.${v.getTypeAsString}") val name = v.getName.toString val node = memberNode(v, name, s"$typeFullName $name", typeFullName) diff --git a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/MemberTests.scala b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/MemberTests.scala index 22132a1ac9cc..b5baf8dd5b06 100644 --- a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/MemberTests.scala +++ b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/MemberTests.scala @@ -20,6 +20,27 @@ class NewMemberTests extends JavaSrcCode2CpgFixture { cpg.member.name("x").astChildren.size shouldBe 0 } + "member with generic class" should { + val cpg = code(""" + |import org.apache.kafka.clients.consumer.Consumer; + |public class CountryPopulationConsumer { + | + | private Consumer consumer; + | + | void foo() { + | consumer.poll(1000); + | } + |}""".stripMargin) + + "have a resolved typeFullName" in { + cpg.member.name("consumer").typeFullName.head shouldBe "org.apache.kafka.clients.consumer.Consumer" + } + + "have a resolved package name in methodFullName" in { + cpg.call("poll").methodFullName.head.split(":").head shouldBe "org.apache.kafka.clients.consumer.Consumer.poll" + } + } + "enum entries with anonymous classes should not result in subtrees to the member node" in { val cpg = code(""" |enum Foo { From 88d1aeb8a5012975531bc589a872c48f85a82e46 Mon Sep 17 00:00:00 2001 From: Khemraj Rathore Date: Sat, 3 Jun 2023 16:00:02 +0530 Subject: [PATCH 2/2] PR comments for Issue no 105 --- .../joern/javasrc2cpg/passes/AstCreator.scala | 38 ++++++++++++------- .../javasrc2cpg/querying/MemberTests.scala | 12 +++++- .../main/scala/io/joern/x2cpg/Defines.scala | 2 + 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/passes/AstCreator.scala b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/passes/AstCreator.scala index 0de377b75424..889511e6f0b0 100644 --- a/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/passes/AstCreator.scala +++ b/joern-cli/frontends/javasrc2cpg/src/main/scala/io/joern/javasrc2cpg/passes/AstCreator.scala @@ -727,20 +727,32 @@ class AstCreator(filename: String, javaParserAst: CompilationUnit, global: Globa // TODO: Should be able to find expected type here val annotations = fieldDeclaration.getAnnotations - // variable can be declared with generic type, so we need to get rid of the <> part of it + // variable can be declared with generic type, so we need to get rid of the <> part of it to get the package information + // and append the <> when forming the typeFullName again // Ex - private Consumer consumer; - // From Consumer we need to get to Consumer so splitting it by '<' - - val typeFullName = - typeInfoCalc - .fullName(v.getType) - .orElse( - scopeStack.lookupVariableType( - v.getTypeAsString.split("<").headOption.getOrElse(v.getTypeAsString), - wildcardFallback = true - ) - ) - .getOrElse(s"${Defines.UnresolvedNamespace}.${v.getTypeAsString}") + // From Consumer we need to get to Consumer so splitting it by '<' and then combining with '<' to + // form typeFullName as Consumer + + val typeFullNameWithoutGenericSplit = typeInfoCalc + .fullName(v.getType) + .orElse(scopeStack.lookupVariableType(v.getTypeAsString, wildcardFallback = true)) + .getOrElse(s"${Defines.UnresolvedNamespace}.${v.getTypeAsString}") + val typeFullName = { + // Check if the typeFullName is unresolved and if it has generic information to resolve the typeFullName + if ( + typeFullNameWithoutGenericSplit + .contains(Defines.UnresolvedNamespace) && v.getTypeAsString.contains(Defines.LeftAngularBracket) + ) { + val splitByLeftAngular = v.getTypeAsString.split(Defines.LeftAngularBracket) + scopeStack.lookupVariableType(splitByLeftAngular.head, wildcardFallback = true) match { + case Some(fullName) => + fullName + splitByLeftAngular + .slice(1, splitByLeftAngular.size) + .mkString(Defines.LeftAngularBracket, Defines.LeftAngularBracket, "") + case None => typeFullNameWithoutGenericSplit + } + } else typeFullNameWithoutGenericSplit + } val name = v.getName.toString val node = memberNode(v, name, s"$typeFullName $name", typeFullName) val memberAst = Ast(node) diff --git a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/MemberTests.scala b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/MemberTests.scala index b5baf8dd5b06..b1327386e8d2 100644 --- a/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/MemberTests.scala +++ b/joern-cli/frontends/javasrc2cpg/src/test/scala/io/joern/javasrc2cpg/querying/MemberTests.scala @@ -33,11 +33,19 @@ class NewMemberTests extends JavaSrcCode2CpgFixture { |}""".stripMargin) "have a resolved typeFullName" in { - cpg.member.name("consumer").typeFullName.head shouldBe "org.apache.kafka.clients.consumer.Consumer" + cpg.member + .name("consumer") + .typeFullName + .head shouldBe "org.apache.kafka.clients.consumer.Consumer" } "have a resolved package name in methodFullName" in { - cpg.call("poll").methodFullName.head.split(":").head shouldBe "org.apache.kafka.clients.consumer.Consumer.poll" + cpg + .call("poll") + .methodFullName + .head + .split(":") + .head shouldBe "org.apache.kafka.clients.consumer.Consumer.poll" } } diff --git a/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/Defines.scala b/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/Defines.scala index 34e8d3589706..301cd82b55ad 100644 --- a/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/Defines.scala +++ b/joern-cli/frontends/x2cpg/src/main/scala/io/joern/x2cpg/Defines.scala @@ -25,4 +25,6 @@ object Defines { // In some languages like Javascript dynamic calls do not provide any statically known // method/function interface information. In those cases please use this value. val DynamicCallUnknownFullName = "" + + val LeftAngularBracket = "<" }