Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[java] Resolve External Non-JDK Types with Type Arguments #4527

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
@@ -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}
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*

Expand Down Expand Up @@ -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<String> stringList = new ArrayList<>();
| var stringIntMap = new HashMap<String, Integer>();
| }
|}
|
|""".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<String> stringList = new ArrayList<>();
| var stringIntMap = new HashMap<String, Integer>();
| }
|}
|
|""".stripMargin)
.withConfig(Config().withKeepTypeArguments(true))

cpg.identifier("stringList").typeFullName.head shouldBe "java.util.List<java.lang.String>"
cpg.identifier("stringIntMap").typeFullName.head shouldBe "java.util.HashMap<java.lang.String,java.lang.Integer>"
}

"show the fully qualified type arguments for `Map`" in {
cpg.identifier("stringIntMap").typeFullName.head shouldBe "java.util.HashMap<java.lang.String,java.lang.Integer>"
"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<String> kafkaProducer = new FlinkKafkaProducer<String>("kafka-topic", schema, kafkaProps);
| }
|}
|""".stripMargin).withConfig(Config().withKeepTypeArguments(true))

cpg.call
.codeExact("new FlinkKafkaProducer<String>(\"kafka-topic\", schema, kafkaProps)")
.filterNot(_.name == Operators.alloc)
.map(_.methodFullName)
.head shouldBe "org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer<java.lang.String>.<init>:<unresolvedSignature>(3)"
}

}

}
Loading